【问题标题】:Moving a component to another parent component in Angular在 Angular 中将组件移动到另一个父组件
【发布时间】:2017-07-13 01:02:26
【问题描述】:

这个问题是 this question 的 Angular 版本。

下面的sn-p总结了我的第一次尝试:

class NewParentComponent {

  constructor(elRef: ElementRef) {
    elRef.nativeElement.appendChild(myMovableElement);
    // where the reference to myMovableElement may come from a service.
  }
}

此尝试存在以下问题:

  1. 我们不应该直接在 Angular 中操作 DOM。是否可以通过Renderer 以某种方式移动组件? (答案:根据cgTag's answer,使用Renderer2)。
  2. 如果移动子组件后原始父组件被销毁,则在子组件上调用ngOnDestroy方法。

请参阅 this Plunker 演示该问题。这个 Plunker 建立在 Angular Dynamic Component Loader example 之上。打开浏览器控制台日志,注意“destroyed!”每次广告横幅更改时都会记录文本,即使“拖我!”文本被拖入下拉框。

【问题讨论】:

  • 您需要移动组件还是仅仅移动 DOM 节点?如果是组件,你如何创建它们?
  • 我需要将一个子组件(不仅仅是一个节点)从一个父组件移动(拖放)到另一个父组件。一旦子组件被移动,原来的父组件可能会被销毁,不应该在子组件上调用ngOnDestroy方法。
  • 好的,您是否动态创建和注入它们?创建一个plunker。渲染器不会帮助您移动组件
  • 我会尝试组装一个简洁的 Plunker,但是父组件是使用this approach 创建和销毁的。看这个例子,想象一个位于广告横幅内的组件,可以拖放到广告横幅之外。一旦将该组件移出广告横幅,您就不想在广告横幅被销毁时在该组件上调用 ngOnDestroy。我希望这会有所帮助!
  • 是的,这是正确的做法,好吧,创造最简单的plunker,我去看看

标签: angular


【解决方案1】:

使用ViewContainerRef 可以实现将子组件从一个父组件移动到另一个父组件:

oldParentViewContainerRef.detach(index);
newParentViewContainerRef.insert(viewRef);

index 是视图容器中子组件视图的索引。这可以使用以下方法获得:

let index = oldParentViewContainerRef.indexOf(viewRef);

viewRef 对应子组件的视图。如果子组件是在ComponentFactoryResolver 的帮助下以编程方式创建的,您只能获得viewRef(请参阅下面的 Angular v13 编辑):

let factory = componentFactoryResolver.resolveComponentFactory(MyChildComponentType);
let componentRef = oldParentViewContainerRef.createComponent(factory);
let viewRef = componentRef.hostView;

这里的要点是,如果您希望能够在不同的父组件之间移动组件,请确保以编程方式创建这些可移动组件,而不是在 HTML 中静态创建。这为您提供了这些可移动组件的 ViewRef 实例,它与 ViewContainerRef 很好地配合使用。

例如,请参阅工作 Plunker

编辑:Angular v13

从 Angular v13 开始,不再需要 ComponentFactoryResolver 以编程方式创建组件。现在可以按如下方式创建组件:

let componentRef = oldParentViewContainerRef.createComponent(MyChildComponentType);
let viewRef = componentRef.hostView;

【讨论】:

    【解决方案2】:

    是的,您可以使用 Renderer2 类执行此操作,这是首选方式。使用该类的原因是为了让您的应用程序与 Angular Universal 兼容。

    Angular 不像在 AngularJS 中那样耦合到 DOM 树。 Angular 现在创建 ViewRef 对象来处理组件和 DOM 之间的耦合。这意味着ViewRef 使用的本机元素可以在不破坏更改滞留树或注入树的情况下移动。

    虽然 Renderer2 类在 API 方法中受到限制。它确实有你需要的方法。

    abstract appendChild(parent: any, newChild: any): void;
    abstract insertBefore(parent: any, newChild: any, refChild: any): void;
    abstract removeChild(parent: any, oldChild: any): void;
    

    我编写了一个将窗口作为组件的 UI 库。这些窗口可以停靠在父窗口面板中。我能做到的唯一方法是移动组件的 DOM 元素。

    我唯一关心的是组件的生命周期。当我的窗户被破坏时,它们会将这些元素移回原来的位置。我不知道如果元素被不同的父元素从 DOM 中移除,ngOnDestroy 是否会被调用。

    【讨论】:

    • 感谢@ThinkingMedia。我忽略了Renderer documentation 中的弃用说明,甚至没有看Renderer2。您对 UI 库和生命周期问题的体验实际上听起来很像我的用例。即使使用 Renderer2 移动组件时,我也遇到了生命周期问题。我的情况如下:
    • 我有 2 个父组件:WindowA 和 WindowB。 WindowA 被动态创建和销毁。当 WindowA 创建(即打开)时,用户可以将子组件移动(拖放)到 WindowB 中。当 WindowA 被销毁(即关闭)时,仍会在移入 WindowB 的子组件中调用 ngOnDestroy 方法。
    • @SamHerrmann 在我的应用程序中,我有一个辅助隐藏组件,它是可移动组件的所有者。当要显示可移动组件时,它被可见的父级“借用”,然后返回给可能负责销毁它的所有者。
    【解决方案3】:

    不建议操作 DOM,但不一定是错误的。例如,复杂的 UI 组件几乎肯定需要直接操作 DOM(例如对话框和下拉菜单的某些实现)。要避免的事情是泄漏,开发人员在没有角度的情况下操作 DOM,并且在不再需要操作后无法清理 DOM。

    在 DOM 中移动组件时,我发现在包装器中移动它最有帮助。例如,当我在模态组件中包含组件时,模态的父元素位于嵌套在添加组件的级别的 DOM 中,并且模态的子元素附加到 body 标记。当模态框需要关闭时,对话框子元素会被附加回包装器,将组件带回包装器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-18
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 2020-05-22
      • 1970-01-01
      • 2018-08-13
      • 2018-12-31
      相关资源
      最近更新 更多