angularangular-forms

How to add FormControl dynamically to FormGroup in angular 5?


I am trying to build a data-table compatible with FormGroup & FormArray. I will pass header details and rows to this component with parentForm group.

I have created a small add button, on clicking it, I will add new items dynamically. In this case, UI is updating fine, but if I print formGroup.value(), I am getting only the initial value passed. Not the updated formControl. I'm not sure what I am missing.

export class FormTableComponent implements OnChanges {
    @Input() headers: Array<string>;
    @Input() rows: Array<any>;
    @Input() parentFG: FormGroup;
    @Input() entriesName: string;

    get entries(): FormArray {
        return <FormArray>this.parentFG.get(this.entriesName);
    }

    constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) {}

    ngOnChanges() {
        this.createFormControls();
    }

    createFormControls() {
        this.parentFG.controls[this.entriesName] = this.fb.array([]);
        this.rows.forEach(row => {
            this.entries.controls.push(this.fb.group(row));
        });
    }
}

DEMO


Solution

  • You have a couple of issues: 1. you push new row to controls of FormArray other than FormArray itself

    this.entries.controls.push(this.fb.group(row));
    

    should be

    this.entries.push(this.fb.group(row));
    

    2. don't create new FormArray everytime and reload every single row when invoke createFormControls(), you can access it by your get entries() method

    Solution 1:

    Check length of entries, if it is 0, initialize it with rows otherwise just push last row into the existing FormArray

    createFormControls() {
            const keys = Object.keys(this.entries.value);
            if( keys.length === 0) {
              this.rows.forEach(row => {
                  this.entries.push(this.fb.group(row));
              });
            } else {
              const row = this.rows[this.rows.length - 1];
              this.entries.push(this.fb.group(row));
            }
        }
    

    Solution 2:

    Personally, I prefer putting createFormArray() and appendTo() or popFrom() into the parent Component. It is clean and easy read. We don't need ChangeDetectionRef in this case.

    I have moved createFormControls() from form-table component to dummy component and rename it to createFormArray() and changed addNewRow() a little bit, now everything works properly

    // create FormArray once
    createFormArray() {
        const elements: any[] =[];
    
        this.rows.forEach(row => {
            elements.push(this.fb.group(row));
        });
        return this.fb.array(elements);
    }
    
    addNewRow() {
      const newRow =    {
                name: 'NAFFD',
                age: 12,
                place: 'TN'
            };
    
        this.rows.push(newRow);  // update rows
    
        const persons =  this.dummyForm.get('persons') as FormArray;
        persons.push(this.fb.group(newRow));  // update FormArray
    }
    

    When initializing your dummy form, do below:

       this.dummyForm = this.fb.group({
            persons: this.createFormArray(),
            store: this.fb.group({
                branch: '',
                code: ''
            })
        });
    

    enter image description here

    Hope it helps and happy coding!