Descripción general de las directivas Angular de arrastrar y soltar
La Ignite UI for Angular Drag and Drop permite arrastrar elementos por la página. Las funciones admitidas incluyen arrastre libre, uso de un controlador de arrastre, fantasma de arrastre, animaciones y múltiples estrategias de colocación.
Getting Started with Ignite UI for Angular Drag and Drop
Para comenzar con las directivas de arrastrar y soltar Ignite UI for Angular, primero debe instalar Ignite UI for Angular. En una aplicación Angular existente, escriba el siguiente comando:
ng add igniteui-angular
cmd
Para obtener una introducción completa a la Ignite UI for Angular, lea el tema de introducción.
El siguiente paso es importar IgxDragDropModule en su archivo app.module.ts.
Alternativamente, a partir de 16.0.0, puede importar IgxDragDirective e IgxDropDirective como dependencias independientes, o usar el token IGX_DRAG_DROP_DIRECTIVES para importar el componente y todos sus componentes y directivas de soporte.
Ahora que tiene importadas las directivas o el módulo Ignite UI for Angular Drag and Drop, puede comenzar a usar las directivas igxDrag e igxDrop.
Using the Angular Drag Directive
Cuando es necesario arrastrar un elemento dentro de su aplicación Angular de un lugar a otro en la página, la directiva igxDrag está diseñada para ayudar a lograr este comportamiento. En combinación con la directiva igxDrop, también se puede realizar la colocación del elemento arrastrado, para que pueda tener una aplicación totalmente interactiva.
Dragging Fundamentals
Una operación de arrastre comienza cuando el usuario final desliza al menos 5 píxeles en cualquier dirección. Esto es personalizable y se puede cambiar usando la entrada dragTolerance. De lo contrario, la interacción se considera un clic y se activa un evento dragClick.
Cuando comienza el arrastre, se activa el evento dragStart. Para evitar que se produzca cualquier movimiento real, el evento se puede cancelar estableciendo la propiedad cancel en true.
Antes de realizar cualquier movimiento real, también se activa el evento dragMove, que contiene la última y siguiente posición del puntero. Se activa cada vez que se detecta un movimiento al arrastrar un elemento.
Después de que el usuario suelta el mouse o toca, el elemento fantasma de arrastre se elimina del DOM y se emitirá el evento dragEnd.
Debido a la naturaleza del evento dragMove, se puede activar muchas veces en un corto período de tiempo, lo que puede causar problemas de rendimiento para operaciones complejas realizadas cuando se activa.
Dragging With Ghost
La directiva igxDrag se puede aplicar a cualquier elemento DOM simplemente agregándolo a su plantilla.
<divigxDrag>Drag me</div>html
El comportamiento predeterminado de la directiva igxDrag es dejar el elemento base sin modificar y crear un elemento fantasma cuando el usuario final realiza la operación de arrastre.
Antes de que el fantasma se represente en la página, se activa un evento ghostCreate que contiene información del elemento fantasma que está a punto de agregarse. El evento se activa justo después del evento dragStart. Si se cancela dragStart, no se creará ningún fantasma y el evento ghostCreate no se activará en consecuencia.
Justo antes de que el fantasma esté a punto de ser eliminado, se activará el evento ghostDestroy.
Customizing The Ghost
El elemento fantasma por defecto es una copia del elemento base en el que se utiliza igxDrag. Se puede personalizar proporcionando una referencia de plantilla a la entrada ghostTemplate directamente.
Si desea mover el elemento base al que se aplica la directiva igxDrag, puede hacerlo configurando la entrada ghost en false. De esa manera, no se representará ningún elemento fantasma adicional y, si necesita aplicar un estilo personalizado al arrastrar un elemento, puede aplicarlo directamente al elemento base.
<divigxDrag [ghost]="false">Drag me</div>html
Dragging Using a Handle
Puedes especificar un elemento que sea hijo de igxDrag por el cual arrastrar, ya que por defecto se usa todo el elemento para realizar esa acción. Se puede hacer usando la directiva igxDragHandle y se puede aplicar a múltiples elementos dentro de igxDrag.
Cuando se arrastra un elemento, no se aplican animaciones de forma predeterminada.
Puede aplicar animación de transición a igxDrag en cualquier momento, pero se recomienda usarla cuando finaliza el arrastre o cuando el elemento no está arrastrado actualmente. Esto se puede lograr utilizando los métodos transitionToOrigin y transitionTo.
El método transitionToOrigin, como su nombre indica, anima el elemento actualmente arrastrado o su fantasma a la posición inicial, donde comenzó el arrastre. El método transitionTo anima el elemento a una ubicación específica relativa a la página (es decir, pageX y pageY) o a la posición de un elemento específico. Si el elemento no se está arrastrando actualmente, se animará de todos modos o creará un fantasma y lo animará a la posición deseada.
Ambas funciones tienen argumentos que puede configurar para personalizar la animación de transición y establecer la duración, la función de sincronización o el retraso. Si se establece una ubicación de inicio específica, animará el elemento a partir de allí.
Cuando finalice la animación de transición, si se crea un fantasma, se eliminará y la directiva igxDrag volverá a su estado inicial. Si no se crea ningún fantasma, mantendrá su posición. En ambos casos, se activará el evento transitioned, dependiendo de cuánto dure la animación. Si no se aplica ninguna animación, se activará instantáneamente.
Puedes tener otros tipos de animaciones que impliquen transformaciones de elementos. Esto se puede hacer como cualquier otro elemento, ya sea usando animaciones Angular o animaciones CSS directas en el elemento igxDrag base o en su fantasma. Si desea aplicarlos al fantasma, deberá definir un fantasma personalizado y aplicar animaciones a su elemento.
Reordene los elementos de la lista usando el controlador de arrastre. Mientras arrastra un elemento de la lista, otros elementos de la lista se reordenarán con animación.
import {
Component,
ElementRef,
QueryList,
ViewChild,
ViewChildren
} from'@angular/core';
import {
IDragBaseEventArgs,
IDragMoveEventArgs,
IgxDragDirective,
IgxDragLocation
} from'igniteui-angular';
@Component({
selector: 'app-list-reorder-sample',
templateUrl: './list-reorder-sample.component.html',
styleUrls: ['./list-reorder-sample.component.scss']
})
exportclassListReorderSampleComponent{
@ViewChildren('dragDirRef', { read: IgxDragDirective })
public dragDirs: QueryList<IgxDragDirective>;
@ViewChild('listContainer', { read: ElementRef })
public listContainer: ElementRef;
public employees = [
{ id: 0, name: 'Ivan Cornejo', title: 'Senior Product Owner' },
{ id: 1, name: 'Amish Shiravadakar', title: 'Business Tools Director' },
{ id: 2, name: 'Elsi Hansdottir', title: 'Financial Director' },
{ id: 3, name: 'Benito Noboa', title: 'Marketing Specialist' },
{ id: 4, name: 'Beth Murphy', title: 'Platform Lead for Web' }
];
public newIndex = null;
public animationDuration = 0.3;
private listItemHeight = 55;
public getDragDirectiveRef(id: number): IgxDragDirective {
returnthis.dragDirs.find((item) => item.data.id === id);
}
publiconDragStart(event: IDragBaseEventArgs, dragIndex: number) {
// Record the current index as basis for moving up/down.this.newIndex = dragIndex;
// Sets specific class when dragging.
event.owner.data.dragged = true;
}
publiconDragEnd(event: IDragBaseEventArgs, itemIndex: number) {
if (this.newIndex !== null) {
// When we have moved the dragged element up/down, animate it to its new location.const moveDown = this.newIndex > itemIndex;
// If the new position is below add the height moved down, otherwise subtract it.const prefix = moveDown ? 1 : -1;
// The height that the new position differs from the current. We know that each item is 55px height.const movedHeight = prefix * Math.abs(this.newIndex - itemIndex) * this.listItemHeight;
const originLocation = event.owner.originLocation;
event.owner.transitionTo(
new IgxDragLocation(originLocation.pageX, originLocation.pageY + movedHeight),
{ duration: this.animationDuration }
);
} else {
// Otherwise animate it to its original position, since it is unchanged.
event.owner.transitionToOrigin({ duration: this.animationDuration });
}
}
publiconTransitioned(event: IDragBaseEventArgs, itemIndex: number) {
// We can have other items transitioned when they move to free up space where the dragged element would be.if (event.owner.data.dragged && this.newIndex != null && this.newIndex !== itemIndex) {
// If the element finished transitioning is the one were dragging,// We can update all elements their new position in the list.this.shiftElements(itemIndex, this.newIndex);
event.owner.setLocation(event.owner.originLocation);
this.newIndex = null;
}
// Disables the specific class when dragging.
event.owner.data.dragged = false;
}
publiconDragMove(event: IDragMoveEventArgs, itemIndex: number) {
const containerPosY = this.listContainer.nativeElement.getBoundingClientRect().top;
// Relative position of the dragged element to the list container.const relativePosY = event.nextPageY - containerPosY;
let newIndex = Math.floor(relativePosY / this.listItemHeight);
newIndex = newIndex < 0 ? 0 : (newIndex >= this.employees.length ? this.employees.length - 1 : newIndex);
if (newIndex === this.newIndex) {
// If the current new index is unchanged do nothing.return;
}
const movingDown = newIndex > itemIndex;
if (movingDown && newIndex > this.newIndex ||
(!movingDown && newIndex < this.newIndex && newIndex !== itemIndex)) {
// If we are moving the dragged element down and the new index is bigger than the current// this means that the element we are stepping into is not shifted up and should be shifted.// Same if we moving the dragged element up and the new index is smaller than the current.const elementToMove = this.getDragDirectiveRef(this.employees[newIndex].id);
const currentLocation = elementToMove.location;
const prefix = movingDown ? -1 : 1;
elementToMove.transitionTo(
new IgxDragLocation(currentLocation.pageX, currentLocation.pageY + prefix * this.listItemHeight),
{ duration: this.animationDuration }
);
} else {
// Otherwise if are moving up but the new index is still bigger than the current, this means that// the item we are stepping into is already shifted and should be returned to its original position.// Same if we are moving down and the new index is still smaller than the current.const elementToMove = this.getDragDirectiveRef(this.employees[this.newIndex].id);
elementToMove.transitionToOrigin({ duration: this.animationDuration });
}
this.newIndex = newIndex;
}
privateshiftElements(draggedIndex: number, targetIndex: number) {
// Move the dragged element in DOM to the new position.const movedElem = this.employees.splice(draggedIndex, 1);
this.employees.splice(targetIndex, 0, movedElem[0]);
this.dragDirs.forEach((dir) => {
if (this.employees[targetIndex].id !== dir.data.id) {
// Reset each element its location since it will be repositioned in the DOM except the element we drag.
dir.setLocation(dir.originLocation);
dir.data.shifted = false;
}
});
}
}
ts
<igx-list #listContainer><igx-list-item *ngFor="let employee of employees; index as targetIndex;"
#dragDirRef="drag"igxDrop
[igxDrag]="{ id: employee.id, dragged: false }"
(dragStart)="onDragStart($event, targetIndex)"
(dragMove)="onDragMove($event, targetIndex)"
(dragEnd)="onDragEnd($event, targetIndex)"
(transitioned)="onTransitioned($event, targetIndex)"
[ghost]="false"
[class.dragged]="dragDirRef.data && dragDirRef.data.dragged"><h4igxListLineTitle>{{employee.name}}</h4><h6igxListLineSubTitle>{{employee.title}}</h6><igx-iconigxDragHandleigxListAction>drag_indicator</igx-icon></igx-list-item></igx-list>html
Si el usuario desea tener hijos interactuables del elemento principal que tiene una instancia de igxDrag, puede configurar la directiva igxDragIgnore para que igxDrag los ignore y no realice ninguna acción de arrastre. Esto permitirá que estos elementos sean completamente interactivos y reciban todos los eventos del mouse.
Cuando un elemento que se está arrastrando usando la directiva igxDrag necesita colocarse en un área, se puede usar igxDrop para lograr este comportamiento. Proporciona eventos que puede utilizar para determinar si un elemento ingresa a los límites del elemento al que se aplica y si se libera dentro de él.
La directiva igxDrop se puede aplicar a cualquier elemento DOM al igual que la directiva igxDrag.
De forma predeterminada, la directiva igxDrop no aplica ninguna lógica para modificar la posición del elemento arrastrado en el DOM. Es por eso que necesitas especificar una dropStrategy o aplicar una lógica personalizada. Las estrategias de caída se analizan en la siguiente sección.
Drop Strategies
El igxDrop viene con 4 estrategias de colocación que son: Default, Prepend, Insert y Append:
Default: no realiza ninguna acción cuando un elemento se coloca en un elemento igxDrop y se implementa como una clase denominada IgxDefaultDropStrategy.
Append: siempre inserta el elemento eliminado como último elemento secundario y se implementa como una clase denominada IgxAppendDropStrategy.
Prepend: siempre inserta el elemento eliminado como el primer elemento secundario y se implementa como una clase denominada IgxPrependDropStrategy.
Insert: inserta el elemento arrastrado en la última posición. Sin embargo, si hay un elemento secundario debajo del elemento cuando se soltó, el elemento instanciado igxDrag se insertará en la posición de ese elemento secundario y los otros elementos secundarios se desplazarán. Se implementa como una clase denominada IgxInsertDropStrategy.
La forma en que se puede aplicar una estrategia es configurando la entrada dropStrategy en una de las clases enumeradas anteriormente. El valor proporcionado tiene que ser un tipo y no una instancia, ya que igxDrop necesita crear y administrar la instancia en sí.
public appendStrategy = IgxAppendDropStrategy;
typescript
Cuando se utiliza una estrategia de eliminación específica, su comportamiento se puede cancelar en los eventos dropped estableciendo la propiedad cancel en verdadero. El evento dropped es específico de igxDrop. Si no tiene una estrategia de caída aplicada a igxDrop, cancelar el evento no tendría efectos secundarios.
Si desea implementar su propia lógica de eliminación, le recomendamos vincularse al evento dropped y ejecutar su lógica allí o extender la clase IgxDefaultDropStrategy.
Linking Drag to Drop Element
Usando la entrada dragChannel y dropChannel en las directivas igxDrag e igxDrop respectivamente, puede vincular diferentes elementos para interactuar solo entre sí. Por ejemplo, si es necesario restringir un elemento igxDrag para que pueda colocarse en un elemento igxDrop específico y no todos estén disponibles, esto se puede lograr fácilmente asignándoles el mismo canal.
<divigxDrag [dragChannel]="['Mammals', 'Land']"> Human </div><divigxDrag [dragChannel]="['Mammals', 'Water']"> Dolphin </div><divigxDrag [dragChannel]="['Insects', 'Air']"> Butterfly </div><divigxDrag [dragChannel]="['Insects', 'Land']"> Ant </div><divigxDrop [dropChannel]="['Mammals']"> Mammals </div><divigxDrop [dropChannel]="['Insects']"> Insects </div><divigxDrop [dropChannel]="['Land']"> Land </div>html
Arrastre los correos electrónicos de la derecha a las carpetas de la izquierda.
Dado que tanto igxDrag como igxDrop combinados se pueden usar en muchos escenarios de aplicaciones diferentes y complejos, el siguiente ejemplo demuestra cómo se pueden usar en un tablero Kanban.
El usuario podría reordenar las tarjetas en cada columna. Esto se hace estableciendo también un área de colocación para cada tarjeta, de modo que podamos detectar cuándo otra tarjeta ha ingresado a su área y cambiarlas en tiempo de ejecución, para brindar una mejor experiencia de usuario.
No será un tablero Kanban sin la posibilidad de cambiar tarjetas entre columnas. Una tarjeta se puede mover directamente de una columna a otra en una posición específica. Esto se logra aquí con un objeto ficticio, por lo que crearía un área visual donde se colocará la tarjeta si se suelta. El objeto ficticio se elimina una vez que finaliza el arrastre de una tarjeta o sale de otra columna.