Ion-list not updating when items change

Hi! I’m having an odd problem that only seemed to surface when I began using Ionic2 with Angular2 (as opposed to originally just using Angular2).

I have a Page that contains an ion-list, and I do an ngFor to loop through and create the ion-items. The relevant HTML:

<ion-list>
    <ion-item *ngFor="#unit of units">
    {{unit.description}}
    </ion-item>
  </ion-list>

In my Page, the constructor sets its units property to be the units property of an injected service:

export class UnitsPage {

    units: Unit[] = [];

    constructor(unitService: UnitService) {
        this.units = unitService.units;
    }

The injected unitService takes a little while to get its units, so at the time the constructor fires, this.units is still an empty array. When it finally gets populated, the ion-list does not update. If I put the ‘this.units’ assignment inside a setTimeout, it works just fine. It’s almost as if one-time binding is going on here.

Thoughts?

2 Likes

Have you tried putting the “units” population inside a lifecycle hook? Does this happen the first time you load the page or the second time? the constructor only gets called once, then the page is cached on the nav stack.

Even though the constructor is only called once, it shouldn’t matter - units, being an object (array) is passed by reference, so eventually it is populated, and should display.

I hadn’t yet tried putting this in a lifecycle hook, which I can do, but it just seems unnecessary.

Sorry, I didn’t notice it was actually a property and not a function call.
(EDIT) I think I got it. The setTimeout allows you to skip a tick on the ChangeDetection cycle. When the view gets rendered the array is empty, then it gets populated, but the view is already there so no interaction -> not action. There are some articles by Viktor Savkin that shed some light on this issue.
E.g. http://victorsavkin.com/post/137821436516/managing-state-in-angular-2-applications
http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation

Hmm so from a quick look, you could trying

<ion-list>
    <ion-item *ngFor="#unit of units | async">
    {{unit.description}}
    </ion-item>
  </ion-list>

Which will handle the async updates.

That would only work if units is a Promise or Observable - it’s only an Array. I tried it anyway, but as expected, got an error:

EXCEPTION: Invalid argument '' for pipe 'AsyncPipe' in [units | async in UnitsPage@5:14]

I guess I just don’t understand - if a property of a component/page is bound to via a template, and that property changes, then the template should produce the updated value, right? Even if units was populated initially, but then some other internal process changed it, I’d expect the template to reflect the updated value.

Thanks for the links, @sphaso! I’m trying to read through and digest them and understand the way it applies to this issue.

You’re welcome, they’re pretty deep, still trying to fully $digest them myself. The main point to take home is: don’t expect data binding to work exactly the way it did in ng1.

Yup - I definitely don’t expect that. It’s just that it worked perfectly fine in many different cases for this app when I was using stock angular 2. When I began to retrofit Ionic 2 in, I immediately hit this scenario. That’s why I’m posting here - I assume(d) it is/was an issue with how I’m using Ionic, or some Ionic behavior that I don’t fully understand.

Would it be feasible to have UnitService expose a Promise or Observable instead of the raw array? That would seem to be a more clear indication that time is needed, and would let you seamlessly use the async pipe.

Hello, those link show no content.
Maybe I need to login?