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

2 Answers 2

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
          })
        ]
      })
    ];
Sign up to request clarification or add additional context in comments.

3 Comments

Eliseo Thanks a lot.. I hope everything will work fine for me after seeing the stackblitz.. If any doubts will revert back to you..
Eliseo, I have posted another question stackoverflow.com/questions/53113962/… 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??
Hi Eliseo, Here is the another new question for you stackoverflow.com/questions/53220425/… for patching of data to those form elements during edit..
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>

9 Comments

Thanks for your help.. Kindly provide stackblitz that will help me out.. I have implemented your code but i couldn't get any inputs stackblitz.com/edit/angular-x4a5b6
You forget add the property "page". In your dinamic-form.component.ts, you need add page:number=0 just after your instruction: payLoad=''
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..
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
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..
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.