Home > Software design >  Dynamically add fields in Dynamic Form Array
Dynamically add fields in Dynamic Form Array

Time:01-05

I have a form which has a form array like

    this.myForm = this.fb.group({
      arr: this.fb.array([this.createItem()])
    })

I can dynamically add fields to the array as

    createItem() {
    return this.fb.group({
      name: [''],
      pay: [''],
      type: []
    })
  

    addItem() {
    this.arr = this.myForm.get('arr') as FormArray;
    this.arr.push(this.createItem());}

the type control is a dropdown with values as 


    
  types = [
    { id: 10, name: 'A' },
    { id: 20, name: 'B' },
    { id: 30, name: 'C' },
  ];

My Question is if the user selects Type 'A' for a group I want to add two more fields 'phone' and 'address' and for 'B' and 'C', I want the particular group to not add fields.

For Example if user selects 'A' for first entry of form Array I will add controls 'phone' and 'address' to the 'this.fb.group' and if user changes it to 'B' or 'C' for that row in array I will remove these two fields.

I would like to subscribe to the type formcontrol check type and add controls but how to do it? The stackblitz is https://stackblitz.com/edit/angular6-dynamic-form-array-fmyxvz?file=src/app/app.component.html

CodePudding user response:

You can subscribe to type FormControl valueChanges and based on its value, add or remove FormControls using addControl and removeControl methods.

Below is the code snippet that might help you get going:

    createItem() {
      const newFormGroup = this.fb.group({
        name: [''],
        pay: [''],
        type: [],
      });
      newFormGroup.get('type').valueChanges.subscribe((value) => {  // should be unsubscribed later to avoid memory leaks
        if (value == 10) {  // 10 is the id for A
          this.addAdditionalControls(newFormGroup);
        } else {
          this.removeAdditionalControls(newFormGroup);
        }
      });
      return newFormGroup;
    }
    
    addAdditionalControls(formGroup: FormGroup) {;
      formGroup.addControl('phone', new FormControl(''));
      formGroup.addControl('address', new FormControl(''));
    }
    
    removeAdditionalControls(formGroup: FormGroup) {
      if (formGroup.get('phone') && formGroup.get('address')) {
        formGroup.removeControl('phone');
        formGroup.removeControl('address');
      }
}

In html file you can conditionally display the dynamic controls:

    <select placeholder="Type" formControlName="type">
      <option *ngFor="let type of types" [value]="type.id">
        {{ type.name }}
      </option>
    </select>
    <ng-container *ngIf="a.get('phone')">
      <br /><br />
      <label for="phone">Phone:</label>
      <input type="text" name="phone" formControlName="phone" />
    </ng-container>
    <ng-container *ngIf="a.get('address')">
      <br /><br />
      <label for="address">address:</label>
      <input type="text" name="address" formControlName="address" />
    </ng-container>

CodePudding user response:

I think that your FormArray should be an FormArray of FormGroups with the same "structur", only not show the inputs if the type is not "A". For this only need use in the .html

*ngIf="myForm.get('arr.' i '.type').value!='A'

-where "i" is the index you iterate the formArray-. If you need a Validator, use a custom validator over the group

createItem() {
    return this.fb.group(
      {
        name: [''],
        pay: [''],
        type: [],
      },
      { validator: this.requiredIfType() }
    );
  }
  requiredIfType() {
    return (control: AbstractControl) => {
      const group = control as FormGroup;
      if (control.get('type').value != 10) return null;
      let error = null;
      if (!control.get('name').value) error = { nameRequired: true };
      if (!control.get('pay').value) error = { ...error, payRequired: true };

      return error;
    };
  }

and you can use

    <div  *ngIf="myForm.get('arr.'   i   '.name').touched &&
                  myForm.get('arr.'   i).errors?.nameRequired">
      Name required
    </div>

    <div  *ngIf="myForm.get('arr.'   i   '.pay').touched &&
                  myForm.get('arr.'   i).errors?.payRequired">
      Payment required
    </div>

See stackblitz

  •  Tags:  
  • Related