我有两个组件:ParentComponent > ChildComponent
和一个服务,例如 TitleService
。
ParentComponent
看起来像这样:
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
的代码如下:
export class ChildComponent implements OnInit {
constructor (
private route: ActivatedRoute,
private titleService: TitleService
) {
}
ngOnInit (): void {
// Updating title.
this.titleService.setTitle(this.route.snapshot.data.title);
}
}
这个想法非常简单: ParentController
在屏幕上显示标题。为了始终呈现实际的标题,它订阅TitleService
并侦听事件。当标题更改时,事件发生,并更新标题。当
ChildComponent
加载时,它从路由器获取数据(动态解析),并告诉TitleService
使用新值更新标题。问题在于此解决方案会导致以下错误:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'Dynamic Title'.
看起来该值在变更检测轮次中已更新。我需要重新排列代码以获得更好的实现,还是必须在某个地方启动另一个变更检测轮次?
- 我可以将
setTitle()
和onTitleChange()
调用移动到相应的构造函数中,但我已经读过,在构造函数逻辑中进行任何“繁重”的工作(除了初始化本地属性)被认为是不好的做法。
- 此外,标题应该由子组件确定,因此无法从中提取此逻辑。更新
我已经实施了一个最小示例,以更好地说明这个问题。您可以在GitHub存储库中找到它。
经过彻底的调查,发现仅在
ParentComponent
中使用*ngIf="title"
时才会出现问题。<p>Parent Component</p>
<p>Title: "<span *ngIf="title">{{ title }}</span>"</p>
<hr>
<app-child></app-child>