Operações de dados remotos do Angular Grid

    O Ignite UI for Angular Grid suporta operações remotas de dados, como virtualização remota, classificação remota, filtragem remota e outras. Isso permite que o desenvolvedor execute essas tarefas em um servidor, recupere os dados produzidos e os exiba no Grid.

    Angular Grid Remote Data Operations Overview Example

    Por padrão, o Grid usa sua própria lógica para executar operações de dados. Você pode executar essas tarefas remotamente e alimentar os dados resultantes para a Grade aproveitando determinadas entradas e eventos, que são expostos pela Grade.

    Remote Virtualization

    O IgxGrid suporta o cenário em que os blocos de dados são solicitados de um serviço remoto, expondo o comportamento implementado na igxForOf diretiva que ele usa internamente.

    Para utilizar esse recurso, você precisa se inscrever no dataPreLoad saída para que você faça a solicitação apropriada com base nos argumentos recebidos, bem como defina o arquivo público IgxGrid propriedade totalItemCount com as respectivas informações provenientes do serviço.

    <igx-grid #grid [data]="remoteData | async" [autoGenerate]="false"
              (dataPreLoad)="processData(false)"
              (sortingDone)="processData(true)">
        <igx-column [field]="'ProductID'" [sortable]="true"></igx-column>
        <igx-column [field]="'ProductName'" [sortable]="true"></igx-column>
        <igx-column [field]="'UnitPrice'" [dataType]="'number'" [formatter]="formatCurrency" [sortable]="true"></igx-column>
    </igx-grid>
    
    public ngAfterViewInit() {
        this.grid.isLoading = true;
    
        this._remoteService.getData(this.grid.virtualizationState, this.grid.sortingExpressions[0], true, (data) => {
                this.grid.totalItemCount = data['@odata.count'];
                this.grid.isLoading = false;
        });
    }
    
    public processData(reset) {
        if (this.prevRequest) {
            this.prevRequest.unsubscribe();
        }
    
        this._prevRequest = this._remoteService.getData(this.grid.virtualizationState,
            this.grid.sortingExpressions[0], reset, () => {
            ...
            this.cdr.detectChanges();
        });
    }
    

    Ao solicitar dados, você precisa utilizar a IForOfState interface, que fornece as startIndex propriedades e chunkSize.

    Note

    O primeiro chunkSize sempre será 0 e deve ser determinado por você com base no cenário de aplicativo específico.

    Remote Virtualization Demo

    Infinite Scroll

    Um design popular para cenários que exigem a busca de dados por partes de um ponto final é a chamada rolagem infinita. Para grades de dados, é caracterizado pelo aumento contínuo dos dados carregados acionados pela rolagem do usuário final até a parte inferior. Os próximos parágrafos explicam como você pode usar a API disponível para obter facilmente a rolagem IgxGrid infinita.

    Para implementar a rolagem infinita, você precisa buscar os dados em pedaços. Os dados que já foram buscados devem ser armazenados localmente e você deve determinar o comprimento de um pedaço e quantos pedaços existem. Você também precisa acompanhar o último índice de linha de dados visível na grade. Dessa forma, usando as startIndex propriedades e chunkSize, você pode determinar se o usuário rola para cima e você precisa mostrar a ele os dados já buscados ou rolar para baixo e buscar mais dados do ponto final.

    A primeira coisa a fazer é usar o gancho do ngAfterViewInit ciclo de vida para buscar a primeira parte dos dados. Definir a totalItemCount propriedade é importante, pois permite que a grade dimensione sua barra de rolagem corretamente.

    public ngAfterViewInit() {
        this._remoteService.loadDataForPage(this.page, this.pageSize, (request) => {
            if (request.data) {
                this.grid.totalItemCount = this.page * this.pageSize;
                this.grid.data = this._remoteService.getCachedData({startIndex: 0, chunkSize: 10});
                this.totalItems = request.data['@odata.count'];
                this.totalPageCount = Math.ceil(this.totalItems / this.pageSize);
                this.grid.isLoading = false;
            }
        });
    }
    

    Além disso, você precisa assinar a dataPreLoad saída, para que possa fornecer os dados necessários para a grade quando ela tentar exibir uma parte diferente, em vez da carregada no momento. No manipulador de eventos, você precisa determinar se deseja buscar novos dados ou retornar dados que já estão armazenados em cache localmente.

    public handlePreLoad() {
        const isLastChunk = this.grid.totalItemCount ===
                            this.grid.virtualizationState.startIndex + this.grid.virtualizationState.chunkSize;
        // when last chunk reached load another page of data
        if (isLastChunk) {
            if (this.totalPageCount === this.page) {
                this.grid.data = this._remoteService.getCachedData(this.grid.virtualizationState);
                return;
            }
            this.page++;
            this.grid.isLoading = true;
            this._remoteService.loadDataForPage(this.page, this.pageSize, (request) => {
                if (request.data) {
                    this.grid.totalItemCount = Math.min(this.page * this.pageSize, this.totalItems);
                    this.grid.data = this._remoteService.getCachedData(this.grid.virtualizationState);
                    this.grid.isLoading = false;
                }
            });
        } else {
            this.grid.data = this._remoteService.getCachedData(this.grid.virtualizationState);
        }
    }
    

    Infinite Scroll Demo

    Remote Sorting/Filtering

    Para fornecer classificação e filtragem remotas, você precisa se inscrever no dataPreLoad, sortingExpressionsChange e filteringExpressionsTreeChange saídas, para que você faça a solicitação apropriada com base nos argumentos recebidos, bem como defina o IgxGrid propriedade totalItemCount com as respectivas informações provenientes do serviço.

    Também aproveitaremos a função rxjs debounceTime, que emite um valor da fonte Observable somente após um determinado intervalo de tempo ter passado sem outra emissão de origem. Dessa forma, a operação remota será acionada somente quando o tempo especificado tiver passado sem que o usuário a interrompa.

    const DEBOUNCE_TIME = 300;
    ...
    public ngAfterViewInit() {
        ...
        this.grid.dataPreLoad.pipe(
            debounceTime(DEBOUNCE_TIME),
            takeUntil(this.destroy$)
        ).subscribe(() => {
            this.processData();
        });
    
        this.grid.filteringExpressionsTreeChange.pipe(
            debounceTime(DEBOUNCE_TIME),
            takeUntil(this.destroy$)
        ).subscribe(() => {
            this.processData(true);
        });
    
        this.grid.sortingExpressionsChange.pipe(
            debounceTime(DEBOUNCE_TIME),
            takeUntil(this.destroy$)
        ).subscribe(() => {
            this.processData();
        });
    }
    

    Quando a classificação e filtragem remotas são fornecidas, geralmente não precisamos da classificação e filtragem integradas da grade. Podemos desativá-los definindo as sortStrategy entradas e da filterStrategy grade para as NoopSortingStrategy​ ​NoopFilteringStrategy respectivas instâncias.

    <igx-grid #grid [data]="remoteData | async" [height]="'500px'" [width]="'100%'" [autoGenerate]='false'
            [filterStrategy]="noopFilterStrategy"
            [sortStrategy]="noopSortStrategy"
            [allowFiltering]="true">
        ...
    </igx-grid>
    
    public noopFilterStrategy = NoopFilteringStrategy.instance();
    public noopSortStrategy = NoopSortingStrategy.instance();
    
    Note

    Quando dados remotos são solicitados, a operação de filtragem diferencia maiúsculas de minúsculas.

    Remote Sorting/Filtering Demo

    Você pode ver o resultado do código acima no início deste artigo na seção Demonstração.

    Unique Column Values Strategy

    Os itens de lista dentro da caixa de diálogo Filtragem de Estilo do Excel representam os valores exclusivos para a respectiva coluna. O Grid gera esses valores com base em sua fonte de dados por padrão. No caso de filtragem remota, os dados da grade não contêm todos os dados do servidor. Para fornecer os valores exclusivos manualmente e carregá-los sob demanda, podemos aproveitar a entrada do uniqueColumnValuesStrategy Grid. Essa entrada é, na verdade, um método que fornece três argumentos:

    • column: a respectiva instância da coluna.
    • filteringExpressionsTree: a árvore de expressões de filtragem, que é reduzida com base na respectiva coluna.
    • done- Retorno de chamada que deve ser chamado com os valores de coluna recém-gerados quando eles são recuperados do servidor.

    O desenvolvedor pode gerar manualmente os valores de coluna exclusivos necessários com base nas informações, fornecidas pela coluna e pelos argumentos filteringExpressionsTree e, em seguida, invocar o retorno de chamada done.

    Note

    Quando a uniqueColumnValuesStrategy entrada é fornecida, o processo de geração de valores exclusivos padrão na filtragem de estilo do Excel não será usado.

    <igx-grid #grid1 [data]="data" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy">
        ...
    </igx-grid>
    
    public columnValuesStrategy = (column: ColumnType,
                                   columnExprTree: IFilteringExpressionsTree,
                                   done: (uniqueValues: any[]) => void) => {
        // Get specific column data.
        this.remoteValuesService.getColumnData(column, columnExprTree, uniqueValues => done(uniqueValues));
    }
    

    Unique Column Values Strategy Demo

    Para fornecer um modelo de carregamento personalizado para a filtragem de estilo do Excel, podemos usar a igxExcelStyleLoading diretiva:

    <igx-grid [data]="data" [filterMode]="'excelStyleFilter'" [uniqueColumnValuesStrategy]="columnValuesStrategy">
        ...
        <ng-template igxExcelStyleLoading>
            Loading ...
        </ng-template>
    </igx-grid>
    

    Remote Paging

    O recurso de paginação pode operar com dados remotos. Para demonstrar isso, vamos primeiro declarar nosso serviço que será responsável pela busca de dados. Precisaremos da contagem de todos os itens de dados para calcular a contagem de páginas. Essa lógica será adicionada ao nosso serviço.

    @Injectable()
    export class RemotePagingService {
        public remoteData: BehaviorSubject<any[]>;
        public dataLenght: BehaviorSubject<number> = new BehaviorSubject(0);
        public url = 'https://www.igniteui.com/api/products';
    
        constructor(private http: HttpClient) {
            this.remoteData = new BehaviorSubject([]) as any;
        }
    
        public getData(index?: number, perPage?: number): any {
            let qS = '';
    
            if (perPage) {
                qS = `?$skip=${index}&$top=${perPage}&$count=true`;
            }
    
            this.http
                .get(`${this.url + qS}`).pipe(
                    map((data: any) => data)
                ).subscribe((data) => this.remoteData.next(data));
        }
    
        public getDataLength(): any {
            return this.http.get(this.url).pipe(
                map((data: any) => data.length)
            );
        }
    }
    

    Após declarar o serviço, precisamos criar um componente, que será responsável pela construção do Grid e assinatura de dados.

    export class RemotePagingGridSample implements OnInit, AfterViewInit, OnDestroy {
        public data: Observable<any[]>;
        private _dataLengthSubscriber;
    
        constructor(private remoteService: RemoteService) {}
    
        public ngOnInit() {
            this.data = this.remoteService.remoteData.asObservable();
    
            this._dataLengthSubscriber = this.remoteService.getDataLength().subscribe((data) => {
                this.totalCount = data;
                this.grid1.isLoading = false;
            });
        }
    
        public ngOnDestroy() {
            if (this._dataLengthSubscriber) {
                this._dataLengthSubscriber.unsubscribe();
            }
        }
    }
    

    Agora podemos escolher entre configurar nosso próprio modelo de paginação personalizado ou usar o padrão que o igx-paginator fornece. Primeiro, vamos dar uma olhada no que é necessário para configurar a paginação remota usando o modelo de paginação padrão.

    Remote paging with default template

    Se você quiser usar o modelo de paginação padrão, precisará definir a propriedade do totalRecords paginador, somente então a grade poderá calcular o número total da página com base no total de registros remotos. Ao executar uma paginação remota, o paginador passará para a grade apenas os dados da página atual, portanto, a grade não tentará paginar a fonte de dados fornecida. É por isso que devemos definir a propriedade de pagingMode Grid como GridPagingMode.remote. Também é necessário assinar pagingDone ou perPageChange eventos para buscar os dados do seu serviço remoto, depende do caso de uso qual evento será usado.

    <igx-grid #grid1 [data]="data | async" [isLoading]="isLoading" [pagingMode]="mode">
        <igx-column field="ID"></igx-column>
        ...
        <igx-paginator [(page)]="page" [(perPage)]="perPage"  [totalRecords]="totalCount"
            (pagingDone)="paginate($event.current)">
        </igx-paginator>
    </igx-grid>
    
    public totalCount = 0;
    public data: Observable<any[]>;
    public mode = GridPagingMode.remote;
    public isLoading = true;
    @ViewChild('grid1', { static: true }) public grid1: IgxGridComponent;
    
    private _dataLengthSubscriber;
    
    public set perPage(val: number) {
        this._perPage = val;
        this.paginate(0);
    }
    
    public ngOnInit() {
        this.data = this.remoteService.remoteData.asObservable();
    
        this._dataLengthSubscriber = this.remoteService.getDataLength().subscribe((data: any) => {
            this.totalCount = data;
            this.grid1.isLoading = false;
        });
    }
    
    public ngAfterViewInit() {
        const skip = this.page * this.perPage;
        this.remoteService.getData(skip, this.perPage);
    }
    
    public paginate(page: number) {
        this.page = page;
        const skip = this.page * this.perPage;
        const top = this.perPage;
    
        this.remoteService.getData(skip, top);
    }
    

    Remote Paging with custom igx-paginator-content

    Quando definimos um conteúdo de paginador personalizado, precisamos definir o conteúdo de forma a obter os dados apenas para a página solicitada e passar os parâmetros skip e​ ​top corretos para o serviço remoto de acordo com a página e os itens selecionados perPage. Vamos usar o <igx-paginator> para facilitar nossa configuração de exemplo, junto com o e IgxPageNavigationComponent que foram introduzidos IgxPageSizeSelectorComponent-igx-page-size adicionará o menu suspenso e o rótulo por página e igx-page-nav adicionará os botões e rótulos de ação de navegação.

    <igx-paginator #paginator
        [totalRecords]="totalCount"
        [(page)]="page"
        [(perPage)]="perPage"
        [selectOptions]="selectOptions"
        (pageChange)="paginate($event)"
        (perPageChange)="perPageChange($event)">
        <igx-paginator-content>
    	    <igx-page-size></igx-page-size>
            [This is my custom content]
    	    <igx-page-nav></igx-page-nav>
        </igx-paginator-content>
    </igx-paginator>
    
    @ViewChild('grid1', { static: true }) public grid1: IgxGridComponent;
    
    private _perPage = 15;
    private _dataLengthSubscriber: { unsubscribe: () => void; } | undefined;
    
    constructor(private remoteService: RemotePagingService) { }
    
    public ngAfterViewInit() {
        this.grid1.isLoading = true;
        this.remoteService.getData(0, this.perPage);
    }
    
    public paginate(page: number) {
        this.page = page;
        const skip = this.page * this.perPage;
        const top = this.perPage;
    
        this.remoteService.getData(skip, top);
    }
    
    public perPageChange(perPage: number) {
        const skip = this.page * perPage;
        const top = perPage;
    
        this.remoteService.getData(skip, top);
    }
    
    Note

    Para que a paginação remota seja configurada corretamente, um GridPagingMode.Remote deve ser definido:

    <igx-grid #grid1 [data]="data | async" width="100%" height="580px" [pagingMode]="mode"></igx-grid>
    ...
    public mode = GridPagingMode.Remote;
    

    A última etapa será declarar o conteúdo do paginador com base em seus requisitos.

    <igx-paginator-content>
        <igx-page-size></igx-page-size>
        [This is my custom content]
        <igx-page-nav></igx-page-nav>
    </igx-paginator-content>
    

    Após todas as alterações acima, o seguinte resultado será alcançado.

    Remote Paging with custom paginator

    Em alguns casos, você pode querer definir seu próprio comportamento de paginação e é quando podemos aproveitar o modelo de paginação e adicionar nossa lógica personalizada junto com ele. Vamos estender o exemplo de paginação remota para demonstrar isso:

    Abaixo, você encontrará os métodos que definimos para implementar nossas próprias next ações e previous ações de página.

    @ViewChild('grid1', { static: true }) public grid1: IgxGridComponent;
    
    public ngAfterViewInit() {
        this.grid1.isLoading = true;
        this.remoteService.getData(0, this.perPage);
    }
    
    public nextPage() {
        this.firstPage = false;
        this.page++;
        const skip = this.page * this.perPage;
        const top = this.perPage;
        this.remoteService.getData(skip, top);
        if (this.page + 1 >= this.totalPages) {
            this.lastPage = true;
        }
        this.setNumberOfPagingItems(this.page, this.totalPages);
    }
    
    public previousPage() {
        this.lastPage = false;
        this.page--;
        const skip = this.page * this.perPage;
        const top = this.perPage;
        this.remoteService.getData(skip, top);
        if (this.page <= 0) {
            this.firstPage = true;
        }
        this.setNumberOfPagingItems(this.page, this.totalPages);
    }
    
    public paginate(page: number, recalculate = false) {
        this.page = page;
        const skip = this.page * this.perPage;
        const top = this.perPage;
        if (recalculate) {
            this.totalPages = Math.ceil(this.totalCount / this.perPage);
        }
        this.setNumberOfPagingItems(this.page, this.totalPages);
        this.remoteService.getData(skip, top);
        this.buttonDeselection(this.page, this.totalPages);
    }
    

    Remote Paging with Batch editing

    Com os exemplos até agora, esclarecemos como configurar o IgxGrid com dados remotos. Agora, vamos nos concentrar em habilitar a edição em lote para a grade seguindo o tópico/guia de edição em lote.

    Antes de continuar com o exemplo, é bom esclarecer o caso de uso atual. Quando a paginação é feita no servidor, a grade contém os dados apenas para a página atual e se adicionarmos novas linhas, as linhas recém-adicionadas (com Edição em Lote) serão concatenadas com os dados atuais que a grade contém. Portanto, se o servidor não retornar dados para uma determinada página, a fonte de dados da grade será composta apenas pelas linhas recém-adicionadas, que a grade paginará com base nas configurações de paginação definidas (página, por página).

    public ngOnInit() {
        this._dataLengthSubscriber = this.remoteService.getDataLength().subscribe((data) => {
            this.totalCount = data;
            this._recordOnServer = data;
            this._totalPagesOnServer = Math.floor(this.totalCount / this.perPage);
            this.grid1.isLoading = false;
        });
    }
    

    Para lidar com esse caso de uso corretamente, precisamos implementar alguma lógica personalizada. Primeiro, temos que saber o número total de registros que estão no servidor. Dado isso, calculamos o número total de páginas de dados no servidor (ver this._totalPagesOnServer) e com base em seu valor, implementaremos a lógica de paginação personalizada.

    
    public paginate(page: number) {
        this.grid1.endEdit(true);
        if (page > this._totalPagesOnServer) {
            if (this.page !== this._totalPagesOnServer) {
                const skipEl = this._totalPagesOnServer * this.perPage;
                this.remoteService.getData(skipEl, this.perPage);
            }
            this.page = page - this._totalPagesOnServer;
            this.page = page;
            return;
        } else {
            this.page = 0;
        }
        this.page = page;
        const skip = this.page * this.perPage;
        this.remoteService.getData(skip, this.perPage);
    }
    
    

    Como você pode ver no método paginate, a lógica de paginação personalizada é executada, com base no_totalPagesOnServer valor.

    Paginação remota com demonstração de edição em lote

    Known Issues and Limitations

    • Quando a grade não tiver nenhuma primaryKey configuração e os cenários de dados remotos estiverem habilitados (ao paginar, classificar, filtrar, rolar solicitações de gatilho para um servidor remoto para recuperar os dados a serem exibidos na grade), uma linha perderá o seguinte estado após a conclusão de uma solicitação de dados:
      • Seleção de linha
      • Expandir/recolher linha
      • Edição de linha
      • Fixação de linha
    • Em cenários de dados remotos, quando a grade tiver um primaryKey argumento set,event rowSelectionChanging.oldSelection não conterá o objeto de dados de linha completa para as linhas que estão atualmente fora da exibição de dados. Nesse caso, rowSelectionChanging.oldSelection o objeto conterá apenas uma propriedade, que é o primaryKey campo. Para o restante das linhas, atualmente na exibição de dados, rowSelectionChanging.oldSelection conterá os dados da linha inteira.

    API References

    Additional Resources

    Nossa comunidade é ativa e sempre acolhedora para novas ideias.