Arrastre de filas en cuadrícula de árbol Angular

    En Ignite UI for Angular cuadrícula de árbol, RowDrag se inicializa en el componente raíz igx-tree-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.

    Angular Tree Grid Row Drag Example

    Configuración

    Para habilitar el arrastre de filas para su igx-tree-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-tree-grid [rowDraggable]="true">
     ...
    </igx-tree-grid>
    

    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 un igx-tree-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.

    Drop Areas

    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 igxDropdirectiva .

    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]
    })
    

    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>
    

    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 IgxTreeGridRowDragComponent {
    
        public onRowDragEnd(args) {
            args.animation = true;
        }
    
    }
    

    Drop Area Event Handlers

    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 IgxTreeGridRowDragComponent {
        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;
                }
            }
        }
    }
    

    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'
    }
    

    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 IgxTreeGridRowDragComponent {
    
        public onDropAllowed(args: IDropDroppedEventArgs) {
            const draggedRow: RowType = args.dragData;
            draggedRow.delete();
        }
    
    }
    

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

    Note

    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.

    Templating the drag ghost

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

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

    El resultado de la configuración se puede ver a continuación en una igx-tree-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

    Templating the drag icon

    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-tree-grid:

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

    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",
        ...
    }
    

    Styling the drop area

    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%;
    }
    

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

    Demostración de ejemplo

    Application Demo

    Row Reordering Demo

    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-tree-grid igxPreventDocumentScroll  #treeGrid [data]="localData" [rowDraggable]="true" foreignKey="ParentID"
        [primaryKey]="'ID'" (rowDragStart)="rowDragStart($event)" igxDrop (dropped)="dropInGrid($event)">
        ...
    </igx-tree-grid>
    
    Note

    ¡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 TreeGridRowReorderComponent {
        public rowDragStart(args: any): void {
            const targetRow = args.dragData;
            if (targetRow.expanded) {
                this.treeGrid.collapseRow(targetrow.key);
            }
        }
    
        public dropInGrid(args: IDropDroppedEventArgs): void {
            const draggedRow = args.dragData;
            const event = args.originalEvent;
            const cursorPosition: Point = { x: event.clientX, y: event.clientY };
            this.moveRow(draggedRow, cursorPosition);
        }
    
        private moveRow(draggedRow: RowType, cursorPosition: Point): void {
            const row = this.catchCursorPosOnElem(this.treeGrid.rowList.toArray(), cursorPosition);
            if (!row) { return; }
            if (row.data.ParentID === -1) {
                this.performDrop(draggedRow, row).ParentID = -1;
            } else {
                if (row.data.ParentID === draggedrow.data.ParentID) {
                    this.performDrop(draggedRow, row);
                } else {
                    const rowIndex = this.getRowIndex(draggedrow.data);
                    this.localData[rowIndex].ParentID = row.data.ParentID;
                }
            }
            if (draggedRow.selected) {
                this.treeGrid.selectRows([this.treeGrid.rowList.toArray()
                    .find((r) => r.rowID === draggedrow.key).rowID], false);
            }
    
            this.localData = [...this.localData];
        }
    
        private performDrop(
            draggedRow: IgxTreeGridRowComponent, targetRow: IgxTreeGridRowComponent) {
            const draggedRowIndex = this.getRowIndex(draggedrow.data);
            const targetRowIndex: number = this.getRowIndex(targetrow.data);
            if (draggedRowIndex === -1 || targetRowIndex === -1) { return; }
            this.localData.splice(draggedRowIndex, 1);
            this.localData.splice(targetRowIndex, 0, draggedrow.data);
            return this.localData[targetRowIndex];
        }
    
        private getRowIndex(rowData: any): number {
            return this.localData.indexOf(rowData);
        }
    
        private catchCursorPosOnElem(rowListArr: IgxTreeGridRowComponent[], cursorPosition: Point)
            : IgxTreeGridRowComponent {
            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;
                }
            }
    
            return null;
        }
    }
    

    ¡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.

    Limitations

    Actualmente, no existen limitaciones conocidas para la directiva rowDraggable.

    API References

    Additional Resources

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