前提・概要
Angular MaterialのMatTableでサーバサイドページングを行うには、DataSourceを自前で実装する必要がある。
公式マニュアルには、クライアントサイドページングの例しか載っていなかったため、整理しておく。
もうすぐAngular6が出るが、まだ正式版ではないため、Angular4で実現した方法を記載する。
また、概略を掴みやすくするため、エラー処理は省略している。
下準備
モジュール
MatTableModuleとMatPaginatorModuleをimportしておく。
エンティティ
ユーザ情報をサーバサイドページングして表示する。
export class User {
public userId: string;
public userName: string;
public email: string;
}
サービス
ページ番号と、1ページの件数を指定して、サーバからレコードを取得する。
@Injectable()
export class UserService {
constructor(private http: HttpClient) { }
/**
* ユーザ情報を取得する。
*
* @param page 取得するページ番号(0-based)
* @param size 1ページの件数
*/
public getUsers(page: number, size: number): Observable<any> {
return this.http.get('http://localhost:8080/users', {
params: new HttpParams().set('page', page).set('size', size)
});
}
}
getUsers()の戻り値は、全件の件数と、指定ページのユーザ情報の配列を持つJSONとする。
Spring Data(JPA)のPageは、この構造に近い形となっている。
{
"totalElements": 51,
"contents": [
{"userId": "user1", "userName": "ユーザ1", "email": "user1@example.com"},
{"userId": "user2", "userName": "ユーザ2", "email": "user2@example.com"},
{"userId": "user3", "userName": "ユーザ3", "email": "user3@example.com"},
{"userId": "user4", "userName": "ユーザ4", "email": "user4@example.com"},
{"userId": "user5", "userName": "ユーザ5", "email": "user5@example.com"}
]
}
Angular Materialとの連携
DataSourceの作成
「@angular/cdk/collections」のDataSourceを実装するクラスを作成する。
サーバから取得したユーザ情報は、userSubjectを経由して、ObservableとしてAngular Material Tableに渡される(connect()の部分)。
export class UserDataSource implements DataSource<User> {
/** ユーザ情報 */
private userSubject = new BehaviorSubject<User[]>([]);
/** 全レコード数 */
public totalElements: number;
constructor(private userService: UserService) { }
connect(collectionViewer: CollectionViewer): Observable<User[]> {
return this.userSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.userSubject.complete();
}
/**
* ユーザ情報を取得する。
*
* @param page 取得するページ番号(0-based)
* @param size 1ページの件数
*/
loadMaster(page: number = 0, size: number = 5): void {
this.userService.getUsers(page, size)
.subscribe(response => {
this.totalElements= response.totalElements;
this.userSubject.next(response.contents);
});
}
}
コンポーネント
ngOnInitでDataSourceを作成し、最初のページをサーバから取得する。
ページングが行われた場合に、そのページのユーザ情報をサーバから取得する処理を、ngAfterViewInitで登録する。
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements AfterViewInit, OnInit {
/** ユーザ情報をMatTableで表示するためのDataSource */
public dataSource: UserDataSource;
/** ページングコンポーネント */
@ViewChild(MatPaginator)
public paginator: MatPaginator;
/** カラムの表示順序 */
public columns = ['userId', 'userName', 'email'];
constructor(private userService: UserService) { }
ngOnInit() {
this.dataSource = new UserDataSource(this.userService);
this.dataSource.loadMaster();
}
ngAfterViewInit() {
this.paginator.page.map(() => this.loadMaster()).subscribe();
}
private loadMaster() {
this.dataSource.loadMaster(this.paginator.pageIndex, this.paginator.pageSize);
}
}
テンプレート
クライアントサイドページングと同じように、mat-tableのdataSourceに、作成したDataSourceを指定する。
<div>
<mat-table [dataSource]="dataSource">
<ng-container matColumnDef="userId">
<mat-header-cell *matHeaderCellDef>ユーザID</mat-header-cell>
<mat-cell *matCellDef="let record">{{record.userId}}</a></mat-cell>
</ng-container>
<ng-container matColumnDef="userName">
<mat-header-cell *matHeaderCellDef>ユーザ名</mat-header-cell>
<mat-cell *matCellDef="let record">{{record.userName}}</mat-cell>
</ng-container>
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef>メールアドレス</mat-header-cell>
<mat-cell *matCellDef="let record">{{record.email}}</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="columns"></mat-header-row>
<mat-row *matRowDef="let row; columns: columns"></mat-row>
</mat-table>
<mat-paginator [length]="dataSource.totalElements" [pageSize]="5"></mat-paginator>
</div>
これで、ページングを行うたびにUserComponentのloadMaster()が呼ばれて、サーバサイドにユーザ情報を取りに行くようになった。
終わりに
DataSourceの作成がひと手間かかるが、それさえできれば簡単に実現できた。