Davide C
Davide C

Reputation: 976

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

Upvotes: 1

Views: 669

Answers (2)

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38807

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.

Upvotes: 3

Arun Mohan
Arun Mohan

Reputation: 1227

Actually what you are doing is correct. To check what I mean change your code to this:

<input type=checkbox (change)="change(item)" [(ngModel)]="item.b"/>

change(item) {
 console.log(item);
}

This is not reflecting on the dom because the items array is mapped to the same memory location and changing an element inside it won't cause a change detection in angular to trigger a display change.

Upvotes: 2

Related Questions