How to have different ckdDragPlaceholders for the source and sink droplists?

Viewed 1014

I am using Angular material's Drag and Drop module to move some elements.

More concretely, I have multiple cdkDropLists, and one cdkDrag in each of them:

<div *ngFor="let l of lists">
  <div cdkDropList [cdkDropListConnectedTo]="all_lists">
    <div cdkDrag>
      content
    </div>
  </div>
</div>

I would like to have a placeholder whenever I move an element from my list, like:

 <div cdkDrag>
      content
      <div *cdkDragPlaceholder>
          placeholder content
      </div>
 </div>

However, when I do this, the placeholder content is shown both in place of the missing (currently being dragged) element (source list) as well as in place of its new location (sink list). I would like to be able to keep the default behaviour (showing root element aka content) for the sink list and have the placeholder only for the source list.

I am not able to distinguish the two places though, as it seems even in the "sink" location, it's the sources' elements' HTML is showing (which somewhat makes sense, as placeholder is a property of a cdkDrag and not cdkDropList).

How can I have a different placeholder in place of my element's original location vs the new location?

Stackblitz example.

Intuition: the (multiple) lists are decks of cards, while moving a card from A to B, I see a second-to-top (custom placeholder) card in the A deck, but when I hover the top-A card over B, I see the top-A card there.

1 Answers

If I understood your question correctly you want to show the custom placeholder only when you are dragging to another list and show the default when dragging inside the same list.

To detect movement outside the source list you can use cdkDragEntered event which triggers any time an item is dragged inside a list from outside. In the CdkDragEnter event data you have the information which list you entered and all the information about the item being dragged. The item contains the information to which list it currently belongs. This means that another list has been entered when the list we entered is not the same as the item list. Once you have this information you can then change what is displayed inside the placeholder based on this information.

I have changed your StackBlitz sample to this approach.

The component code:

export class CdkDragDropConnectedSortingGroupExample {
  todo = [
    'Get to work',
    'Pick up groceries',
    'Go home',
    'Fall asleep'
  ];

  done = [
    'Get up',
    'Brush teeth',
    'Take a shower',
  ];

  another = [
    'Check e-mail',
    'Walk dog'
  ]

  lists = [this.todo, this.done, this.another];

  public draggingOutsideSourceList: boolean = false;

  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);
    }
    this.draggingOutsideSourceList = false; // always reset after drag drop finishes
  }

  onCdkDragEntered(event: CdkDragEnter<string>) {
    this.draggingOutsideSourceList = event.container !== event.item.dropContainer;
  }
}

The template:

<div cdkDropListGroup>
    <div class="example-container todoList" *ngFor="let list of lists">
        <div cdkDropList [cdkDropListData]="list" class="example-list" (cdkDropListDropped)="drop($event)">

            <div cdkDrag *ngFor="let item of list" (cdkDragEntered)="onCdkDragEntered($event)">
                <ng-template #itemTpl>
                    <div class="example-box">{{item}}</div>
                </ng-template>
                <div *cdkDragPlaceholder>
                    <div *ngIf="draggingOutsideSourceList; else itemTpl" class="customPlaceHolder">
                        Custom placeholder content only in sink list
                    </div>
                </div>
                <ng-container *ngTemplateOutlet="itemTpl"></ng-container>
            </div>
        </div>
    </div>
</div>

As you can see I create three lists use the cdkDragEntered event to determine if the dragging is outside or inside the source list by setting the draggingOutsideSourceList property which is used in the template to choose what to display in the placeholder.

Here is also a link to a fork of your StackBlitz where you can see it working.

Related