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