Angular 19 + PrimeNG ตอนที่ 9: Service และการเรียกใช้ RESTful API

บทความนี้เป็นส่วนหนึ่งของซีรีส์ Angular + PrimeNG 19 (CSR, non-standalone) เรียบเรียงจากคู่มืออบรมของ Wutthiphon Tassana — Wirabyte CO., LTD. — ตอนที่ 9/10

Service ใน Angular นั้นใช้สำหรับจัดการข้อมูลและการประมวลผลที่ไม่เกี่ยวข้องกับ UI โดยตรง เช่น การติดต่อ API (ทั้ง Restful/Web Socket หรืออื่น ๆ) กับเซิร์ฟเวอร์ , การจัดเก็บข้อมูลฝั่งผู้ใช้ (Local Storage / Session Storage) , การทำงานร่วมกับ RxJS เพื่อส่งข้อมูลหรือแจ้งอัปเดตให้ Component อื่น ๆ ที่ subscribe อยู่ โดยแนวคิดคือ ารแยก Business Logic ออกจาก Component ทำให้ Component มีหน้าที่แค่แสดงผล UI (Component ควรทำเฉพาะเรื่องของ UI ส่วน Service จัดการด้านข้อมูลทั้งหมด ทำให้โค้ดง่ายต่อการดูแลในระยะยาว)

การสร้างและใช้งาน Service

วิธีการสร้าง Service ใช้คำสั่ง

ng generate service <path/component name>
หรือ
ng g s <path/component name>

โดยเมื่อสร้างแล้วจะมี Folder ตามชื่อ Service ที่สร้างปรากฏขึ้นภายใต้ Folder src/app

เช่นสร้าง data Service

ng generate service services/data

หากสามารถสร้างได้ ก็จะขึ้นว่า CREATE Service

สังเกต: จะมี Folder services ถูกสร้างขึ้นก่อนตามด้วย ไฟล์ data.service.ts (+.spec)

โดยเมื่อสร้างมาก็จะได้ไฟล์ลักษณะประมาณนี้ โดยชื่อของ Class จะมาตามชื่อของ Service ที่เราตั้งตามด้วยคำว่า “Service”

import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class <ServiceName>Service {
constructor() { }
}

ตัวอย่าง Data Service

วิธีการเรียกใช้ใน Component

ให้ Import Service ที่ Component

จากนั้นเรียกใช้ที่ Constructor

จากนั้นก็สามารถเรียกใช้ Method ต่าง ๆ ใน Service ผ่านตัวแปรที่ประกาศไว้ที่ Constructor ได้เลย

this.<service>.<method>.?<service method action>

การเรียกใช้งาน RESTful API

วิธีการเรียกใช้ข้อมูลจาก API ใน Angular จะใช้ Module HTTPClient ซึ่งมีติดมากับ Angular อยู่แล้ว โดยให้ Import provideHttpClient ที่ app.module ก่อน จากนั้นจึงค่อยเรียกใช้ Module HttpClient ที่ Service

app.module.ts

ที่ไฟล์ Service ให้ Import Module HttpClient และ Observable จาก rxjs

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export class <ServiceName>Service {
constructor(private http: HttpClient) {}
}

จากนั้นให้ประกาศตัวแปร API_URL และ HTTP_OPTIONS เพื่อกำหนดเส้นทางและคุณลักษณะ API (สามารถประกาศที่ Service / Environment อื่น ๆ เพื่อให้สะดวกต่อการเรียกใช้)

const API_URL = '<API URL>';
const HTTP_OPTIONS = {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
};

เป็นการบอกว่า ให้ส่งข้อมูลไปหา API ในรูปแบบ JSON

คราวนี้ก็สามารถเรียกใช้ API ได้เลยผ่าน HttpClient โดยใน Service ให้เราทำการสร้าง Method แล้วเรียก API ดังนี้

<MethodName>(): Observable<any> {
return this.<HttpClient>.<method><any>(API_URL + '<sub path>',?{<body>}, HTTP_OPTIONS);
}

หากทราบประเภทข้อมูลที่จะ return กลับมาให้แทนที่ <any> ด้วย Interface ของข้อมูลที่จะ return กลับมาได้เลย

ตัวอย่าง (กรณีประกาศ HttpClient ให้เรียกผ่านตัวแปร http, คุณลักษณะ HTTP ใน HTTP_OPTIONS)

HTTP Methodวิธีเรียกใช้
GETthis.http..get(API_URL + ‘<sub path>’, HTTP_OPTIONS);
POSTthis.http.post(API_URL + ‘<sub path>’,{body}, HTTP_OPTIONS);
PUTthis.http.put(API_URL + ‘<sub path>’,{body}, HTTP_OPTIONS);
PATCHthis.http.patch(API_URL + ‘<sub path>’,{body}, HTTP_OPTIONS);
DELETEthis.http..delete(API_URL + ‘<sub path>’ + <var>, HTTP_OPTIONS);

ตัวอย่าง

วิธีเรียกใช้

ที่ Component ที่เราจะเรียกใช้งานให้ Import Service แล้วเรียกใช้ Service ผ่าน Method โดยใช้ method subscribe ของ Observable rxjs

this.<service>.<method>. subscribe({
next: (res) => {
// กระบวนการหลังจากได้ Response //
},
error: (err) => {
// กระบวนการหลังจากได้ Response  //
}
})

next: (res) => {} เมื่อ request สำเร็จและมีการ response status 200 ข้อมูลจะอยู่ในส่วนนี้ error: (err) => {} เมื่อ request error และมีการ response status 400/500 ข้อมูลจะมาส่วนนี้แทน

🧪 LAB: ทดลองเรียกข้อมูลจาก API มาแสดงผล

ทดลองเรียกข้อมูลจาก Restful API ที่กำหนดให้มาแสดงผล

[Get] https://learning.wutthiphon.space/wutthiphon/api/ users

ข้อมูลที่จะ Return กลับมาเป็นข้อมูลผู้ใช้ 20 คน เป็น Array[] ประกอบด้วย

  • birthday: string
  • full_name: string
  • gender: string
  • password: string
  • username: string

ให้เรียกข้อมูลแล้วเก็บข้อมูลเพื่อ Loop ด้วย *ngFor แสดงผลผ่าน UI

ทดสอบเรียกผ่าน Constructor เพื่อให้มีการโหลดทันทีที่มีการโหลดหน้า

ผลลัพธ์

ทดลองจัดให้อยู่ในรูปที่สวยงาม เช่นตาราง

Session Storage / Local Storage

บนเว็บเบราว์เซอร์สมัยใหม่ (Chrome, Firefox, Edge, Safari) มี Web Storage API ซึ่งใช้เก็บข้อมูลฝั่ง client แบบง่าย ๆ โดยแบ่งออกเป็น 2 ประเภทหลัก

  • Local Storage
  • เป็นพื้นที่เก็บข้อมูล ถาวร (persist) จนกว่าจะถูกลบ
  • เหมาะสำหรับเก็บข้อมูลที่ไม่ลับมาก
  • Session Storage
  • เป็นพื้นที่เก็บข้อมูล ชั่วคราว สำหรับ session ปัจจุบันของ Browser Tab
  • เมื่อปิด tab หรือ window ข้อมูลจะหาย

การใช้งาน Angular จะนิยมใช้ที่ Service โดยใช้ Method ในการดำเนินการต่าง ๆ โดยใช้ window.<local/session_storage> แบบเดียวกับ JavaScript ทั่วไป

วิธีการเรียกใช้สามารถ Import และเรียก Method ของ Service ผ่าน Component ได้เลย

this.tokenService.saveUsername('ADMIN');
console.log(this.tokenService.getUsername());
this.tokenService.clearUsername();

การ Update Component อื่นให้รับค่าใหม่

ใน Angular เราจะใช้ Subject / BehaviorSubject ของ rxjs ในการส่งค่าเมื่อเกิดการเปลี่ยนแปลง โดยหลักการคือให้ Component ติดตามตัวแปรไว้เมื่อเกิดการเปลี่ยนแปลง Service จะส่งข้อมูลใหม่ไปยัง Component ที่มีการติดตามไว้

โดยเริ่มจากให้ Import BehaviorSubject ที่ Service ที่ต้องการใช้งาน

import { BehaviorSubject } from 'rxjs';

จากนั้นสร้างตัวแปร BehaviorSubject โดยเริ่มต้นอาจเป็นค่าว่างไว้ก่อน

private <name> = new BehaviorSubject<string | null>(null);

กำหนดให้ส่งตัวแปรใหม่ไปยัง BehaviorSubject

this.<BehaviorSubject name>.next(‘<data>’);

สร้าง Method สำหรับให้ Component ติดตามไว้

public <MethodName>() {
return this.usernameSubject.asObservable();
}

ตัวอย่าง

เมื่อทำการเก็บ Username แล้วให้แสดงผลชื่อบน Header (Navbar)

token.service.ts

ทดลองเรียกใช้งานโดย Home Component จะเป็นการ Set ค่าแล้วให้แสดงผลที่ Navbar ของ Header

home.component.ts

home.component.html

template/header/header.component.ts

template/header/header.component.html

ผลลัพธ์

ก่อนคลิก “บันทึก Username” => ลองกรอกค่าแล้วบันทึก

เมื่อบันทึก => Navbar จะ Update และแสดงผลทันที (ในการใช้งานจริงบางกรณีอาจต้องใช้ ViewEncapsulation)

ที่ Console Log

ลองตรวจสอบตัวแปรผ่าน browser

เมื่อคลิก “ล้าง Username” => การแสดงผล Username บน Navbar จะหายไปตามเงื่อนไข

🧪 Final LAB: ทำหน้า Login เข้าสู่ระบบ

Concept: สร้าง Component login ใช้งานกับ path /login จากนั้นเขียนหน้าและระบบ login โดยให้มีการตรวจสอบการกรอกข้อมูลจากผู้ใช้ (Pre Validate) หากกรอก Username และ Password ไม่ครบให้มีข้อความแสดงบอก พร้อมกรอบสีแดงที่ Input หากกรอกครบแล้วคลิก Submit ให้ทำการส่งข้อมูลเพื่อทำการ Login โดยหาก Login สำเร็จให้มีการแสดงชื่อผู้ใช้พร้อมปุ่มออกจากระบบที่ Navbar แต่หากไม่สำเร็จให้แสดงข้อความ Error “ชื่อผู้ใช้งานหรือรหัสผ่านไม่ถูกต้อง”

Optional: Checkbox จดจำฉันให้เก็บข้อมูลใน localStorage โดยหาก Login สำเร็จให้เก็บ Username และ Password ชองผู้ใช้ไว้ เมื่อ User ออกจากระบบแล้วเข้าหน้า Login ให้ fill ข้อมูลใน Input อัตโนมัติ (พร้อมติ๊กว่าจดจำฉัน) หากนำออกหลัง Login สำเร็จให้ทำการลบค่าออก

Restful API สำหรับทดสอบการ Login

[POST] https://learning.wutthiphon.space/wutthiphon/api/auth/login/

ค่าที่ต้องส่งกรณีถูกต้อง

“username”: “user”

“password”: “P@ssw0rd”

**ทดลองการประยุกต์ใช้การแสดงผลกับ Prime NG โดยศึกษาผ่าน Document ของ Prime NG ได้เลยโดยจะถือเป็นคะแนนพิเศษ เช่นเมื่อ Login ผิดพลากให้ขึ้นข้อความและ toast และ dialog แจ้งเตือน การจัด form / layout ของหน้า ใช้ grid 12 ช่อง flex etc. การใช้ Input สำเร็จรูป p-inputtext p-password p-checkbox

[*** LAB นี้ไม่มีเฉลย ***]

ตัวอย่าง

กรณี ติ๊กจดจำฉัน

หลังเข้าสู่ระบบ (Design ด้วยตนเองได้เลย)

Wutthiphon Tassana
Wutthiphon Tassana