1

I'm trying to build a main/dashboard page like the image given below. What I am trying to achieve is to be able to drag and drop the components labeled from 1-6 (they will all be separate components). I'm not sure if angular CDK drag and drop can be used to achieve this functionality. One more thing I'm trying to do is have some sort of drag and drop animation where on page load, a few components switch positions.

Can someone let me know if this is possible using angular CDK drag and drop or will I need to use some external package?

enter image description here

Haris Ali
  • 55
  • 1
  • 9
  • Just enclosed each component in a cdkList with one unique element, then enclosed all of them in a `cdkDropListGroup`. But really I don't know if you only want to "interchange" positions or some components can be "removed" or "added". – Eliseo Mar 03 '22 at 21:31
  • Yes i only want to interchange positions of the components. They cannot be added or removed, just rearranged/reordered. – Haris Ali Mar 04 '22 at 04:45

1 Answers1

0

Really it's a bit complex but let's go

The first we need take account to distribute a series of component is that we can use any way. I choose use "masonary" from bootstrap 4 (but you can make as you choose)

When we drag a cdkList and this is not distributed horizontal or vertical else a "grid" we enter in problems. so I choose create a series of cdkDropList with an unique element. But as I want not move while I'm dragging, when is dragging I put in a position absolute. So our array items will be like

[{data:1,style:null},{data:2,style:null}] 

And when we are going to drag, we calcule the positions. For this, we need use ViewChildren to get the cdkDragList, but use {read:ElementRef} because we are only interested in the ElementRef

  @ViewChildren(CdkDropList,{read:ElementRef}) cards:QueryList<ElementRef>
  wrapperStyle={width:'',height:''}
  calculePosition()
  {
    let width=0;
    let height=0;
    this.cards.forEach((x,index)=>{
       const rect=x.nativeElement.getBoundingClientRect()
       if (width<rect.left+rect.width)
        width=rect.left+rect.width;
        if (height<rect.top+rect.height)
        height=rect.top+rect.height;

       this.items[index].style={
         position:'absolute',
         top:rect.top+'px',
         left:rect.left+'px',
         width:rect.width+'px',
         height:rect.height+'px'
       }
       this.onDrag=true;
    })
    this.wrapperStyle={width:width+'px',height:height+'px'}
  }

Our .html can be like

<div [ngStyle]="onDrag?wrapperStyle:null" cdkDropListGroup 
        [ngClass]="onDrag?'position-relative':'card-columns'">
  <ng-container *ngFor="let item of items;let index=index">
    <div [ngStyle]="onDrag?item.style:null"
      cdkDropList
      orientation="vertical"
      cdkDropListSortingDisabled="true"
      [cdkDropListData]="index"
      (cdkDropListDropped)="drop($event)"
    >
      <div class="card" cdkDrag (mousedown)="calculePosition()" >
        <div #place>
        </div>
        <div *cdkDragPlaceholder></div>
      </div>
    </div>
  </ng-container>
</div>

See how with the variable "onDrag" we use position absolute or not. See also that as "data" we put the "index" of the component. This allow us our function drop becomes like

  drop(event: CdkDragDrop<any,any>) {
    
    const currentIndex=event.container.data;
    const previousIndex=event.previousContainer.data;
    this.items=this.items.map((x,i)=>{

        if (i==previousIndex)
        return this.items[currentIndex]
        if (i==currentIndex)
        return this.items[previousIndex]
        return x
    })
    this.onDrag=false;
  }

Just only need attach to our "#place" the components (based in the "item.data")

For this, first use viewChildren with read:ViewContainerRef and use AfterViewInit

  @ViewChildren('place', { read: ViewContainerRef }) places: QueryList<ViewContainerRef>
  ngAfterViewInit()
  {
    this.places.forEach((x,index)=>{
      switch (this.items[index].data)
      {
          case 0:
            x.createComponent(HelloComponent)
            break;
            default:
              x.createComponent(ByComponent)
              break;
        }
    })
  }

a little stackblizt

NOTE: You only need store in the localstore the array "items" to "save the configuration"

Eliseo
  • 50,109
  • 4
  • 29
  • 67