我一直在玩ng-template,发现自己在某种延迟中挣扎。
我在Stackblitz中创建了一个简单的示例。https://stackblitz.com/edit/angular-template-series-v?file = pparent/parent1.component.ts
a parent 组件通过模板将 hello 组件发送到其 child 通过模板。
这个想法是从儿童使用 ngif 。
Hello 组件到达 ngoninit 钩周期时,它会发出输出。父组件抓住此输出并添加消息显示。
但是,当创建组件而不是稍后一个周期时,该消息未显示。在示例中,您需要单击两次按钮"切换内容"才能使消息"创建"显示。
我该如何解决此问题以使消息出现在同一周期中?
这是由于变化检测如何在Angular中起作用。它与使用ng-template
没有链接,因为此修改后的Stackblitz显示(使用Grand Child而不使用NG-Template(。
2个有关变更检测的事情:
- 事件发生后触发(单击,提交,..(,XHR请求或计时器。
- 它发生在组件树的顶部到底部
基本上,当您单击"切换"按钮时:
- 触发处理程序(在您的示例中切换
showContent
的值,但别无其他。hello
组件是不是在此阶段创建的( - 更改检测启动,始终从上到下开始。
- Angular首先检查是否需要为父组件更新视图。由于什么都没有改变,因此尚未更新父的视图。
- 然后,子组件检测其
showContentValue
是否已更改。这导致创建hello
组件。 -
hello
组件初始化后,发出了created
事件。父组件收到了此事件,并相应地更新了消息的数组。但是目前已经检查了父母的内容,因此Angular不会对父级视图进行修改。
再次单击"切换"按钮时,同一周期再次运行,这次显示了上一个周期中的数组中的消息
这是一个修改的stackblitz示例,其中具有生命周期钩的日志
注意
强迫变更检测再次运行将在这里解决您的问题,但是如果您不是直接从子女到父母的交流,则最好使用共享服务(基于可观察到的(来广播无关组件之间的消息
这是因为您正在打破角变化检测周期。
单击第一个切换按钮后,正在发生的事情:
1-您单击切换
2-区域J,在单击事件中有挂钩的通知(只需放置(
3-区域确保点击事件完成
4-区域将使Angular知道async
事件发生了,我们必须检查组件并确保视图(DOM(反映模型(模型/JavaScript世界(。因此change detection
将运行,它将检测所有更改并发现模板中有绑定(ng-if
(。
5- ngIf
变为真后,,请注意,这是到目前为止的同步,创建ng-container
并创建templateOutlet
并创建您的hello
组件。
再次注意:这都是同步。
6-视图已更新,更改检测现已脱离钩子。
7-在您的hello
组件内部,您要触发另一个事件,即created
,并通知父组件,并且在您的父组件内您突变数组,而Angular并不关心它,因为没有异步事件,您也没有更新任何输入,在变更检测完成后,您刚刚解雇了另一个同步事件。
8-数组现在已更新,但是该视图不是(在父组件中(
9-您再次单击,同样的事件发生,并且角度更新了第一个更改检测运行中的父视图,并且您会看到数组在视图中反射。
证明我是对的:d:
onTemplateCreated() {
console.log('Got the event from chil');
this.template.push('Template created');
console.log('this.template',this.template);
}
您会看到,单击后,控制台显示正确的数组,但是视图不是您再次单击的视图。
现在让我们转到我的代码
onTemplateCreated() {
console.log('Got the event from chil');
this.template = [...this.template,'Template created'];
}
由于我没有突变并且正在更新扭矩,因此Angular现在必须关心此更改,,但是仍然存在问题,Angular的更改检测器已完成,您在运行intouth的情况下更新了模型任何async
事件,所以我们遇到麻烦,因为Angular不喜欢在更改检测完成后更新模型,
因此,您将被抛出一个错误,说:
表达changedafterithasbeencheckederror:表达在检查后发生了变化。先前的值
要解决这个问题,首先阅读我的另一篇文章以理解它,其次,您需要告诉Angular,您知道自己在做什么并手动运行更改检测:
this._cd.detectChanges
或者,您可以使其异步:
onTemplateCreated() {
console.log('Got the event from chil');
setTimeout(()=>{
this.template = [...this.template,'Template created'];
})
}
如果您在父级中运行更改eTector-它将首次单击。
。这是一个示例:stackblitz