/ ANGULAR

Angular 강좌(8) - Material Table

Angular 강좌는 여러 절로 구성되어 있습니다.


Material Table Component

이번 포스트에서는 이전에 만들었던 list-box Component가 표현하는 부분을 Material Table을 이용해서 작성해 보겠습니다. 기본적인 테이블 구성과 함께 Pagination 까지 추가해서 간단하게 Paging까지 구현해보겠습니다.

사실 설명할 부분이 많지는 않습니다. DataSource만 Table에 잘 연결하면 알아서 보여주기 때문이죠. 그게 또 Component 기반 개발의 장점이기도 하구요.

먼저 CSS부터 수정하도록 하겠습니다. 다음은 list-box.component.css 파일입니다.

.example-container {
  display: flex;
  flex-direction: column;
  min-width: 300px;
  margin-top: 30px;
}

.mat-table {
  overflow: auto;
  max-height: 500px;
}

.mat-header-cell.mat-sort-header-sorted {
  color: black;
}

.list-table-style {
  font-family: Georgia;
}

.list-header-style {
  background-color: beige;
}

그 다음은 list-box.component.html 파일입니다.


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

    <ng-container matColumnDef="bisbn">
      <mat-header-cell *matHeaderCellDef> ISBN </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.bisbn}} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="btitle">
      <mat-header-cell *matHeaderCellDef> Title </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.btitle}} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="bauthor">
      <mat-header-cell *matHeaderCellDef> Author </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.bauthor}} </mat-cell>
    </ng-container>

    <ng-container matColumnDef="bprice">
      <mat-header-cell *matHeaderCellDef> Price </mat-header-cell>
      <mat-cell *matCellDef="let element"> {{element.bprice}} </mat-cell>
    </ng-container>

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

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

Table Component를 이용하기 때문에 book-search.module.ts에 관련된 Module을 import해 주어야 합니다.

import { MatTableModule } from '@angular/material/table';

그리고 Paging처리를 해야 하기 때문에 MatPaginatorModule 역시 import합니다.

import { MatPaginatorModule } from '@angular/material/paginator';

위의 HTML에서 가장 중요한 부분은 당연히 DataSource를 바인딩 하는 부분입니다.

<mat-table class="list-table-style" #table [dataSource]="dataSource">

Property binding을 이용하여 Component에 있는 dataSource라는 속성과 연결시켰습니다. 이 dataSource라는 속성은 도서정보에 대한 객체배열을 이용해서 만든 MatTableDataSource class의 객체입니다. JSON 데이터를 가져와서 만든객체입니다.

마지막으로 list-box.compoennt.ts파일의 내용입니다.

import { Component, OnInit } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { MatTableDataSource } from '@angular/material';
import { MatPaginator } from '@angular/material';
import { ViewChild } from '@angular/core';

interface IBook {
  bauthor: string;
  bdate: string;
  btranslator: string;
  bpublisher: string;
  btitle: string;
  bprice: number;
  bisbn: string;
  bimgurl: string;
}

@Component({
  selector: 'app-list-box',
  templateUrl: './list-box.component.html',
  styleUrls: ['./list-box.component.css']
})
export class ListBoxComponent {

  displayedColumns = ['bisbn', 'btitle', 'bauthor', 'bprice'];
  dataSource;
  books: IBook[];

  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor(private http: HttpClient) {
    this.http.get<IBook[]>('assets/data/book.json')
      .subscribe(res => {
        this.books = res;
        this.dataSource = new MatTableDataSource<IBook>(this.books);
        this.dataSource.paginator = this.paginator;
      });
  }
}

Code Review

원래 Code Review란 표현은 Code Inspection에서 기인한 용어로 코드를 실제로 실행하지 않고 사람이 검토하는 과정을 통해 논리적인 잠재 오류를 찾아내고 이를 개선하는 작업을 지칭합니다.

그런데 여기서는 그냥 위의 코드를 살펴보자는 의미로 사용했습니다. ^^;;

코드를 좀 간단히 설명해보죠.

먼저 Table을 생성하는 구문은 다음과 같습니다.

<mat-table [dataSource]="dataArray">
  ...
</mat-table>

위의 코드에서 dataArray라고 되어있는 부분이 실제 Table에 rendering되는 데이터입니다. 배열형태로 되어 있고 배열안의 각각의 객체를 row로 가져와서 화면에 출력하게 됩니다.

다음은 Table의 컬럼을 표현하는 template입니다. 구문은 다음과 같습니다.


<ng-container matColumnDef="bisbn">
    <mat-header-cell *matHeaderCellDef> ISBN </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{element.bisbn}} </mat-cell>
</ng-container>

matColumnDef 속성은 사용할 컬럼의 이름입니다. 이 부분은 list-box.component.ts파일안에 컬럼명에 대한 배열이 정의되는데 이 부분과 매칭되어야 합니다. 다음은 list-box.component.ts안에 정의된 컬럼명에 대한 배열입니다.

displayedColumns = ['bisbn', 'btitle', 'bauthor', 'bprice'];

그리고 아래의 구문에 의해 ISBN 컬럼의 제목과 내용이 출력됩니다. dataSource에 연결된 모든 row를 가져와서 element라는 변수에 반복적으로 할당 하면서 element.bisbn값을 테이블에 출력하라는 말입니다.


<mat-header-cell *matHeaderCellDef> ISBN </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.bisbn}} </mat-cell>

이와 같은 형태로 하나의 컬럼에 대한 데이터를 화면에 출력할 수 있습니다. 우리는 총 4개의 컬럼을 화면에 출력하고 있는 것이죠.

list-box.compoennt.ts에서는 사용할 데이터를 HttpClient의 get() method로 가져온 후 이를 다음의 코드를 이용해서 객체화 시켰습니다.

this.dataSource = new MatTableDataSource<IBook>(this.books);

dataSource와 연결시키기 위해 위에 있는 코드처럼 객체를 생성해서 연결하셔야 합니다.

Paginator의 사용은 코드에 나온것처럼 사용하시면 됩니다. 내부적으로 처리되기 때문에 사용하는 방법만 아시면 충분합니다.

TypeScript를 사용하기 때문에 interface를 이용하여 data type을 명확히 지정했습니다. 이 부분역시 이전 HTML Table Element로 작업했을 때와는 다르게 처리하셔야 합니다.


정리

이제 1차적인 작업은 모두 끝났습니다. 논리적인 설명보다는 실제 사용할 화면을 만들면서 필요한 개념들에 대해서 그때 그때 설명하는 방식을 취했습니다. 이제 Component간의 상태공유에 대한 문제만 해결되면 우리의 프로그램은 얼추 완성할 수 있을 듯 보입니다.

조금만 더 진행시켜 일단 프로그램을 완성한 후 세부적인 내용들에 대해서 다시 짚어가며 살펴보도록 하겠습니다.

End.


Angular 강좌는 아래의 책과 사이트를 참조했습니다. 조금 더 자세한 사항을 알고 싶으시면 해당 사이트를 방문하세요!!