75

In Angular 2 how would I get 2 way binding with NgModel within a repeating list using NgFor. Below is my plunkr and code but I get an error.

Plunkr

@Component({
  selector: 'my-app',
  template: `
  <div>
    <div *ngFor="let item of toDos;let index = index;">
      <input [(ngModel)]="item" placeholder="item">
    </div>
    Below Should be binded to above input box
    <div *ngFor="let item of toDos">
      <label>{{item}}</label>
    </div>
  </div>
  `,
  directives: [MdButton, MdInput]
})
export class AppComponent { 
  toDos: string[] =["Todo1","Todo2","Todo3"];
  constructor() {}
  ngOnInit() {
  }
}
3
  • You get an error because you can't do this: ``` <input [(ngModel)]="item" placeholder="item"> ``` You can't bind ng-model to the reference variable "item." I am not sure what you are trying to accomplish. Can you elaborate? Commented Oct 29, 2016 at 0:22
  • It is ok I found the solution I need to include trackByIndex then bind to toDos [index] instead. will update plunkr shortly. Commented Oct 29, 2016 at 0:58
  • Just post an answer :-) Commented Oct 29, 2016 at 1:36

4 Answers 4

122

After digging around I need to use trackBy on ngFor. Updated plnkr and code below.

Working Plnkr

@Component({
  selector: 'my-app',
  template: `
  <div>
    <div *ngFor="let item of toDos;let index = index;trackBy:trackByIndex;">
      <input [(ngModel)]="toDos[index]" placeholder="item">
    </div>
    Below Should be binded to above input box
    <div *ngFor="let item of toDos">
      <label>{{item}}</label>
    </div>
  </div>
  `,
  directives: [MdButton, MdInput]
})
export class AppComponent { 
  toDos: string[] =["Todo1","Todo2","Todo3"];
  constructor() {}
  ngOnInit() {
  }
  trackByIndex(index: number, obj: any): any {
    return index;
  }
}
Sign up to request clarification or add additional context in comments.

8 Comments

Actually the trackBy does not matter in your code! What matters is [(ngModel)]="toDos[index]" instead of [(ngModel)]="item"
@Lars without the trackBy the input loses focus and seems to hang in my experience.
Without the trackBy ngFor re-creates the DOM every time you change a string in your array, so this is totally necessary.
@Lars trackBy:trackByIndex; is needed otherwise you lose focus on the input after first keypress.
In case you have your <input> inside a <form>, angular force you to put a name on the input, in that case when modifying the string array it will display 3 time "Todo3" but no erro will be throw and everything else will be working. You can replace the name by [ngModelOptions]="{standalone: true}" so that you get all 3 differents strings on the inputs.
|
69

What you have done is not working because of two reasons.

  • You have to use toDos[index] instead of item with ngModel (Read for more info)
  • Each input has to have a unique name

Here's the working solution for your problem.

<div>
<div *ngFor="let item of toDos;let index = index;">
  <input name=a{{index}} [(ngModel)]="toDos[index]" placeholder="item">
</div>
Below Should be binded to above input box
<div *ngFor="let item of toDos">
  <label>{{item}}</label>
</div>

4 Comments

I searched multiple questions until I got the answer from this one. My problem was "Each input has to have an unique name". This was exactly what I needed!
Glad I could help
"Each input has to have a unique name" - This saved me. Thanks man
"Each input has to have a unique name" fixed it for me. My input values were dissappearing after a submission but now they stay there because each input has a different name. Thanks alot!
0

Try this

@Component({
  selector: 'my-app',
  template: `
  <div>
    <div *ngFor="let item of toDos;let index = index;">
  <input [(ngModel)]="item.text" placeholder="item.text">
    </div>
    Below Should be binded to above input box
    <div *ngFor="let item of toDos">
  <label>{{item.text}}</label>
    </div>
  </div>
  `,
  directives: [MdButton, MdInput]
   })
export class AppComponent { 
  toDos: any[] =[{text:"Todo1"},{text:"Todo2"},{text:"Todo3"}];
  constructor() {}
  ngOnInit() {
  }
}

2 Comments

Down voting for below reasons : 1. Adding a key to the array is not required. Can be done using an array of string as well. So redundancy. 2. "trackBy:trackByIndex;" is a necessity in this case because if the an input is changed it will change the value of "toDos" array which will in-turn re-render the complete "ngFor". Using "trackBy:trackByIndex;" will not allow the re-rendering as because of this code ngFor will re-render only when the indexes will change.
Adding a key to the array shows that any object can be in the array and it can be referenced by this method, it helpful for more and deeper understanding to show that; Although that here the index is not used as showed by Lasitha Yapa below
0

You have to add a name attribute to the ngModel with name + index to make it unique.

<mat-form-field
  #fileNameRef
  appearance="fill"
  color="primary"
  floatLabel="auto"
>
  <input
    matInput
    #fileNameCtrl="ngModel"
    name="originalName{{ index }}"
    [(ngModel)]="file.originalName"
    type="text"
    autocomplete="off"
    autocapitalize="off"
    readonly
  /> 
</mat-form-field>

Comments

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.