ngFor Not Updating

it is unfortunate because keep seeing everyone suggesting the use of ngZone in the apps glad to see it being acknowledged as a workaround/hack as instead of a best practice

4 Likes

I actually tried using beta6 on a copy of the project following your guide without success. Besides that everything looked like android instead of iOS, I still got the same behavior with that function.

@finke This is strange, could you check if all dependencies were updated by running npm outdated?

@iignatov
image
Everything is up to date or at the wanted version.

@finke Double-check if you’re not importing es6-shim somewhere, you should only have es6-shim.min.js in index.html. You can also remove es6-promise from package.json, it shouldn’t be needed anymore. Other than that it could be something with your environment (you could start a new blank project and copy only the minimal code needed to reproduce it) - are you testing on browser/emulator/device?

@iignatov I just reproduced it in a fresh project, but still the same. Also, it doesn’t matter if I use ionic serve or emulate it in the simulator. I’m not importing es6-shim somewhere, scanned the whole project…

@finke Thanks for the update! I created a basic plunker demo but I’m not able to reproduce it there. I’ll try with a local project.

Could you please tell me if you’re using TypeScript or JavaScript, and also which starter template have you used (blank, tabs, etc)?

BTW I forgot to mention about the Android look - it’s the new default for desktop web.

2 Likes

@iignatov That demo looks basically identical. I’m using TypeScript and the blank starter.
The thing is that the places variable on the instance gets set correctly and also the console log looks perfectly fine. It is somehow just not updating the view.

@finke I just created a new project (ionic start Test-beta6 blank --v2 --ts) and applied the code from the plunker and everything works as expected. So I guess that it’s an environment specific problem. I did a quick search for Safari in the Angular 2 Issues and found the following bugs that seem to be related:

In my case, I use a service that returns subjects (observables) to components and updates (using .next()) the components with new values (the service gets new values from a server via http.get()). This works fine on Chrome and FF, but not on Safari. If I debug or put console.log() in the .subscribe functions in the components, I see the new values arriving, but they are not updated in the views in Safari.

It seems like I have all the data long before it is being displayed.

@iignatov It’s the same with chrome…
I’ve edited the plunker so that the observable does not fire immediately like it is in my app, but it still works as it should.

EDIT: I copied the delayed function from plunker to my project and it works. Now, there must be some kind of error in my code. Here are the functions in comparison:

nearbySearch(request: {location: any, radius: string, types: string[]}) {
return Observable.create(observer => {
  if (this._places) {
    this._places.nearbySearch(request, (r,s) => {
      if (s == google.maps.places.PlacesServiceStatus.OK) {
          observer.next(r);
          observer.complete();
      }
    });
  }
});
}


demo() {
 return Observable.create(observer => {
   let r = [{place_id: 1}, {place_id: 2}, {place_id: 3}];

   setTimeout(() => {
    observer.next(r);
    observer.complete();
   }, 2000);
 });
}

@finke The code looks OK. I guess that it might be something zone-related, e.g. like the following zone.js issue: Losing zone in RxJS and fetch. I’ll suggest you to check in which zone your code is being executed:

onGetNear() {
  // ...
  this._dataService.nearbySearch(req).subscribe(res => {
    this.places = res;
    console.log(Zone.current.name);
    console.log(this.places);
  });
}
1 Like

@iignatov Yeah, it is exactly that issue. Before I subscribe to the Observable, the current zone is ‘Angular’ and once I’m in the subscription, the zone switches to ‘root’.

1 Like

@finke Thanks for the confirmation!

In such case there is not much that you can do, other that using the zone.run() workaround. I would recommend you to use NgZone and to add comments about the workaround. Something like this (so that you can find it and remove it once the issue is fixed):

// ...
  constructor(private _zone: NgZone) {
  }

  onGetNear() {
    //..
    this._dataService.nearbySearch(req).subscribe(res => {
      // HACK: Workaround for a bug in zone.js (0.6.12):
      // https://github.com/angular/zone.js/issues/304
      this._zone.run(() => this.places = res);
    });
  }
// ...

You could also vote for the zone.js bug so that it hopefully get fixed sooner:

1 Like

@iignatov Sure! Thank you!

I’m yet another one who’s been beaten by the so called “zone bug” in Ionic 2. I don’t have anything to add except this has happened to me when using socket.io

Having read the docs of NgZone, the bug may be there because socket events are run by Ionic for some reason outside the page zone. It looks like the run method, brought me back to it!

For reference, this is what I did:

socket.on("event", d => { this.ngZone.run(() => { this.data = false; this.interval = this.startInterval(); }); });

I think wrapping with promises is stylistically better than accessing NgZone directly.

After updating to Ionic 2 RC0 I still have problems with view updating If I dont use zone.run.
My case is using zeroconf plugin, the services added won’t appear until a new event is detected (like slide menu dragged)

1 Like

i have seen that behavior also… i believe there are some issues in the RC0 release

2 Likes

Same issue for me. Have you found any reasoning?

I found a similar problem in my code (RC0 release).
####service.ts
private updated = new BehaviorSubject(false);
updated$ = this.updated.asObservable();

update() {
  this.updated.next(true);
}

initListeners() {
  this.myObject.on('chat', c => {
        console.log('onChat', c);
        this.update();
  });
}

In another class I subscribe to the Observable and update an array which is looped through using ngFor.
####chat-list.ts

subscription:Subscription;
constructor(
    public navCtrl: NavController,
    private service: MyService
  ) {}
  
  ngOnInit() {
    this.subscription = this.service.updated$
       .subscribe(updated => this.chatList = updated ? this.service.getChatList() : []);
  }
  
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }

####chat-list.html
<ion-content *ngIf=“chatList”>

Today
<chat-list-item *ngFor=“let chat of chatList” (click)=“openChat(chat)”
[chat]=“chat”>

In my case, the array would successfully update but the view in the ngFor loop would not be updated unless some other action eg. click, triggered an update.
After searching and doing quite a bit of reading and landing here I realized in my case it was due to the call back being run within the javascript object (myObject) which was attached to my service. In a case such as this, it is vital that zone.run( is used to ensure that the procedure call is made in the Angular zone to ensure updates propogate to DOM view changes.

####service.ts (edited)
constructor(private zone: NgZone) {
}

this.myObject.on(‘chat’, c => {
console.log(‘onChat’, c);
this.zone.run(() => this.update());
});