I have a angular 4 application.
The app.component.html looks like this
<!--The content below is only a placeholder and can be replaced.-->
<loading-bar color="#FF0000" [height]="3" [animationTime]="0.3" [runInterval]="100" [progress]="0"></loading-bar>
<div class="app app-header-fixed" value="test">
<app-header *ngIf="header" name="app_header"></app-header>
<app-left-menu *ngIf="leftmenu" name="app_leftMenu"></app-left-menu>
<div id="content" class="app-content abc_plain_wraper" role="main">
<router-outlet></router-outlet>
</div>
<app-right-menu *ngIf="rightmenu" name="app_rightMenu"></app-right-menu>
</div>
Basically on Login they are hidden and after successful login they are visible again on logout they are hidden.
For this i have a mange_component service which emits a event on change in value of the header,rightmenu and left menu.
Manage Component service method
import { Component, NgModule, VERSION, Injectable, Output, EventEmitter} from '@angular/core';
import { ComponentStatus } from '../model/component-status';
import { Observable } from 'rxjs/Rx';
@Injectable()
export class ManageComponentStatusService {
@Output() OnChange: EventEmitter<any> = new EventEmitter();
@Output() ManageHeader: EventEmitter<any> = new EventEmitter();
componentStatus: any;
constructor() {
this.componentStatus = new ComponentStatus();
this.componentStatus.header = false;
this.componentStatus.leftMenu = false;
this.componentStatus.rightMenu = false;
this.componentStatus.plainHeader = true;
}
public setComponentStatus(header: any, left: any, right: any) {
this.componentStatus.header = header;
this.componentStatus.leftMenu = left;
this.componentStatus.rightMenu = right;
this.OnChange.emit(this.componentStatus);
}
public getComponentStatus(): Observable<any> {
return this.OnChange;
}
public setPlainHeader(status:any) {
this.componentStatus.plainHeader = status;
var self = this;
setTimeout(function () {
self.ManageHeader.emit(self.componentStatus.plainHeader);
}, 300);
}
public restoreHeader() {
this.componentStatus.plainHeader = true;
}
public getHeaderStatus(): Observable<any>{
return this.ManageHeader;
}
}
In the app.component.ts we set the component value and also have a subscribe to check the changes
export class AppComponent implements OnInit {
title = 'test App';
authSettings: any;
appConstants: any;
_router: any;
header: any=false;
leftmenu: any=false;
rightmenu: any=false;
compModel: any;
constructor(private ngAuthService: NgAuthService, private tenantServ: TenantService,
private loadingBarService: LoadingBarService, private router: Router,
public manageComp: ManageComponentStatusService,public socialServ: SocialMediaSettingsService) {
this._router = router;
this.authSettings = ngAuthService.getNgAuth();
this.appConstants = ngAuthService.getAppConstant();
//this.manageComp.setComponentStatus(false, false, false);
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.loadingBarService.start();
//console.log("navigation start");
}
else if (event instanceof NavigationEnd) {
//console.log("navigation end");
this.loadingBarService.complete();
}
});
this.compModel = this.manageComp.getComponentStatus();
}
logOut() {
this.manageComp.setComponentStatus(false, false, false);
this._router.navigate(['login']);
}
ngOnInit() {
//this.header = this.compModel.header;
//this.leftmenu = this.compModel.leftMenu;
//this.rightmenu = this.compModel.rightMenu;
this._router.navigate(['login']);
/*subscribing to change*/
this.manageComp.getComponentStatus().subscribe(
data => {
this.header = data.header;
this.leftmenu = data.leftMenu;
this.rightmenu = data.rightMenu;
});
}
ngOnChanges() {
alert("change detected");
}
}
after successful login the route loaded is abc Below is the abc component .I'm adding only the relevant part
ngOnInit() {
this.compStatus.setComponentStatus(true, true, true);//UPDATING HERE
this.userServ.setUserProfile();
this.compStatus.setPlainHeader(true);
this.opts = {
barBackground: '#C9C9C9',
gridBackground: '#D9D9D9',
barBorderRadius: '1',
barWidth: '2',
gridWidth: '1'
};
this.socialMediaData = this.socialSetServ.getSocialProvider();
this.isContentEditable = this.commonServ.getAppConfig().contentEditable;
}
when the login page loads i get this exception.
ng:///AppModule/AppComponent.ngfactory.js:40 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'false'.
i know i had something to do with change detaction ,but have no idea how to correct this.
This error happens because the value of the variable changed before change Detection can work on the previous one.
A work around for this is to use The setTimeout function schedules a macrotask then will be executed in the following VM turn.
you can put this code this.compStatus.setComponentStatus(true, true, true);
inside set Timeout
setTimeout(() => {
this.compStatus.setComponentStatus(true, true, true);
}, 0);