Guias Detalhados

Drag and drop

Visão geral

Esta página descreve as directives de drag and drop que permitem criar rapidamente interfaces de drag and drop com os seguintes recursos:

  • Dragging livre
  • Criar uma lista de elementos draggable reordenáveis
  • Transferir elementos draggable entre listas
  • Animações de dragging
  • Bloquear elementos draggable ao longo de um eixo ou elemento
  • Adicionar drag handles customizados
  • Adicionar previews durante o drag
  • Adicionar drag placeholder customizado

Para a referência completa da API, consulte a página de referência da API de drag and drop do Angular CDK.

Antes de começar

Instalação do CDK

O Component Dev Kit (CDK) é um conjunto de primitivos de comportamento para construir components. Para usar as directives de drag and drop, primeiro instale @angular/cdk do npm. Você pode fazer isso do seu terminal usando Angular CLI:

ng add @angular/cdk

Importando drag and drop

Para usar drag and drop, importe o que você precisa das directives no seu component.

import {Component} from '@angular/core';import {CdkDrag} from '@angular/cdk/drag-drop';@Component({selector: 'my-custom-component',templateUrl: 'my-custom-component.html',imports: [CdkDrag],})export class DragDropExample {}

Criar elementos draggable

Você pode tornar qualquer elemento draggable adicionando a directive cdkDrag. Por padrão, todos os elementos draggable suportam dragging livre.

app/app.component.html

<div class="example-box" cdkDrag>  Drag me around</div>

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Basic Drag&Drop */@Component({  selector: 'cdk-drag-drop-overview-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropOverviewExample {}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  position: relative;  z-index: 1;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}

Criar uma lista de elementos draggable reordenáveis

Adicione a directive cdkDropList a um elemento pai para agrupar elementos draggable em uma coleção reordenável. Isso define onde elementos draggable podem ser soltos. Os elementos draggable no grupo de drop list se reorganizam automaticamente conforme um elemento se move.

As directives de drag and drop não atualizam seu modelo de dados. Para atualizar o modelo de dados, escute o evento cdkDropListDropped (assim que o usuário terminar de arrastar) e atualize o modelo de dados manualmente.

app/app.component.html

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">  @for (movie of movies; track movie) {    <div class="example-box" cdkDrag>{{movie}}</div>  }</div>

app/app.component.ts

import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop sorting */@Component({  selector: 'cdk-drag-drop-sorting-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropSortingExample {  movies = [    'Episode I - The Phantom Menace',    'Episode II - Attack of the Clones',    'Episode III - Revenge of the Sith',    'Episode IV - A New Hope',    'Episode V - The Empire Strikes Back',    'Episode VI - Return of the Jedi',    'Episode VII - The Force Awakens',    'Episode VIII - The Last Jedi',    'Episode IX – The Rise of Skywalker',  ];  drop(event: CdkDragDrop<string[]>) {    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  width: 500px;  max-width: 100%;  border: solid 1px #ccc;  min-height: 60px;  display: block;  background: white;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  border: none;  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Você pode usar o token de injeção CDK_DROP_LIST que pode ser usado para referenciar instâncias de cdkDropList. Para mais informações, consulte o guia de dependency injection e a API do token de injeção drop list.

Transferir elementos draggable entre listas

A directive cdkDropList suporta transferir elementos draggable entre drop lists conectadas. Existem duas formas de conectar uma ou mais instâncias de cdkDropList:

  • Definir a propriedade cdkDropListConnectedTo para outra drop list.
  • Envolver os elementos em um elemento com o atributo cdkDropListGroup.

A directive cdkDropListConnectedTo funciona tanto com uma referência direta a outro cdkDropList quanto referenciando o id de outro drop container.

<!-- Isto é válido --><div cdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div><div cdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div><!-- Isto também é válido --><div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div><div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>

app/app.component.html

<div class="example-container">  <h2>To do</h2>  <div    cdkDropList    #todoList="cdkDropList"    [cdkDropListData]="todo"    [cdkDropListConnectedTo]="[doneList]"    class="example-list"    (cdkDropListDropped)="drop($event)">    @for (item of todo; track item) {      <div class="example-box" cdkDrag>{{item}}</div>    }  </div></div><div class="example-container">  <h2>Done</h2>  <div    cdkDropList    #doneList="cdkDropList"    [cdkDropListData]="done"    [cdkDropListConnectedTo]="[todoList]"    class="example-list"    (cdkDropListDropped)="drop($event)">    @for (item of done; track item) {      <div class="example-box" cdkDrag>{{item}}</div>    }  </div></div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDropList,  moveItemInArray,  transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop connected sorting */@Component({  selector: 'cdk-drag-drop-connected-sorting-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropConnectedSortingExample {  todo = ['Get to work', 'Pick up groceries', 'Go home', 'Fall asleep'];  done = ['Get up', 'Brush teeth', 'Take a shower', 'Check e-mail', 'Walk dog'];  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,      );    }  }}

app/app.component.css

.example-container {  width: 400px;  max-width: 100%;  margin: 0 25px 25px 0;  display: inline-block;  vertical-align: top;}.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Use a directive cdkDropListGroup se você tiver um número desconhecido de drop lists conectadas para configurar a conexão automaticamente. Qualquer novo cdkDropList que for adicionado sob um grupo conecta-se automaticamente a todas as outras listas.

<div cdkDropListGroup>  <!-- Todas as listas aqui serão conectadas. -->  @for (list of lists; track list) {    <div cdkDropList></div>  }</div>

app/app.component.html

<div cdkDropListGroup>  <div class="example-container">    <h2>To do</h2>    <div      cdkDropList      [cdkDropListData]="todo"      class="example-list"      (cdkDropListDropped)="drop($event)">      @for (item of todo; track item) {        <div class="example-box" cdkDrag>{{item}}</div>      }    </div>  </div>  <div class="example-container">    <h2>Done</h2>    <div      cdkDropList      [cdkDropListData]="done"      class="example-list"      (cdkDropListDropped)="drop($event)">      @for (item of done; track item) {        <div class="example-box" cdkDrag>{{item}}</div>      }    </div>  </div></div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDropList,  CdkDropListGroup,  moveItemInArray,  transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop connected sorting group */@Component({  selector: 'cdk-drag-drop-connected-sorting-group-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropListGroup, CdkDropList, CdkDrag],})export class CdkDragDropConnectedSortingGroupExample {  todo = ['Get to work', 'Pick up groceries', 'Go home', 'Fall asleep'];  done = ['Get up', 'Brush teeth', 'Take a shower', 'Check e-mail', 'Walk dog'];  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,      );    }  }}

app/app.component.css

.example-container {  width: 400px;  max-width: 100%;  margin: 0 25px 25px 0;  display: inline-block;  vertical-align: top;}.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Você pode usar o token de injeção CDK_DROP_LIST_GROUP que pode ser usado para referenciar instâncias de cdkDropListGroup. Para mais informações, consulte o guia de dependency injection e a API do token de injeção drop list group.

Dragging seletivo

Por padrão, um usuário pode mover elementos cdkDrag de um container para outro container conectado. Para controle mais refinado sobre quais elementos podem ser soltos em um container, use cdkDropListEnterPredicate. O Angular chama o predicate sempre que um elemento draggable entra em um novo container. Dependendo se o predicate retorna true ou false, o item pode ou não ser permitido no novo container.

app/app.component.html

<div class="example-container">  <h2>Available numbers</h2>  <div    id="all"    cdkDropList    [cdkDropListData]="all"    cdkDropListConnectedTo="even"    class="example-list"    (cdkDropListDropped)="drop($event)"    [cdkDropListEnterPredicate]="noReturnPredicate">    @for (number of all; track number) {      <div          class="example-box"          [cdkDragData]="number"          cdkDrag>{{number}}</div>    }  </div></div><div class="example-container">  <h2>Even numbers</h2>  <div    id="even"    cdkDropList    [cdkDropListData]="even"    cdkDropListConnectedTo="all"    class="example-list"    (cdkDropListDropped)="drop($event)"    [cdkDropListEnterPredicate]="evenPredicate">    @for (number of even; track number) {      <div          class="example-box"          cdkDrag          [cdkDragData]="number">{{number}}</div>    }  </div></div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDropList,  moveItemInArray,  transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop enter predicate */@Component({  selector: 'cdk-drag-drop-enter-predicate-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropEnterPredicateExample {  all = [1, 2, 3, 4, 5, 6, 7, 8, 9];  even = [10];  drop(event: CdkDragDrop<number[]>) {    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,      );    }  }  /** Predicate function that only allows even numbers to be dropped into a list. */  evenPredicate(item: CdkDrag<number>) {    return item.data % 2 === 0;  }  /** Predicate function that doesn't allow items to be dropped into a list. */  noReturnPredicate() {    return false;  }}

app/app.component.css

.example-container {  width: 400px;  max-width: 100%;  margin: 0 25px 25px 0;  display: inline-block;  vertical-align: top;}.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Anexar dados

Você pode associar alguns dados arbitrários tanto com cdkDrag quanto com cdkDropList definindo cdkDragData ou cdkDropListData, respectivamente. Você pode fazer binding aos eventos disparados de ambas as directives que incluirão esses dados, permitindo que você identifique facilmente a origem da interação de drag ou drop.

@for (list of lists; track list) {  <div cdkDropList [cdkDropListData]="list" (cdkDropListDropped)="drop($event)">    @for (item of list; track item) {      <div cdkDrag [cdkDragData]="item"></div>    }  </div>}

Customizações de dragging

Customizar drag handle

Por padrão, o usuário pode arrastar todo o elemento cdkDrag para movê-lo. Para restringir o usuário a poder fazer isso apenas usando um elemento handle, adicione a directive cdkDragHandle a um elemento dentro de cdkDrag. Você pode ter quantos elementos cdkDragHandle você quiser.

app/app.component.html

<div class="example-box" cdkDrag>  I can only be dragged using the handle  <div class="example-handle" cdkDragHandle>    <svg width="24px" fill="currentColor" viewBox="0 0 24 24">      <path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"></path>      <path d="M0 0h24v24H0z" fill="none"></path>    </svg>  </div></div>

app/app.component.ts

import {CdkDrag, CdkDragHandle} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop with a handle */@Component({  selector: 'cdk-drag-drop-handle-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag, CdkDragHandle],})export class CdkDragDropHandleExample {}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  padding: 10px;  box-sizing: border-box;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  position: relative;  z-index: 1;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.example-handle {  position: absolute;  top: 10px;  right: 10px;  color: #ccc;  cursor: move;  width: 24px;  height: 24px;}

Você pode usar o token de injeção CDK_DRAG_HANDLE que pode ser usado para referenciar instâncias de cdkDragHandle. Para mais informações, consulte o guia de dependency injection e a API do token de injeção drag handle.

Customizar drag preview

Um elemento preview torna-se visível quando um elemento cdkDrag está sendo arrastado. Por padrão, o preview é um clone do elemento original posicionado ao lado do cursor do usuário.

Para customizar o preview, forneça um template customizado via *cdkDragPreview. O preview customizado não corresponderá ao tamanho do elemento draggable original, já que não são feitas suposições sobre o conteúdo do elemento. Para corresponder ao tamanho do elemento para o drag preview, passe true para o input matchSize.

O elemento clonado remove seu atributo id para evitar ter múltiplos elementos com o mesmo id na página. Isso fará com que qualquer CSS que tenha como alvo esse id não seja aplicado.

app/app.component.html

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">  @for (movie of movies; track movie) {    <div class="example-box" cdkDrag>      {{movie.title}}      <img *cdkDragPreview [src]="movie.poster" [alt]="movie.title">    </div>  }</div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDragPreview,  CdkDropList,  moveItemInArray,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop custom preview */@Component({  selector: 'cdk-drag-drop-custom-preview-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag, CdkDragPreview],})export class CdkDragDropCustomPreviewExample {  // tslint:disable:max-line-length  movies = [    {      title: 'Episode I - The Phantom Menace',      poster: 'https://upload.wikimedia.org/wikipedia/en/4/40/Star_Wars_Phantom_Menace_poster.jpg',    },    {      title: 'Episode II - Attack of the Clones',      poster:        'https://upload.wikimedia.org/wikipedia/en/3/32/Star_Wars_-_Episode_II_Attack_of_the_Clones_%28movie_poster%29.jpg',    },    {      title: 'Episode III - Revenge of the Sith',      poster:        'https://upload.wikimedia.org/wikipedia/en/9/93/Star_Wars_Episode_III_Revenge_of_the_Sith_poster.jpg',    },    {      title: 'Episode IV - A New Hope',      poster: 'https://upload.wikimedia.org/wikipedia/en/8/87/StarWarsMoviePoster1977.jpg',    },    {      title: 'Episode V - The Empire Strikes Back',      poster:        'https://upload.wikimedia.org/wikipedia/en/3/3f/The_Empire_Strikes_Back_%281980_film%29.jpg',    },    {      title: 'Episode VI - Return of the Jedi',      poster: 'https://upload.wikimedia.org/wikipedia/en/b/b2/ReturnOfTheJediPoster1983.jpg',    },    {      title: 'Episode VII - The Force Awakens',      poster:        'https://upload.wikimedia.org/wikipedia/en/a/a2/Star_Wars_The_Force_Awakens_Theatrical_Poster.jpg',    },    {      title: 'Episode VIII - The Last Jedi',      poster: 'https://upload.wikimedia.org/wikipedia/en/7/7f/Star_Wars_The_Last_Jedi.jpg',    },    {      title: 'Episode IX – The Rise of Skywalker',      poster:        'https://upload.wikimedia.org/wikipedia/en/a/af/Star_Wars_The_Rise_of_Skywalker_poster.jpg',    },  ];  // tslint:enable:max-line-length  drop(event: CdkDragDrop<{title: string; poster: string}[]>) {    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  width: 500px;  max-width: 100%;  border: solid 1px #ccc;  min-height: 60px;  display: block;  background: white;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Você pode usar o token de injeção CDK_DRAG_PREVIEW que pode ser usado para referenciar instâncias de cdkDragPreview. Para mais informações, consulte o guia de dependency injection e a API do token de injeção drag preview.

Customizar ponto de inserção do drag

Por padrão, o Angular insere o preview do cdkDrag no <body> da página para evitar problemas com posicionamento e overflow. Isso pode não ser desejável em alguns casos porque o preview não terá seus estilos herdados aplicados.

Você pode mudar onde o Angular insere o preview usando o input cdkDragPreviewContainer em cdkDrag. Os valores possíveis são:

Valor Descrição Vantagens Desvantagens
global Valor padrão. Angular insere o preview no ou na shadow root mais próxima. Preview não será afetado por z-index ou overflow: hidden. Também não afetará seletores :nth-child e layouts flex. Não mantém estilos herdados.
parent Angular insere o preview dentro do pai do elemento que está sendo arrastado. Preview herda os mesmos estilos que o elemento arrastado. Preview pode ser cortado por overflow: hidden ou ser colocado sob outros elementos devido a z-index. Além disso, pode afetar seletores :nth-child e alguns layouts flex.
ElementRef ou HTMLElement Angular insere o preview no elemento especificado. Preview herda estilos do elemento container especificado. Preview pode ser cortado por overflow: hidden ou ser colocado sob outros elementos devido a z-index. Além disso, pode afetar seletores :nth-child e alguns layouts flex.

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar previewContainer dentro da configuração se o valor for global ou parent. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Customizar drag placeholder

Enquanto um elemento cdkDrag está sendo arrastado, a directive cria um elemento placeholder que mostra onde o elemento será colocado quando solto. Por padrão, o placeholder é um clone do elemento que está sendo arrastado. Você pode substituir o placeholder por um customizado usando a directive *cdkDragPlaceholder:

app/app.component.html

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">  @for (movie of movies; track movie) {    <div class="example-box" cdkDrag>      <div class="example-custom-placeholder" *cdkDragPlaceholder></div>      {{movie}}    </div>  }</div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDragPlaceholder,  CdkDropList,  moveItemInArray,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop custom placeholder */@Component({  selector: 'cdk-drag-drop-custom-placeholder-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag, CdkDragPlaceholder],})export class CdkDragDropCustomPlaceholderExample {  movies = [    'Episode I - The Phantom Menace',    'Episode II - Attack of the Clones',    'Episode III - Revenge of the Sith',    'Episode IV - A New Hope',    'Episode V - The Empire Strikes Back',    'Episode VI - Return of the Jedi',    'Episode VII - The Force Awakens',    'Episode VIII - The Last Jedi',    'Episode IX - The Rise of Skywalker',  ];  drop(event: CdkDragDrop<string[]>) {    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  width: 500px;  max-width: 100%;  border: solid 1px #ccc;  min-height: 60px;  display: block;  background: white;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-custom-placeholder {  background: #ccc;  border: dotted 3px #999;  min-height: 60px;  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Você pode usar o token de injeção CDK_DRAG_PLACEHOLDER que pode ser usado para referenciar instâncias de cdkDragPlaceholder. Para mais informações, consulte o guia de dependency injection e a API do token de injeção drag placeholder.

Customizar drag root element

Defina o atributo cdkDragRootElement se houver um elemento que você quer tornar draggable mas você não tem acesso direto a ele.

O atributo aceita um selector e procura no DOM até encontrar um elemento que corresponda ao selector. Se um elemento for encontrado, ele se torna draggable. Isso é útil para casos como tornar um dialog draggable.

app/app.component.html

<button type="button" (click)="openDialog()">Open a draggable dialog</button><ng-template>  <div class="example-dialog-content" cdkDrag cdkDragRootElement=".cdk-overlay-pane">    Drag the dialog around!  </div></ng-template>

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Overlay, OverlayRef} from '@angular/cdk/overlay';import {TemplatePortal} from '@angular/cdk/portal';import {  AfterViewInit,  Component,  OnDestroy,  TemplateRef,  ViewChild,  ViewContainerRef,  inject,} from '@angular/core';/** * @title Drag&Drop with alternate root element */@Component({  selector: 'cdk-drag-drop-root-element-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropRootElementExample implements AfterViewInit, OnDestroy {  private _overlay = inject(Overlay);  private _viewContainerRef = inject(ViewContainerRef);  @ViewChild(TemplateRef) _dialogTemplate!: TemplateRef<any>;  private _overlayRef!: OverlayRef;  private _portal!: TemplatePortal;  ngAfterViewInit() {    this._portal = new TemplatePortal(this._dialogTemplate, this._viewContainerRef);    this._overlayRef = this._overlay.create({      positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),      hasBackdrop: true,    });    this._overlayRef.backdropClick().subscribe(() => this._overlayRef.detach());  }  ngOnDestroy() {    this._overlayRef.dispose();  }  openDialog() {    this._overlayRef.attach(this._portal);  }}

app/app.component.css

.example-dialog-content {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: flex;  justify-content: center;  align-items: center;  background: #fff;  border-radius: 4px;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-dialog-content:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar rootElementSelector dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Definir posição DOM de um elemento draggable

Por padrão, elementos cdkDrag não em um cdkDropList movem-se de sua posição DOM normal apenas quando um usuário move manualmente o elemento. Use o input cdkDragFreeDragPosition para definir explicitamente a posição do elemento. Um caso de uso comum para isso é restaurar a posição de um elemento draggable depois que um usuário navegou para outra página e então retornou.

app/app.component.html

<p>  <button type="button" (click)="changePosition()">Change element position</button></p><div class="example-box" cdkDrag [cdkDragFreeDragPosition]="dragPosition">  Drag me around</div>

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Programmatically setting the free drag position */@Component({  selector: 'cdk-drag-drop-free-drag-position-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropFreeDragPositionExample {  dragPosition = {x: 0, y: 0};  changePosition() {    this.dragPosition = {x: this.dragPosition.x + 50, y: this.dragPosition.y + 50};  }}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  position: relative;  z-index: 1;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}

Restringir movimento dentro de um elemento

Para impedir que o usuário possa arrastar um elemento cdkDrag para fora de outro elemento, passe um selector CSS para o atributo cdkDragBoundary. Este atributo aceita um selector e procura no DOM até encontrar um elemento que corresponda a ele. Se uma correspondência for encontrada, o elemento se torna o limite que o elemento draggable não pode ser arrastado para fora. cdkDragBoundary também pode ser usado quando cdkDrag está colocado dentro de um cdkDropList.

app/app.component.html

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

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop boundary */@Component({  selector: 'cdk-drag-drop-boundary-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropBoundaryExample {}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: inline-flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  margin-right: 25px;  position: relative;  z-index: 1;  box-sizing: border-box;  padding: 10px;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.example-boundary {  width: 400px;  height: 400px;  max-width: 100%;  border: dotted #ccc 2px;}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar boundaryElement dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Restringir movimento ao longo de um eixo

Por padrão, cdkDrag permite movimento livre em todas as direções. Para restringir dragging a um eixo específico, defina cdkDragLockAxis como "x" ou "y" em cdkDrag. Para restringir dragging para múltiplos elementos draggable dentro de cdkDropList, defina cdkDropListLockAxis em cdkDropList em vez disso.

app/app.component.html

<div class="example-box" cdkDragLockAxis="y" cdkDrag>  I can only be dragged up/down</div><div class="example-box" cdkDragLockAxis="x" cdkDrag>  I can only be dragged left/right</div>

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop position locking */@Component({  selector: 'cdk-drag-drop-axis-lock-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropAxisLockExample {}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: inline-flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  margin-right: 25px;  position: relative;  z-index: 1;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar lockAxis dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Atrasar dragging

Por padrão, quando o usuário coloca seu pointer sobre um cdkDrag, a sequência de dragging começa. Esse comportamento pode não ser desejável em casos como elementos draggable fullscreen em dispositivos touch onde o usuário pode acidentalmente acionar um evento de drag enquanto rola na página.

Você pode atrasar a sequência de dragging usando o input cdkDragStartDelay. O input espera que o usuário segure seu pointer pelo número especificado de milissegundos antes de arrastar o elemento.

app/app.component.html

<div class="example-box" cdkDrag [cdkDragStartDelay]="1000">  Dragging starts after one second</div>

app/app.component.ts

import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Delay dragging */@Component({  selector: 'cdk-drag-drop-delay-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDrag],})export class CdkDragDropDelayExample {}

app/app.component.css

.example-box {  width: 200px;  height: 200px;  border: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  cursor: move;  display: flex;  justify-content: center;  align-items: center;  text-align: center;  background: #fff;  border-radius: 4px;  position: relative;  z-index: 1;  transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);  box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),              0 2px 2px 0 rgba(0, 0, 0, 0.14),              0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active {  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar dragStartDelay dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Desabilitar dragging

Se você quiser desabilitar dragging para um item de drag particular, defina o input cdkDragDisabled em um item cdkDrag como true ou false. Você pode desabilitar uma lista inteira usando o input cdkDropListDisabled em um cdkDropList. Também é possível desabilitar um handle específico via cdkDragHandleDisabled em cdkDragHandle.

app/app.component.html

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">  @for (item of items; track item) {    <div      class="example-box"      cdkDrag      [cdkDragDisabled]="item.disabled">{{item.value}}</div>  }</div>

app/app.component.ts

import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop disabled */@Component({  selector: 'cdk-drag-drop-disabled-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropDisabledExample {  items = [    {value: 'I can be dragged', disabled: false},    {value: 'I cannot be dragged', disabled: true},    {value: 'I can also be dragged', disabled: false},  ];  drop(event: CdkDragDrop<string[]>) {    moveItemInArray(this.items, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  width: 500px;  max-width: 100%;  border: solid 1px #ccc;  min-height: 60px;  display: block;  background: white;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.example-box.cdk-drag-disabled {  background: #ccc;  cursor: not-allowed;  user-select: none;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar draggingDisabled dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Customizações de sorting

Orientação de lista

Por padrão, a directive cdkDropList assume que listas são verticais. Isso pode ser alterado definindo a propriedade cdkDropListOrientation como horizontal.

app/app.component.html

<div cdkDropList cdkDropListOrientation="horizontal" class="example-list" (cdkDropListDropped)="drop($event)">  @for (timePeriod of timePeriods; track timePeriod) {    <div class="example-box" cdkDrag>{{timePeriod}}</div>  }</div>

app/app.component.ts

import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop horizontal sorting */@Component({  selector: 'cdk-drag-drop-horizontal-sorting-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropHorizontalSortingExample {  timePeriods = [    'Bronze age',    'Iron age',    'Middle ages',    'Early modern period',    'Long nineteenth century',  ];  drop(event: CdkDragDrop<string[]>) {    moveItemInArray(this.timePeriods, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  width: 1000px;  max-width: 100%;  border: solid 1px #ccc;  min-height: 60px;  display: flex;  flex-direction: row;  background: white;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border-right: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;  flex-grow: 1;  flex-basis: 0;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar listOrientation dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

List wrapping

Por padrão, o cdkDropList ordena os elementos draggable movendo-os usando uma transform CSS. Isso permite que a ordenação seja animada, o que proporciona uma melhor experiência do usuário. No entanto, isso também vem com a desvantagem de que a drop list funciona apenas em uma direção: verticalmente ou horizontalmente.

Se você tiver uma lista ordenável que precisa quebrar para novas linhas, você pode definir o atributo cdkDropListOrientation como mixed. Isso faz com que a lista use uma estratégia diferente de ordenar os elementos que envolve movê-los no DOM. No entanto, a lista não pode mais animar a ação de ordenação.

app/app.component.html

<div cdkDropList cdkDropListOrientation="mixed" class="example-list" (cdkDropListDropped)="drop($event)">  @for (item of items; track item) {    <div class="example-box" cdkDrag>{{item}}</div>  }</div>

app/app.component.ts

import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop horizontal wrapping list */@Component({  selector: 'cdk-drag-drop-mixed-sorting-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropMixedSortingExample {  items = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];  drop(event: CdkDragDrop<string[]>) {    moveItemInArray(this.items, event.previousIndex, event.currentIndex);  }}

app/app.component.css

.example-list {  display: flex;  flex-wrap: wrap;  width: 505px;  max-width: 100%;  gap: 15px;  padding: 15px;  border: solid 1px #ccc;  min-height: 60px;  border-radius: 4px;  overflow: hidden;}.example-box {  padding: 20px 10px;  border: solid 1px #ccc;  border-radius: 4px;  color: rgba(0, 0, 0, 0.87);  display: inline-block;  box-sizing: border-box;  cursor: move;  background: white;  text-align: center;  font-size: 14px;  min-width: 115px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Sorting seletivo

Por padrão, elementos cdkDrag são ordenados em qualquer posição dentro de um cdkDropList. Para mudar esse comportamento, defina o atributo cdkDropListSortPredicate que recebe uma função. A função predicate é chamada sempre que um elemento draggable está prestes a ser movido para um novo índice dentro da drop list. Se o predicate retornar true, o item será movido para o novo índice, caso contrário manterá sua posição atual.

app/app.component.html

<div  cdkDropList  class="example-list"  (cdkDropListDropped)="drop($event)"  [cdkDropListSortPredicate]="sortPredicate">  @for (number of numbers; track number) {    <div      class="example-box"      [cdkDragData]="number"      cdkDrag>{{number}}</div>  }</div>

app/app.component.ts

import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop sort predicate */@Component({  selector: 'cdk-drag-drop-sort-predicate-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropSortPredicateExample {  numbers = [1, 2, 3, 4, 5, 6, 7, 8];  drop(event: CdkDragDrop<unknown>) {    moveItemInArray(this.numbers, event.previousIndex, event.currentIndex);  }  /**   * Predicate function that only allows even numbers to be   * sorted into even indices and odd numbers at odd indices.   */  sortPredicate(index: number, item: CdkDrag<number>) {    return (index + 1) % 2 === item.data % 2;  }}

app/app.component.css

.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;  width: 400px;  max-width: 100%;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Desabilitar sorting

Existem casos onde elementos draggable podem ser arrastados de um cdkDropList para outro, no entanto, o usuário não deveria poder ordená-los dentro da lista de origem. Para esses casos, adicione o atributo cdkDropListSortingDisabled para prevenir que elementos draggable em um cdkDropList sejam ordenados. Isso preserva a posição inicial do elemento arrastado na lista de origem se ele não for arrastado para uma nova posição válida.

app/app.component.html

<div cdkDropListGroup>  <div class="example-container">    <h2>Available items</h2>    <div      cdkDropList      [cdkDropListData]="items"      class="example-list"      cdkDropListSortingDisabled      (cdkDropListDropped)="drop($event)">      @for (item of items; track item) {        <div class="example-box" cdkDrag>{{item}}</div>      }    </div>  </div>  <div class="example-container">    <h2>Shopping basket</h2>    <div      cdkDropList      [cdkDropListData]="basket"      class="example-list"      (cdkDropListDropped)="drop($event)">      @for (item of basket; track item) {        <div class="example-box" cdkDrag>{{item}}</div>      }    </div>  </div></div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDropList,  CdkDropListGroup,  moveItemInArray,  transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop disabled sorting */@Component({  selector: 'cdk-drag-drop-disabled-sorting-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropListGroup, CdkDropList, CdkDrag],})export class CdkDragDropDisabledSortingExample {  items = ['Carrots', 'Tomatoes', 'Onions', 'Apples', 'Avocados'];  basket = ['Oranges', 'Bananas', 'Cucumbers'];  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,      );    }  }}

app/app.component.css

HTMLTSCSS.example-container {  width: 400px;  max-width: 100%;  margin: 0 25px 25px 0;  display: inline-block;  vertical-align: top;}.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),              0 8px 10px 1px rgba(0, 0, 0, 0.14),              0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder {  opacity: 0;}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Alternativamente, você pode modificar o token de injeção CDK_DRAG_CONFIG para atualizar sortingDisabled dentro da configuração. Para mais informações, consulte o guia de dependency injection, API do token de injeção drag config, e a API drag drop config.

Copiar itens entre listas

Por padrão, quando um item é arrastado de uma lista para outra, ele é movido para fora de sua lista original. No entanto, você pode configurar as directives para copiar o item, deixando o item original na lista de origem.

Para habilitar cópia, você pode definir o input cdkDropListHasAnchor. Isso diz ao cdkDropList para criar um elemento "anchor" que permanece no container original e não se move com o item. Se o usuário mover o item de volta para o container original, o anchor é removido automaticamente. O elemento anchor pode ser estilizado visando a classe CSS .cdk-drag-anchor.

Combinar cdkDropListHasAnchor com cdkDropListSortingDisabled torna possível construir uma lista da qual um usuário pode copiar itens sem poder reordenar a lista de origem (por exemplo, uma lista de produtos e um carrinho de compras).

app/app.component.html

<div class="example-container">  <h2>Products</h2>  <div    cdkDropList    [cdkDropListData]="products"    [cdkDropListConnectedTo]="[cartList]"    cdkDropListSortingDisabled    cdkDropListHasAnchor    class="example-list"  >    @for (product of products; track $index) {      <div class="example-box" cdkDrag [cdkDragData]="product">{{product}}</div>    }  </div></div><div class="example-container">  <h2>Shopping cart</h2>  <div    cdkDropList    #cartList="cdkDropList"    [cdkDropListData]="cart"    class="example-list"    (cdkDropListDropped)="drop($event)"  >    @for (product of cart; track $index) {      <div class="example-box" cdkDrag>{{product}}</div>    }  </div></div>

app/app.component.ts

import {  CdkDrag,  CdkDragDrop,  CdkDropList,  copyArrayItem,  moveItemInArray,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop copy between lists */@Component({  selector: 'cdk-drag-drop-copy-list-example',  templateUrl: 'app.component.html',  styleUrl: 'app.component.css',  imports: [CdkDropList, CdkDrag],})export class CdkDragDropCopyListExample {  products = ['Bananas', 'Oranges', 'Bread', 'Butter', 'Soda', 'Eggs'];  cart = ['Tomatoes'];  drop(event: CdkDragDrop<string[]>) {    if (event.previousContainer === event.container) {      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);    } else {      copyArrayItem(        event.previousContainer.data,        event.container.data,        event.previousIndex,        event.currentIndex,      );    }  }}

app/app.component.css

.example-container {  width: 400px;  max-width: 100%;  margin: 0 25px 25px 0;  display: inline-block;  vertical-align: top;  font-family: sans-serif;}.example-list {  border: solid 1px #ccc;  min-height: 60px;  background: white;  border-radius: 4px;  overflow: hidden;  display: block;}.example-box {  padding: 20px 10px;  border-bottom: solid 1px #ccc;  color: rgba(0, 0, 0, 0.87);  display: flex;  flex-direction: row;  align-items: center;  justify-content: space-between;  box-sizing: border-box;  cursor: move;  background: white;  font-size: 14px;  font-family: sans-serif;}.cdk-drag-preview {  box-sizing: border-box;  border-radius: 4px;  box-shadow:    0 5px 5px -3px rgba(0, 0, 0, 0.2),    0 8px 10px 1px rgba(0, 0, 0, 0.14),    0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-animating {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child {  border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {  transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}

Customizar animações

Drag and drop suporta animações para ambos:

  • Ordenar um elemento draggable dentro de uma lista
  • Mover o elemento draggable da posição onde o usuário o soltou para a posição final dentro da lista

Para configurar suas animações, defina uma transition CSS que tenha como alvo a propriedade transform. As seguintes classes podem ser usadas para animações:

Nome da classe CSS Resultado de adicionar transition
.cdk-drag Animar elementos draggable conforme eles estão sendo ordenados.
.cdk-drag-animating Animar o elemento draggable da sua posição solta para a posição final dentro do cdkDropList.

Esta classe CSS é aplicada a um elemento cdkDrag apenas quando a ação de dragging foi interrompida.

Estilização

Tanto as directives cdkDrag quanto cdkDropList aplicam apenas estilos essenciais necessários para funcionalidade. Aplicações podem customizar seus estilos visando essas classes CSS especificadas.

Nome da classe CSS Descrição
.cdk-drop-list Selector para os elementos container cdkDropList.
.cdk-drag Selector para elementos cdkDrag.
.cdk-drag-disabled Selector para elementos cdkDrag desabilitados.
.cdk-drag-handle Selector para o elemento host do cdkDragHandle.
.cdk-drag-preview Selector para o elemento drag preview. Este é o elemento que aparece ao lado do cursor conforme um usuário arrasta um elemento em uma lista ordenável.

O elemento se parece exatamente com o elemento que está sendo arrastado, a menos que seja customizado com um template customizado através de *cdkDragPreview.
.cdk-drag-placeholder Selector para o elemento drag placeholder. Este é o elemento que é mostrado no local onde o elemento draggable será arrastado assim que a ação de dragging terminar.

Este elemento se parece exatamente com o elemento que está sendo ordenado, a menos que seja customizado com a directive cdkDragPlaceholder.
.cdk-drop-list-dragging Selector para elemento container cdkDropList que tem um elemento draggable sendo arrastado atualmente.
.cdk-drop-list-disabled Selector para elementos container cdkDropList que estão desabilitados.
.cdk-drop-list-receiving Selector para elemento container cdkDropList que tem um elemento draggable que pode receber de uma drop list conectada que está sendo arrastada atualmente.
.cdk-drag-anchor Selector para o elemento anchor que é criado quando cdkDropListHasAnchor está habilitado. Este elemento indica a posição da qual o item arrastado começou.

Dragging em um container com scroll

Se seus itens draggable estiverem dentro de um container com scroll (por exemplo, um div com overflow: auto), o scroll automático não funcionará a menos que o container com scroll tenha a directive cdkScrollable. Sem ela, o CDK não pode detectar ou controlar o comportamento de scroll do container durante operações de drag.

Integrações com outros components

A funcionalidade de drag-and-drop do CDK pode ser integrada com diferentes components. Casos de uso comuns incluem components MatTable ordenáveis e components MatTabGroup ordenáveis.