【问题标题】:Destroying and reloading a child component销毁和重新加载子组件
【发布时间】:2017-11-27 05:33:42
【问题描述】:

我需要能够完全重新加载子组件。似乎实现这一点的最佳方法是使用带有布尔值的简单*ngIf;设置为false删除组件,设置为true重新初始化,即:

<app-child *ngIf="enabled"></app-child>

但是,似乎仅仅这样做还不足以快速删除/重新初始化组件:

reloadTree(){
  this.enabled = false;
  this.enabled = true; // doesn't work- child ngOnInit isn't called
}

相反,我必须使用setTimeout 才能使用:

reloadTree(){
  this.enabled = false;
  const self = this;
  setTimeout(function(){
    self.enabled = true;
  }, 1);
}

我认为这与 Angular 呈现模板的方式有关?这不是特别优雅 - 任何人都可以提出更好的方法来实现我在这里尝试做的事情吗?谢谢

【问题讨论】:

  • 为什么首先需要重新加载它。这似乎是一个设计问题。相反,您可能应该向它传递一个不同的输入,或者让它对从 observable 发出的事件做出反应。
  • 您可能需要在标志切换之间主动点击角度变化检测。这个ChangeDetectionRef 在这里可能会有所帮助。注入它并在两者之间调用this.ref.detectChanges
  • 我承认这有点小题大做。不值得进入这里但足以说我已经花了几天时间解决一个特定的问题,这似乎是解决它的唯一方法。 @BenediktSchmidt-谢谢,您能详细说明一下吗?
  • 您当前需要使用 setTimeout 的问题是,角度可能永远不会注意到您已经更改了值。因此 DOM 保持不变,组件可能不会被破坏。我不是 100% 肯定,因为我不知道角度何时检测到变化,但是更改一个值并在下一行重新更改它可能太快了。并且 *ngIf 仅在 angular 获取变量的任何更改时更新。因此,我想您必须手动告诉 Angular 发生了一些变化。
  • 澄清一下,您只想在调用 reloadTree() 函数后调用子组件的 ngOnInit 方法?您可以使用@ViewChild 直接调用子组件的方法。

标签: angular


【解决方案1】:

所以只是为了记录,而不是试图争论这是否是一种干净的方法,这里是你可以解决标志切换问题的方法。主要思想是销毁子组件并在之后再次创建它,您可以在子组件上使用一个简单的 *ngIf 标志。

如果该标志切换为 false,则子组件将被销毁并从 DOM 中完全移除。之后您可以再次将其设置为 true 以创建一个新实例(如果我没记错的话)。

给定代码中的问题以及使用setTimeout 方法的变通方法的需要是角度需要知道更改以对其做出反应。在这种情况下,两行代码中的切换可能太快了,以至于 Angular 甚至无法掌握更改(或者编译器甚至根本删除了第一行,所以什么都没有改变,但不确定),因此组件既不会被移除,也不会创建新的实例。

reloadTree(){
  this.enabled = false; // switching to false
  this.enabled = true; // and back to true 
  // this does not notify angular that something has actually changed
}

我们需要做的是手动告诉 Angular 一个值发生了变化。这可以通过 angulars ChangeDetectionRef 类来完成,该类可以注入到组件中。在切换enabled 标志之间,我们通知 Angular 以查找更改,因此它可以通过完全删除组件来对此做出反应。然后我们可以将其设置回 true 以创建一个新实例。

constructor(private changeDetector: ChangeDetectorRef){}

reloadTree(){
    this.enabled = false;
    // now notify angular to check for updates
    this.changeDetector.detectChanges();
    // change detection should remove the component now
    // then we can enable it again to create a new instance
    this.enabled = true; 
}

【讨论】:

  • 很棒的东西,这工作做得很好。谢谢。
  • 工作得很好..谢谢!
  • 太棒了,这就是我们所说的hack。
  • 太棒了,这就是我们所说的hack。
  • 我需要添加“this.changeDetector.detectChanges();”将“启用”设置为 true 后。
【解决方案2】:

问题是你为什么要销毁组件,我认为这是错误的做法,将需要重新加载的数据放入服务并通过 RxJS 订阅者或 EventEmitter 通知组件发生了变化,其他组件可以通过将服务注入构造函数并调用触发方法来触发更新事件。

【讨论】:

  • 我听到你的声音响亮而清晰,并且已经尝试过你建议的一切,但由于各种原因它不起作用。这与带有嵌套列表的 Kendo UI 可排序插件有关。我不知道如何让它识别新数据。可能我不了解他们的方法,或者缺少文档,但我现在不能再浪费时间了。如有必要,我会坚持使用setTimeout...
  • 我认为您的问题是 Zone.js,角度使用区域概念来消除处理更改检测的开销。如果某些东西在 ngZone 之外运行,Angular 将不会更新数据,因为没有触发更改检测,当区域完成时,每个 Angular 任务都会在区域内运行,方法 oneEventDone 将被调用,并且 Angular 将启动 changeDetection(更改检测主题有效开箱即用,当您在角度世界中工作时,当您使用一些外部东西时,您需要触发它或在新的自定义 ngZone 中运行它(zone.run(() => {..})) )
  • 我有一个类似的用例:Angular-QueryBuilder 在初始化时接受配置对象,但在配置更改时不会更新。配置可以通过我表单上的其他控件进行更改。我想这可以通过路由和服务来解决,但开销似乎不值得。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
  • 2020-05-16
  • 2020-09-18
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多