【问题标题】:Angular2 component: impossible to emit an @Output EventEmitter inside ngOnDestroy?Angular2 组件:不可能在 ngOnDestroy 中发出 @Output EventEmitter?
【发布时间】:2016-06-03 08:39:45
【问题描述】:

[angular2 rc1]

有没有可能有这样的组件:

export class MyComp {
  @Output() myEvent = new EventEmitter(false)

  ngOnDestroy() {
    this.myEvent.emit('ngOnDestroy hook');
  }
}

并在父级中捕获它:

<myComp (myEvent)="test($event)"></myComp>

这似乎是不可能的,但我想知道为什么?

我知道我可以使用服务来遍历。

Plunker here

【问题讨论】:

标签: angular


【解决方案1】:

服务是一个很好的解决方案,但您想了解原因:

TL/DR 解释:

在调用 ngOnDestroy() 之前,该订阅已为您取消订阅。之所以会发生这种自动化,是因为您订阅了一个模板,对模板进行了角度解析,并且足够了解这样做。

更详细的解释:

Angular为每个组件生成一个工厂,这个过程称为代码生成,Angular创建的工厂函数返回一个根据你的组件元数据创建的唯一View类型的实例,继承AppView 类。

组件实例的整个生命周期由视图管理。例如,变更检测,当然还有生命周期挂钩。

在调用 ngOnDestroy 生命周期钩子之前(通过组件的内部视图)调用内部销毁函数,它会为您做一些清理工作。 清理操作之一是取消订阅所有事件,例如单击、鼠标移动和 自定义事件,这些事件是在子组件中定义的 @Output 发射器。

这是静态完成的,这意味着代码生成器知道事件订阅,因此它添加了硬编码逻辑来删除它。 重要的是,这可以节省大量样板代码并防止内存泄漏 - 框架会为您清理。

同样重要的是要注意 Angular 可以做到这一点,因为它有很多关于正在发生的事情的元数据。 它知道它是一个订阅,因为它解析 Template 并找到一个事件表达式(例如:(click)="something()"),还因为 MyComp 中的 @Output 装饰器

AppView 类中可以很清楚地看到逻辑 - LINK 每个视图都继承自 AppView 并实现(除其他外)一个名为 destroyInternal 的函数。 顺序是在销毁时调用 destroyLocal()(定义在 AppView 上),destroyLocal 将清理所有订阅和一次性项目,然后它会调用 destroyInternal然后 destroyInternal 将在您的组件实例上调用 ngOnDestroy()

解决方案:

现在,服务可能是一个很好的解决方案,但如果您了解发生了什么,您可以在没有外部服务帮助的情况下解决这个问题,这非常简单。 正如我们现在所知,Angular 将取消订阅模板中注册的订阅,如果我们手动注册(即:使用代码),我们将能够触发事件。

虽然简单,但它有一些样板,因为我们需要:

  • 我们需要获取对子组件的引用,我们将使用 ViewChild
  • 我们需要订阅子组件实例上的事件,但我们只有可以在ngAfterViewInit 生命周期钩子被触发后完成。
  • 我们需要在完成后取消订阅,这可以通过多种方式完成,最好的方法是完成 EventEmitter - 我们将在定义 EventEmitter 的组件中简单地通过致电myEvent.complete()。它会为我们处置一切。

这里是销毁函数:

ngOnDestroy() {
  this.myEvent.emit('ngOnDestroy hook');
  this.myEvent.complete();
}

这里是 MyApp 组件:

export class MyApp {
  displayComp = true

  @ViewChild('mycomp', {read: MyComp}) myComp: MyComp;
  constructor() {}

  ngAfterViewInit() {
    this.myComp.myEvent.subscribe( () => alert('myApp > receive event via MANUAL Subscription'));
  }

  test(e) {
    alert('myApp > receive event');
  }
}

这是一个工作的 plunker - LINK

更新:我刚刚在 Angular 存储库中注意到此问题的 pending PR,现在 12 天仍未合并,合并社区贡献需要时间,所以希望它会在下一个 RC 中落地。

【讨论】:

    【解决方案2】:

    这个 PR 应该可以解决这个问题。它改变了在事件分离之前调用ngOnDestroy() 的行为。

    https://github.com/angular/angular/pull/9946

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-19
      • 2018-12-25
      • 2017-12-15
      • 1970-01-01
      • 2017-06-26
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      相关资源
      最近更新 更多