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