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

Angular 7 - Add drag and drop behaviour to dynamically created components

这是我在 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);

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

谢谢。

更新

虽然这适用于一种类型的组件,但如果您需要使用不同的动态类型的组件,请阅读下面 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>

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

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

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>

终于,经过一周的寻找,终于成功了! 谢谢!

通过使用 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>
  • 在这种情况下,您可以直接通过组件数组访问组件实例。

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

要求是创建可拖动的可编辑行。用户可以 add/delete 行。

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