Search code examples
htmlangularangular-reactive-formsangular-formsformarray

input values does not fill correctly in FormArray


I have trouble with filling the right values into input fields of a FormArray. Here is my html:

<div formArrayName="roles" *ngFor="let role of roles.controls; let i=index">
    <div [formGroupName]="i">
        <div class="form-group row">
            <label class="col-md-2 col-form-label" attr.for="{{'roleNameId' + i}}">Role Name {{i+1}}</label>
            <div class="col-md-6">
                <input class="form-control"
                        attr.id="{{'roleNameId' + i}}"
                        type="text"
                        formControlName="roleName">
            </div>
            <label class="col-md-2 col-form-label" attr.for="{{'identifierId' + i}}">
                <input
                    type="checkbox"
                    attr.id="{{'identifierId' + i}}"
                    formControlName="identifier"> identifier
            </label>
            <div class="col-md-2">
                 <button type="button" (click)="removeRole(i)" class="btn btn-danger oi oi-trash"></button>
            </div>
        </div>      
    </div>
</div>

So this html is being rendered x times depending on the number of roles i have. This works just fine and no errors are shown.

But here is the Problem: When i remove one Role which is not the last of the Array, the input fields filled with the right roles change. Here are the roles: enter image description here

When i remove role 2 for example, the form looks like this: enter image description here

role2 was being removed correctly, but role3 disappeared and the last two roles have the name of the last role (role8)

Why this happens? When i look to the formArray where all roles are saved, the array has the correct values, here is the evidence:

enter image description here

why the correct values are not shown in the view? Thank you for your help

EDIT: Here is the relevant typescript code:

this.sourcesForm = this.fb.group({
        sourceName: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(255)]],
        sourceType: ['QUERY'],
        dsl: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(255)]],
        list: [''],
        roles: this.fb.array([this.buildRole()]),
        database: [''],
});

// save roles to temporary variable to map for the roles Formarray
let roles = [];
dslStatement.roles.forEach(role => {
    let oneRole = {
        roleName: role.name,
        identifier: true
    }
    if (role.role === 'IDENTIFIER')
        oneRole.identifier = true;
    else
        oneRole.identifier = false
    roles.push(oneRole);
});
// convert the variable to FormGroups, then the FormGroups to one FormArray 
// and at last set the FormArray to the roles of sourcesForm
const rolesFormGroups = roles.map(roles => this.fb.group(roles));
const rolesFormArray = this.fb.array(rolesFormGroups);
this.sourcesForm.setControl('roles', rolesFormArray);

buildRole(): FormGroup {
    return this.fb.group({
        roleName: '',
        identifier: true
    });
}

addRole() {
    this.roles.push(this.buildRole());
}

removeRole(i: number) {
    this.roles.removeAt(i);
    console.log(this.roles.value);
    console.log(this.roles.controls);
}

Solution

  • UPDATE I built a stackblitz attempting to demonstrating the issue here: https://stackblitz.com/edit/angular-dk-formarray

    It uses Angular 5.0 and does NOT have the problem.

    I was able to reproduce this with the app from my course referenced above (Angular 4):

    enter image description here

    After deleting the "a" row, I get this:

    enter image description here

    Notice that the "a" is gone, but now I have two "c"s instead of "b" and "c".

    Interestingly, if I save and then come back to the page, it displays correctly with "b" and "c".

    So it appeared to be a bug that was fixed in Angular 5.

    I was able to find a "work around" that worked for me in Angular 4.x:

    deleteTag(index: number): void {
        this.tags.removeAt(index);
        this.productForm.setControl('tags', this.fb.array(this.tags.value || []));
    }
    

    You may be able to do something similar to this (but I'm not sure your roles are set up just like my tags?)

    But you are probably better off moving to Angular 5.