简体   繁体   中英

Angular *ngFor with ngModel using Select has unexpected binding

I'm trying to create a dynamic form for users in the building industry, which will analyse inputs for a building (of any number of storeys) on a per-storey basis as such:

  1. User is initially presented with a form for a one-storey form, but given the option to add an extra storey:

  2. We should be able to add any number of extra storeys, and delete a specific storey if desired.

Method

To accomplish this, I'm trying to utilise *ngFor and iterate over an array which will take in the data, using ngModel to bind to each object in the array.

component.html

<form *ngFor = "let storey of storeyData; let i = index; trackBy: trackByFn(i)">
    <md-select placeholder="Floor type" name ="floorTypeSelector{{i}}" [(ngModel)]="storeyData[i].floorTypes[0]">
        <md-option *ngFor="let floorType of floorTypes" [value]="floorType.value">
            {{floorType.viewValue}}
        </md-option>
     </md-select>

<button md-raised-button (click)="incrementStoreyNumber()">
    <md-icon>library_add</md-icon>
     Add storey
</button> 

component.ts

export class FloorDetailsFormComponent implements OnInit {

selectedFloorType = [];
floorTypes = [
{value: 'concreteSlab', viewValue: 'Concrete slab'},
{value: 'suspendedTimber', viewValue: 'Suspended timber'},
{value: 'suspendedSlab', viewValue: 'Suspended slab'},
{value: 'wafflePod', viewValue: 'Waffle pod'}
]; 

storeyData = [{floorTypes: [],floorAreas:[] }];
storeyDataTemplate = {floorTypes: [], floorAreas:[]};

incrementStoreyNumber(){
    this.storeyData.push(this.storeyDataTemplate);
}

trackByFn(index){
 return index;
}
constructor() { }
ngOnInit() {
}

Problem

It seems that the first two storeys bind to their variables correctly, however changing the selected values of any of the 2nd to nth storeys will change the all other storeys (except the first).

After searching other posts about similar issues, I'm still at a loss as to why this is happening. One problem others had was that the name of the element wasn't being distinguished for each iteration of the *ngFor loop, however looking at my console.log, I can see the name of each element being indexed as it should.

One interesting thing I've seen, is that if I expand the storeyData array to a length of n storeys in the typescript file, all storeys up to n bind to their own independent variable as they should, and all storeys n+1 that are added later have the same problem.

I've tried using the trackBy feature, however I can't seem to get this to work either. I really lack understanding of what's going on under the hood when I'm trying to expand the *ngFor range on the fly. Perhaps this is just bad practice? If you could help me out here I'd be extremely grateful (even if its "hey, read up on this ")

The issue is in this line:

this.storeyData.push(this.storeyDataTemplate);

When you add storeyDataTemplate to storeyData, it's that same object who gets bound each time you do push, and ngFor tracks the same object. If you change to:

this.storeyData.push({floorTypes: [], floorAreas:[]});

it will work.

DEMO

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM