Carga de red jerárquica bajo demanda

    La Ignite UI for Angular IgxHierarchicalGrid permite una representación rápida al solicitar que se recupere la cantidad mínima de datos del servidor para que el usuario pueda ver el resultado e interactuar con los datos visibles lo más rápido posible. Inicialmente, solo se recuperan y representan los datos de la cuadrícula raíz; solo después de que el usuario expanda una fila que contiene una cuadrícula secundaria, recibirá los datos para esa cuadrícula secundaria en particular. Este mecanismo, también conocido como Carga bajo demanda, se puede configurar fácilmente para funcionar con cualquier dato remoto.

    Este tema demuestra cómo configurar la carga bajo demanda mediante la creación de un proveedor de servicios remotos que se comunica con un servicio oData v4 remoto ya disponible. Aquí está la demostración funcional y luego la revisaremos paso a paso y describiremos el proceso de creación.

    Angular Hierarchical Grid Load On Demand Example

    Remote Service Provider

    Primero, prepararemos a nuestro proveedor de servicios para que estemos listos para obtener los datos que necesitaríamos para la cuadrícula jerárquica.

    Obteniendo datos básicos

    Nos comunicaremos con nuestro servicio backend a través del protocolo HTTP utilizando la interfaz XMLHttpRequest que proporcionan los navegadores. Para lograr esto más fácilmente, utilizaremos el módulo HttpClient de Angular que ofrece una API HTTP de cliente simplificada. De esa manera para poder obtener nuestros datos necesitaremos este método simple en nuestro servicio:

    public getData(dataState): Observable<any[]> {
        return this.http.get(this.buildUrl(dataState)).pipe(
            map(response => response['value']),
        );
    }
    

    Como puede ver, this.http será una referencia a nuestro módulo HttpCLient, y buildUrl() será el método que generará nuestra URL en función de los datos que hemos recibido. Mapeamos nuestra respuesta para obtener solo el valor de nuestro resultado y devolver un Observable, ya que se ejecuta de forma asincrónica. De esa manera podremos suscribirnos más tarde, procesarlo más en nuestra aplicación y pasarlo a nuestra red.

    Construyendo nuestra URL de solicitud

    A continuación definiremos cómo debemos construir nuestra URL para la solicitud GET. Aquí es donde podremos obtener los datos de nuestra grilla principal pero también de cualquier grilla secundaria dentro de ella. Usaremos los datos Customers desde aquí para nuestro nivel raíz y usaremos Order y Order_Details para los niveles inferiores. El modelo diferirá según la aplicación, pero usaremos el siguiente:

    Lo primero que necesitamos es la key de nuestra tabla para determinar de dónde obtener los datos para la cuadrícula deseada, la clave principal de la fila principal y su ID única. Todo esto lo definiremos en una interfaz llamada IDataState. Un ejemplo:

    export interface IDataState {
        key: string;
        parentID: any;
        parentKey: string;
        rootLevel: boolean;
    }
    
    //...
    public buildUrl(dataState: IDataState): string {
        let qS = "";
        if (dataState) {
            qS += `${dataState.key}?`;
    
            if (!dataState.rootLevel) {
                if (typeof dataState.parentID === "string") {
                    qS += `$filter=${dataState.parentKey} eq '${dataState.parentID}'`;
                } else {
                    qS += `$filter=${dataState.parentKey} eq ${dataState.parentID}`;
                }
            }
        }
        return `${this.url}${qS}`;
    }
    //...
    

    Resultado

    Finalmente, así es como se vería nuestro remote-lod.service.ts:

    import { HttpClient } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    export interface IDataState {
        key: string;
        parentID: any;
        parentKey: string;
        rootLevel: boolean;
    }
    
    @Injectable()
    export class RemoteLoDService {
        url = `https://services.odata.org/V4/Northwind/Northwind.svc/`;
    
        constructor(private http: HttpClient) { }
    
        public getData(dataState: IDataState): Observable<any[]> {
            return this.http.get(this.buildUrl(dataState)).pipe(
                map((response) => response['value'])
            );
        }
    
        public buildUrl(dataState: IDataState): string {
            let qS = "";
            if (dataState) {
                qS += `${dataState.key}?`;
    
                if (!dataState.rootLevel) {
                    if (typeof dataState.parentID === "string") {
                        qS += `$filter=${dataState.parentKey} eq '${dataState.parentID}'`;
                    } else {
                        qS += `$filter=${dataState.parentKey} eq ${dataState.parentID}`;
                    }
                }
            }
            return `${this.url}${qS}`;
        }
    }
    

    Hierarchical Grid Setup

    A continuación configuraremos nuestra red jerárquica y la conectaremos a nuestro proveedor de servicios remoto.

    Definición de plantilla

    Primero definiremos nuestra plantilla de cuadrícula jerárquica con los niveles de jerarquía que esperamos tener. Sabemos que nuestra primaryKey de la cuadrícula raíz para los clientes es su CustomerID, para sus pedidos en el primer nivel, OrderID y, respectivamente, para los detalles del pedido, ProductID. Conocer cada tabla de la base de datos y sus claves nos permite definir nuestra plantilla inicial:

    <igx-hierarchical-grid #hGrid [primaryKey]="'CustomerID'" [autoGenerate]="false" [height]="'600px'" [width]="'100%'">
        <igx-column field="CustomerID" [hidden]="true"></igx-column>
        <igx-column field="CompanyName"></igx-column>
        <igx-column field="ContactName"></igx-column>
        <igx-column field="ContactTitle"></igx-column>
        <igx-column field="Country"></igx-column>
        <igx-column field="Phone"></igx-column>
        <igx-row-island [key]="'Orders'" [primaryKey]="'OrderID'" [autoGenerate]="false" >
            <igx-column field="OrderID" [hidden]="true"></igx-column>
            <igx-column field="ShipCountry"></igx-column>
            <igx-column field="ShipCity"></igx-column>
            <igx-column field="ShipAddress"></igx-column>
            <igx-column field="OrderDate"></igx-column>
            <igx-row-island [key]="'Order_Details'" [primaryKey]="'ProductID'" [autoGenerate]="false" >
                <igx-column field="ProductID" [hidden]="true"></igx-column>
                <igx-column field="Quantity"></igx-column>
                <igx-column field="UnitPrice"></igx-column>
                <igx-column field="Discount"></igx-column>
            </igx-row-island>
        </igx-row-island>
    </igx-hierarchical-grid>
    

    Sin embargo, falta una cosa en nuestra plantilla: los datos de nuestra cuadrícula jerárquica de nivel raíz y, eventualmente, de sus elementos secundarios. Configuraremos fácilmente los datos de la cuadrícula raíz después de obtener sus datos del servicio en nuestro código más adelante, ya que podemos usar la referencia #hGrid. Configurar los datos para cualquier niño que haya sido expandido es un poco diferente.

    Cuando se expande una fila por primera vez, se representa un nuevo hijo IgxHierarchicalGrid y necesitamos obtener la referencia de la cuadrícula recién creada para configurar sus datos. Es por eso que cada componente IgxRowIsland proporciona el evento gridCreated que se activa cuando se crea una nueva cuadrícula secundaria para esa isla de fila específica. Podemos usarlo para obtener la referencia que necesitamos para la nueva red, solicitar sus datos al servicio y aplicarlos.

    Podemos usar un método para todas las islas de filas, ya que creamos nuestro servicio de modo que solo necesite información si es el nivel raíz, la clave de la isla de filas, la clave principal de la fila principal y su identificador único. Se puede acceder a toda esta información directamente desde los argumentos del evento o desde la isla de fila responsable de desencadenar el evento.

    Llamemos al método que usaremos gridCreated. Dado que el evento gridCreated proporciona la propiedad parentID, una referencia a la isla de fila como owner y la nueva propiedad grid secundaria, se pasará como primer argumento. Solo nos falta información sobre la primaryKey de la fila principal, pero podemos pasarla fácilmente como un segundo argumento, dependiendo de qué isla de fila vinculemos.

    El archivo de plantilla hierarchical-grid-lod.component.html, con estos cambios agregados, se vería así:

    <igx-hierarchical-grid #hGrid [primaryKey]="'CustomerID'" [autoGenerate]="false" [height]="'600px'" [width]="'100%'">
        <igx-column field="CustomerID" [hidden]="true"></igx-column>
        <igx-column field="CompanyName"></igx-column>
        <igx-column field="ContactName"></igx-column>
        <igx-column field="ContactTitle"></igx-column>
        <igx-column field="Country"></igx-column>
        <igx-column field="Phone"></igx-column>
        <igx-row-island [key]="'Orders'" [primaryKey]="'OrderID'" [autoGenerate]="false" (gridCreated)="gridCreated($event, 'CustomerID')">
            <igx-column field="OrderID" [hidden]="true"></igx-column>
            <igx-column field="ShipCountry"></igx-column>
            <igx-column field="ShipCity"></igx-column>
            <igx-column field="ShipAddress"></igx-column>
            <igx-column field="OrderDate"></igx-column>
            <igx-row-island [key]="'Order_Details'" [primaryKey]="'ProductID'" [autoGenerate]="false" (gridCreated)="gridCreated($event, 'OrderID')">
                <igx-column field="ProductID" [hidden]="true"></igx-column>
                <igx-column field="Quantity"></igx-column>
                <igx-column field="UnitPrice"></igx-column>
                <igx-column field="Discount"></igx-column>
            </igx-row-island>
        </igx-row-island>
    </igx-hierarchical-grid>
    

    Conectando nuestro servicio

    Uno de nuestros pasos finales ahora será conectar nuestro servicio creado previamente a nuestra red jerárquica. Como lo definimos como Injectable, podemos pasarlo como proveedor a nuestra aplicación. También obtendremos una referencia a nuestra grilla raíz, usando la consulta ViewChild para configurar sus datos:

    @Component({
        providers: [RemoteLoDService],
        selector: "app-hierarchical-grid-lod",
        styleUrls: ["./hierarchical-grid-lod.component.scss"],
        templateUrl: "./hierarchical-grid-lod.component.html"
    })
    export class HierarchicalGridLoDSampleComponent {
        @ViewChild("hGrid")
        public hGrid: IgxHierarchicalGridComponent;
    
        constructor(private remoteService: RemoteLoDService) { }
    }
    

    Para asegurarnos de que nuestra cuadrícula se represente antes de solicitar sus datos al servicio y asignarlos, usaremos el enlace del ciclo de vida AfterViewInit. Como no tiene padres, solo podemos pasar ese rootLevel es true y la clave para ello al getData de nuestro servicio. Dado que devuelve un observable, tendremos que suscribirnos a él:

    public ngAfterViewInit() {
        this.remoteService.getData({ parentID: null, rootLevel: true, key: "Customers" }).subscribe((data) => {
            this.hGrid.data = data;
            this.hGrid.cdr.detectChanges();
        });
    }
    

    A continuación, solo necesitamos crear nuestro método gridCreated que solicitará datos para cualquier nueva cuadrícula secundaria creada. Será similar a obtener los datos de la cuadrícula del nivel raíz, solo que esta vez necesitaremos pasar más información, como parentID y parentKey. rootLevel será false para cualquier niño:

    public gridCreated(event: IGridCreatedEventArgs, _parentKey: string) {
        const dataState = {
            key: event.owner.key,
            parentID: event.parentID,
            parentKey: _parentKey,
            rootLevel: false
        };
        this.remoteService.getData(dataState).subscribe(
            (data) => {
                event.grid.data = data;
                event.grid.cdr.detectChanges();
            }
        );
    }
    

    Con esto, la configuración de nuestra aplicación casi está terminada. Este último paso tiene como objetivo mejorar la experiencia del usuario informándole que los datos aún se están cargando para que no tenga que mirar una cuadrícula vacía mientras tanto. Es por eso que IgxHierarchicalGrid admite un indicador de carga que se puede mostrar mientras la cuadrícula está vacía. Si se reciben nuevos datos, el indicador de carga se ocultará y se representarán los datos.

    Configuración de indicación de carga

    IgxHierarchicalGrid puede mostrar un indicador de carga estableciendo la propiedad isLoading en true mientras no hay datos. Necesitamos configurarlo inicialmente para la cuadrícula raíz y también al crear nuevas cuadrículas secundarias, hasta que se carguen sus datos. Siempre podemos establecerlo en true en nuestra plantilla, pero queremos ocultarlo y mostrar que la cuadrícula no tiene datos si el servicio devuelve una matriz vacía configurándolo en false.

    En este caso, la versión final de nuestro hierarchical-grid-lod.component.ts se vería así:

    import { AfterViewInit, Component, ViewChild } from "@angular/core";
    import {
        IGridCreatedEventArgs,
        IgxHierarchicalGridComponent,
        IgxRowIslandComponent
    } from "igniteui-angular";
    import { RemoteLoDService } from "../services/remote-lod.service";
    
    @Component({
        providers: [RemoteLoDService],
        selector: "app-hierarchical-grid-lod",
        styleUrls: ["./hierarchical-grid-lod.component.scss"],
        templateUrl: "./hierarchical-grid-lod.component.html"
    })
    export class HierarchicalGridLoDSampleComponent implements AfterViewInit {
        @ViewChild("hGrid")
        public hGrid: IgxHierarchicalGridComponent;
    
        constructor(private remoteService: RemoteLoDService) { }
    
        public ngAfterViewInit() {
            this.hGrid.isLoading = true;
            this.remoteService.getData({ parentID: null, rootLevel: true, key: "Customers" }).subscribe((data) => {
                this.hGrid.isLoading = false;
                this.hGrid.data = data;
                this.hGrid.cdr.detectChanges();
            });
        }
    
        public gridCreated(event: IGridCreatedEventArgs, _parentKey: string) {
            const dataState = {
                key: event.owner.key,
                parentID: event.parentID,
                parentKey: _parentKey,
                rootLevel: false
            };
            event.grid.isLoading = true;
            this.remoteService.getData(dataState).subscribe(
                (data) => {
                    event.grid.isLoading = false;
                    event.grid.data = data;
                    event.grid.cdr.detectChanges();
                }
            );
        }
    }
    

    API References

    Additional Resources

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