Search code examples
javascriptangularng-class

Angular 2 change detection, ngClass model gets updated by sibling component but the template does't get refreshed


I have a setup that goes something like this:

main.ts - Usual structure, no problem there

app.component.ts - Usual structure, no problem there

app.component.html

<navigation-cart></navigation-cart>
<div id="content">
    ... other stuff ...
    <start-arrow></start-arrow>
</div>

ui-layout.service.ts - This service takes care of opening and closing all regions in the app.

import {Injectable} from '@angular/core';

@Injectable()

export class UiLayoutService {

    startArrowIsVisible: boolean = true;

    openStartArrow() {
        this.startArrowIsVisible = true;
        console.log('openStartArrow', this.startArrowIsVisible);
    }

    closeStartArrow() {
        this.startArrowIsVisible = false;
        console.log('closeStartArrow', this.startArrowIsVisible);
    }
    ...

}

Both navigation-cart.component.ts and start-arrow.component.ts have the UiLayoutService injected

start-arrow.component.html

<a (click)="uiLayoutService.openStartArrow();">Open</a>
<a (click)="uiLayoutService.closeStartArrow();">Close</a>

<div id="start-arrow" [ngClass]="{active: uiLayoutService.startArrowIsVisible}">
    ... Some pretty content here ...
</div>

navigation-cart.component.html

<a (click)="uiLayoutService.openStartArrow();">Open</a>
<a (click)="uiLayoutService.closeStartArrow();">Close</a>
... Some other content...

Here's what's troubling me:

  • If I click on Open and Close links from within <start-arrow> it all works just fine. The template gets updated and <div id="start-arrow"> get's closed and or reopened as expected. The console shows these actions running just fine.
  • If I click on Open and Close links from within <navigation-cart> the console fires the messages, I see the startArrowIsVisible value getting updated, but no template update happens in <start-arrow>. [ngClass]="{active: uiLayoutService.startArrowIsVisible} just doesn't seem to trigger the template update. I guess that this is because of optimisations done in order to prevent rendering the layout for all the app needlessly. How can I trigger the layout refresh in this situation?

Solution

  • I assume you provide the UiLayoutService service multiple times and when click, the value is updated on a different instance than the one the components view is bound to. Angular DI creates a new instance per provider. If you want a single instance to be shared among your application, just share it once on the root component (or another common parent)