Angular 8 Pagination and Sorting

Phai Panda
5 min readSep 29, 2019

--

by Angular Material

Table & Paginator by Angular Material

เตรียมความพร้อม

บทความนี้ต้องการ backend ซึ่งได้อธิบายไว้แล้วที่นี่

เขื่อว่าเพื่อนๆที่เข้ามาอ่านบทความนี้คงเคยเขียน angular มาบ้างแล้ว ดังนั้นผมจะขอข้ามขั้นตอนการติดตั้ง Node.js และ Angular CLI ไปนะครับ

สร้างโปรเจกต์

ng new bookdb-front
  • เลือก SCSS
  • ไม่เลือก Routing

เพิ่ม Angular Material

ng add @angular/material
  • เลือก theme ตามใจชอบ ผมได้เลือก purple-green

แล้วไปกำหนดค่า theme ในไฟล์ styles.scss

@import '@angular/material/prebuilt-themes/purple-green.css';...

ทดสอบเพิ่ม MatToolbarModule ในไฟล์ app.module.ts

import { MatToolbarModule } from '@angular/material/toolbar';...imports: [...MatToolbarModule...],

จากนั้นเพิ่ม <mat-toolbar> ในไฟล์ app.component.html ส่วนแท็กอื่นๆลบทิ้งให้หมด

<mat-toolbar color="primary">Welcome to bookdb</mat-toolbar>

หากทุกอย่างเรียบร้อยดีต้องได้แบบนี้

ที่เหลือเป็นการแสดงหาความรู้ด้วยตนเองเกี่ยวกับ Angular Material เข้าไปอ่าน เข้าไปลองหยิบมาใช้งาน เชื่อว่าจะชื่นชอบ theme ตัวนี้ครับ

Angular Material: Table

ถัดมาเราต้องการ Table เพื่อแสดงรายการหนังสือ จะต้องเพิ่ม MatTableModule เข้าไปในไฟล์ app.module.ts

import { MatTableModule } from '@angular/material/table';...imports: [...MatToolbarModule,
MatTableModule
...],

เปิดไฟล์ app.component.ts หัวใจของการใช้ table ก็คือ

displayedColumns = ['isbn','name','authors','year','pages'];dataSource = new MatTableDataSource<TableData>();

displayColumns พูดว่าจะให้ แสดง/ซ่อน column ใดบ้างตามลำดับ รายชื่อ column ที่อยู่ในนี้คือแสดงทั้งหมด โดยที่ชื่อ column ที่ต้องการให้แสดงจะต้องสามารถหยิบหามาจาก TableData หรือจะสร้างขึ้นเองก็ได้-สำคัญคือต้องอ้างอิงถึงได้

dataSource พูดว่าค่าของ table คืออะไร สามารถให้ค่าเป็น array ของ data ได้ ตัวอย่างเช่น [1, ‘AAA’, 10.5, true, {}] แต่ในที่นี้ผมให้ค่าเป็น MatTableDataSource ซึ่งจะต้อง include เข้ามา

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

MatTableDataSource เราสามารถกำหนด T ของสิ่งที่ต้องการให้มันสนใจได้ ในที่นี้เลือกเป็น interface ชื่อ TableData และนี่คือหน้าตาของมัน

interface TableData {    isbn: string;    name: string;    authors: string;    year: string;    pages: number;}

หากถามว่า MatTableDataSource เนี่ยต้องกำหนด T ตลอดไหม คำตอบคือ ไม่ เราอาจใส่ T หรือ any หรือไม่ใส่อะไรเลยก็ได้ ตัวอย่าง

dataSource = new MatTableDataSource();

T หมายถึง class หรือ interface ใดๆที่ต้องการระบุเฉพาะเจาะจง เพื่อให้ compiler ตรวจสอบความถูกต้องให้ก่อน runtime

เปิดไฟล์ app.component.html เราจะต้องวางแท็ก <table> เพื่อกำหนดโครงสร้างของตาราง

<table>...</table>

จากนั้นระบุ dataSource ให้กับมัน (เพราะชื่อตัวแปรมันเหมือนกัน ตัวหนาคือ dataSource ที่เราเพิ่งทำความรู้จักไป​ก่อนหน้านี้)

<table mat-table [dataSource]="dataSource">

เพื่อนๆจะเห็นว่า <table> นี่ก็แท็ก HTML ธรรมดา แต่ที่มันสามารถเข้าใจ [dataSource] ได้ก็เพราะว่ามันถูกจัดการด้วย MatTableModule

เอาล่ะใส่ style ให้มันด้วย จะได้สวยๆ

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

อีกนิดเรื่อง style ใส่ table ความกว้าง 100% เพื่อให้มันได้ยืดเต็มจอ ด้วยการเปิดไฟล์ app.component.scss แล้วเพิ่ม

table {    width: 100%;}

ต่อไปเป็นเรื่องของการแสดง column และข้อมูลภายใน ประกอบไปด้วยสองส่วนสำคัญได้แก่

  1. ส่วนของ loop
  2. ส่วนของการแสดง data

ส่วนของ loop

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr><tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

ส่วนของการแสดง data

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

โดยที่ทั้งสองส่วนนี้จะต้องอยู่ภายใน <table> … </table>

จากตัวอย่างของส่วนที่แสดง data อันดับแรก matColumnDef จะสื่อถึงชื่อ column ที่อยู่ในตัวแปร displayedColumns ที่เราได้กล่าวไปแล้วในตอนต้น มันจะต้องเขียนเหมือนกัน ไม่เช่นนั้นข้อมูลจะไม่แสดงและอาจก่อให้เกิด error

อันดับที่สอง element.isbn ตรงนี้ element คือตัวแปรที่ประกาศขึ้นมา (ใช้ชื่ออื่นก็ได้) เพื่อรับเอาค่าที่ถูก loop มาให้ในแต่ละรอบ ส่วน .isbn ก็คือชื่อ property ที่อยู่ใน T หรือก็คือ interface TableData ของ dataSource นั่นเองครับ

ดึงข้อมูลมาแสดงผล

เรามี backend แล้วแล้วก็มี RESTful APIs ที่สามารถดึงข้อมูลมาแสดงผลได้ดังนี้

localhost:8080/books
หน้าตาของข้อมูลที่ถูกเรียก

เริ่มด้วยการเขียน service ไปเรียกข้อมูล สร้าง folder ชื่อ services และไฟล์ชื่อ book.service.ts

src/app/services/book.service.ts

จากนั้นเรียกมาใช้ที่ไฟล์ app.component.ts

แล้วปรับปรุงหน้า app.component.html เป็นดังนี้

เพื่อนจะสังเกตเห็นว่ามี Page กับ Book models เพิ่มขึ้นมา ให้สร้าง folder ชื่อ models ด้านในประกอบด้วยไฟล์ page.ts และ book.ts

src/app/models/page.ts
src/app/models/book.ts

มีรายละเอียดแต่ละไฟล์ดังนี้

ไฟล์ page.ts

ไฟล์ book.ts

อย่าลืมว่าเราต้องการ proxy.conf.json ให้สร้างไว้ภายใน src folder เพื่อจะได้เรียก API ระหว่าง port 4200 กับ port 8080 ภายใต้ domain localhost เดียวกันนี้ได้

src/proxy.conf.json

ไฟล์ proxy.conf.json

จากนั้นรันได้แล้วครับ โดยกำหนด proxy ไปด้วย

ng serve --proxy-config proxy.conf.json

ผลลัพธ์

Angular Material: Paginator

มาถึงตัวพระของเราสักทีนะครับ นั่นคือการทำ pagination สำหรับ Angular Material นี้เขาก็ได้เตรียม Paginator component ให้ใช้ ใช้ดีมาก

มาถึงตรงนี้เพื่อนๆก็คงคุ้นเคยไฟล์ต่างๆเป็นอย่างดีแล้ว เช่นเคยเราต้องการ module แล้วก็ <mat-paginator>

เปิดไฟล์ app.module.ts เพิ่ม

import { MatPaginatorModule } from '@angular/material/paginator';...imports: [...MatToolbarModule,
MatTableModule,
MatPaginatorModule
...],

ไฟล์ app.component.html เพิ่ม <mat-paginator> ไว้ท้ายสุด

<mat-paginator [length]="length" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" (page)="changePage($event)"></mat-paginator>

รายละเอียดของ attributes ข้างต้นมีดังต่อไปนี้

  • length คือจำนวนข้อมูลทั้งหมด เช่น 20
  • pageSize คือจำนวนข้อมูลต่อหน้า เช่น 5
  • pageSizeOptions คือช่วงของข้อมูลที่ต้องการให้แสดง มีผลต่อ pageSize เช่น [5, 10, 30, 50]
  • page เป็น output ที่ส่ง event ออกมา ในที่นี้รับด้วยเมธอด changePage แล้วนำ event ที่ส่งออกมานั้นใส่เป็น argument

ทีนี้เปิดไฟล์ app.component.ts เลยจ้า

  1. ประกาศตัวแปรเพิ่ม
  2. กำหนดค่า length จาก API
  3. เมธอด changePage นำ PageEvent เข้ามา มี e.pageIndex ทำให้รู้ค่า offset ว่าอยู่หน้าไหน และ e.pageSize เปรียบเสมือน limit ให้เลือกข้อมูลเป็นจำนวนเท่านั้นตัวตามที่ได้กำหนดไว้

ประกาศตัวแปรเพิ่ม

length = 0;pageSize = 5;pageSizeOptions: number[] = [5, 10];

กำหนดค่า length จาก API

เมธอด changePage

ผลลัพธ์

ลองกำหนด pageSize ใหม่จากการเลือกตรง Items per page เป็น 10

ลองกดเครื่องหมาย > ก็จะเรียกมาอีก 10 ข้อมูล

ยัง ยังไม่จุใจ จริงๆ Book มันมี id แต่เราไม่เลือกนำมาแสดงด้วย (อยากซ่อน id ไว้) ในที่นี่เราต้องการลำดับของข้อมูลที่สามารถคำนวณได้ตาม page ที่เปลี่ยนไป

เพิ่มลำดับ

การเพิ่มลำดับนี้ก็คือการสร้าง column ใหม่ จะให้ชื่อว่า no โดยกำหนดลงไปใน displayedColumns

displayedColumns = ['no','isbn','name','authors','year','pages'];

จากนั้นสร้างอีกหนึ่ง column ชื่อ No ไว้ใน tempalte ด้วย

<ng-container matColumnDef="no"><th mat-header-cell *matHeaderCellDef mat-sort-header>No</th><td mat-cell *matCellDef="let element; let i = index">{{no + (i + 1)}}</td></ng-container>

แล้วกลับไปที่ app.component.ts กำหนดค่าเริ่มต้นให้ตัวแปร no เท่ากับศูนย์

no = 0;

ไปดูผลลัพธ์

ความตลกคือกดเครื่องหมาย > หรือ < แล้ว No ค่าของมันไม่เปลี่ยนตาม แบบนี้ก็ต้องสร้าง logic เพิ่มในเมธอด changePage ถูกต้องไหม

งดงาม

Angular Material: Sort Header

มาถึงอย่างสุดท้ายของบทความนี้แล้ว ก็คือการเรียงลำดับข้อมูล จาก มากไปหาน้อย หรือ น้อยไปหามาก ด้วยการกดที่หัว column

Angular Material จัดการเรื่องนี้ไว้แล้ว

ง่ายมาก หัวใจสำคัญมีอยู่แค่ 2 อย่าง

  1. ควบคุม table ด้วย matSort
  2. กำหนด mat-sort-header ไว้ในส่วนของ th ซึ่งเป็นหัวของ column ที่ต้องการให้ sort ได้

เริ่มเหมือนเดิม import module ก่อน

import { MatSortModule } from '@angular/material/sort';...imports: [...MatToolbarModule,
MatTableModule,
MatPaginatorModule,
MatSortModule
...],

จากนั้นปรับปรุง template ให้มี matSort และ mat-sort-header เชิญชม

ทีนี้ไปเพิ่มการผูก MatSort เข้ากับ dataSource ของ table โดยเริ่มจากประกาศตัวแปรชื่อ sort มีค่าเป็น ref ที่ผูกกับ template ไว้

@ViewChild(MatSort, { static: true }) sort: MatSort;

จากนั้นที่เมธอด ngOnInit ก็ว่า

this.dataSource.sort = this.sort;

เสร็จสิ้น

ดูผลลัพธ์

สรุป

จะเห็นได้ว่าเมื่อเราใช้ framework เช่น Angular และ Angular Material งานของเราก็จะเสร็จไวขึ้นโดยที่ตัว framework รับประกันความถูกต้องให้ตามวิธีการที่มันกำหนดไว้ งานเร็วสวยงามได้คุณภาพ

ปรบมือหน่อยนะถ้าคิดว่านี่เป็นประโยชน์

--

--