Arrastar linha em Angular grade de árvore

    Em Ignite UI for Angular Tree Grid, RowDrag é inicializado no componente raiz igx-tree-grid e é configurável por meio da rowDraggable entrada. Habilitar o arrastar de linha fornece aos usuários uma alça de arrastar de linha com a qual eles podem iniciar o arrasto de uma linha.

    Angular Tree Grid Row Drag Example

    Configuração

    Para habilitar o arrasto de linha para você igx-tree-grid, tudo o que você precisa fazer é definir a grade rowDraggable para true. Depois que isso estiver ativado, uma alça de arrastar linha será exibida em cada linha. Essa alça pode ser usada para iniciar o arrasto de linha.

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

    Clicar na alça de arrastar e mover o cursor enquanto mantém pressionado o botão fará com que o evento da rowDragStart grade seja acionado. Liberar o clique a qualquer momento fará com que rowDragEnd o evento seja acionado.

    Abaixo, você pode encontrar um passo a passo sobre como configurar um igx-tree-grid para dar suporte ao arrasto de linha e como lidar corretamente com o evento de soltar.

    Neste exemplo, vamos lidar com arrastar uma linha de uma grade para uma área designada e, ao soltá-la, removê-la da grade.

    Drop Areas

    Habilitar o arrasto de linha foi muito fácil, mas agora temos que configurar como lidaremos com a queda de linha. Podemos definir onde queremos que nossas linhas sejam descartadas usando a igxDrop diretiva.

    Primeiro, precisamos importar o em nosso módulo de IgxDragDropModule aplicativo:

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

    Em seguida, em nosso modelo, definimos uma área de soltar usando o seletor da diretiva:

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

    Você pode ativar a animação quando uma linha é solta em uma área não soltável usando o animation​ ​rowDragEnd parâmetro do evento. Se definido como true, a linha arrastada será animada de volta à sua posição original quando solta sobre uma área que não pode ser solta.

    Você pode habilitar a animação assim:

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

    Drop Area Event Handlers

    Depois de definirmos nossa área de soltar no modelo, temos que declarar nossos manipuladores para os igxDrop​ ​enter eventos leave​ ​dropped e eventos no arquivo do.ts nosso componente.

    Primeiro, vamos dar uma olhada em nossos enter manipuladores leave. Nesses métodos, queremos apenas alterar o ícone do fantasma do drag para que possamos indicar ao usuário que ele está acima de uma área que permite soltar a linha:

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

    O changeGhostIcon método privado apenas altera o ícone dentro do fantasma de arrastar. A lógica no método localiza o elemento que contém o ícone (usando a igx-grid__drag-indicator classe aplicada ao contêiner indicador de arrastar), alterando o texto interno do elemento para o passado. Os ícones em si são do material fontesconjunto de e são definidos em separado enum:

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

    Em seguida, temos que definir o que deve acontecer quando o usuário realmente solta a linha dentro da área de soltar.

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

    Depois que a linha é descartada, apenas chamamos o método da delete() linha

    Note

    Ao usar dados de linha dos argumentos do evento (args.dragData.data) ou qualquer outra propriedade de linha, observe que a linha inteira é passada nos argumentos como referência, o que significa que você deve clonar os dados necessários, se quiser distingui-los dos da grade de origem.

    Templating the drag ghost

    O fantasma de arrastar pode ser modelado usando a IgxRowDragGhost diretiva, aplicada a um <ng-template> interior do igx-tree-grid corpo:

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

    O resultado da configuração pode ser exibido abaixo em um igx-tree-grid com arrastar linhas e seleção múltipla habilitada. A demonstração mostra a contagem das linhas arrastadas no momento:

    Exemplo de demonstração

    Templating the drag icon

    O ícone da alça de arrastar pode ser modelado usando o da grade dragIndicatorIconTemplate. No exemplo que estamos construindo, vamos alterar o ícone do padrão (drag_indicator) para drag_handle. Para fazer isso, podemos usar o igxDragIndicatorIcon para passar um modelo dentro do igx-tree-grid corpo:

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

    Depois de definir o novo modelo de ícone, também precisamos ajustar o DEFAULT ícone em nosso DragIcon enum, para que ele seja alterado corretamente pelo changeIcon método:

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

    Styling the drop area

    Uma vez que nossos manipuladores de drop estejam configurados corretamente, tudo o que resta é estilizar um pouco nossa área de drop:

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

    O resultado pode ser visto na demonstração abaixo:

    Exemplo de demonstração

    Application Demo

    Row Reordering Demo

    Com a ajuda dos eventos de arrastar linha da grade e da igxDrop diretiva, você pode criar uma grade que permite reordenar as linhas arrastando-as.

    Como todas as ações acontecerão dentro do corpo da grade, é aí que você deve anexar a igxDrop diretiva:

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

    Certifique-se de que haja um primaryKey especificado para a grade! A lógica precisa de um identificador exclusivo para as linhas para que possam ser reordenadas corretamente

    Uma vez rowDraggable habilitado e uma zona de queda definida, você precisa implementar um manipulador simples para o evento de queda. Quando uma linha é arrastada, verifique o seguinte:

    • A linha é expandida? Em caso afirmativo, recolha-o.
    • A linha foi lançada dentro da grade?
    • Em caso afirmativo, em qual outra linha a linha arrastada foi solta?
    • Depois de encontrar a linha de destino, troque os lugares dos registros na data matriz
    • A linha foi inicialmente selecionada? Em caso afirmativo, marque-o como selecionado.

    Abaixo, você pode ver isso implementado no arquivo do.ts 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;
        }
    }
    

    Com essas etapas fáceis, você configurou uma grade que permite reordenar linhas por meio de arrastar/soltar! Você pode ver o código acima em ação na demonstração a seguir.

    Observe que também temos a seleção de linha habilitada e preservamos a seleção ao soltar a linha arrastada.

    Limitations

    Atualmente, não há limitações conhecidas para a rowDraggable diretiva.

    API References

    Additional Resources

    Nossa comunidade é ativa e sempre acolhedora para novas ideias.