Change-detection not working properly in ng2 component after navigation via angular1 ui-router

916 views Asked by At

In an application that uses ng-upgrade to use some new ng2 (V2.2.4) components inside a big ng1 (V1.5.9) app (using ui-router) I am currently seeing a very strange change-detection issue. As far as I can tell, this has been introduced in some upgrade of ng2, but sadly I can't easily verify that currently.

I have reduced it to a minimal example by now where i simply have this component:

@Component({
    selector:'my-ng2-demo',
    template: '<div>{{status}}</div>'
})
export class MyNg2Demo implements OnDestroy {
    public status:boolean = true;
    private interval:any;

    constructor() {
        this.interval = setInterval(() => {
            this.status = !this.status;
            console.log('status: ' + this.status);
        }, 2000);
    }

    ngOnDestroy():void {
        clearInterval(this.interval);
    }

}

That component is upgraded in the upgrade-adapter and registered as an entry-component in the module.

The component is used in the template of an ng1 controller like this:

<my-ng2-demo></my-ng2-demo>

When I open a browser and navigate directly to that page (the route of the ng1 controller) everything works as expected: the ui shows true/false alternating every 2 seconds.

However: when I navigate away & then come back to the page (visiting some different routes) then suddenly change detection doesn't seem to work. The displayed value alternates only about every 5-6 seconds, seems quite random, although in the console I still see the expected values.

When I change the component's constructor to this:

constructor(private zone: NgZone) {
    this.interval = setInterval(() => {
        zone.run(() => {
            this.status = !this.status;
            console.log('status: ' + this.status);
        });
    }, 2000);
}

It suddenly works again.

Obviously this isn't a feasible solution since the behavior I'm seeing in the real app is much more complex (i would have to add zone.run in dozens of places probably, which would not be maintainable).

I have debugged this for hours and don't understand what is happening. In Chrome's JavaScript profiler, I basically see that the page idles for almost the whole time in those 5-6 seconds where nothing happens.

profiler This is not a screenshot from that demo-component (but it basically looks the same) but from profiling the tab-component I was initially testing (tab-switching took just as long with no visible actions in between).

update:

After a bit more experimentation what I find quite surprising also is that when instead of putting the status-changing code into a zone.run call, I add a call to ApplicationRef.tick() (as mentioned here for example: https://stackoverflow.com/a/34829089/1335619 ) it also does NOT work at all. I don't understand why forcing a complete application-change-detection does nothing, but a zone-run call somehow works.

Example code with tick call which does not work:

constructor(private applicationRef:ApplicationRef) {
    this.interval = setInterval(() => {
        this.status = !this.status;
        console.log('status: ' + this.status);
        applicationRef.tick();
    }, 2000);
}
1

There are 1 answers

1
szmeti On BEST ANSWER

There is a bug in the Angular version you are using. Change detection does not work as it should in ng-upgraded apps.

Please refer to https://github.com/angular/angular/issues/12318 for further details.

If you upgrade to 2.4.4, it should resolve the issue.