skip to Main Content

Using the example from CDK drag and drop, I want to add a preview of the dragged element with left and top positions without the transform style.

HTML

<div class="example-boundary">
  <div class="example-box" cdkDragBoundary=".example-boundary" cdkDrag>
    I can only be dragged within the dotted container
  </div>
</div>

<button> Preview the dragged element </buttona>

TS

import {Component} from '@angular/core';
import {CdkDrag} from '@angular/cdk/drag-drop';

/**
 * @title Drag&Drop boundary
 */
@Component({
  selector: 'cdk-drag-drop-boundary-example',
  templateUrl: 'cdk-drag-drop-boundary-example.html',
  styleUrls: ['cdk-drag-drop-boundary-example.css'],
  standalone: true,
  imports: [CdkDrag],
})
export class CdkDragDropBoundaryExample {}

Current state

When you drag the element you have this div in DOM

<div _ngcontent-ng-c2320506461="" class="example-boundary">
  <div _ngcontent-ng-c2320506461="" cdkdragboundary=".example-boundary" cdkdrag="" class="cdk-drag example-box" style="transform: translate3d(202px, -2px, 0px);"> 
I can only be dragged within the dotted container
 </div>
</div>

Expected result.

When you drag the element and click the preview button it should open the preview element which looks like this.

<div class="example-boundary">
  <div class="example-box" style="left: 96.13%; top: 9.92%; display: block;">
   Now I can't be dragged, sorry
  </div>
</div>

Meaning the transform style should be replaced with left and top positions.

2

Answers


  1. @angular/cdk/drag-drop internally uses transform property for placing the box. Whether top, left and position properties are used or transform is used in internal detail of the package and should be understood to be encapsulated. You can achieve the same results using both ways. If there is anything specific you want to do with top and left properties, you can either compute them from transform and elements original position or code drag function in pure JS.

    Login or Signup to reply.
  2. TLDR; Try this working example which does what you ask, basically clones the Angular generated HTML and then unwanted attributes are removed manually


    The main changes are the following:

    TS:

    1. Added a flag called isPreviewShown which toggles which HTML is visible, either the preview or the original
    2. Added a method called showPreview which clones the draggable contents and then manually removes unwanted attributes (you’ll have to maintain this logic, so you may remove other attributes you don’t need)
    3. Replaced cssText style attributes from translate3d to top/left values. I used the following regular expression for it transform:s*translate3d((.+?),(.+?),.+?) and the replaced it to left:$1; top:$2
    isPreviewShown = false;
    
    showPreview() {
        // clone the contents
        this.previewContainer.nativeElement.innerHTML = this.exampleBoundary.nativeElement.innerHTML;
        // clear unwanted attributes
        document.querySelectorAll('.preview-container *').forEach((content: HTMLElement) => {
          const attrs = content.getAttributeNames();
          for (var attr in attrs) {
            if (
              attrs[attr].indexOf('cdk') !== -1   // <-- remove cdk related attributes
              || attrs[attr].indexOf('_ng') === 0 // <-- remove angular relates attributes
              || attrs[attr].indexOf('ng-') === 0 // <-- remove more angular relates attributes
            ) {
              console.log('->> removed: ', attrs[attr])
              content.removeAttribute(attrs[attr]);
            }
          }
          // transform/translate3d --> top/left
          content.style.cssText = content.style.cssText.replace(/transform:s*translate3d((.+?),(.+?),.+?)/i, 'left:$1; top:$2') 
          // remove cdk-drag class
          content.classList.remove('cdk-drag')
        });
        console.log('>> Result: ', this.previewContainer.nativeElement.innerHTML)
        // show the preview
        this.isPreviewShown = true;
        this.cdr.detectChanges();
    }
    

    HTML:

    1. Added a preview-container which is where the preview is going to be shown
    2. Added a main-container which is meant to make the preview to overlap the original content
    3. Added an extra button called "Hide preview element" which hides the preview so you can drag again
    <div class="main-container">
      <div class="example-boundary" #exampleBoundary>
        <div class="example-box" cdkDragBoundary=".example-boundary" cdkDrag>
          I can only be dragged within the dotted container
        </div>
      </div>
      <div class="preview-container" #previewContainer [hidden]="!isPreviewShown">
      </div>
    </div>
    <br>
    <button (click)="hidePreview()" [hidden]="!isPreviewShown"> Hide preview element </button>
    <button (click)="showPreview()" [hidden]="isPreviewShown"> Preview the dragged element </button>
    

    CSS:

    The little css added main need is to add position: relative to the main container so the preview-container size and position is shown relative to the main container.

    .main-container {
      position: relative;
      width: 400px;
      height: 400px;
    }
    .preview-container {
      position: absolute;
      background: white;
      width: 100%;
      height: 100%;
      top: 1px;
      left: 1px;
      z-index: 1;
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search