21

Is it not possible (or not yet possible) to use ngModel against values from ngFor? Is Angular trying to protect me from bad performance?

Works great: http://jsfiddle.net/langdonx/n5pjgev6/

<input type="text" [(ng-model)]="value">{{value}}

Does not work so great: http://jsfiddle.net/langdonx/n5pjgev6/1

<li *ng-for="#name of names">
    <input type="text" [(ng-model)]="name">{{name}}
</li>

EXCEPTION: Cannot reassign a variable binding name

I tried binding to the array as well, which... kind of works, but hijacks focus and also throws an exception: http://jsfiddle.net/langdonx/n5pjgev6/2/

<li *ng-for="#name of names; #i = index">
    <input type="text" [(ng-model)]="names[i]">{{name}}
</li>

EXCEPTION: LifeCycle.tick is called recursively

Edit:

I can get around the LifeCycle.tick issue using a more direct approach, but the focus is still stolen because ngFor redraws things: http://jsfiddle.net/langdonx/n5pjgev6/3/

<li *ng-for="#name of names; #i = index">
    <input type="text" [value]="names[i]" (input)="names[i] = $event.target.value">{{names[i]}}
</li>
Langdon
  • 19,875
  • 18
  • 88
  • 107
  • http://teropa.info/blog/2015/06/09/transclusion.html Read the paragraphs about Managing Transclusion Scope Lifecycle and Repeated Rendering with Element Transclusion. Might give you a bit of insight – cjds Oct 26 '15 at 19:07
  • 2
    Since a late beta there is `trackBy` available to make `ngModel` work with `ngFor` see http://stackoverflow.com/questions/36469710/angular-2-ngmodel-bind-in-nested-ngfor – Günter Zöchbauer May 12 '16 at 08:29
  • 1
    @GünterZöchbauer Pretty messed up you'd mark this as a duplicate for your newer answer. – Langdon May 12 '16 at 15:56

2 Answers2

16

I think ngFor don't like tracking array elements which are primitive values having ngModel on them.

If you remove the ngModel inside the loop, it works.

It works too when I update jsfiddle with :

this.names = [{name: 'John'}, {name: 'Joe'}, {name: 'Jeff'}, {name: 'Jorge'}];

and

<li *ng-for="#n of names"><input type="text" [(ng-model)]="n.name">{{n.name}}</li>
bertrandg
  • 3,147
  • 2
  • 27
  • 35
  • I had the same problem and I solved it by using a wrapper around my value (exactly what you did above). But let me ask: Is this an Angular 2 bug or "feature"? I am not quite sure why this is happening. – AstrOne Jan 06 '16 at 23:37
  • 2
    Not a bug at all. Strings are primitives so there is no reference address and ng2 takes its value instead to identify it inside the loop (ngFor). Then ngModel wants to modify this value -> Exception – bertrandg Jan 07 '16 at 10:33
  • Not tried but I think it behaves almost the same with ng1. – bertrandg Jan 07 '16 at 10:38
7

A solution is to reference the value inside ngModel by its index. Therefore [(ngModel)]="names[index]".

But this is not sufficient because *ngFor tracks items by value. As soon as the value is changed the old value cannot be tracked. So we need to change the tracking function to return an index, thus trackBy: trackByIndex.

This issue is explained here.

Solution:

@Component({
  selector: 'my-app',
  template: `
    <div>
      <input type="text" 
        *ngFor="let name of names; let nameIndex = index; trackBy: trackByIndex"
        [(ngModel)]="names[nameIndex]"/>
      <br/>
      {{ names | json }}
    </div>
  `,
})
export class App {

  names: string[];

  constructor() {
    this.names = ['a', 'b', 'c'];
  }

  public trackByIndex(index: number, item) {
    return index;
  }
}
andreim
  • 3,365
  • 23
  • 21