0

With the help of https://angular.io/guide/dynamic-form, i am making a dynamic form where i am in the need to display two fields at first.

  new TextboxQuestion({
    key: 'firstName',
    label: 'First name',
    value: '',
    required: true,
    order: 1
  }),

  new TextboxQuestion({
    key: 'lastName',
    label: 'Last name',
    value: '',
    required: true,
    order: 2
  }),

These two fields needs to be at first on loading.

After this i will have two buttons as add and remove.

  <button (click)="addNew()"> Add </button> &nbsp;&nbsp;&nbsp;
  <button (click)="removeNew()"> Remove </button> <br><br>

By clicking add, i need to display the next two fields (the following fields),

  new TextboxQuestion({
    key: 'emailAddress',
    label: 'Email',
    type: 'email',
    order: 3
  }),

  new DropdownQuestion({
    key: 'brave',
    label: 'Bravery Rating',
    options: [
      {key: 'solid',  value: 'Solid'},
      {key: 'great',  value: 'Great'},
      {key: 'good',   value: 'Good'},
      {key: 'unproven', value: 'Unproven'}
    ],
    order: 4
  })

Order 1 & 2 in the initial state and after clicking add the next two order 3 & 4 needs to get displayed.

KIndly help me to achieve the result of adding child fields on click add button.

The working stackblitz which displays all at once, https://stackblitz.com/edit/angular-x4a5b6

Maniraj Murugan
  • 8,868
  • 20
  • 67
  • 116

2 Answers2

4

Implementing dynamic form with formArray.

Well, the things are more complex. I make a stackblik, see demo

I'll try to explain how extends the https://angular.io/guide/dynamic-form to allow Form Array.

The first we need make is create a new type of question, a questionArray

import { QuestionBase } from './question-base';

export class ArrayQuestion extends QuestionBase<string> {
  controlType = 'array';
  type: any;

  constructor(options: {} = {}) {
    super(options);
  }
}

We must change question base to add a new property "children"

export class QuestionBase<T> {
  value: T;
  ...
  children:any[];

  constructor(options: {
      value?: T,
      ...
      children?:any
    } = {}) {
    this.value = options.value;
    ...
    this.children=options.children || null;
  }
}

Add change the question-control service to allow manage formArrays

toFormGroup(questions: QuestionBase<any>[]) {
    let group: any = {};

    questions.forEach(question => {
      //If the control type is "array" we create a FormArray
      if (question.controlType=="array") {
         group[question.key]=new FormArray([]);
      }
      else {
        group[question.key] = question.required ? new FormControl(question.value || '', Validators.required)
          : new FormControl(question.value || '');
      }
    });
    return new FormGroup(group);
  }

Well We transform the dynamic-form.component to show a FormArray

<div *ngFor="let question of questions" class="form-row">
    <ng-container *ngIf="question.children">
        <div [formArrayName]="question.key">
            <div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i">
                <div *ngFor="let item of question.children">
                    <app-question [question]="item" [form]="form.get(question.key).at(i)"></app-question>
                </div>
            </div>
        </div>
    </ng-container>
    <ng-container *ngIf="!question.children">
        <app-question [question]="question" [form]="form"></app-question>

    </ng-container>
</div>

And just it doit. Well, how increment and decrement the formArrays? We have two buttons

  <button (click)="addControls('myArray')"> Add </button>
  <button (click)="removeControls('myArray')"> Remove </button> <br><br>

And two functions addControls and removeControls

  addControls(control: string) {
    let question: any = this.questions.find(q => q.key == control);
    let children = question ? question.children : null;
    if (children)
      (this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
  }
  removeControls(control: string){
    let array=this.form.get(control) as FormArray;
    array.removeAt(array.length-1);
  }

Update I forgot add and example of questions:

let questions: QuestionBase<any>[] = [

      new TextboxQuestion({
        key: 'firstName',
        label: 'First name',
        value: '',
        required: true,
        order: 1
      }),

      new TextboxQuestion({
        key: 'lastName',
        label: 'Last name',
        value: '',
        required: true,
        order: 2
      }),
      new ArrayQuestion({
        key: 'myArray',
        value: '',
        order: 3,
        children: [
          new TextboxQuestion({
            key: 'emailAddress',
            label: 'Email',
            type: 'email',
            order: 3
          }),
          new DropdownQuestion({
            key: 'brave',
            label: 'Bravery Rating',
            options: [
              { key: 'solid', value: 'Solid' },
              { key: 'great', value: 'Great' },
              { key: 'good', value: 'Good' },
              { key: 'unproven', value: 'Unproven' }
            ],
            order: 4
          })
        ]
      })
    ];
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Eliseo Thanks a lot.. I hope everything will work fine for me after seeing the stackblitz.. If any doubts will revert back to you.. – Maniraj Murugan Nov 01 '18 at 14:58
  • Eliseo, I have posted another question https://stackoverflow.com/questions/53113962/input-fields-data-as-json-in-angular-form to load the data from JSON for the inputs, Its your solution only but the data alone i need to give as JSON for each inputs.. If you refer the question you will get good understanding.. Can you help me out there?? – Maniraj Murugan Nov 02 '18 at 07:17
  • Hi Eliseo, Here is the another new question for you https://stackoverflow.com/questions/53220425/patch-the-values-in-angular-form/53220463#53220463 for patching of data to those form elements during edit.. – Maniraj Murugan Nov 09 '18 at 05:57
1

The only thing that you need is "paginate" your questions using slice and a variable "page"

That is:

  //add a variable "page" in your dinamic-form.component.ts
  page:number=0;

and

  //in your dinamic-form.component.html, change the *ngFor like
  <form (ngSubmit)="onSubmit()" [formGroup]="form">
    <div *ngFor="let question of questions.slice(page*2,(page+1)*2)" class="form-row">
      <app-question [question]="question" [form]="form"></app-question>
    </div>
   ....
  </form>

Then, your button add can be like

  <button (click)="page=page+1"> Add </button>
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Thanks for your help.. Kindly provide stackblitz that will help me out.. I have implemented your code but i couldn't get any inputs https://stackblitz.com/edit/angular-x4a5b6 – Maniraj Murugan Nov 01 '18 at 12:49
  • You forget add the property "page". In your dinamic-form.component.ts, you need add **page:number=0** just after your instruction: payLoad='' – Eliseo Nov 01 '18 at 13:20
  • If i click add then the old input textboxes are getting hidden and the next two is coming.. I am in the need to display the first two at initial and on click add, the next two should come down and again click the add the added two textboxes (order 3 and 4) needs to get binded on each click over add.. I need to show both order (1 & 2) on click the add button.. I hope you are in half way to help me.. Please Eliseo i request you to help me out which would be much more appreciable.. – Maniraj Murugan Nov 01 '18 at 13:22
  • just replace the slice by slice(0,(page+1)*2). The idea is that you not show all the "questions" just you need in each moment. I supouse you want to show only two in each time – Eliseo Nov 01 '18 at 13:25
  • Just last one question for you in last.. On click the add again i should get append the order ( 3 & 4) on each click over add button.. You was the only one helped me a lot in it.. Kindly answer for this one alone.. I will surely accept your answer please.. – Maniraj Murugan Nov 01 '18 at 13:29
  • Eliseo there?? Hope you understand my requirement well, On i am having the form in which the first two boxes will be static always on filling the first two there will be a add button on click that the next two will be displayed but after each add click that two needs to get append one by one every time (this will be as array of objects).. Atlast while saving the form everything needs to get saved.. Kindly apologize me for asking again and again as you are expert in angular you can help me out.. Stucked for a long in it.. – Maniraj Murugan Nov 01 '18 at 14:48
  • @Mani, The things are more complex. I think you want create a form Array. I create a new answer. I hope this help – Eliseo Nov 01 '18 at 14:54
  • Eliseo there are no words to describe a thanks for you.. You are great.. I really want to give you special thanks for spending your precious and valuable time to give a solution for me.. Thanks a lot.. – Maniraj Murugan Nov 01 '18 at 14:58
  • You're wellcome, It was a pleasure to be able to help you – Eliseo Nov 01 '18 at 15:01