Le Component Dev Kit (CDK) est un ensemble de primitives de comportement pour construire des composants. Pour utiliser les directives de glisser-déposer, installez d'abord @angular/cdk depuis npm. Vous pouvez le faire depuis votre terminal avec Angular CLI :
Vous pouvez rendre n'importe quel élément déplaçable en ajoutant la directive cdkDrag. Par défaut, tous les éléments déplaçables prennent en charge le glissement libre.
app.html
<div class="example-box"cdkDrag>Drag me around</div>
app.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.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropOverviewExample {}
Ajoutez la directive cdkDropList à un élément parent pour regrouper des éléments déplaçables dans une collection réordonnable. Elle définit où les éléments déplaçables peuvent être déposés. Les éléments déplaçables du groupe de la liste de dépôt se réorganisent automatiquement lorsqu'un élément se déplace.
Les directives de glisser-déposer ne mettent pas à jour votre modèle de données. Pour mettre à jour le modèle de données, écoutez l'événement cdkDropListDropped (une fois que l'utilisateur a fini de faire glisser) et mettez à jour le modèle de données manuellement.
app.html
<divcdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> @for (movie of movies; track movie) { <div class="example-box"cdkDrag>{{ movie }}</div> }</div>
app.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.html', styleUrl: 'app.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 directive cdkDropList permet de transférer des éléments déplaçables entre des listes de dépôt connectées. Il existe deux façons de connecter une ou plusieurs instances de cdkDropList entre elles :
Définir la propriété cdkDropListConnectedTo sur une autre liste de dépôt.
Envelopper les éléments dans un élément portant l'attribut cdkDropListGroup.
La directive cdkDropListConnectedTo fonctionne aussi bien avec une référence directe à une autre cdkDropList qu'en référençant l'id d'un autre conteneur de dépôt.
<!-- This is valid --><divcdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div><divcdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div><!-- This is valid as well --><divcdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div><divcdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>
Utilisez la directive cdkDropListGroup si vous avez un nombre inconnu de listes de dépôt connectées, afin d'établir la connexion automatiquement. Toute nouvelle cdkDropList ajoutée dans un groupe se connecte automatiquement à toutes les autres listes.
<divcdkDropListGroup> <!-- All lists in here will be connected. --> @for (list of lists; track list) { <divcdkDropList></div> }</div>
Par défaut, un utilisateur peut déplacer des éléments cdkDrag d'un conteneur vers un autre conteneur connecté. Pour contrôler plus finement quels éléments peuvent être déposés dans un conteneur, utilisez cdkDropListEnterPredicate. Angular appelle le prédicat chaque fois qu'un élément déplaçable entre dans un nouveau conteneur. Selon que le prédicat renvoie true ou false, l'élément est autorisé ou non à entrer dans le nouveau conteneur.
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.html', styleUrl: 'app.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; }}
Vous pouvez associer des données arbitraires à cdkDrag et à cdkDropList en définissant respectivement cdkDragData ou cdkDropListData. Vous pouvez vous lier aux événements émis par ces deux directives, qui incluent ces données, ce qui vous permet d'identifier facilement l'origine de l'interaction de glissement ou de dépôt.
Par défaut, l'utilisateur peut faire glisser l'ensemble de l'élément cdkDrag pour le déplacer. Pour qu'il ne puisse le faire qu'au moyen d'un élément servant de poignée, ajoutez la directive cdkDragHandle à un élément situé à l'intérieur de cdkDrag. Vous pouvez avoir autant d'éléments cdkDragHandle que vous le souhaitez.
app.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.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.html', styleUrl: 'app.css', imports: [CdkDrag, CdkDragHandle],})export class CdkDragDropHandleExample {}
Un élément d'aperçu devient visible lorsqu'un élément cdkDrag est en cours de glissement. Par défaut, l'aperçu est un clone de l'élément d'origine, positionné à côté du curseur de l'utilisateur.
Pour personnaliser l'aperçu, fournissez un template personnalisé via *cdkDragPreview. L'aperçu personnalisé n'adopte pas la taille de l'élément d'origine en cours de glissement, car aucune hypothèse n'est faite sur le contenu de l'élément. Pour que l'aperçu de glissement adopte la taille de l'élément, passez true à l'input matchSize.
L'élément cloné perd son attribut id afin d'éviter d'avoir plusieurs éléments avec le même id dans la page. Par conséquent, tout CSS qui cible cet id ne sera pas appliqué.
Par défaut, Angular insère l'aperçu de cdkDrag dans le <body> de la page afin d'éviter les problèmes de positionnement et de débordement. Cela peut ne pas être souhaitable dans certains cas, car les styles hérités ne seront pas appliqués à l'aperçu.
Vous pouvez changer l'endroit où Angular insère l'aperçu grâce à l'input cdkDragPreviewContainer de cdkDrag. Les valeurs possibles sont :
Valeur
Description
Avantages
Inconvénients
global
Valeur par défaut. Angular insère l'aperçu dans le ou dans la shadow root la plus proche.
L'aperçu n'est pas affecté par z-index ni par overflow: hidden. Il n'affecte pas non plus les selectors :nth-child ni les mises en page flex.
Ne conserve pas les styles hérités.
parent
Angular insère l'aperçu dans le parent de l'élément en cours de glissement.
L'aperçu hérite des mêmes styles que l'élément glissé.
L'aperçu peut être rogné par overflow: hidden ou placé sous d'autres éléments à cause de z-index. De plus, il peut affecter les selectors :nth-child et certaines mises en page flex.
L'aperçu hérite des styles de l'élément conteneur spécifié.
L'aperçu peut être rogné par overflow: hidden ou placé sous d'autres éléments à cause de z-index. De plus, il peut affecter les selectors :nth-child et certaines mises en page flex.
Pendant qu'un élément cdkDrag est en cours de glissement, la directive crée un élément placeholder qui indique où l'élément sera placé une fois déposé. Par défaut, le placeholder est un clone de l'élément en cours de glissement. Vous pouvez remplacer le placeholder par un placeholder personnalisé grâce à la directive *cdkDragPlaceholder :
app.html
<divcdkDropList 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.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.html', styleUrl: 'app.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); }}
Définissez l'attribut cdkDragRootElement s'il existe un élément que vous souhaitez rendre déplaçable mais auquel vous n'avez pas accès directement.
L'attribut accepte un selector et remonte le DOM jusqu'à trouver un élément qui correspond au selector. Si un élément est trouvé, il devient déplaçable. C'est utile par exemple pour rendre une boîte de dialogue déplaçable.
app.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.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,ViewContainerRef, inject,viewChild,} from '@angular/core';/** * @title Drag&Drop with alternate root element */@Component({ selector: 'cdk-drag-drop-root-element-example', templateUrl: 'app.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropRootElementExample implementsAfterViewInit, OnDestroy { private _overlay = inject(Overlay); private _viewContainerRef = inject(ViewContainerRef); private _dialogTemplate =viewChild.required(TemplateRef); 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); }}
Par défaut, les éléments cdkDrag qui ne sont pas dans une cdkDropList ne quittent leur position normale dans le DOM que lorsqu'un utilisateur déplace manuellement l'élément. Utilisez l'input cdkDragFreeDragPosition pour définir explicitement la position de l'élément. Un cas d'usage courant consiste à restaurer la position d'un élément déplaçable après qu'un utilisateur a quitté la page puis y est revenu.
app.html
<p> <button type="button" (click)="changePosition()">Change element position</button></p><div class="example-box"cdkDrag [cdkDragFreeDragPosition]="dragPosition">Drag me around</div>
app.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.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropFreeDragPositionExample { dragPosition = {x: 0, y: 0}; changePosition() { this.dragPosition = {x: this.dragPosition.x + 50, y: this.dragPosition.y + 50}; }}
Pour empêcher l'utilisateur de faire glisser un élément cdkDrag hors d'un autre élément, passez un selector CSS à l'attribut cdkDragBoundary. Cet attribut accepte un selector et remonte le DOM jusqu'à trouver un élément qui y correspond. Si une correspondance est trouvée, l'élément devient la limite hors de laquelle l'élément déplaçable ne peut pas être glissé. cdkDragBoundary peut aussi être utilisé lorsque cdkDrag est placé dans une cdkDropList.
app.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.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.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropBoundaryExample {}
Par défaut, cdkDrag permet un déplacement libre dans toutes les directions. Pour restreindre le glissement à un axe précis, définissez cdkDragLockAxis à « x » ou « y » sur cdkDrag. Pour restreindre le glissement de plusieurs éléments déplaçables dans une cdkDropList, définissez plutôt cdkDropListLockAxis sur cdkDropList.
app.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.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.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropAxisLockExample {}
Par défaut, lorsque l'utilisateur appuie avec son pointeur sur un cdkDrag, la séquence de glissement commence. Ce comportement peut ne pas être souhaitable, par exemple pour des éléments déplaçables en plein écran sur des appareils tactiles, où l'utilisateur risque de déclencher accidentellement un événement de glissement en faisant défiler la page.
Vous pouvez retarder la séquence de glissement grâce à l'input cdkDragStartDelay. L'input attend que l'utilisateur maintienne son pointeur enfoncé pendant le nombre de millisecondes spécifié avant de faire glisser l'élément.
app.html
<div class="example-box"cdkDrag [cdkDragStartDelay]="1000">Dragging starts after one second</div>
app.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.html', styleUrl: 'app.css', imports: [CdkDrag],})export class CdkDragDropDelayExample {}
Pour désactiver le glissement d'un élément particulier, définissez l'input cdkDragDisabled d'un élément cdkDrag à true ou false. Vous pouvez désactiver une liste entière avec l'input cdkDropListDisabled d'une cdkDropList. Il est également possible de désactiver une poignée précise via cdkDragHandleDisabled sur cdkDragHandle.
Par défaut, la directive cdkDropList suppose que les listes sont verticales. Vous pouvez changer cela en définissant la propriété cdkDropListOrientation à horizontal.
Par défaut, cdkDropList trie les éléments déplaçables en les déplaçant au moyen d'une transformation CSS. Cela permet d'animer le tri, ce qui offre une meilleure expérience utilisateur. En contrepartie, la liste de dépôt ne fonctionne que dans une seule direction : verticalement ou horizontalement.
Si vous avez une liste triable qui doit passer à la ligne, vous pouvez définir l'attribut cdkDropListOrientation à mixed. La liste utilise alors une autre stratégie de tri des éléments, qui consiste à les déplacer dans le DOM. En revanche, la liste ne peut plus animer l'action de tri.
Par défaut, les éléments cdkDrag peuvent être triés à n'importe quelle position dans une cdkDropList. Pour changer ce comportement, définissez l'attribut cdkDropListSortPredicate, qui reçoit une fonction. La fonction prédicat est appelée chaque fois qu'un élément déplaçable est sur le point d'être déplacé vers un nouvel index de la liste de dépôt. Si le prédicat renvoie true, l'élément est déplacé vers le nouvel index ; sinon, il conserve sa position actuelle.
app.html
<divcdkDropList 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.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.html', styleUrl: 'app.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; }}
Dans certains cas, des éléments déplaçables peuvent être glissés d'une cdkDropList vers une autre, sans que l'utilisateur puisse les trier dans la liste source. Dans ces cas, ajoutez l'attribut cdkDropListSortingDisabled pour empêcher le tri des éléments déplaçables d'une cdkDropList. L'élément glissé conserve alors sa position initiale dans la liste source s'il n'est pas déposé à une nouvelle position valide.
Par défaut, lorsqu'un élément est glissé d'une liste vers une autre, il est retiré de sa liste d'origine. Vous pouvez toutefois configurer les directives pour copier l'élément, en laissant l'élément d'origine dans la liste source.
Pour activer la copie, définissez l'input cdkDropListHasAnchor. Il indique à cdkDropList de créer un élément « ancre » qui reste dans le conteneur d'origine et ne se déplace pas avec l'élément. Si l'utilisateur ramène l'élément dans le conteneur d'origine, l'ancre est retirée automatiquement. Vous pouvez styliser l'élément ancre en ciblant la classe CSS .cdk-drag-anchor.
Combiner cdkDropListHasAnchor avec cdkDropListSortingDisabled permet de construire une liste depuis laquelle un utilisateur peut copier des éléments sans pouvoir réordonner la liste source (par exemple une liste de produits et un panier d'achat).
Le glisser-déposer prend en charge les animations pour :
Le tri d'un élément déplaçable dans une liste
Le déplacement de l'élément déplaçable depuis la position où l'utilisateur l'a déposé jusqu'à sa position finale dans la liste
Pour configurer vos animations, définissez une transition CSS qui cible la propriété transform. Les classes suivantes peuvent être utilisées pour les animations :
Nom de la classe CSS
Résultat de l'ajout d'une transition
.cdk-drag
Anime les éléments déplaçables pendant leur tri.
.cdk-drag-animating
Anime l'élément déplaçable depuis la position où il a été déposé jusqu'à sa position finale dans la cdkDropList.
Cette classe CSS n'est appliquée à un élément cdkDrag qu'une fois l'action de glissement terminée.
Les directives cdkDrag et cdkDropList n'appliquent que les styles essentiels à leur fonctionnement. Les applications peuvent personnaliser leurs styles en ciblant les classes CSS suivantes.
Selector de l'élément d'aperçu de glissement. C'est l'élément qui apparaît à côté du curseur lorsqu'un utilisateur fait glisser un élément dans une liste triable.
Cet élément est identique à l'élément en cours de glissement, sauf s'il est personnalisé avec un template personnalisé via *cdkDragPreview.
.cdk-drag-placeholder
Selector de l'élément placeholder de glissement. C'est l'élément affiché à l'endroit où l'élément déplaçable sera déposé une fois l'action de glissement terminée.
Cet élément est identique à l'élément en cours de tri, sauf s'il est personnalisé avec la directive cdkDragPlaceholder.
.cdk-drop-list-dragging
Selector de l'élément conteneur cdkDropList qui contient un élément déplaçable en cours de glissement.
.cdk-drop-list-disabled
Selector des éléments conteneurs cdkDropList désactivés.
.cdk-drop-list-receiving
Selector de l'élément conteneur cdkDropList qui peut recevoir un élément déplaçable en cours de glissement depuis une liste de dépôt connectée.
.cdk-drag-anchor
Selector de l'élément ancre créé lorsque cdkDropListHasAnchor est activé. Cet élément indique la position d'où l'élément glissé est parti.
Si vos éléments déplaçables se trouvent dans un conteneur avec défilement (par exemple une div avec overflow: auto), le défilement automatique ne fonctionne pas tant que le conteneur n'a pas la directive cdkScrollable. Sans elle, le CDK ne peut ni détecter ni contrôler le comportement de défilement du conteneur pendant les opérations de glissement.
La fonctionnalité de glisser-déposer du CDK peut être intégrée à différents composants. Les cas d'usage courants incluent des composants MatTable triables et des composants MatTabGroup triables.