Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
158 views
in Technique[技术] by (71.8m points)

javascript - ngModel cannot detect array changes correctly

The component model:

private SomeArray = [{ key: "Initial" }];

User can add/remove items dynamically:

addField() {
    this.SomeArray.push({ key: Math.random().toString() });
}

removeField(index: number) {
    this.SomeArray.splice(index, 1);
}

Template markup:

 <div class="col-xs-12">
     <button (click)="addField()" type="button">Add</button>
 </div>

 <div *ngFor="let field of SomeArray; let i = index;">
     <input [(ngModel)]="field.key" #modelField="ngModel" [name]=" 'SomeArray['+i+'].key' " type="text" class="form-control" required />
     <div [hidden]="modelField.pristine || !(modelField.errors && modelField.errors.required)" class="alert alert-danger">
        Required error
     </div>

    <button (click)="removeField(i)" class="btn btn-danger">Remove</button>
 </div>

This works untill user removes any item from SomeArray. If I add some two items initially: enter image description here

and remove the one with 1 index:

enter image description here

then after adding another item Angular treat it as item has both 0 and 1 index (the new item "occupies" both two inputs):

enter image description here

(item with key 0.1345... is not displayed)

It's worth to noting items of SomeArray are as expected, but data binding fails. What can be the reason of it?

Update: Thanks to the comments of @Stefan Svrkota and @AJT_82 it's known for me the issue can be resolved by adding [ngModelOptions]="{standalone: true}" to the needed input. But I couldn't stop thinking about the reason of the issue in my cause, without setting standalone option (there is unique value for each name attribute so it's excepted nothing wrong here).

Finally I have found that behavior occurs when input elements are into <form> tag only - Plunker sample here (enclosing of template with form tag is the reason that issue).

Any ideas of this behavior?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The reason why it happens is ngFor mixes name properties when you delete some item.

When you use ngModel inside form each ngModel control will be added to form controls collection.

Let's see what happens if we have added three items and clicked on Remove the second

1) Step1 - SomeArray[1].key exists in collection controls enter image description here

2) Step2 - SomeArray[1].key has been removed from controls collection

enter image description here

3) Step3 - Html looks like

enter image description here

4) Step4 We are adding a new item

enter image description here

So formGroup returns existing item.

How we can solve it?

1) Don't wrap our controls in form tag

2) Add ngNoForm attribute to form

<form ngNoForm>

3) Use

[ngModelOptions]="{standalone: true}

With all three solutions above:

  • We can remove [name] property binding

  • We can't use the built in Form group validation

4) Use trackBy for ngFor

template.html

<div *ngFor="let field of SomeArray; let i = index; trackBy: trackByFn">

component.ts

trackByFn(i: number) {
  return i;
}

Plunker Example

This way our built in form will work properly


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...