【问题标题】:Angular template communication one tick delayed角模板通信延迟一滴
【发布时间】:2018-09-07 00:59:54
【问题描述】:

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

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

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

这个想法是从 Child 使用 ngIf

Hello 组件到达 ngOnInit 钩子循环时,它会发出一个输出。父组件抓取这个输出并添加一条消息来显示。

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

我怎样才能解决这个问题以使消息出现在同一个周期中?

【问题讨论】:

  • 您可以在不明确使用 ng-template 的情况下拥有相同的行为:stackblitz.com/edit/… Just Child -> 祖父母通信延迟

标签: angular ng-template


【解决方案1】:

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

这里是一个例子:stackblitz

【讨论】:

  • 感谢您的回复。如果我运行 changeDetector 我不只是在寻找一个新的周期?我想知道为什么输出和视图之间存在循环不匹配。
  • 我认为当你只插入带有 ng-content 的组件时 - 它需要一个勾号。但是,如果您插入模板,则 ngOutlet 会创建带有子组件的视图 - 它可能需要更多的滴答声,并且在这种情况下,父属性更新可能会在 changeDetection 循环之后完成。所以需要另外触发
  • 您可以尝试单独放置模板并将其作为输入参数提供给孩子。也许会有所帮助,但不确定
【解决方案2】:

这是因为您打破了 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- 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:表达式在检查后已更改。以前的值

要解决这个问题,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'];
   })
  }

【讨论】:

    【解决方案3】:

    这是由于更改检测在 Angular 中的工作方式。它与使用 ng-template 无关,因为这个 modified stackblitz 显示(使用孙子而不使用 ng-template)。

    关于变更检测的 2 件事:

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

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

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

    当你再次点击切换按钮时,同样的循环再次运行,这次显示上一个循环推入数组的消息

    这是一个modified stackblitz example,带有用于生命周期挂钩的日志

    注意

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

    【讨论】:

      猜你喜欢
      • 2017-09-25
      • 2020-04-03
      • 1970-01-01
      • 2020-09-18
      • 2016-03-16
      • 1970-01-01
      • 2018-05-22
      • 2021-02-12
      • 1970-01-01
      相关资源
      最近更新 更多