I have two component: ParentComponent > ChildComponent
and a service, e.g. TitleService
.
ParentComponent
looks like this:
export class ParentComponent implements OnInit, OnDestroy {
title: string;
private titleSubscription: Subscription;
constructor (private titleService: TitleService) {
}
ngOnInit (): void {
// Watching for title change.
this.titleSubscription = this.titleService.onTitleChange()
.subscribe(title => this.title = title)
;
}
ngOnDestroy (): void {
if (this.titleSubscription) {
this.titleSubscription.unsubscribe();
}
}
}
ChildComponent
looks like this:
export class ChildComponent implements OnInit {
constructor (
private route: ActivatedRoute,
private titleService: TitleService
) {
}
ngOnInit (): void {
// Updating title.
this.titleService.setTitle(this.route.snapshot.data.title);
}
}
The idea is very simple: ParentController
displays the title on screen. In order to always render the actual title it subscribes to the TitleService
and listens for events. When title is changed, the event happens and title is updated.
When ChildComponent
loads, it gets data from the router (which is resolved dynamically) and tells TitleService
to update the title with the new value.
The problem is this solution causes this error:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'Dynamic Title'.
It looks like the value is updated in a change detection round.
Do I need to re-arrange the code to have a better implementation or do I have to initiate another change detection round somewhere?
I can move the
setTitle()
andonTitleChange()
calls to the respected constructors, but I've read that it's considered a bad practice to do any "heavy-lifting" in the constructor logic, besides initializing local properties.Also, the title should be determined by the child component, so this logic couldn't be extracted from it.
Update
I've implemented a minimal example to better demonstrate the issue. You can find it in the GitHub repository.
After thorough investigation the problem only occurred when using *ngIf="title"
in ParentComponent
:
<p>Parent Component</p>
<p>Title: "<span *ngIf="title">{{ title }}</span>"</p>
<hr>
<app-child></app-child>