Angular 7 - 向动态创建的组件添加拖放行为

Posted

技术标签:

【中文标题】Angular 7 - 向动态创建的组件添加拖放行为【英文标题】:Angular 7 - Add drag and drop behaviour to dynamically created components 【发布时间】:2019-09-30 04:11:01 【问题描述】:

这是我在 SO 上提出的上一个问题的延续: Add directives to component selector when it is declared - Angular 7

我在单击按钮时动态创建组件。组件以类似列表的方式在另一个下方显示。我想引入拖放行为,以便用户可以在创建组件后重新排列组件。

在上一个问题中,我尝试使用 Angular-Material,但意识到可能无法将它用于组件,因为在组件的选择器标签中添加“cdkDrag”指令的问题,以及 cdkDropList和 cdkDrag 可能需要在同一个模板中。

我在模板中有一个这样的 div:

<div cdkDropList style="margin: 20px" (cdkDropListDropped)="drop($event)">
    <div #container></div>
</div>

而且,我正在按如下方式创建自定义组件:

@ViewChild('container', read: ViewContainerRef)
  container: ViewContainerRef;

const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
const component = this.container.createComponent(childComponent);

这很好用。是否有可能创建可拖动的动态创建的组件?

谢谢。

【问题讨论】:

【参考方案1】:

我通过使用 createComponent 方法动态生成组件并通过 ViewComponentRef 方法处理移动来解决这个问题:

container.component.html

<div cdkDropList (cdkDropListDropped)="drop($event)">
    <ng-container #cmpContainer></ng-container>
</div>

container.component.ts

import CdkDragDrop, moveItemInArray from "@angular/cdk/drag-drop";
import DynamicComponent from './dynamic.component.ts';

@ViewChild('cmpContainer', static: true, read: ViewContainerRef) cmpContainer: ViewContainerRef;
components: ComponentRef<DynamicComponent>[] = [];

addComponent() 
    const factory = this.cfr.resolveComponentFactory(DynamicComponent);
    const component: ComponentRef<DynamicComponent> = this.cmpContainer.createComponent(factory);
    this.components.push(component);


drop(event: CdkDragDrop<DynamicComponent[]>) 
    this.cmpContainer.move(this.components[event.previousIndex].hostView, event.currentIndex);
    moveItemInArray(this.components, prevIndex, currentIndex);

dynamic.component.html

<div cdkDrag>
    <div cdkDragHandle></div>
</div>
在这种情况下,您可以直接通过 components 数组访问组件实例。

【讨论】:

【参考方案2】:

更新

虽然这适用于单一类型的组件,但如果您需要使用不同的动态类型的组件,请阅读下面 Chaitanya Bangera 的评论!

原评论

应该使用这样的东西(CmpComponent 将是您要插入的组件):

  components: CmpComponent[];

const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
this.components.push(childComponent);


drop(event: CdkDragDrop<CmpComponent[]>) 
  moveItemInArray(this.components, event.previousIndex, event.currentIndex);
<div cdkDropList style="margin: 20px" (cdkDropListDropped)="drop($event)">
    <div cdkDrag *ngFor="let cmp of components">
        <app-cmp></app-cmp>
    </div>
</div>

【讨论】:

谢谢,我看看,虽然我觉得我可能会遇到困难,因为用户可以动态创建几个可用组件之一。它就像用户可以单击和创建的小部件列表,因此模板中的 可能不起作用。编辑:不过,我会为单个组件尝试它,以检查该方法是否有效。 我让它使用你的代码作为基础并使用 ng-container 而不是 *ngFor 的组件选择器来工作,它终于可以工作了。我会将您的答案标记为已接受,因为它适用于单个模板。我在下面添加了适用于不同组件的答案。 @ChaitanyaBangera 很高兴它成功了!我更新了我的答案,以便有相同问题的人更容易找到您的评论。 @ChaitanyaBangera 你能告诉我你是如何以这种方式处理组件参数的吗?我的意思是,您是使用动态生成值的输入/输出,还是有某种方法可以访问组件实例?和你有同样的问题,不知道如何处理组件参数。【参考方案3】:

感谢 MauriceNino 的回复,终于可以使用了。我将 Maurice 的回答标记为已接受,因为他们的解决方案适用于单个组件。

在让 Maurice 的解决方案适用于多个组件时,我遇到了一个名为 ng-container 的神奇概念!多么救命啊!!我的解决方案如下:

components=[];

const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
this.components.push(childComponent);


drop(event: CdkDragDrop<CmpComponent[]>) 
  moveItemInArray(this.components, event.previousIndex, event.currentIndex);

现在是模板:

<div cdkDropList class="example-list" style="margin: 20px" (cdkDropListDropped)="drop($event)">
    <ng-container *ngFor="let cmp of components">
        <ng-container *ngIf="cmp.componentType.name=='Component1'">
            <app-Component1 cdkDrag></app-Component1>
        </ng-container>
        <ng-container *ngIf="cmp.componentType.name=='Component2'">
            <app-Component2 cdkDrag></app-Component2>
        </ng-container>
        <ng-container *ngIf="cmp.componentType.name=='Component3'">
            <app-Component3 cdkDrag></app-Component3>
        </ng-container>

    </ng-container>
</div>

终于,经过一周的搜索,终于成功了! 谢谢!

【讨论】:

drop(event: CdkDragDrop) 你能告诉我CMpCOmponet[]是指的吗?【参考方案4】:

您可以在每个ng-container 周围创建div 并为其设置cdkDrag 属性。

【讨论】:

【参考方案5】:

要求是创建可拖动的可编辑行。用户可以添加/删除行。

在这里将 cdk drag(带有 cdkdraglist 指令)指令应用于所有动态创建的元素。所以这个 cdk 拖放将起作用。但是 Angular 不允许运行时将指令添加到模板中的 am 元素。结论是,为了实现这个功能,我们必须支持网格框架(如 ag-grid)。

【讨论】:

这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker.

以上是关于Angular 7 - 向动态创建的组件添加拖放行为的主要内容,如果未能解决你的问题,请参考以下文章

Angular 7 拖放 - 动态创建拖放区

Angular 7 - 使用父可放置容器拖放

在两个单独的 QTableWidgets 之间拖放行

Angular使用总结 --- 通过指令动态添加组件

Angular - 从父组件向动态子组件发出事件

Angular 表单:如何动态添加/删除子表单组件