Search code examples
angulartypescriptangular-materialangular-cdkangular-cdk-drag-drop

CDK Drag Drop does not correctly change the position of images


I created an image gallery and I intend to change the position between them. For this I am using the Drag & Drop cdk library.

My problem is that the exchange of images does not always happen correctly, sometimes I exchange the first image with the second and this exchange does not happen.

How can I have this fully functional, horizontally and vertically?

Is there a way Drag can only start if the image is stored in the mdc-image-list - with-text-protection class?

Thank you !

DEMO

.HTML

  <ul class="mdc-image-list my-image-list" style="padding-left: 10px;padding-right: 10px;" cdkDropList [cdkDropListData]="data"
           (cdkDropListDropped)="drop($event)">
    <ng-container *ngFor="let product of data; let  j = index;">
      <li class="mdc-image-list__item" cdkDrag>
        <div class="mdc-image-list__image-aspect-container">
          <ng-container *ngIf="product.image == null; else productImage">
            <img src="" class="mdc-image-list__image imagenotfound">
          </ng-container>
          <ng-template #productImage>
            <img [src]="product.image" class="mdc-image-list__image">
          </ng-template>
        </div>
        <div class="mdc-image-list--with-text-protection">
        <div class="mdc-image-list__supporting mdc-image-list__supporting">
        <span class="mdc-image-list__label">{{product.name}}</span>
        </div>
        </div>
      </li>
    </ng-container>
  </ul>

.ts

    drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }
  }

Images


Solution

  • The only way I find to allow drag and drop in a grid is that each element of the grid was one cdkDropList.

    Advertisment: The elements dont' move while we are dragging

    Well, as data of each cdkDropList will be an object, the data we want transport and the index

    [cdkDropListData]="{item:product,index:j}"
    

    So, our function dropEvent change the array data acording this values

      drop(event: CdkDragDrop<any>) {
        this.data[event.previousContainer.data.index]={...event.container.data.item}
        this.data[event.container.data.index]={...event.previousContainer.data.item}
        event.currentIndex=0;
      }
    

    Yes! we are not force to use the drop as show the examples. Think that the only thing we want is change an array.

    There are another thing to do, make that the "dragPlaceHolder becomes an empty div

    <div cdkDrag>
         ....
        <div  *cdkDragPlaceholder></div>
    </div>
    

    Well, the html is a few complex because the own mdc-image-list is complex. It's important notice that all the cdkDropList must be enclosed in a div ckdDropListGroup

    <div cdkDropListGroup>
    
        <ul class="mdc-image-list my-image-list" style="padding-left: 10px;padding-right: 10px;">
            <ng-container *ngFor="let product of data; let  j = index;">
                <li class="mdc-image-list__item" cdkDropList [cdkDropListData]="{item:product,index:j}"
                    cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)">
                    <div cdkDrag>
                        <div class="mdc-image-list__image-aspect-container">
                            <ng-container *ngIf="product.image == null; else productImage">
                                <img src="" class="mdc-image-list__image imagenotfound">
              </ng-container>
                                <ng-template #productImage>
                                    <img [src]="product.image" class="mdc-image-list__image">
              </ng-template>
                        </div>
                        <div class="mdc-image-list--with-text-protection">
                            <div class="mdc-image-list__supporting mdc-image-list__supporting">
                                <span class="mdc-image-list__label">{{product.name}}</span>
                            </div>
                        </div>
                        <div class="example-custom-placeholder" *cdkDragPlaceholder></div>
                    </div>
                </li>
            </ng-container>
        </ul>
    </div>
    

    here is your forked stackblitz

    NOTE: If someone want a more simple example see this another stackblitz