Angular .removeAt(i) at FormArray does not update in DOM - Angular
Asked Answered
A

4

7

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>
Aloe answered 30/10, 2018 at 1:10 Comment(13)
Could you please share the code?Wavemeter
@NinjaJami added the code :DAloe
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?Scheelite
@Scheelite i copied the code as is and the adding works for me but the removing doesn't. that's what got me puzzled.Aloe
@Aloe did you try to run the stackblitz shared by j3ff ? It's working in that sample.Wavemeter
@NinjaJami yes that's the code i posted above. it's only removing elements from the FormArray but the input fields are still in the site.Aloe
@Aloe hmm..got itWavemeter
@Aloe when i run the code, it's removing from the UI as well as from the dom. are you saying. that's weirdWavemeter
@NinjaJami I don't know what could be causing the issue... it's not removing it on my local projectAloe
@Aloe are you getting any error in console?Wavemeter
@Aloe just log the form array after remove code to ensure whether it's removing from the collectionWavemeter
Long shot: perhaps attempt to implement a detector? this.detector.detectChanges imported from changeDetectorRef I believeBoesch
could you do a short stackblitz of it when pasting your code i get errors thanks in advance.Chiliad
A
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.

Acceptable answered 5/10, 2020 at 12:50 Comment(2)
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.Jodyjoe
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 elementPolyphagia
L
2

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`

Leshalesher answered 20/12, 2021 at 8:40 Comment(0)
V
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()

Villainy answered 30/10, 2018 at 2:14 Comment(1)
After many unsuccessful attempts with updateValueAndValidity() this solved it. Thanks! Although I used the ChangeDetector detectChanges() - angular.io/api/core/ChangeDetectorRef#detectChangesPizza
V
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

Volant answered 30/10, 2018 at 7:4 Comment(6)
dataObject is an array of objects so I can't get the index with field :(Aloe
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.Volant
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 :(Aloe
I believe that will work. I am not getting you, where you see the problem.Volant
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.Literalminded
@AjayOjha If I try your solution on my side, I get: Cannot assign to read only property '0' of objectFroh

© 2022 - 2024 — McMap. All rights reserved.