我一直在玩ng-template,发现自己正在努力解决某些问题。
我在stackblitz中创建了一个简单的例子。 https://stackblitz.com/edit/angular-template-series-v?file=app%2Fparent%2Fparent1.component.ts
父组件通过模板将 Hello 组件发送到子组件。
这个想法是从中控制模板 儿童使用 ngIf 。
当 Hello 组件到达 ngOnInit 挂钩周期时,它会发出输出。父组件抓取此输出并添加要显示的消息。
但是创建组件时不会显示消息,而是在一个周期之后显示。在示例中,您需要单击两次按钮"切换内容"为了使消息"模板创建"出现。
如何解决这个问题,以使消息出现在同一个循环中?
答案 0 :(得分:3)
这是由于角度变化检测的工作原理。它与使用ng-template
无关,因为此modified stackblitz显示(使用不使用ng-template的大孩子)。
关于变化检测的2件事要知道:
基本上,当您点击切换按钮时:
showContent
的值,但没有其他内容。hello
组件在此阶段 showContentValue
已更改。这会导致创建hello
组件。 hello
组件后,会发出created
事件。父组件收到此事件并相应地更新消息数组。但是此时已经检查了父母的内容,因此Angular不会修改父视图。再次单击切换按钮时,会再次运行相同的循环,这次显示上一循环中数组中推送的消息
这是一个带有生命周期挂钩日志的modified stackblitz example
注意强>
强制更改检测再次运行将在此处解决您的问题,但如果您没有直接从子级到父级进行通信,则最好使用共享服务(基于observables)在不相关的组件之间广播消息
答案 1 :(得分:1)
如果您在父级中运行changeDetector,它将从第一次单击更新页面。
以下是一个示例:stackblitz
答案 2 :(得分:1)
这是因为你打破了Angular变化检测周期。
点击第一个切换按钮后,会发生以下情况:
1-您点击切换
2- ZoneJS,其中点击事件中的钩子会收到通知(简单地说)
3- Zone确保点击事件已完成
4- Zone将让Angular知道发生了async
事件,我们必须检查组件并确保视图(DOM)反映模型(模型/ javascript世界)。因此change detection
将运行,它将检测所有更改并发现模板中存在绑定(ng-if
)。
5- ngIf
变为真后,并注意,这是完全同步,ng-container
已创建,templateOutlet
已创建,您的将呈现hello
组件。
再次注意:这是全部同步。
6 - 视图已更新,更改检测现已解除。
7 - 在您的hello
组件中,您可以触发另一个created
事件并通知父组件,并在您的父组件内改变数组,并且Angular并不关心这一点,因为没有异步事件,也没有更新任何输入,你刚刚在更改检测完成工作后触发了另一个同步事件。
8-现在更新了数组,但视图不是(在父组件中)
9 - 再次单击,同样发生,Angular在第一次更改检测运行中更新父视图,并且您看到数组正在视图中反映。
证明我是对的: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的变化检测器已经完成并且您在没有运行的情况下更新了模型任何async
事件,所以我们遇到麻烦,因为Angular不喜欢在更改检测完成后更新模型,
所以你会被抛出一个错误,上面写着:
ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。以前的值
要解决这个问题,first read my other post to understand it,其次,您需要告诉Angular您知道自己在做什么并手动运行更改检测:
this._cd.detectChanges
或者,您可以将其设为异步:
onTemplateCreated() {
console.log('Got the event from chil');
setTimeout(()=>{
this.template = [...this.template,'Template created'];
})
}