Angular Material 2 表服务器端分页 [英] Angular Material 2 table server-side pagination

查看:27
本文介绍了Angular Material 2 表服务器端分页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现 Angular Material 2,MatPaginator 服务器端分页.我怎样才能做到这一点?

下面是代码示例:

 

<mat-table #table [dataSource]="dataSource"><!-- 位置列--><ng-container matColumnDef="position"><mat-h​​eader-cell *matHeaderCellDef>号</mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.position}} </mat-cell></ng-容器><!-- 名称列--><ng-container matColumnDef="name"><mat-h​​eader-cell *matHeaderCellDef>名称</mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.name}} </mat-cell></ng-容器><!-- 权重栏--><ng-container matColumnDef="weight"><mat-h​​eader-cell *matHeaderCellDef>重量</mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.weight}} </mat-cell></ng-容器><!-- 符号列--><ng-container matColumnDef="symbol"><mat-h​​eader-cell *matHeaderCellDef>符号</mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.symbol}} </mat-cell></ng-容器><mat-h​​eader-row *matHeaderRowDef="displayedColumns"></mat-h​​eader-row><mat-row *matRowDef="let row; columns:displayedColumns;"></mat-row></mat-table><mat-paginator #paginator[页面大小]="10"[pageSizeOptions]="[5, 10, 20]"></mat-paginator>

分页组件:

import {Component, ViewChild} from '@angular/core';从@angular/material"导入 {MatPaginator, MatTableDataSource};/*** @title 表格分页*/@成分({选择器:'表格分页示例',styleUrls: ['table-pagination-example.css'],templateUrl: 'table-pagination-example.html',})导出类 TablePaginationExample {displayColumns = ['位置','名称','重量','符号'];dataSource = new MatTableDataSource(ELEMENT_DATA);@ViewChild(MatPaginator) 分页器:MatPaginator;/*** 在视图初始化之后设置分页器,因为该组件将* 能够查询其视图以获取已初始化的分页器.*/ngAfterViewInit() {this.dataSource.paginator = this.paginator;}}导出接口元素{名称:字符串;位置:编号;重量:数量;符号:字符串;}const ELEMENT_DATA: 元素 [] = [{位置:1,名称:'氢',重量:1.0079,符号:'H'},{位置:2,名称:'Helium',重量:4.0026,符号:'He'},{位置:3,名称:'锂',重量:6.941,符号:'锂'},{位置:4,名称:'铍',重量:9.0122,符号:'Be'},{位置:5,名称:'硼',重量:10.811,符号:'B'},{位置:6,名称:'碳',重量:12.0107,符号:'C'},{位置:7,名称:'氮',重量:14.0067,符号:'N'},{位置:8,名称:'氧气',重量:15.9994,符号:'O'},{位置:9,名称:'氟',重量:18.9984,符号:'F'},{位置:10,名称:'霓虹',重量:20.1797,符号:'Ne'},{位置:11,名称:'钠',重量:22.9897,符号:'Na'},{位置:12,名称:'镁',重量:24.305,符号:'镁'},{位置:13,名称:'铝',重量:26.9815,符号:'铝'},{位置:14,名称:'硅',重量:28.0855,符号:'Si'},{位置:15,名称:'磷',重量:30.9738,符号:'P'},{位置:16,名称:'硫',重量:32.065,符号:'S'},{位置:17,名称:'氯',重量:35.453,符号:'Cl'},{位置:18,名称:'氩',重量:39.948,符号:'Ar'},{位置:19,名称:'钾',重量:39.0983,符号:'K'},{位置:20,名称:'钙',重量:40.078,符号:'Ca'},];

如何实现服务器端分页,在下一页点击的更改事件或页面大小更改时触发以获取下一组记录.

https://stackblitz.com/angular/qxxpqbqolyb?file=app%2Ftable-pagination-example.ts

解决方案

基于 Wilfredo 的回答 (https://stackoverflow.com/a/47994113/986160) 我编译了一个完整的工作示例,因为问题中也缺少一些部分.这是使用 Angular 5 和 Material Design(仍然需要插入过滤)的服务器端分页和排序的更一般情况 - 希望对某人有所帮助:

分页组件:

import { ViewChild, Component, Inject, OnInit, AfterViewInit } from '@angular/core';从'./entity.json'导入{EntityJson};从 './entity.service' 导入 { EntityService };从 '@angular/material' 导入 { MatPaginator, MatSort, MatTableDataSource };从 'rxjs/Observable' 导入 { Observable };从'rxjs/observable/merge'导入{合并};从 'rxjs/observable/of' 导入 { of as observableOf };从'rxjs/operators/catchError'导入{catchError};从'rxjs/operators/map'导入{地图};import { startWith } from 'rxjs/operators/startWith';从 'rxjs/operators/switchMap' 导入 { switchMap };@成分({选择器:'实体最新页面',提供者:[EntityService],样式:[`:主机垫表{显示:弹性;弹性方向:列;最小宽度:100px;最大宽度:800px;边距:0 自动;}`],模板:`<mat-card><mat-card-title>实体列表<button mat-button [routerLink]="['/create/entity']">创建</mat-card-title><mat-card-content><mat-table #table matSort [dataSource]="entitiesDataSource" matSort class="mat-elevation-z2"><ng-container matColumnDef="id"><mat-h​​eader-cell *matHeaderCellDef mat-sort-header>Id </mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.id}} </mat-cell></ng-容器><ng-container matColumnDef="name"><mat-h​​eader-cell *matHeaderCellDef mat-sort-header>名称</mat-h​​eader-cell><mat-cell *matCellDef="let element">{{element.name}} </mat-cell></ng-容器><mat-h​​eader-row *matHeaderRowDef="displayedColumns"></mat-h​​eader-row><mat-row *matRowDef="let row; columns:displayedColumns;"></mat-row></mat-table></mat-card-content><mat-card-content><mat-paginator #paginator [length]="resultsLength"[页面大小]="5"[pageSizeOptions]="[5, 10, 20]"></mat-paginator></mat-card-content></mat-card>`})导出类 EntityLatestPageComponent 实现 AfterViewInit {私有实体:EntityJson[];私有实体数据源:MatTableDataSource= 新 MatTableDataSource();私人显示列 = ['id', 'name'];结果长度 = 0;isLoadingResults = false;isRateLimitReached = false;@ViewChild(MatPaginator) 分页器:MatPaginator;@ViewChild(MatSort) 排序:MatSort;公共构造函数(@Inject(实体服务)私有实体服务:实体服务){}公共 ngAfterViewInit() {//如果用户更改了排序顺序,则重置回第一页.this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);合并(this.sort.sortChange,this.paginator.page).管道(从...开始({}),switchMap(() => {this.isLoadingResults = true;返回 this.entityService.fetchLatest(this.sort.active, this.sort.direction,this.paginator.pageIndex + 1, this.paginator.pageSize,(总计) =>this.resultsLength = total);}),地图(数据=> {this.isLoadingResults = false;this.isRateLimitReached = false;//替代响应头;//this.resultsLength = data.total;返回数据;}),catchError(() => {this.isLoadingResults = false;this.isRateLimitReached = true;返回 observableOf([]);})).subscribe(data => this.entitiesDataSource.data = data);}}

<小时>

服务:

import { EntityJson } from './entity.json';从'../common/api.helper'导入{ApiHelper};从@angular/http"导入 { Http, Headers, Response, RequestOptions };从'@angular/core'导入{注入,可注入};从 'rxjs/Observable' 导入 { Observable };从'../auth/authentication.service'导入{AuthenticationService};import { stringify } from 'query-string';@Injectable()导出类 EntityService {私有选项:RequestOptions;私有 apiPrefix:字符串;私有 apiEndpoint:字符串;构造函数(@Inject(Http) 私有 http: Http,@Inject(AuthenticationService) 私有 authService: AuthenticationService) {this.options = authService.prepareRequestHeaders();this.apiPrefix = 'http://localhost:4200/api/v1/';this.apiEndpoint = this.apiPrefix + '实体';}public fetchLatest(sort: string = '', order: string = '', page: number = 1, perPage: number = 5, initTotal: Function = () => {}): Observable{return this.http.get(this.apiEndpoint +'?' + EntityService.createUrlQuery({sort: {field: sort, order: order}, pagination: { page, perPage }}), this.options).map((res) => {const total = res.headers.get('x-total-count').split('/').pop();initTotal(总计);返回 JSON.parse(res.text()).content});}//应该放在一个util静态 createUrlQuery(params: any) {如果(!参数){返回 "";}让页面;让每页;让场;让订单;让查询:any = {};如果(参数.分页){page = params.pagination.page;perPage = params.pagination.perPage;查询范围 = JSON.stringify([页,每页,]);}如果(参数.排序){字段 = params.sort.field;订单 = params.sort.order;如果(字段&&订单){query.sort = JSON.stringify([field, order]);}别的 {query.sort = JSON.stringify(['id', 'ASC']);}}如果(!params.filter){params.filter = {};}如果(Array.isArray(params.ids)){params.filter.id = params.ids;}如果(参数.过滤器){query.filter = JSON.stringify(params.filter)}控制台日志(查询,字符串化(查询));返回字符串化(查询);}}

<小时>

Spring Boot Rest 控制器端点

@GetMapping("entities")公共可迭代<实体>过滤(@RequestParam(required = false, name = "filter") String filterStr,@RequestParam(required = false, name = "range") String rangeStr, @RequestParam(required = false, name="sort") String sortStr) {//我自己的帮手 - 来源:https://github.com/zifnab87/react-admin-java-rest//FilterWrapper wrapper = filterService.extractFilterWrapper(filterStr, rangeStr, sortStr);//返回 filterService.filterBy(wrapper, repo);}

<小时>

一些注意事项:

  1. 确保导入模块:MatTableModuleMatPaginatorModuleMatSortModule 以及 Material Design 的其他模块.
  2. 我决定从我通过 Spring Boot @ControllerAdvice 填充的 Response-Header x-total-count 填充 resultsLength (total).或者,您可以从 EntityService(例如 Spring Boot 的 Page)返回的对象中获取此信息,尽管这意味着您需要使用 any 作为如果您想要类型安全",则为项目中的所有实体返回类型或声明包装类对象.

I am trying to Achieve Angular Material 2, MatPaginator server side paging. How can I achieve that?

Below is the code example:

  <div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource">

    <!-- Position Column -->
    <ng-container matColumnDef="position">
      <mat-header-cell *matHeaderCellDef> No. </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.position}} </mat-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="name">
      <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
    </ng-container>

    <!-- Weight Column -->
    <ng-container matColumnDef="weight">
      <mat-header-cell *matHeaderCellDef> Weight </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.weight}} </mat-cell>
    </ng-container>

    <!-- Symbol Column -->
    <ng-container matColumnDef="symbol">
      <mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>

  <mat-paginator #paginator
                 [pageSize]="10"
                 [pageSizeOptions]="[5, 10, 20]">
  </mat-paginator>
</div>

Pagination Component:

import {Component, ViewChild} from '@angular/core';
import {MatPaginator, MatTableDataSource} from '@angular/material';

/**
 * @title Table with pagination
 */
@Component({
  selector: 'table-pagination-example',
  styleUrls: ['table-pagination-example.css'],
  templateUrl: 'table-pagination-example.html',
})
export class TablePaginationExample {
  displayedColumns = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource<Element>(ELEMENT_DATA);

  @ViewChild(MatPaginator) paginator: MatPaginator;

  /**
   * Set the paginator after the view init since this component will
   * be able to query its view for the initialized paginator.
   */
  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
  }
}

export interface Element {
  name: string;
  position: number;
  weight: number;
  symbol: string;
}

const ELEMENT_DATA: Element[] = [
  {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
  {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
  {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
  {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
  {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
  {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
  {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
  {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
  {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
  {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
  {position: 11, name: 'Sodium', weight: 22.9897, symbol: 'Na'},
  {position: 12, name: 'Magnesium', weight: 24.305, symbol: 'Mg'},
  {position: 13, name: 'Aluminum', weight: 26.9815, symbol: 'Al'},
  {position: 14, name: 'Silicon', weight: 28.0855, symbol: 'Si'},
  {position: 15, name: 'Phosphorus', weight: 30.9738, symbol: 'P'},
  {position: 16, name: 'Sulfur', weight: 32.065, symbol: 'S'},
  {position: 17, name: 'Chlorine', weight: 35.453, symbol: 'Cl'},
  {position: 18, name: 'Argon', weight: 39.948, symbol: 'Ar'},
  {position: 19, name: 'Potassium', weight: 39.0983, symbol: 'K'},
  {position: 20, name: 'Calcium', weight: 40.078, symbol: 'Ca'},
];

How can I achieve server side pagination, which would trigger on change event of next page click or page size changes to get next set of records.

https://stackblitz.com/angular/qxxpqbqolyb?file=app%2Ftable-pagination-example.ts

解决方案

Based on Wilfredo's answer (https://stackoverflow.com/a/47994113/986160) I compiled a full working example since some pieces were missing from question as well. Here is a more general case for server-side pagination and sorting using Angular 5 and Material Design (still need to plug in filtering) - hopefully it will be helpful to someone:

Paging Component:

import { ViewChild, Component, Inject, OnInit, AfterViewInit } from '@angular/core';
import { EntityJson } from './entity.json';
import { EntityService } from './entity.service';
import { MatPaginator, MatSort, MatTableDataSource } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { merge } from 'rxjs/observable/merge';
import { of as observableOf } from 'rxjs/observable/of';
import { catchError } from 'rxjs/operators/catchError';
import { map } from 'rxjs/operators/map';
import { startWith } from 'rxjs/operators/startWith';
import { switchMap } from 'rxjs/operators/switchMap';

@Component({
    selector: 'entity-latest-page',
    providers: [EntityService],
    styles: [`
        :host mat-table {
           display: flex;
           flex-direction: column;
           min-width: 100px;
           max-width: 800px;
           margin: 0 auto;
        }
    `],
    template:
    `<mat-card>
        <mat-card-title>Entity List 
        <button mat-button [routerLink]="['/create/entity']">
            CREATE
        </button>
        </mat-card-title>
        <mat-card-content>
            <mat-table #table matSort [dataSource]="entitiesDataSource" matSort class="mat-elevation-z2">
                <ng-container matColumnDef="id">
                    <mat-header-cell *matHeaderCellDef mat-sort-header> Id </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.id}} </mat-cell>
                </ng-container>
                <ng-container matColumnDef="name">
                    <mat-header-cell *matHeaderCellDef  mat-sort-header> Name </mat-header-cell>
                    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
                </ng-container>
                <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
                <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
            </mat-table>
        </mat-card-content>
        <mat-card-content>
            <mat-paginator #paginator [length]="resultsLength"
                [pageSize]="5"
                [pageSizeOptions]="[5, 10, 20]">
            </mat-paginator>
        </mat-card-content>
    </mat-card>
    `
})
export class EntityLatestPageComponent implements AfterViewInit {

    private entities: EntityJson[];
    private entitiesDataSource: MatTableDataSource<EntityJson> = new MatTableDataSource();
    private displayedColumns = ['id', 'name'];

    resultsLength = 0;
    isLoadingResults = false;
    isRateLimitReached = false;

    @ViewChild(MatPaginator) paginator: MatPaginator;
    @ViewChild(MatSort) sort: MatSort;

    public constructor( @Inject(EntityService) private entityService: EntityService) {
    }

    public ngAfterViewInit() {

        // If the user changes the sort order, reset back to the first page.
        this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
        merge(this.sort.sortChange, this.paginator.page)
        .pipe(
            startWith({}),
            switchMap(() => {
            this.isLoadingResults = true;
            return this.entityService.fetchLatest(this.sort.active, this.sort.direction, 
                  this.paginator.pageIndex + 1, this.paginator.pageSize, 
                  (total) =>  this.resultsLength = total);
            }),
            map(data => {
            this.isLoadingResults = false;
            this.isRateLimitReached = false;
            //alternatively to response headers;
            //this.resultsLength = data.total;
            return data;
            }),
            catchError(() => {
            this.isLoadingResults = false;
            this.isRateLimitReached = true;
            return observableOf([]);
            })
        ).subscribe(data => this.entitiesDataSource.data = data);
    } 
}


Service:

import { EntityJson } from './entity.json';
import { ApiHelper } from '../common/api.helper';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AuthenticationService } from '../auth/authentication.service';
import { stringify } from 'query-string';

@Injectable()
export class EntityService {

    private options: RequestOptions;
    private apiPrefix: string;
    private apiEndpoint: string;

    constructor(
        @Inject(Http) private http: Http,
        @Inject(AuthenticationService) private authService: AuthenticationService) {

        this.options = authService.prepareRequestHeaders();
        this.apiPrefix = 'http://localhost:4200/api/v1/';
        this.apiEndpoint = this.apiPrefix + 'entities';
    }

    public fetchLatest(sort: string = '', order: string = '', page: number = 1, perPage: number = 5, initTotal: Function = () => {}): Observable<EntityJson[]> {
        return this.http.get(this.apiEndpoint +'?' + EntityService.createUrlQuery({sort: {field: sort, order: order}, pagination: { page, perPage }}), this.options)
            .map((res) => {
                const total = res.headers.get('x-total-count').split('/').pop();
                initTotal(total);
                return JSON.parse(res.text()).content
            });
    }

    //should be put in a util
    static createUrlQuery(params: any) {
        if (!params) {
            return "";
        }

        let page;
        let perPage;
        let field;
        let order;
        let query: any = {};
        if (params.pagination) {
             page = params.pagination.page;
             perPage =  params.pagination.perPage;
             query.range = JSON.stringify([
                page,
                perPage,
            ]);
        }
        if (params.sort) {
            field = params.sort.field;
            order = params.sort.order;
            if (field && order) {
                query.sort = JSON.stringify([field, order]);
            }
            else {
                query.sort = JSON.stringify(['id', 'ASC']);
            }
        }
        if (!params.filter) {
            params.filter = {};
        }
        if (Array.isArray(params.ids)) {
            params.filter.id = params.ids;
        }

        if (params.filter) {
            query.filter = JSON.stringify(params.filter)
        }
        console.log(query, stringify(query));
        return stringify(query);
    }
}


Spring Boot Rest Controller Endpoint

@GetMapping("entities")
public Iterable<Entity> filterBy(
        @RequestParam(required = false, name = "filter") String filterStr,
        @RequestParam(required = false, name = "range") String rangeStr, @RequestParam(required = false, name="sort") String sortStr) {
    //my own helpers - for source: https://github.com/zifnab87/react-admin-java-rest
    //FilterWrapper wrapper = filterService.extractFilterWrapper(filterStr, rangeStr, sortStr);
    //return filterService.filterBy(wrapper, repo);
}


Some notes:

  1. Make sure you import Modules: MatTableModule, MatPaginatorModule and MatSortModule along other modules from Material Design.
  2. I decided to populate resultsLength (total) from Response-Header x-total-count which I populate through Spring Boot @ControllerAdvice. Alternatively you can get this information from the object returned from EntityService (e.g Page for Spring Boot) although that implies you will need to use any as return type or declare wrapper class objects for all of the entities in your project if you want to be "type-safe".

这篇关于Angular Material 2 表服务器端分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆