Arrastre de filas en cuadrícula jerárquica Angular

    En Ignite UI for Angular cuadrícula jerárquica, RowDrag se inicializa en el componente raíz igx-hierarchical-grid y se puede configurar a través de la rowDraggable entrada. Al habilitar el arrastre de filas, los usuarios disponen de un controlador de arrastre de filas con el que pueden iniciar el arrastre de una fila.

    Ejemplo de arrastre de filas en cuadrícula jerárquica Angular

    EXAMPLE
    TS
    HTML
    SCSS

    ¿Te gusta esta muestra? Obtenga acceso a nuestro kit de herramientas de Ignite UI for Angular completo y comience a crear sus propias aplicaciones en minutos. Descárgalo gratis.

    Configuración

    Para habilitar el arrastre de filas para su igx-hierarchical-grid, todo lo que necesita hacer es configurar el rowDraggable de la cuadrícula en true. Una vez que esto esté habilitado, se mostrará un controlador de arrastre de fila en cada fila. Este controlador se puede utilizar para iniciar el arrastre de filas.

    <igx-hierarchical-grid [rowDraggable]="true">
     ...
    </igx-hierarchical-grid>
    html

    Al hacer clic en el controlador de arrastre y mover el cursor mientras se mantiene presionado el botón, se activará el evento rowDragStart de la cuadrícula. Si suelta el clic en cualquier momento, se activará el evento rowDragEnd.

    A continuación, puede encontrar un tutorial sobre cómo configurar una igx-hierarchical-grid para admitir el arrastre de filas y cómo manejar adecuadamente el evento de colocación.

    En este ejemplo, nos encargaremos de arrastrar una fila desde una cuadrícula a un área designada y, al soltarla, la eliminaremos de la cuadrícula.

    Áreas de caída

    Habilitar el arrastre de filas fue bastante fácil, pero ahora tenemos que configurar cómo manejaremos el descenso de filas. Podemos definir dónde queremos que se eliminen nuestras filas usando la directiva igxDrop..

    Primero necesitamos importar IgxDragDropModule en nuestro módulo de aplicación:

    import { ..., IgxDragDropModule } from 'igniteui-angular';
    // import { ..., IgxDragDropModule } from '@infragistics/igniteui-angular'; for licensed package
    ...
    @NgModule({
        imports: [..., IgxDragDropModule]
    })
    typescript

    Luego, en nuestra plantilla, definimos un área de colocación usando el selector de directivas:

    <div class="drop-area" igxDrop (enter)="onEnterAllowed($event)" (leave)="onLeaveAllowed($event)"
    (dropped)="onDropAllowed($event)">
        <igx-icon>delete</igx-icon>
        <div>Drag a row here to delete it</div>
    </div>
    html

    Puede habilitar la animación cuando una fila se coloca en un área no desplegable utilizando el parámetro animation del evento rowDragEnd. Si se establece en verdadero, la fila arrastrada volverá a su posición original cuando se suelte sobre un área que no se puede soltar.

    Puede habilitar animaciones como esta:

    export class IgxHierarchicalGridRowDragComponent {
    
        public onRowDragEnd(args) {
            args.animation = true;
        }
    
    }
    typescript

    Controladores de eventos del área de entrega

    Una vez que hemos definido nuestra área de colocación en la plantilla, tenemos que declarar nuestros controladores para el igxDrop 's enter, leave y dropped eventos en nuestro componente.ts archivo.

    Primero, echemos un vistazo a nuestros controladores enter y leave. En esos métodos, solo queremos cambiar el ícono del fantasma del arrastre para poder indicarle al usuario que está encima de un área que le permite soltar la fila:

    export class IgxHierarchicalGridRowDragComponent {
        public onEnterAllowed(args) {
            this.changeGhostIcon(args.drag.ghostElement, DragIcon.ALLOW);
        }
    
        public onLeaveAllowed(args) {
            this.changeGhostIcon(args.drag.ghostElement, DragIcon.DEFAULT);
        }
    
        private changeGhostIcon(ghost, icon: string) {
            if (ghost) {
                const currentIcon = ghost.querySelector('.igx-grid__drag-indicator > igx-icon');
                if (currentIcon) {
                    currentIcon.innerText = icon;
                }
            }
        }
    }
    typescript

    El changeGhostIcon ​ ​privado El método simplemente cambia el ícono dentro del fantasma de arrastre. La lógica del método encuentra el elemento que contiene el icono (usando el igx-grid__drag-indicator clase que se aplica al contenedor del indicador de arrastre), cambiando el texto interno del elemento al pasado. Los iconos en sí son del material conjunto de fuentes y se definen en un separado enum:

    enum DragIcon {
        DEFAULT = 'drag_indicator',
        ALLOW = 'remove'
    }
    typescript

    A continuación, tenemos que definir qué debería suceder cuando el usuario realmente coloca la fila dentro del área de colocación.

    export class IgxHierarchicalGridRowDragComponent {
    
        public onDropAllowed(args: IDropDroppedEventArgs) {
            const draggedRow: RowType = args.dragData;
            draggedRow.delete();
        }
    
    }
    typescript

    Una vez que se elimina la fila, simplemente llamamos al método delete() de la fila.

    Cuando utilice datos de fila de los argumentos del evento (args.dragData.data) o cualquier otra propiedad de fila, tenga en cuenta que la fila completa se pasa en los argumentos como referencia, lo que significa que debe clonar los datos que necesita, si desea distinguirlo del de la cuadrícula de origen.

    Plantilla del fantasma de arrastre

    Se puede crear una plantilla para el fantasma de arrastre usando la directiva IgxRowDragGhost, aplicada a un <ng-template> dentro del cuerpo de igx-hierarchical-grid:

    <igx-hierarchical-grid>
    ...
       <ng-template igxRowDragGhost>
            <div>
                <igx-icon fontSet="material">arrow_right_alt</igx-icon>
            </div>
        </ng-template>
    ...
    </igx-hierarchical-grid>
    html

    El resultado de la configuración se puede ver a continuación en una igx-hierarchical-grid con el arrastre de filas y la selección múltiple habilitada. La demostración muestra el recuento de las filas arrastradas actualmente:

    Demostración de ejemplo

    Se puede crear una plantilla para el fantasma de arrastre en cada nivel de la cuadrícula, lo que permite tener varias plantillas de fantasma o solo proporcionar una plantilla para una isla de una sola fila.

    <igx-hierarchical-grid>
    ...
        <ng-template igxRowDragGhost>
            <div>
                <igx-icon fontSet="material">arrow_right_alt</igx-icon> 
            </div>
        </ng-template>
        <igx-row-island>
            ...
            <ng-template IgxRowDragGhost>
                <img src="smile.gif" height="42" width="42">
            </ng-template>
        </igx-row-island>
    ...
    </igx-hierarchical-grid>
    html

    EXAMPLE
    TS
    HTML
    SCSS

    Plantilla del icono de arrastrar

    El icono del controlador de arrastre se puede crear como plantilla usando dragIndicatorIconTemplate de la cuadrícula. En el ejemplo que estamos creando, cambiemos el ícono predeterminado (drag_indicator) a drag_handle. Para hacerlo, podemos usar igxDragIndicatorIcon para pasar una plantilla dentro del cuerpo de igx-hierarchical-grid:

    <igx-hierarchical-grid>
    ...
        <ng-template igxDragIndicatorIcon>
            <igx-icon>drag_handle</igx-icon>
        </ng-template>
    ...
    </igx-hierarchical-grid>
    html

    Una vez que hayamos configurado la nueva plantilla de ícono, también necesitamos ajustar el ícono DEFAULT en nuestra DragIcon enum, para que se cambie correctamente mediante el método changeIcon:

    enum DragIcon {
        DEFAULT = "drag_handle",
        ...
    }
    typescript

    Diseñar el área de caída

    Una vez que nuestros controladores de entrega estén configurados correctamente, todo lo que queda es darle un poco de estilo a nuestra área de entrega:

    .drop-area {
        width: 160px;
        height: 160px;
        background-color: #d3d3d3;
        border: 1px dashed #131313;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-flow: column;
        text-align: center;
        margin: 8px;
    }
    
    :host {
        display: flex;
        justify-content: center;
        align-items: center;
        flex-flow: column;
        width: 100%;
    }
    css

    El resultado se puede ver en la demostración a continuación:

    Demostración de ejemplo

    EXAMPLE
    TS
    HTML
    SCSS

    Demostración de la aplicación

    Demostración de reordenamiento de filas

    Con la ayuda de los eventos de arrastre de filas de la cuadrícula y la directiva igxDrop, puede crear una cuadrícula que le permita reordenar las filas arrastrándolas.

    Dado que todas las acciones sucederán dentro del cuerpo del grid, ahí es donde debes adjuntar la directiva igxDrop:

    <igx-hierarchical-grid #grid [data]="localData" [primaryKey]="'id'"
        [rowDraggable]="true" (rowDragStart)="rowDragStart($event)" igxDrop (dropped)="rowDrop($event)">
        ...
    </igx-hierarchical-grid>
    html

    ¡Asegúrese de que haya una primaryKey especificada para la cuadrícula! La lógica necesita un identificador único para las filas para que puedan reordenarse correctamente.

    Una vez que rowDraggable esté habilitado y se haya definido una zona de colocación, debe implementar un controlador simple para el evento de colocación. Cuando se arrastra una fila, verifique lo siguiente:

    • ¿Se amplía la fila? Si es así, colapsarlo.
    • ¿Se dejó caer la fila dentro de la cuadrícula?
    • Si es así, ¿en qué otra fila se soltó la fila arrastrada?
    • Una vez que haya encontrado la fila de destino, intercambie las ubicaciones de los registros en la matriz data.
    • ¿Se seleccionó inicialmente la fila? Si es así, márquelo como seleccionado.

    A continuación, puede ver esto implementado en el archivo.ts del componente:

    export class HGridRowReorderComponent {
        public rowDragStart(args: any): void {
            const targetRow = args.dragData;
            if (targetRow.expanded) {
                targetRow.expanded = false;
            }
        }
    
        public rowDrop(args: IDropDroppedEventArgs): void {
            const targetRow = args.dragData;
            const event = args.originalEvent;
            const cursorPosition: Point = { x: event.clientX, y: event.clientY };
            this.moveRow(targetRow, cursorPosition);
        }
    
        private moveRow(draggedRow: RowType, cursorPosition: Point): void {
            // const parent: IgxHierarchicalGridComponent = (draggedRow as any).grid;
            // const parent = args.drag.ghostContext.grid;
            const parent = this.hGrid;
            const rowIndex: number = this.getTargetRowIndex(parent.rowList.toArray(), cursorPosition);
            if (rowIndex === -1) { return; }
            const wasSelected = draggedRow.selected;
            draggedRow.delete();
            parent.data.splice(rowIndex, 0, draggedRow.data);
            if (wasSelected) {
                parent.selectRows([parent.rowList.toArray()
                    .find((r) => r.rowID === draggedRow.key).rowID], false);
            }
        }
    
        private getTargetRowIndex(rowListArr: RowType[], cursorPosition: Point): number {
            const targetElem: IgxHierarchicalRowComponent = this.catchCursorPosOnElem(rowListArr, cursorPosition);
            return rowListArr.indexOf(rowListArr.find((r) => r.rowData.id === targetElem.rowData.id));
        }
    
        private catchCursorPosOnElem(rowListArr: any[], cursorPosition: Point)
            : IgxHierarchicalRowComponent {
            for (const row of rowListArr) {
                const rowRect = row.nativeElement.getBoundingClientRect();
                if (cursorPosition.y > rowRect.top + window.scrollY && cursorPosition.y < rowRect.bottom + window.scrollY &&
                    cursorPosition.x > rowRect.left + window.scrollX && cursorPosition.x < rowRect.right + window.scrollX) {
                    return row;
                } else if (row === rowListArr[rowListArr.length - 1] && cursorPosition.y > rowRect.bottom) {
                    return row;
                }
            }
        }
    }
    typescript

    ¡Con estos sencillos pasos, ha configurado una cuadrícula que permite reordenar filas mediante arrastrar y soltar! Puede ver el código anterior en acción en la siguiente demostración.

    Observe que también tenemos habilitada la selección de filas y conservamos la selección al soltar la fila arrastrada.

    EXAMPLE
    TS
    HTML
    SCSS

    Limitaciones

    Actualmente, no existen limitaciones conocidas para la directiva rowDraggable.

    App Builder | CTA Banner

    Referencias de API

    Recursos adicionales

    Nuestra comunidad es activa y siempre da la bienvenida a nuevas ideas.