Angular 模板通信延迟一滴
Angular template communication one tick delayed
我一直在使用 ng-template 并发现自己遇到了某种延迟。
我在 stackblitz 中创建了一个简单的示例。 https://stackblitz.com/edit/angular-template-series-v?file=app%2Fparent%2Fparent1.component.ts
A Parent 组件通过模板将 Hello 组件发送给它的 Child。
我们的想法是从
Child 使用 ngIf.
当 Hello 组件到达 ngOnInit 挂钩周期时,它会发出输出。父组件获取此输出并添加一条消息以显示。
但是在创建组件时而不是在一个周期之后显示该消息。在示例中,您需要单击按钮 "Toggle content" 两次才能显示消息 "Template created"。
如何解决此问题才能使消息出现在同一周期中?
如果您 运行 在父项中更改检测器 - 它将从第一次点击开始更新页面。
这里有一个例子:stackblitz
这是因为您打破了 Angular 变更检测周期。
这是您单击第一个切换按钮后发生的情况:
1- 您点击了开关
2- ZoneJS,在点击事件中有钩子得到通知(简单地说)
3- Zone 确保点击事件完成
4- Zone 会让 Angular 知道发生了 async
事件,我们必须检查组件并确保视图 ( DOM ) 反映了模型 ( model/the 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: Expression has changed after it was checked. Previous value
要解决此问题,,其次,您需要告诉 Angular 您知道自己在做什么,并且 运行 手动更改检测:
this._cd.detectChanges
或者,你可以让它异步:
onTemplateCreated() {
console.log('Got the event from chil');
setTimeout(()=>{
this.template = [...this.template,'Template created'];
})
}
这是由于 Angular 中更改检测的工作方式。它没有链接到使用 ng-template
,因为这个 modified stackblitz 显示(使用孙子而不使用 ng-template)。
关于变化检测需要知道的 2 件事:
- 它在事件(点击、提交等)、xhr 请求或计时器后触发。
- 它从组件树的顶部到底部出现
基本上,当您单击切换按钮时:
- 处理程序被触发(在您的示例中切换
showContent
的值,但仅此而已。hello
组件是 NOT 在此阶段创建)
- 更改检测开始,始终从上到下开始。
- angular首先检查父组件是否需要更新视图。由于尚未发生任何变化,父视图未更新。
- 子组件随后检测到其
showContentValue
已更改。这会导致创建 hello
组件。
hello
组件初始化后,created
事件被触发。父组件收到此事件并相应地更新消息数组。但是父视图此时已经检查过了,所以Angular不会对父视图做任何修改。
当你再次点击切换按钮时,则再次循环运行s,此时显示上一次循环推送到数组中的消息
这是一个 modified stackblitz example 生命周期挂钩日志
备注
强制更改检测再次 运行 将解决您的问题,但如果您不是直接从子级到父级进行通信,您最好使用共享服务(基于可观察对象)在它们之间广播消息不相关的组件
我一直在使用 ng-template 并发现自己遇到了某种延迟。
我在 stackblitz 中创建了一个简单的示例。 https://stackblitz.com/edit/angular-template-series-v?file=app%2Fparent%2Fparent1.component.ts
A Parent 组件通过模板将 Hello 组件发送给它的 Child。
我们的想法是从 Child 使用 ngIf.
当 Hello 组件到达 ngOnInit 挂钩周期时,它会发出输出。父组件获取此输出并添加一条消息以显示。
但是在创建组件时而不是在一个周期之后显示该消息。在示例中,您需要单击按钮 "Toggle content" 两次才能显示消息 "Template created"。
如何解决此问题才能使消息出现在同一周期中?
如果您 运行 在父项中更改检测器 - 它将从第一次点击开始更新页面。
这里有一个例子:stackblitz
这是因为您打破了 Angular 变更检测周期。
这是您单击第一个切换按钮后发生的情况:
1- 您点击了开关
2- ZoneJS,在点击事件中有钩子得到通知(简单地说)
3- Zone 确保点击事件完成
4- Zone 会让 Angular 知道发生了 async
事件,我们必须检查组件并确保视图 ( DOM ) 反映了模型 ( model/the 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: Expression has changed after it was checked. Previous value
要解决此问题,
this._cd.detectChanges
或者,你可以让它异步:
onTemplateCreated() {
console.log('Got the event from chil');
setTimeout(()=>{
this.template = [...this.template,'Template created'];
})
}
这是由于 Angular 中更改检测的工作方式。它没有链接到使用 ng-template
,因为这个 modified stackblitz 显示(使用孙子而不使用 ng-template)。
关于变化检测需要知道的 2 件事:
- 它在事件(点击、提交等)、xhr 请求或计时器后触发。
- 它从组件树的顶部到底部出现
基本上,当您单击切换按钮时:
- 处理程序被触发(在您的示例中切换
showContent
的值,但仅此而已。hello
组件是 NOT 在此阶段创建) - 更改检测开始,始终从上到下开始。
- angular首先检查父组件是否需要更新视图。由于尚未发生任何变化,父视图未更新。
- 子组件随后检测到其
showContentValue
已更改。这会导致创建hello
组件。 hello
组件初始化后,created
事件被触发。父组件收到此事件并相应地更新消息数组。但是父视图此时已经检查过了,所以Angular不会对父视图做任何修改。
当你再次点击切换按钮时,则再次循环运行s,此时显示上一次循环推送到数组中的消息
这是一个 modified stackblitz example 生命周期挂钩日志
备注
强制更改检测再次 运行 将解决您的问题,但如果您不是直接从子级到父级进行通信,您最好使用共享服务(基于可观察对象)在它们之间广播消息不相关的组件