0
votes

In our application, the router-outlet is place within a togglable sidebar inside the app.component. The visibility of the sidebar is managed by the SideBarService. The button used for toggling the sidebar uses an *ngIf to show either a show or hide arrow.

<div id="sidebar" [style.width.px]="this.sideBarService.getWidth()">
    <router-outlet></router-outlet>
    <a id="sidebar-toggle-button" (click)="toggleSideBar()">
        <div *ngIf="this.sideBarService.hidden; then inactive else active"></div>
        <ng-template #active><i class="material-icons ui-icon-chevron-left"></i></ng-template>
        <ng-template #inactive><i class="material-icons ui-icon-chevron-right"></i></ng-template>
    </a>
</div>

And the service:

@Injectable()
export class SideBarService{
    public width: number;
    public hidden;

    public changeWidth(width: number): void {
        this.width = width;
    }

    public getWidth(): number {
        return (this.hidden === true ? 0 : this.width);
    }

    public hide(): void {
        this.hidden = true;
    }

    public show(): void {    
        this.hidden = false;
    }
}

Now, the place where the sideBarService.hidden value is being changed is from components shown in the router-outlet. Maybe that is the reason why I'm constantly getting the following error:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: undefined'. Current value: 'ngIf: false'.

I believe it is because the change is triggered from a child component of the app-component. But I do not understand how I should solve this issue.

1
instead of directly calling the service variable from template call it through the component - Sachila Ranawaka
You mean binding a local variable inside app.component to the the service.hidden variable and check the local variable in *ngIf? - netik
You could move your hide call into a setTimeout with a delay of 0 so that it runs on the next tick? - Alex Peters
@AlexPeters: This is exactly how I solved it as a workaround, but it feels to me like using setTimeout isn't a proper solution. Or is it the only way in my case? - netik
try to test constructor(private cdr:ChangeDetectorRef){} and after hide call this.cdr.detectChanges . - Fateme Fazli

1 Answers

3
votes

you can use angular ChangeDetectorRef that Checks view and its children. Use in combination with detach to implement local change detection checks.

import { Component , ChangeDetectorRef } from '@angular/core';

...

constructor(private cdr:ChangeDetectorRef){}

and after hide call:

this.cdr.detectChanges();