ngFor Not Updating

Hi,

I am building an app with version 2.0.0-beta.4 and I am running into the following problem:

I have got an ngFor bound to an array.
<ion-item *ngFor="#place of places"> {{place.place_id}} </ion-item>

With a button, I call the onGetNear() function:
<button (click)="onGetNear()">Near</button>

onGetNear() {
 let req = { ... };

 this._dataService.nearbySearch(req).subscribe(res => {
   this.places = res;
   console.log(this.places);
 });
}

Now, the thing is that my console logs the response correctly on every button press, but the DOM isnā€™t updating. Once I click another button or open the sidemenu, ngFor displays all items correctly.

Any ideas where this behavior comes from?

Solved it!

Wrapped this.places = res in a zone.run() and now the view gets updated!

Using zone.run() is a workaround rather than a real solution. There was a bug in beta.4 which caused problems with the updates. Check out the linked post for more details:

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