[Fixed] angular 7 reactive forms: dynamically add / remove input fields for each input field in the form

Issue

I have to dynamically create a form with data received from the server with the option of adding and removing input fields for each existing input fields.

This is the data i receive from the server

documents = [{id: 1, service_item_id: 311101, name: 'document 1'}, {id: 2, service_item_id: 311102,, name: 'document 2'}, {id: 3, service_item_id: 311103,, name: 'document 3'}, {id: 4, service_item_id: 311104,, name: 'document 4'}]

I have created a function to create the form

createControls(controls) {
    console.log(controls);
    for (let control of controls) {
        const newFormControl = new FormControl();
        this.myForm.addControl(control.service_item_id, newFormControl);
    }
}

this.createControls(this.documents);

And in the Html I have created this

<form [formGroup]="myForm" (ngSubmit)="submitForm()">
    <div *ngFor="let c of documentation; let index = index">
        <label>
            {{ c.name}}
        </label>
        <input type="number" [formControlName]="c.service_item_id" [placeholder]="c.name" />
        <button class="btn btn-primary btn-sm" (click)="add(c.service_item_id)">Add</button>
        <button class="btn btn-primary btn-sm" (click)="remove(c.service_item_id, index)">Remove</button>
    </div>
    <button type="submit" class="btn btn-primary btn-small">Submit</button>
</form>

My add and remove functions are

add(item): void {
    (this.myForm.get(item) as FormArray).push(
      this.fb.control(null)
    );
  }

  remove(item, index) {
    (this.myForm.get(item) as FormArray).removeAt(index);
  }

Only the form is getting created but add and remove buttons are not working.

Please do help me resolve this issue.
Thanks in advance

Solution

I think what will work best for your situation is to use FormArray. With FormArray you can push and remove items just like you would do with a normal array

  myForm = this.fb.group({
    documents: this.fb.array([])
  });

  get documentsControl(): FormArray {
    return this.myForm.get("documents") as FormArray;
  }
  documents = [
    { id: 1, service_item_id: 311101, name: "document 1" },
    { id: 2, service_item_id: 311102, name: "document 2" },
    { id: 3, service_item_id: 311103, name: "document 3" },
    { id: 4, service_item_id: 311104, name: "document 4" }
  ];
  ngOnInit() {
    this.documents.forEach(document =>
      this.documentsControl.push(
        this.fb.group({
          id: [document.id],
          name: [document.name],
          service_item_id: [document.service_item_id]
        })
      )
    );
  }
  constructor(private fb: FormBuilder) {}
  submitForm() {}
  add() {
    this.documentsControl.push(
      this.fb.group({
        id: [null],
        name: [null],
        service_item_id: [null]
      })
    );
  }
  remove(index) {
    this.documentsControl.removeAt(index);
  }

to add items we use push while to remove items we use removeAt

Below is the html

<form [formGroup]="myForm" (ngSubmit)="submitForm()">
    <ng-container formArrayName='documents'>
      <div *ngFor="let c of documentsControl.controls; let index = index" [formGroupName]='index'>
        <label>
            {{ c.value.name}}
        </label>
        <input type="number" formControlName="service_item_id" placeholder="name" />
        <button class="btn btn-primary btn-sm" (click)="add()">Add</button>
        <button class="btn btn-primary btn-sm" (click)="remove(index)">Remove</button>
    </div>
    </ng-container>
    <button type="submit" class="btn btn-primary btn-small">Submit</button>
</form>

Edit

To add items below there clicked button we can implement insert()

  insert(index) {
    const serviceItenId = this.documentsControl.controls[index].get('service_item_id').value
    this.documentsControl.insert(
      index + 1,
      this.fb.group({
        id: [null],
        name: [null],
        service_item_id: [serviceItenId]
      })
    );
  }

I have updated the below Demo to reflect this

See Demo

Leave a Reply

(*) Required, Your email will not be published