/ ANGULAR

Angular 강좌(16) - Material Table Event

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


Angular Material Table Event

이번 포스트는 list-box Component의 Angular Material Table의 row를 클릭하면 해당 책의 세부정보를 detail-box Component의 View에 출력하는 부분을 작성해 보겠습니다. 이 작업 역시 BehaviorSubject를 이용해 선택한 책의 정보를 자동으로 갱신해 출력할 수 있도록 처리하겠습니다.

Service에 Client에 의해 선택된 책의 세부정보가 담긴 객체정보가 존재해야 합니다. detail-box Component는 이 정보를 subscribe해야 하고 만약 Client에 의해 다른 책이 선택된다면 이 정보를 갱신해주면 될 듯 합니다.

다음은 http-support.service.ts 파일의 내용입니다.

import { Injectable } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { BehaviorSubject } from "rxjs/BehaviorSubject";

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

@Injectable()
export class HttpSupportService {

  books: IBook[];

  // Client에 의해 선택된 책의 정보 - 초기화
  selectedBook: IBook = {
    bauthor: '',
    bdate: '',
    btranslator: '',
    bpublisher: '',
    btitle: '',
    bprice: 0,
    bisbn: '',
    bimgurl: ''
  };

  constructor(private http: HttpClient) { }

  updateBooks: BehaviorSubject<IBook[]> = new BehaviorSubject<IBook[]>(this.books);
  
  // selectedBook에 대한 BehaviorSubject 객체 생성
  updateSelectedBook: BehaviorSubject<IBook> = new BehaviorSubject<IBook>(this.selectedBook);

  getJsonData(url:string, name:string, category:string, keyword:string) {
    this.http.get<IBook[]>(`${url}${name}`)
        .subscribe(res => {
           let tmp = null;
           // 도서종류와 검색어를 이용한 도서 데이터 Filtering 시작
           if( category == 'all' ) {
             tmp = res.filter(function(item,idx,arr) {
               if(item.btitle.includes(keyword)) {
                 return true;
               } else {
                 return false;
               }
             });
           } else if( category == 'country') {
             tmp = res.filter(function(item,idx,arr) {
               if(item.btitle.includes(keyword)) {
                 return true;
               } else {
                 return false;
               }
             }).filter(function(item,idx,arr) {
               if(item.btranslator == '') {
                 return true;
               } else {
                 return false;
               }
             });
           } else if( category == 'foreign') {
             tmp = res.filter(function(item,idx,arr) {
               if(item.btitle.includes(keyword)) {
                 return true;
               } else {
                 return false;
               }
             }).filter(function(item,idx,arr) {
               if(item.btranslator != '') {
                 return true;
               } else {
                 return false;
               }
             });
           }
          // 도서종류와 검색어를 이용한 도서 데이터 Filtering 끝
          this.updateBooks.next(tmp);
          //this.books = tmp;
          //console.log(this.books);
        });
  }

  getBooks(): IBook[] {
    return this.books;
  }
}

이제 Material Table row event를 처리해야 합니다. 이 부분은 API 사용이기 때문에 별다른 설명이 없습니다. 다음의 코드를 이용하시면 됩니다.

다음은 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">  </mat-cell>
    </ng-container>

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

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

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

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

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

아래쪽에 <mat-row>에 대한 이벤트 처리만 유의해서 보시면 됩니다. Table의 각 row를 클릭할 때 마다 rowSelect()를 이용해 method를 호출하면서 현재 선택된 row 객체를 인자로 넘겨줍니다.

다음은 list-box.component.ts 파일의 내용입니다.

import { Component, OnInit } from '@angular/core';
import { MatTableDataSource } from '@angular/material';
import { MatPaginator } from '@angular/material';
import { ViewChild } from '@angular/core';
import { HttpSupportService } from "../http-support.service";
import { SelectionModel } from '@angular/cdk/collections';

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[];

  // event 처리
  selection = new SelectionModel<IBook>(false, []);

  @ViewChild(MatPaginator) paginator: MatPaginator;

  constructor(private httpSupportService:HttpSupportService) {
    this.httpSupportService.updateBooks.subscribe(data => {
      this.books = data;
      this.dataSource = new MatTableDataSource<IBook>(this.books);
      this.dataSource.paginator = this.paginator;
    })
  }

  rowSelect(row) {
    this.selection.select(row);
    this.httpSupportService.updateSelectedBook.next(this.selection.selected[0]);
  }

}

SelectionModel을 이용해 사용자가 선택한 row에 대한 정보를 획득합니다. Service의 updateSelectedBook의 next()를 호출하여 detail-box Component에서 구독하고 있을 선택된 책의 정보를 갱신합니다.

마지막으로 detail-box.component.ts 파일입니다.

import { Component, OnInit } from '@angular/core';
import {HttpSupportService} from "../http-support.service";

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

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

  book: IBook;

  constructor(private httpSupportService:HttpSupportService) {
    this.httpSupportService.updateSelectedBook.subscribe(selectedBook => {
      this.book = selectedBook;
    });
  }

  ngOnInit() {
  }

}

기존에 sample data를 삭제하고 Service를 이용하여 updateSelectedBook 객체를 subscribe하게끔 처리했습니다.

Table의 이벤트 처리하는 부분만 잘 보시면 됩니다. 데이터 공유는 이전 포스트에서 설명했던 방식 그대로 똑같이 적용해서 처리했습니다.

여기까지해서 기본적인 도서 검색에 대한 프로그램을 완성했습니다. 다음 포스트들은 실습과 약간 무관하지만 알아야 하는 부분들에 대해서 정리해 보겠습니다.

End.


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