Reputation: 1275
I have the following Angular component (simplified for question):
<md-sidenav-container fxFlex>
<div fxFlex class="app-content">
This is the content
</div>
<button *ngIf="newsbar.opened" type="button" (click)="closeBar(newsbar)">Close</button>
<button *ngIf="!newsbar.opened" type="button" (click)="openBar(newsbar)">Open</button>
<md-sidenav #newsbar mode="side" [opened]="showNewsBar()" align="end" class="app-news-bar">
<app-news-bar></app-news-bar>
</md-sidenav>
</md-sidenav-container>
And the following is the function which dictates whether the newsbar is open or not (based on the window width).
showNewsBar() {
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
if (width >= 1280) {
return true;
} else {
return false;
}
}
When running the app, everything works as expected. When the window is smaller or becomes smaller than 1280px, the sidebar closes and the "Open" button is shown. When the window is larger or becomes larger than 1280px, the sidebar opens and the "Close" button is shown. But, whenever the window is resized to trigger a change in the function showNewsBar()
, I get an error.
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.
When I resize the window so it becomes larger and the newsbar opens, Previous value: 'false'. Current value: 'true'.
The line that gives me the error are the buttons. For some reason, Angular doesn't like that I'm binding the *ngIf directives to the local MdSidenav reference's "opened" property.
Again, everything works as expected. Nothing wrong with the functionality. So why does this error occur? And is there a way to fix it?
Upvotes: 0
Views: 2345
Reputation: 13307
You will find some good reasons for this error from this question. Basically, [opened]="showNewsBar()" is causing this error.
Quoting @GünterZöchbauer from his answer,
Don't bind to methods or functions in the view, instead bind to fields and update the fields in event handlers. If you must bind to methods, ensure that they always return the same value instance as long as there wasn't actually a change. Change detection will call these methods a lot.
Instead of using [opened]="showNewsBar()"
, you can bind opened
to a boolean variable, and set/change the variable value using the ngOnInit
lifecycle hook and window:resize
event. Here's an example:
html:
<div fxFlex class="app-content">
This is the content
</div>
<button *ngIf="openFlag" type="button" (click)="sidenav.close(); openFlag = false">Close</button>
<button *ngIf="!openFlag" type="button" (click)="sidenav.open(); openFlag = true">Open</button>
<md-sidenav-container (window:resize)="onResize($event)" style="height: 91vh;background: #FFFFFF">
<md-sidenav #sidenav mode="side" [opened]="openFlag" align="end" style="background: orange">
Sidenav!
</md-sidenav>
</md-sidenav-container>
Option 1: (window:resize)
ts:
ngOnInit(){
this.onResize();
}
onResize(event) {
let width;
if(event != undefined){
width = event.target.innerWidth;
console.log(event.target.innerWidth);
}
else{
console.log(document.body.clientWidth);
width = document.body.clientWidth;
}
if (width >= 1280) {
this.openFlag = true;
}
else {
this.openFlag = false;
}
}
Option 2: Flex-layout
I see that you have fxFlex
means you are using flex-layout. Flex-layout comes with ObservableMedia
(doc). You can use that too to set the boolean value of openFlag
.
This option doesn't require (window:resize)
event in the view.
ts:
import {MediaChange, ObservableMedia} from "@angular/flex-layout";
constructor(media: ObservableMedia) {
media.asObservable()
.subscribe((change: MediaChange) => {
if(change.mqAlias == 'lg' || change.mqAlias == 'xl'){
this.openFlag = true;
}
else{
this.openFlag = false;
}
});
}
Upvotes: 3