LoginSignup
16
14

More than 5 years have passed since last update.

Angular MaterialのMatTableでサーバサイドページング

Last updated at Posted at 2018-05-01

前提・概要

Angular MaterialのMatTableでサーバサイドページングを行うには、DataSourceを自前で実装する必要がある。
公式マニュアルには、クライアントサイドページングの例しか載っていなかったため、整理しておく。
もうすぐAngular6が出るが、まだ正式版ではないため、Angular4で実現した方法を記載する。
また、概略を掴みやすくするため、エラー処理は省略している。

下準備

モジュール

MatTableModuleとMatPaginatorModuleをimportしておく。

エンティティ

ユーザ情報をサーバサイドページングして表示する。

user.ts
export class User {
  public userId: string;
  public userName: string;
  public email: string;
}

サービス

ページ番号と、1ページの件数を指定して、サーバからレコードを取得する。

user.service
@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()の部分)。

datasource.ts
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で登録する。

user-list.component.ts
@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を指定する。

user-list.component.html
<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の作成がひと手間かかるが、それさえできれば簡単に実現できた。

16
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14