7

我一直在玩 ng-template,发现自己在为某种延迟而苦苦挣扎。

我在 stackblitz 中创建了一个简单的示例。https://stackblitz.com/edit/angular-template-series-v?file=app%2Fparent%2Fparent1.component.ts

Parent组件通过模板向其Child发送Hello组件。

这个想法是使用ngIfChild控制模板。

Hello组件到达ngOnInit钩子循环时,它会发出一个输出。父组件获取此输出并添加要显示的消息。

但是在创建组件时不会显示该消息,而是在一个周期后显示。在示例中,您需要单击两次“切换内容”按钮才能显示“已创建模板”消息。

我该如何解决这个问题才能使消息出现在同一个周期中?

4

3 回答 3

4

这是由于变化检测在 Angular 中的工作方式。它没有链接到 using ng-template,因为这个修改后的 stackblitz显示(使用孙子而不使用 ng-template)。

关于变更检测需要了解的 2 件事:

  • 它在事件(单击、提交、..)、xhr 请求或计时器之后触发。
  • 它从组件树的顶部到底部发生

基本上,当您单击切换按钮时:

  • 处理程序被触发(showContent在您的示例中切换 's 值,但仅此而已。在此阶段未创建组件hello
  • 变化检测开始,总是从上到下。
  • 角度首先检查是否需要为父组件更新视图。由于尚未发生任何变化,因此父视图未更新。
  • 然后子组件检测到它showContentValue已经改变。这会导致hello组件的创建。
  • 初始化hello组件后,将created发出事件。父组件收到此事件并相应地更新消息数组。但是此时已经检查了父视图的内容,因此 Angular 不会对父视图进行任何修改。

再次单击切换按钮时,将再次运行相同的循环,这一次将显示上一个循环中推入数组的消息

这是一个修改过的 stackblitz 示例,其中包含生命周期挂钩的日志

笔记

强制更改检测再次运行将解决您的问题,但如果您不直接从子级到父级进行通信,您最好使用共享服务(基于可观察对象)在不相关的组件之间广播消息

于 2018-04-09T12:38:18.720 回答
2

这是因为您打破了 Angular 更改检测周期。

这是您单击第一个切换按钮后发生的情况:

1-您单击切换

2- ZoneJS,在点击事件中有钩子被通知(简单地说)

3-区域确保点击事件完成

4- Zone 会让 Angular 知道async事件已经发生,我们必须检查组件并确保视图(DOM)反映了模型(模型/javascript 世界)。所以change detection会运行,它会检测所有的变化并发现模板中有绑定(ng-if)。

5- 之后ngIf变为真,请注意,到目前为止,这一切都是同步的,ng-container被创建的和你的templateOutlet被创建的,你的hello组件将被渲染。

再次注意:这都是同步的。

6 - 视图已更新,更改检测现已解除。

7 - 在你的hello组件内部你触发另一个事件,它是created通知父组件和你的父组件内部你正在改变数组,而 Angular 并不关心这一点,因为没有异步事件,你也没有更新了任何输入,您刚刚在更改检测完成他的工作后触发了另一个同步事件。

8- Array 现在已更新,但视图未更新(在父组件中)

9 - 再次单击,同样的情况发生,Angular 在第一次更改检测运行中更新父视图,您会看到数组反映在视图中。

为了证明我是对的:D:

 onTemplateCreated() {
    console.log('Got the event from chil');
   this.template.push('Template created');
    console.log('this.template',this.template);
  }

您会看到在 click 之后,控制台显示了正确的数组,但是直到您再次单击后才会显示视图。

现在让我们切换到我的代码

 onTemplateCreated() {
    console.log('Got the event from chil');
   this.template = [...this.template,'Template created'];
  }

因为我没有发生变异并且引用正在更新,Angular 现在必须关心这个变化,但是仍然有一个问题,Angular 的变化检测器已经完成并且你在没有运行任何async事件的情况下更新了模型,所以我们遇到了麻烦,因为 Angular 不喜欢在变更检测完成后更新模型,

所以你会被抛出一个错误,上面写着:

ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。以前的值

要解决这个问题,首先阅读我的另一篇文章以了解它,其次,您需要告诉 Angular 您知道自己在做什么并手动运行更改检测:

  this._cd.detectChanges

或者,您可以使其异步:

onTemplateCreated() {
    console.log('Got the event from chil');
   setTimeout(()=>{
      this.template = [...this.template,'Template created'];
   })
  }
于 2018-04-09T12:10:42.743 回答
1

如果你在 parent 中运行 changeDetector - 它会从第一次点击更新页面。

这是一个例子:stackblitz

于 2018-04-05T17:27:12.067 回答