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

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.

I have a div as such in the template:

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

Answers:

Answer

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!

Original Comment

Should work with something like this (CmpComponent would be your component that you want to insert):

  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>

Answer

Finally got it to work, thanks to the reply from MauriceNino. I am going to mark Maurice's answer as accepted, since their solution works fine for a single component.

While getting Maurice's solution to work for multiple components, I came across this magical concept called 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!

Answer

I'm done with this problem by generating components dynamically with createComponent method and processing move by ViewComponentRef method:

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

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

addComponent() {
    const factory = this.cfr.resolveComponentFactory(Dynamic);
    const component: ComponentRef<Dynamic> = this.cmpContainer.createComponent(factory);
}

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

dynamic.component.html

<div cdkDrag>
    <div cdkDragHandle></div>
</div>
  • In this case, you can access component instance directly through the components array.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.