8

I thought it was an issue with my implementation but seems that my code for creating dynamic FormArray should be functional based from this question I raised. When I integrate it to my project, the remove function does delete the element from the FormArray, but it does not reflect in the interface/ does not remove object from the DOM. What could be causing this?

import {
  Component,
  VERSION
} from '@angular/core';
import {
  FormGroup,
  FormControl,
  FormArray,
  Validators,
  FormBuilder
} from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  objectProps: any[];

  public dataObject = [{
      "label": "Name",
      "name": "name",
      "type": "text",
      "data": ""
    },
    {
      "label": "Contacts",
      "name": "contacts",
      "type": "inputarray",
      "array": []
    }
  ]
  form: FormGroup;

  constructor(private _fb: FormBuilder) {}

  ngOnInit() {

    const formGroup = {};
    for (let field of this.dataObject) {
      if (field.type == "inputarray") {
        console.log("THIS IS " + field.type)
        formGroup[field.name] = this._fb.array([''])
      } else {
        console.log("THIS IS " + field.type)
        formGroup[field.name] = new FormControl(field.data || '') //, this.mapValidators(field.validation));
      }
    }

    this.form = new FormGroup(formGroup);
  }

  addFormInput(field) {
    const form = new FormControl('');
    ( < FormArray > this.form.controls[field]).push(form);
  }

  removeFormInput(field, i) {
    ( < FormArray > this.form.controls[field]).removeAt(i);
  }
}
<form *ngIf="form" novalidate (ngSubmit)="onSubmit(form.value)" [formGroup]="form">
  <div *ngFor="let field of dataObject">
    <h4>{{field.label}}</h4>
    <div [ngSwitch]="field.type">
      <input *ngSwitchCase="'text'" [formControlName]="field.name" [id]="field.name" [type]="field.type" class="form-control">
      <div *ngSwitchCase="'inputarray'">
        <div formArrayName="{{field.name}}" [id]="field.name">
          <div *ngFor="let item of form.get(field.name).controls; let i = index;" class="array-line">
            <div>
              <input class="form-control" [formControlName]="i" [placeholder]="i">
            </div>
            <div>
              <button id="btn-remove" type="button" class="btn" (click)="removeFormInput(field.name, i)">x</button>
            </div>
          </div>
        </div>
        <div>
          <button id="btn-add" type="button" class="btn" (click)="addFormInput(field.name)">Add</button>
        </div>
      </div>
    </div>
  </div>
  <button type="submit" class="btn btn-danger btn-block" style="float: right; width:180px" [disabled]="!form.valid">Save</button>

13
  • Could you please share the code? Commented Oct 30, 2018 at 1:34
  • @NinjaJami added the code :D Commented Oct 30, 2018 at 1:40
  • strange ... i copy-pasted your code in StackBlitz and it works fine: stackblitz.com/edit/angular-stackoverflow-53056007 ... is there something else you forgot to share? Commented Oct 30, 2018 at 2:32
  • @j3ff i copied the code as is and the adding works for me but the removing doesn't. that's what got me puzzled. Commented Oct 30, 2018 at 2:50
  • @caricature did you try to run the stackblitz shared by j3ff ? It's working in that sample. Commented Oct 30, 2018 at 2:58

4 Answers 4

8

This is not a great solution but I solved my problem by manipulating value and after removing the control.

I simply moved the item that I wanted to remove to the end of the array and then I removed the last item.

removeItem(index: number): void {
  const value = this.formArray.value;

  this.formArray.setValue(
    value.slice(0, index).concat(
      value.slice(index + 1),
    ).concat(value[index]),
  );

  this.formArray.removeAt(value.length - 1);
}

I hope it helps someone struggling with this issue in the future.

Sign up to request clarification or add additional context in comments.

3 Comments

normally trackBy function that returns item will solve this problem but I also had another problem that required trackBy to return the index of item so I have implemented Your solution which works just fine.
It helps me a lot!. Still good solution on Angular 17, 2024. Without messing around changeDetections. I integrate it on a two select form, and it gets the right place of the DOM's element
Either we are all doing something wrong or this is a real issue. Your solution worked for me as well in Angular 19.
3

I was facing this problem as well. The solution for me was to get rid of/fix the trackBy function in NgFor*. I think you need to introduce a proper trackBy function and it might solve your error.

sauce: How to use `trackBy` with `ngFor`

Comments

1

Maybe you can try to force change detection using the reference to application. To do that inject the ApplicationRef in the constructor an call the tick(); on your removeFormInput method.

constructor(private _fb: FormBuilder, private appRef: ApplicationRef) {}

And in removeFormInput

removeFormInput(field, i) {
    (<FormArray>this.form.controls[field]).removeAt(i);
    this.appRef.tick();
}

Take a look at angular documentation: API > @angular/core /ApplicationRef.tick()

1 Comment

After many unsuccessful attempts with updateValueAndValidity() this solved it. Thanks! Although I used the ChangeDetector detectChanges() - angular.io/api/core/ChangeDetectorRef#detectChanges
0

replace below function, you are not removing the row object from 'dataObject'.

removeFormInput(field, i) {
    ( < FormArray > this.form.controls[field]).removeAt(i);
    this.dataObject.splice(this.dataObject.indexOf(field),1);
  }

Take a look here Add and Remove form items I build on stackblitz, for me its working fine, adding and removing items... Take a look.

working version

6 Comments

dataObject is an array of objects so I can't get the index with field :(
Sorry, my mistake. Check the edited code. I am getting index based on field. Second way is you pass the index from html also you just need to replace two lines in the html. lines are : (line number 2) - *ngFor="let field of dataObject; let rowIndex = index" and (line number 13) - (click)="removeFormInput(field.name, i,rowIndex)". pass this 'rowIndex' in array of splice method. Please let me know, if any confusion.
splice isn't working for me either :( also using rowIndex isn't always ok because the counting will not be inline with the FormArray indices :(
I believe that will work. I am not getting you, where you see the problem.
What you built on stackblitz does not work either. When you list down 0,1,2,3, then click x on 1, the list would become: 0,1,2. When the question of the OP is asking for is that the list would become 0.2,3.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.