简体   繁体   English

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

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

This is in continuation of the previous question I asked on SO: Add directives to component selector when it is declared - Angular 7这是我在 SO 上提出的上一个问题的延续: 在声明时向组件选择器添加指令 - Angular 7

I am dynamically creating components on a button click.我在单击按钮时动态创建组件。 The components are displayed one below another in a list like manner.组件以类似列表的方式依次显示。 I want to introduce drag-drop behaviour so that the user can rearrange the components after creating them.我想引入拖放行为,以便用户可以在创建组件后重新排列组件。

In the previous question, I tried using Angular-Material, but realised it might not be possible to use it for components, due to the issue of adding "cdkDrag" directive to the component's selector tag, and the fact that the cdkDropList and cdkDrag might need to be in the same template.在上一个问题中,我尝试使用 Angular-Material,但意识到它可能无法用于组件,因为在组件的选择器标签中添加了“cdkDrag”指令的问题,以及 cdkDropList 和 cdkDrag 可能需要在同一个模板中。

I have a div as such in the template:我在模板中有一个 div:

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

And, I am creating custom components as follows:而且,我正在按如下方式创建自定义组件:

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

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

This works fine.这工作正常。 Is it possible at all to create draggable dynamically created components?是否有可能创建可拖动的动态创建的组件?

Thank you.谢谢你。

I'm done with this problem by generating components dynamically with createComponent method and processing move by ViewComponentRef method:我通过使用 createComponent 方法动态生成组件并通过 ViewComponentRef 方法处理移动来解决这个问题:

container.component.html容器组件.html

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

container.component.ts容器.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动态组件.html

<div cdkDrag>
    <div cdkDragHandle></div>
</div>
  • In this case, you can access component instance directly through the components array.在这种情况下,您可以直接通过 components 数组访问组件实例。

Update更新

While this works fine with one single type of component, if you need to use different dynamic types of components, read Chaitanya Bangera's comment down below!虽然这适用于单一类型的组件,但如果您需要使用不同的动态类型的组件,请阅读下面的 Chaitanya Bangera 的评论!

Original Comment原始评论

Should work with something like this (CmpComponent would be your component that you want to insert):应该使用这样的东西(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>

Finally got it to work, thanks to the reply from MauriceNino.感谢 MauriceNino 的回复,终于让它工作了。 I am going to mark Maurice's answer as accepted, since their solution works fine for a single component.我要将 Maurice 的答案标记为已接受,因为他们的解决方案适用于单个组件。

While getting Maurice's solution to work for multiple components, I came across this magical concept called ng-container!在让 Maurice 的解决方案适用于多个组件时,我遇到了一个叫做 ng-container 的神奇概念! What a life-saver!!多么救命啊!! My solution is as follows:我的解决方案如下:

components=[];

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


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

Now for the template:现在为模板:

<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>

Finally, after a week of searching, it finally works!终于,经过一周的搜索,终于成功了! Thank you!谢谢!

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

I had a requirement to apply cdk drag (with cdkdraglist directive ) directive to all the dynamically created elements.我需要将 cdk drag(使用 cdkdraglist 指令)指令应用于所有动态创建的元素。 so this cdk drag and drop will work.所以这个cdk拖放将起作用。 but angular is not allowing runtime addition of directive to am element in template.但是 angular 不允许在运行时向模板中的 am 元素添加指令。 concluding that ,to achive this feature we have to take support in of a grid system.结论是,要实现此功能,我们必须支持网格系统。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM