Angular Material 2表服务器端分页 [英] Angular Material 2 table server-side pagination
问题描述
我正在尝试实现Angular Material 2,MatPaginator服务器端分页.我该如何实现?
I am trying to Achieve Angular Material 2, MatPaginator server side paging. How can I achieve that?
下面是代码示例:
<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>
分页组件:
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
推荐答案
基于Wilfredo的回答( https://stackoverflow.com/a/47994113/986160 )我整理了一个完整的工作示例,因为一些问题也被遗漏了.这是使用Angular 5和Material Design进行服务器端分页和排序的更一般的情况(仍然需要插入过滤)-希望它对某人有帮助:
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:
分页组件:
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);
}
}
服务:
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端点
@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);
}
一些注意事项:
- 确保您导入模块:
MatTableModule
,MatPaginatorModule
和MatSortModule
以及Material Design的其他模块. - 我决定从Response-Header
x-total-count
(通过Spring Boot@ControllerAdvice
填充)中填充resultsLength
(总计).或者,您可以从EntityService
返回的对象(例如,对于Spring Boot是Page
)获取此信息,尽管这意味着您将需要使用any
作为返回类型或为项目中所有实体声明包装类对象如果您想成为类型安全".
- Make sure you import Modules:
MatTableModule
,MatPaginatorModule
andMatSortModule
along other modules from Material Design. - I decided to populate
resultsLength
(total) from Response-Headerx-total-count
which I populate through Spring Boot@ControllerAdvice
. Alternatively you can get this information from the object returned fromEntityService
(e.gPage
for Spring Boot) although that implies you will need to useany
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屋!