El Component Dev Kit (CDK) es un conjunto de primitivas de comportamiento para construir componentes. Para usar las directivas de drag and drop, primero instala @angular/cdk desde npm. Puedes hacerlo desde tu terminal usando Angular CLI:
Puedes hacer que cualquier elemento sea arrastrable agregando la directiva cdkDrag. Por defecto, todos los elementos arrastrables soporta arrastre libre.
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 {}
Añade la directiva cdkDropList a un elemento padre para agrupar elementos arrastrables en una colección reordenable. Esto define dónde se pueden soltar los elementos arrastrables. Los elementos arrastrables en el grupo de la lista (drop list) se reorganizan automáticamente cuando un elemento se mueve.
Las directivas de drag and drop no actualizan tu modelo de datos. Para actualizar el modelo de datos, escucha el evento cdkDropListDropped (una vez que el usuario termina de arrastrar) y actualiza el modelo de datos manualmente.
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); }}
La directiva cdkDropList soporta transferir elementos arrastrables entre listas conectadas (connected drop lists). Hay dos formas de conectar una o más instancias de cdkDropList:
Establecer la propiedad cdkDropListConnectedTo a otra lista (drop list).
Envolver los elementos en un elemento con el atributo cdkDropListGroup.
La directiva cdkDropListConnectedTo funciona tanto con una referencia directa a otro cdkDropList como referenciando el id de otro contenedor (drop container).
<!-- This is valid --><div cdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div><div cdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div><!-- This is valid as well --><div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div><div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>
Usa la directiva cdkDropListGroup si tienes un número desconocido de listas conectadas (connected drop lists) para establecer la conexión automáticamente. Cualquier nuevo cdkDropList que se agregue bajo un grupo se conecta automáticamente a todas las otras listas.
<div cdkDropListGroup> <!-- Todas las listas aquí estarán conectadas. --> @for (list of lists; track list) { <div cdkDropList></div> }</div>
Por defecto, un usuario puede mover elementos cdkDrag de un contenedor a otro contenedor conectado. Para un control más granular sobre qué elementos se pueden soltar en un contenedor, usa cdkDropListEnterPredicate. Angular llama al predicado cada vez que un elemento arrastrable entra en un nuevo contenedor. Dependiendo de si el predicado devuelve true o false, el elemento puede o no ser permitido en el nuevo contenedor.
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; }}
Puedes asociar algunos datos arbitrarios tanto con cdkDrag como con cdkDropList estableciendo cdkDragData o cdkDropListData, respectivamente. Puedes vincular a los eventos disparados desde ambas directivas que incluirán estos datos, permitiéndote identificar fácilmente el origen de la interacción de arrastre o soltar.
Por defecto, el usuario puede arrastrar todo el elemento cdkDrag para moverlo. Para restringir al usuario a que solo pueda hacerlo usando un elemento manejador, añade la directiva cdkDragHandle a un elemento dentro de cdkDrag. Puedes tener tantos elementos cdkDragHandle como desees.
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 {}
Un elemento de vista previa se vuelve visible cuando un elemento cdkDrag está siendo arrastrado. Por defecto, la vista previa es un clon del elemento original posicionado junto al cursor del usuario.
Para personalizar la vista previa, proporciona una plantilla personalizada a través de *cdkDragPreview. La vista previa personalizada no coincidirá con el tamaño del elemento original arrastrado ya que no se hacen suposiciones sobre el contenido del elemento. Para que coincida con el tamaño del elemento para la vista previa de arrastre, pasa true al input matchSize.
El elemento clonado elimina su atributo id para evitar tener múltiples elementos con el mismo id en la página. Esto hará que cualquier CSS que apunte a ese id no se aplique.
Por defecto, Angular inserta la vista previa de cdkDrag en el <body> de la página para evitar problemas con el posicionamiento y el desbordamiento. Esto puede no ser deseable en algunos casos porque la vista previa no tendrá sus estilos heredados aplicados.
Puedes cambiar dónde Angular inserta la vista previa usando el input cdkDragPreviewContainer en cdkDrag. Los valores posibles son:
Valor
Descripción
Ventajas
Desventajas
global
Valor por defecto. Angular inserta la vista previa en el o en el shadow root (shadow root (raíz de sombra)) más cercano.
La vista previa no se verá afectada por z-index o overflow: hidden. Tampoco afectará selectores :nth-child y layouts flex.
No retiene estilos heredados.
parent
Angular inserta la vista previa dentro del padre del elemento que está siendo arrastrado.
La vista previa hereda los mismos estilos que el elemento arrastrado.
La vista previa puede ser recortada por overflow: hidden o colocada debajo de otros elementos debido a z-index. Además, puede afectar selectores :nth-child y algunos layouts flex.
Angular inserta la vista previa en el elemento especificado.
PLa vista previa hereda estilos del elemento contenedor especificado.
La vista previa puede ser recortada por overflow: hidden o colocada debajo de otros elementos debido a z-index. Además, puede afectar selectores :nth-child y algunos layouts flex.
Mientras un elemento cdkDrag está siendo arrastrado, la directiva crea un elemento marcador de posición que muestra dónde se colocará el elemento cuando se suelte. Por defecto, el marcador de posición es un clon del elemento que está siendo arrastrado. Puedes reemplazar el marcador de posición con uno personalizado usando la directiva *cdkDragPlaceholder:
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); }}
Establece el atributo cdkDragRootElement si hay un elemento que quieres hacer arrastrable pero no tienes acceso directo a él.
El atributo acepta un selector y busca en el DOM hasta encontrar un elemento que coincida con el selector. Si se encuentra un elemento, se vuelve arrastrable. Esto es útil para casos como hacer un diálogo arrastrable.
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 implementsAfterViewInit, 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); }}
Por defecto, los elementos cdkDrag que no están en un cdkDropList se mueven de su posición DOM normal solo cuando un usuario mueve manualmente el elemento. Usa el input cdkDragFreeDragPosition para establecer explícitamente la posición del elemento. Un caso de uso común para esto es restaurar la posición de un elemento arrastrable después de que un usuario ha navegado y luego regresado.
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}; }}
To stop the user from being able to drag a cdkDrag element outside of another element, pass a CSS selector to the cdkDragBoundary attribute. This attribute accepts a selector and looks up the DOM until it finds an element that matches it. If a match is found, the element becomes the boundary that the draggable element can't be dragged outside of cdkDragBoundary can also be used when cdkDrag is placed inside a 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 {}
Por defecto, cdkDrag permite movimiento libre en todas las direcciones. Para restringir el arrastre a un eje específico, establece cdkDragLockAxis a "x" o "y" en cdkDrag. Para restringir el arrastre para múltiples elementos arrastrables dentro de cdkDropList, establece cdkDropListLockAxis en cdkDropList en su lugar.
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 {}
Por defecto, cuando el usuario presiona su puntero en un cdkDrag, comienza la secuencia de arrastre. Este comportamiento puede no ser deseable en casos como elementos arrastrables de pantalla completa en dispositivos táctiles donde el usuario podría activar accidentalmente un evento de arrastre mientras hace scroll en la página.
Puedes retrasar la secuencia de arrastre usando el input cdkDragStartDelay. El input espera a que el usuario mantenga presionado su puntero durante el número especificado de milisegundos antes de arrastrar el 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 {}
Si quieres deshabilitar el arrastre para un elemento de arrastre particular, establece el input cdkDragDisabled en un elemento cdkDrag a true o false. Puedes deshabilitar una lista completa usando el input cdkDropListDisabled en un cdkDropList. También es posible deshabilitar una manija específica a través de cdkDragHandleDisabled en cdkDragHandle.
Por defecto, la directiva cdkDropList asume que las listas son verticales. Esto se puede cambiar estableciendo la propiedad cdkDropListOrientation a horizontal.
Por defecto, el cdkDropList ordena los elementos arrastrables moviéndolos usando una transformación CSS. Esto permite que el ordenamiento sea animado, lo que proporciona una mejor experiencia de usuario. Sin embargo, esto también viene con la desventaja de que la lista (drop list) funciona solo en una dirección: vertical u horizontalmente.
Si tienes una lista ordenable que necesita envolverse en nuevas líneas, puedes establecer el atributo cdkDropListOrientation a mixed. Esto hace que la lista use una estrategia diferente de ordenamiento de elementos que implica moverlos en el DOM. Sin embargo, la lista ya no puede animar la acción de ordenamiento.
Por defecto, los elementos cdkDrag se ordenan en cualquier posición dentro de un cdkDropList. Para cambiar este comportamiento, establece el atributo cdkDropListSortPredicate que toma una función. La función predicado se llama cada vez que un elemento arrastrable está a punto de ser movido a un nuevo índice dentro de la lista (drop list). Si el predicado devuelve true, el elemento se moverá al nuevo índice, de lo contrario mantendrá su posición actual.
Hay casos donde los elementos arrastrables pueden ser arrastrados de un cdkDropList a otro, sin embargo, el usuario no debería poder ordenarlos dentro de la lista de origen. Para estos casos, agrega el atributo cdkDropListSortingDisabled para evitar que los elementos arrastrables en un cdkDropList se ordenen. Esto preserva la posición inicial del elemento arrastrado en la lista de origen si no se arrastra a una nueva posición válida.
By default, when an item is dragged from one list to another, it is moved out of its original list. However, you can configure the directives to copy the item, leaving the original item in the source list.
To enable copying, you can set the cdkDropListHasAnchor input. This tells the cdkDropList to create an "anchor" element that stays in the original container and doesn't move with the item. If the user moves the item back into the original container, the anchor is removed automatically. The anchor element can be styled by targeting the .cdk-drag-anchor CSS class.
Combining cdkDropListHasAnchor with cdkDropListSortingDisabled makes it possible to construct a list from which a user can copy items without being able to reorder the source list (e.g. a product list and a shopping cart).
Tanto las directivas cdkDrag como cdkDropList solo aplican estilos esenciales necesarios para la funcionalidad. Las aplicaciones pueden personalizar sus estilos apuntando a estas clases CSS especificadas.
Nombre de la clase CSS
Descripción
.cdk-drop-list
Selector para los elementos contenedores de cdkDropList.
.cdk-drag
Selector para elementos cdkDrag.
.cdk-drag-disabled
Selector para elementos cdkDrag deshabilitados.
.cdk-drag-handle
Selector para el elemento host del cdkDragHandle.
.cdk-drag-preview
Selector para el elemento de vista previa de arrastre. Este es el elemento que aparece junto al cursor cuando un usuario arrastra un elemento en una lista ordenable.
El elemento se ve exactamente como el elemento que está siendo arrastrado a menos que se personalice con una plantilla personalizada a través de *cdkDragPreview.
.cdk-drag-placeholder
Selector para el elemento marcador de posición de arrastre. Este es el elemento que se muestra en el lugar donde el elemento arrastrable será arrastrado una vez que termine la acción de arrastre.
Este elemento se ve exactamente como el elemento que está siendo ordenado a menos que se personalice con la directiva cdkDragPlaceholder.
.cdk-drop-list-dragging
Selector para el elemento contenedor de cdkDropList que tiene un elemento arrastrable actualmente siendo arrastrado.
.cdk-drop-list-disabled
Selector para elementos contenedores de cdkDropList que están deshabilitados.
.cdk-drop-list-receiving
Selector para el elemento contenedor de cdkDropList que tiene un elemento arrastrable que puede recibir de una lista de soltar conectada que está actualmente siendo arrastrado.
.cdk-drag-anchor
Selector para elemento de aclaje que se crea cuado cdkDropListHasAnchor está habilitado. Este elemento indica la posición desde la cual comenzó el arrastre del elemento
Si tus elementos arrastrables están dentro de un contenedor desplazable (por ejemplo, un div con overflow: auto), el desplazamiento automático no funcionará a menos que el contenedor desplazable tenga la directiva cdkScrollable. Sin esta directiva, el CDK no puede detectar ni controlar el comportamiento de desplazamiento del contenedor durante las operaciones de arrastre.
La funcionalidad de arrastrar y soltar del CDK puede integrarse con diferentes componentes. Los casos de uso más comunes incluyen componentes MatTable ordenables y componentes MatTabGroup ordenables.