Search code examples
angularrxjsangular-ngmodel

Angular ngModel inside ngFor, with pipe and map, not working


I'm having problems in this situation:

@Component({
  selector: 'my-app',
  template: `
    {{items | async| json}}

    <div *ngFor="let item of items | async">
      <input type=checkbox [(ngModel)]="item.b"/>
    </div>
  `
})
export class AppComponent  {
  items = of([{
    name: '1',
  },
  {
    name: '2',
  },
  {
    name: '3',
  }])
  .pipe(map(i=>{
    return i.map(i=>{
      return {
        i: i,
        b: false
      }
    })
  }))
}

Stackblitz app

The problem is that the ngModel is not working and I can't see the b property change. If I remove the map pipe and I put the boolean property in the first array, all is working. Am I missing something? What's the problem?

Thanks


Solution

  • You're not doing anything wrong. If you render out {{item.b}} in the ngFor you will see the value is changing between true and false correctly. As mentioned in the other answer this is because of references and change detection. You can also simply save the observable data a property on your class using ngOnInit and subscribe:

    import { Component } from "@angular/core";
    import { of } from "rxjs";
    import { map } from "rxjs/operators";
    
    @Component({
      selector: "my-app",
      template: `
        {{ items | json }}
    
        <form #myForm="ngForm">
          <div *ngFor="let item of items">
            <input [name]="item.i.name" type="checkbox" [(ngModel)]="item.b" />
          </div>
        </form>
      `
    })
    export class AppComponent {
      items: any[] = [];
    
      ngOnInit() {
        this.getData().subscribe(data => (this.items = data));
      }
    
      private getData() {
        return of([
          {
            name: "1"
          },
          {
            name: "2"
          },
          {
            name: "3"
          }
        ]).pipe(
          map(i => {
            return i.map(i => {
              return {
                i: i,
                b: false
              };
            });
          })
        );
      }
    }
    

    Here is an example in action. Don't forget to clean up any observables if needed to avoid memory leaks.