I've read the documentation on this error, and I think I understand why the issue arises. I'm having a problem trying to figure out an alternative architecture that would suit me.
The root component of my site (app.component) has a "loading" component on it. It's a third party component (ngx-loading) that basically throws up a loading indicator to show the user that something is happening in the app that will take a little time. The loading component takes a parameter that tells it whether to show up or not:
<ngx-loading [show]="loading" [config]="{ backdropBorderRadius: '14px' }"></ngx-loading>
I have a site service that holds the data for this, and my application gets the service data OnInit:
this.siteService.getLoading().takeUntil(this.ngUnsubscribe).subscribe(res => {this.loading = res})
The great thing about all this is that I can change the site service value from just about anywhere in my app and control whether this loading indicator pops up. The bad thing is that now I'm seeing this ExpressionChangedAfterItHasBeenCheckedError error.
Again, I think I understand why the error happens, but the solutions I've seen so far won't allow me to keep using this one simple component to handle all my "loading" calls. Or am I missing something?
SiteService:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class SiteService {
private currentRoute = new Subject<string>();
private currentAction = new Subject<string>();
private loading = new BehaviorSubject<boolean>(false);
constructor() {
}
public menuState:string = 'in';
toggleMenuPicker() {
this.menuState = this.menuState === 'out' ? 'in' : 'out';
}
getCurrentRoute(): Observable<string> {
return this.currentRoute.asObservable();
}
setCurrentRoute(route: string) {
this.currentRoute.next(route);
}
getCurrentAction(): Observable<string> {
return this.currentAction.asObservable();
}
setCurrentAction(action: string) {
this.currentAction.next(action);
}
getLoading(): Observable<boolean> {
return this.loading.asObservable();
}
setLoading(show: boolean) {
this.loading.next(show);
}
}
app.component
import { Component, OnDestroy, AfterContentInit } from '@angular/core';
import { trigger, state, style, transition, animate} from '@angular/animations';
import { SiteService } from './site/site.service';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
animations: [
trigger('menuState', [
state('in', style({
transform: 'translate3d(-100%, 0, 0)'
})),
state('out', style({
transform: 'translate3d(0, 0, 0)'
})),
transition('in => out', animate('200ms ease-in-out')),
transition('out => in', animate('200ms ease-in-out'))
])
]
})
export class AppComponent implements OnDestroy, AfterContentInit {
private ngUnsubscribe: Subject<void> = new Subject<void>();
public loading;
constructor(public siteService: SiteService) {
}
ngAfterContentInit() {
this.siteService.getLoading().takeUntil(this.ngUnsubscribe).subscribe(res => {this.loading = res})
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}