เนื้อหาในตอนต่อไปนี้ จะมาดูในเรื่องของการตรวจสอบสิทธิ์การเข้าใช้งาน route path ยกตัวอย่างเช่น
เรามี route path ที่ จำกัดสิทธิ์เฉพาะผู้ใช้ อย่างหน้าจัดการของส่วน Admin เป็นต้น โดยเมื่อมีการเรียก
path ที่ถูกจำกัดสิทธิ์ ก่อนที่จะแสดง path นั้น จะมีการตรวจสอบว่า ผู้ใช้นั้นได้สิทธิ์ในการเข้าถึงแล้วหรือไม่
ในเนื้อหา จะไม่ได้ลงรายละเอียดถึงขั้นตอนการได้สิทธิ์การเข้าถึง แต่จะสมมติผลลัพธ๋ที่ได้อย่างง่าย
เพื่อให้เห็นแนวทางในการใช้งาน route guard
เหตุผลหรือเงื่อนไขที่พอเป็นไปได้ในการใช้งาน route guard
- ผู้ใช้อาจจะไม่ได้รับสิทธิ์ในการเข้าถึง route path ของ component นั้นๆ
- หรือผู้ใช้จำเป็นต้องมีการล็อกอินเพื่อเข้าใช้งานก่อน
- หรือมีการดึงข้อมูลบางอย่างก่อนที่จะแสดง component นั้น อย่างที่เราใช้ resole ในเนื้อหาที่ผ่านมา
- หรือต้องยืนยันการบันทึกข้อมูลที่มีการเปลี่ยนแปลง ก่อนออกจากหน้านั้นๆ
- หรือต้องการให้ผู้ใช้ยืนยัน ว่าจะไม่มีการบันทึกการเปลี่ยนแปลงข้อมูล ก่อนออกจากหน้านั้น
เพื่อจัดการกับเงื่อนไขที่อาจจะเป็นไปได้ข้างต้น เราเพิ่มการกำหนด guard เข้าไปในส่วนของการตั้งค่า route path
ค่าที่ได้จากการกำหนด route guard จะใช้เป็นตัวกำหนดการแสดงของ router โดย
ถ้ามีค่าเป็น true การเปลี่ยนแปลง route path ก็จะดำเนินการต่อ ไปยัง path ที่ต้องการ
ถ้ามีค่าเป็น false จะไม่มีการเปลี่ยนแปลง route path หรืออยู่ที่เดิม
อย่างไรก็ตาม กรณีคืนค่าเป็น false เราสามารถกำหนดให้ router ลิ้งค์ยังไปหน้าต่างๆ ในแต่ละขั้นตอน
ได้ตามความเหมาะสม
การคืนค่าของ guard อาจจะเกิดขั้นในทันที (synchronously) หรือเกิดขึ้นตามมากายหลัง (asynchronously) ก็ได้
กรณีเกิดขึ้นภายหลังก็เช่น guard อาจจะมีการถามเพื่อยืนยันจากผู้ใช้ หรือ อาจจะบันทึกข้อมูลไปยัง server ก่อน
หรือแม้แต่ กำลังทำการดึงข้อมูลจาก server อยู่ เหล่านี้เป็นต้น ก็ทำให้การคืนค่าของ guard เกิดขึ้นภายหลัง และด้วยรูป
แบบข้างต้น ค่าที่ return กลับมาอาจจะเป็นได้ทั้ง Observable<boolean> หรือ Promise<boolean> ดังนั้นผู้ใช้จะต้อง
รอจนกว่า Observable จะได้ค่าเป็น true หรือ false ก่อน
Router สามารถใช้งาน Guard Interface ต่อไปนี้ ตามรูปแบบการทำงาน
CanActivate สำหรับเป็นตัวกลางพิจารณาการเข้าถึง route path
CanActivateChild สำหรับเป็นตัวกลางพิจารณาการเข้าถึง route path ย่อย
CanDeactivate สำหรับเป็นตัวกลางพิจารณาการออกจาก route path ปัจจุบัน
Resolve ดึงข้อมูลที่ต้องการก่อนที่เนื้อหาใน route path จะแสดง
CanLoad สำหรับเป็นตัวกลางพิจารณาการเข้าถึง route path โดยจะมีการโหลด module ของ route path ที่จะเข้าใช้งาน ภายหลัง
สำหรับเนื้อหาเกี่ยวกับการใช้งาน resole interface ดึงข้อมูลก่อนที่เนื้อหาใน route path จะแสดง เรามีตัวอย่าง
ไปแล้วในบทความตามลิ้งค์ด้านล่าง สามารถย้อนไปทบทวนได้
ใช้งาน Observable paramMap และ component reuse ใน Angular ตอนที่ 6 http://niik.in/846
https://www.ninenik.com/content.php?arti_id=846 via @ninenik
เราสามารถใช้งาน Guard ในแต่ละระดับของการกำหนด routing ได้ โดยลำดับการตรวจสอบของ router จะดูในส่วนของ
CanDeactivate และ CanActivateChild guard ก่อน เริ่มจากด้านในที่สุดของ route ย่อยไล่ออกมายังด้านนอกสุด
จากนั้นก็จะตรวจสอบในส่วนของ CanActivate guard จาก route นอกสุดไล่ลงไปตาม route ย่อยด้านใน และถ้าในกรณีมีการ
โหลดแบบ asynchronously ส่วนของ CanLoad guard จะตรวจสอบก่อนที่ module จะถูกโหลด
ตอนนี้ได้อธิบายการทำงาน หลักการใช้งานคร่าวๆ ไปแล้ว ต่อไปจะเป็นส่วนวิธีการใช้งาน จะขอไปแบบเร็ว และข้ามในบาง
ตัวอย่างของโค้ดไป เพื่อไม่ให้เนื้อหายาวจนเกินไป
เริ่มต้น เราจะสร้างหน้า admin ขึ้นมา เพื่อใช้ในการจัดการ user และ product ให้สอดคล้องกับตัวอย่าง ที่ผ่านๆ
มาด้วยคำสั่งต่อไปนี้ตามลำดับ
C:\projects\simplerouter>ng g module admin --routing
C:\projects\simplerouter>ng g c admin/admin --flat
C:\projects\simplerouter>ng g c admin/admin-center --flat
C:\projects\simplerouter>ng g c admin/manage-users --flat
C:\projects\simplerouter>ng g c admin/manage-products --flat
ต่อไปสร้างในส่วนของ login ให้สำหรับ admin เราจะแยกเป็น module เลย เพื่อจะได้เห็นเป็นสัดส่วนชัดเจน เช่นเดียว
กับ module อื่นๆ พร้อมกับสร้าง component หน้าล็อกอินด้วย โดยใช้คำสั่ง ดังนี้
C:\projects\simplerouter>ng g module login --routing
C:\projects\simplerouter>ng g c login/login --flat
ตอนนี้เราเตรียม module และ component ต่างๆ ไว้แล้ว จะได้ไฟล์ และโฟลเดอร์ตามรูปด้านล่าง
เรายังไม่สนใจในส่วนที่สร้างขึ้นมาในตอนนี้ สิ่งที่ต้องทำต่อไปคือ การสร้าง service สำหรับใช้ในการล็อกอิน และล็อกเอาท์
ในที่นี้เราจะสร้างเป็นแบบการจำลองการล็อกอินและล็อกเอาท์ และอีก service ก็คือ guard service ทำหน้าที่เหมือนเป็นด่าน
กำแพงกั้นการเข้าถึงส่วนที่เราจำกัดสิทธิ์ โดย guard service ต้องใช้งานร่วมกับ service ที่ใช้ในการล็อกอิน เพื่อเช็คว่า
ผู้ใช้นั้น ได้สิทธิ์ในการเข้าใช้งานผ่านการล็อกอินแล้วหรือไม่ เราจะใช้ชื่อ service ทั้งสองดังนี้
- AuthService สำหรับเป็น Authorized Service
- AuthGuardService สำหรับเป็น Guard Service
โดยทั้งสองไฟล์นี้ เราจะสร้างไว้ที่ root ของ App หรือที่เดียวกับ AppModule ไฟล์ ให้สร้างด้วยคำสั่งต่อไปนี้
C:\projects\simplerouter>ng g service auth
C:\projects\simplerouter>ng g service auth-guard
เราจะได้ไฟล์ auth.service.ts และ auth-guard.service.ts ใน root ของ App ให้ปรับโค้ดในไฟล์ ตามลำดับดังนี้
คำอธิบายการทำงานแสดงในโค้ด
ไฟล์ auth.service.ts
import { Injectable } from '@angular/core'; // import ในส่วนที่จะใช้งานกับ Observable import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/do'; import 'rxjs/add/operator/delay'; @Injectable() export class AuthService { public isLoggedIn:boolean = false; // กำหนดสถานะล็อกอินเริ่มต้นเป็น false public redirectUrl: string; // กำหนดตัวแปรสำหรับเก็บ url ที่จะลิ้งค์ไป constructor() { } // ฟังก์ชั่นจำลองการล็อกอิน คืนค่าเป็น Observable login(): Observable<boolean> { // เมื่อล็อกอิน return Observable.of(true) // ให้คืนค่า true .delay(3000) // หลังจาก delay 3 วินาที .do(val => this.isLoggedIn = true); // เมื่อถึงเวลาให้กำหนดค่าสถานะเป็น true } // ฟังก์ชั่นจำลองการล็อกเอาท์ logout(): void { // ให้ค่าสถานะล็อกอินเป็น false this.isLoggedIn = false; } }
การกำหนด CanActivate และ CanActivateChild
ไฟล์ auth-guard.service.ts
import { Injectable } from '@angular/core'; // import ส่วนที่จะใช้งาน guard เช่น CanActivate, CanActivateChild เป็นต้นมาใช้งาน import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router'; // import service ที่เช็คสถานะการล็อกอินมาใช้งาน import { AuthService } from './auth.service'; @Injectable() export class AuthGuardService { // inject AuthService และ Router constructor( private authService: AuthService, private router: Router) {} // กำนหนด guard ในส่วนของการใช้งานกับ canActivate canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { console.log('canActivate run'); let url: string = state.url; // เก็บ url ที่พยายามจะเข้าใช้งาน // จะผ่านเข้าใช้งานได้เมื่อ คืนค่าเป็น true โดยเข้าไปเช็คค่าจากคำสั่ง checkLogin() return this.checkLogin(url); // คืนค่าการตรวจสอบสถานะการล็อกอิน } // กำนหนด guard ในส่วนของการใช้งานกับ canActivateChild ส่วนนี้จะใช้กับ path ของ route ย่อย // ถ้าเข้าผ่าน route path ย่อย guard จะเข้ามาเช็คในส่วนนี้ก่อน กลับไปเช็คในส่วนของ canActivate() canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { console.log('canActivateChild run'); // จะเข้าใช้งานได้เมื่อ คืนค่าเป็น true โดยจะใช้ค่าจากการเรียกใช้คำสั่ง canActivate() return this.canActivate(route, state); } // ฟังก์ชั่นเช็คสถานะการล็อกอิน รับค่า url ที่ผู้ใช้พยายามจะเข้าใช้งาน checkLogin(url: string): boolean { // ถ้าตรวจสอบค่าสถานะการล็อกอินแล้วเป็น true ก็ให้คืนค่า true กลับอกไป if (this.authService.isLoggedIn) { return true; } // แต่ถ้ายังไม่ได้ล็อกอิน ให้เก็บ url ที่พยายามจะเข้าใช้งาน สำหรับไว้ลิ้งค์เปลี่ยนหน้า this.authService.redirectUrl = url; // redirectUrl เป็นตัวแปรที่อยู่ใน authService // ลิ้งค์ไปยังหน้าล็อกอิน เพื่อล็อกอินเข้าใช้งานก่อน this.router.navigate(['/login']); return false; // คืนค่า false กรณียังไม่ได้ล็อกอิน } }
ตอนนี้เราได้ service ที่ใช้สำหรับตรวจสอบการล็อกอินเข้าใช้งาน และ service สำหรับป้องกันการเข้าถึงหน้าที่
เราจำกัดสิทธิไว้แล้วเรียบร้อยแล้ว ส่วนต่อไป ก็จะเป็นหน้าล็อกอิน ที่ผู้ใช้จะถูก redirect หรือลิ้งค์กลับมาถ้ายัง
ไม่ได้ทำการล็อกอินเข้าใช้งาน สำหรับตัวอย่างการล็อกอินเข้าใช้งานของเรา จะเป็นเพียงการคลิกที่ปุ่มล็อกอิน
ก็สามารถเข้าใช้งานได้แล้ว เป็นการจำลองการทำงานเท่านั้น แต่รูปแบบสามารถนำไปประยุกต์ได้
จากคำสั่งในตอนต้น ที่เราได้สร้าง module ,route module และ component ของหน้าล็อกอินไว้แล้ว
ให้เราปรับไฟล์ต่างๆ ตามนี้ คำอธิบายแสดงในโค้ด
ไฟล์ login-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LoginComponent } from './login.component'; import { AuthService } from '../auth.service'; // กำหนด route path ให้กับ login component const loginRoutes: Routes = [ { path:'login', component:LoginComponent } ]; @NgModule({ imports: [RouterModule.forChild(loginRoutes)], exports: [RouterModule], providers:[ // กำนหด providers AuthService // เนื่งอจากเราจะมีการใช้งาน AuthService ] }) export class LoginRoutingModule { }
ไฟล์ login.module.ts
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { LoginRoutingModule } from './login-routing.module'; import { LoginComponent } from './login.component'; @NgModule({ imports: [ CommonModule, LoginRoutingModule ], declarations: [LoginComponent] }) export class LoginModule { }
ไฟล์ login.component.html
<p>login works!</p> <div>{{ loginMessage }}</div> <form > <div style="width:50%;"> <div> <label>Username</label> </div> <div> <input type="text" class="form-control" name="username" value=""> </div> <div> <label>Password</label> </div> <div> <input type="password" class="form-control" name="password" value=""> </div> <br> <p> <button class="btn btn-sm btn-success" (click)="login()" type="button">Login</button> <button class="btn btn-sm btn-danger" (click)="cancel()" type="button">Cancel</button> </p> </div> </form>
ส่วนของหน้าล็อกอิน เราจะสร้างฟอร์มง่ายๆ มีให้กรอก username และ password และปุ่ม login กับ cancel
ในการสมมติการทำงาน เราไม่ต้องกรอกอะไร ให้คลิกที่ปุ่ม login ก็จะไปเรียกคำสั่ง login ใน LoginComponent
ให้ทำงาน ผลลัพธ์หน้าล็อกอินจะเป็นดังรูปด้านล่าง
เมื่อคลิกที่ปุ่ม Login ก็จะมีข้อความคำว่า Trying to log in ... ขึ้นอยู่ประมาณ 3 วินาที จากนั้นก็จะลิ้งค์ไปยังหน้า admin
ต่อไปเป็นส่วนของหน้า admin ที่เราจะมีการจำกัดสิทธิ์การเข้าใช้งาน โดยไฟล์ต่างๆ ที่เราได้มาจากการสร้างไว้แล้วในตอน
ต้นนั้น เราจะแก้ไขเพียง ไม่กี่ไฟล์เท่านั้น หลังๆ ก็จะประกอบด้วย admin-routing.module.ts , admin.component.html,
admin-center.component.html, admin-center.component.ts ส่วนไฟล์อื่นๆ ก็แทบจะไม่มีการเปลี่ยนแปลงใดๆ จะแสดง
เป็นข้อความ ค่าเริ่มต้นเช่น manage-users works! ตามแต่ละหน้า component
ไฟล์ admin.module.ts ที่ได้จากการ generate จะได้ประมาณนี้
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdminRoutingModule } from './admin-routing.module'; import { AdminComponent } from './admin.component'; import { AdminCenterComponent } from './admin-center.component'; import { ManageUsersComponent } from './manage-users.component'; import { ManageProductsComponent } from './manage-products.component'; @NgModule({ imports: [ CommonModule, AdminRoutingModule ], declarations: [ AdminComponent, AdminCenterComponent, ManageUsersComponent, ManageProductsComponent ] }) export class AdminModule { }
จะเห็นว่าในไฟล์ admin module เราจะไม่มีการเรียกใช้งาน providers แต่จะเรียกผ่า AdminRoutingModule แทน
ไฟล์ admin-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AdminComponent } from './admin.component'; import { AdminCenterComponent } from './admin-center.component'; import { ManageUsersComponent } from './manage-users.component'; import { ManageProductsComponent } from './manage-products.component'; // import AuthGuardService ที่เราสร้างไว้เข้ามาใช้งาน import { AuthGuardService } from '../auth-guard.service'; const adminRoutes: Routes = [ { path:'admin', component:AdminComponent, // กำหนด guard service ให้กับ canActivate ที่ root ของ module ย่อย canActivate: [AuthGuardService], children:[ { path:'', component:AdminCenterComponent, // กำหนด guard service ให้กับ canActivateChild ให้กับ child ของ module ย่อย canActivateChild: [AuthGuardService], children:[ { path:'users', component:ManageUsersComponent }, { path:'products', component:ManageProductsComponent } ] } ] } ]; @NgModule({ imports: [RouterModule.forChild(adminRoutes)], exports: [RouterModule], providers:[ AuthGuardService // ใช้งาน guard service ที่เราสร้างไว้ ] }) export class AdminRoutingModule { }
ในไฟล์ admin-routing.module.ts จะเห็นว่าทั้ง route path ของ root ใน module ย่อยมีการป้องกันการเข้าถึงด้วย
AuthGuardService โดยใน root จะเรียกใช้งานผ่าน canActivate นั้นหมายความว่า เราจะสามารถเข้ามายัง /admin
ได้ก็ต้องผ่านการตรวจสอบจาก AuthGuardService ก่อน โดยถ้าคืนค่ากลับมาเป็น true ก็จะสามารถเข้าใช้งานได้
ในขณะเดียวกัน AuthGuardService ยังทำการตรวจสอบการเข้าใช้งาน child ของ root ใน module ย่อยผ่าน
canActivateChild ถ้าคืนค่าเป็น true ก็จะสามารถเข้าใช้งานใน /admin/products และ /admin/users ได้
ก่อนจะไปต่อที่ไฟล์ admin ในส่วนอื่นๆ เราย้อนกลับมาเพิ่มลิ้งค์หน้า admin ให้กับ root หลักของ App ที่ไฟล์
app.component.html ดังนี้ก่อน
ไฟล์ app.component.html
<nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" routerLink="/home" routerLinkActive="active">Home</a> <a class="navbar-brand" routerLink="/users" routerLinkActive="active">User</a> <a class="navbar-brand" routerLink="/products" routerLinkActive="active">Product</a> <a class="navbar-brand" [routerLink]="[{ outlets: { popup: ['contact'] } }]" routerLinkActive="active">Contact Us</a> <a class="navbar-brand" routerLink="/admin" routerLinkActive="active">Admin</a> </div> </div> </nav> <div class="container"> <br> <br> <h1>{{title}}</h1> <router-outlet></router-outlet> <router-outlet name="popup"></router-outlet> </div><!-- /.container -->
เราเพิ่มบรรทัดที่ 16 เข้ามา ผลลัพธ์จะแสดงเมนู Admin ตามรูปด้านล่าง
กลับมาที่ไฟล์ในส่วนของหน้า admin กันต่อ ส่วนต่อมาคือ ไฟล์ admin.component.html ซึ่งเป็นหน้าแสดง
ผลของ root ของ module ย่อยที่ชื่อ admin ให้เราปรับเป็นดังนี้
ไฟล์ admin.component.html
<p> admin works! </p> <router-outlet></router-outlet>
โดยเพิ่ม router outlet เข้ามา ส่วนไฟล์ admin.component.ts เราไม่มีการทำงานใดๆ ในไฟล์นี้ จึงไม่มีการแก้ไข
ต่อไปให้ทำเช่นเดียวกันกับไฟล์ admin-center.component.html แต่เนื่องจากไฟล์นี้เราจะเป็นหน้าหลักของ admin
โดยจะมีการเพิ่มเมนูสำหรับเข้าถึง path ย่อยใน admin ด้วย ให้กำหนดไฟล์เป็นดังนี้
ไฟล์ admin-center.component.html
<p> admin-center works! </p> <button [routerLink]="['/admin']" routerLinkActive="btn-info" [routerLinkActiveOptions]="{ exact: true }" class="btn btn-sm btn-default" >Home</button> <button [routerLink]="['/admin/users']" routerLinkActive="btn-info" class="btn btn-sm btn-default" >Manage User</button> <button [routerLink]="['/admin/products']" routerLinkActive="btn-info" class="btn btn-sm btn-default" >Manage Product</button> <button class="btn btn-sm btn-danger" (click)="logout()" type="button">Log Out</button> <router-outlet></router-outlet>
โค้ดข้างต้นเราพยายามขึ้นบรรทัดใหม่เพื่อให้มองโค้ดง่ายขึ้น ข้างต้นเป็นการกำหนดการเชื่อมโยงลิ้งค์ทั่วไป
ผ่านการแสดงผลทาง router outlet สังเกตในบรรทัดที่ 3 มีส่วนใหม่ที่เพิ่มเข้ามาคือ การกำหนด
[routerLinkActiveOptions]="{ exact: true }"
การกำหนดค่านี้ เพื่อระบุว่า ปุ่มนี้จะแสดง css class ชื่อ btn-info ที่ใช้เป็น active ก็ต่อเมื่อมี path เป็น /admin เท่านั้น
ซึ่งถ้าไม่กำหนดส่วนนี้ จะทำให้เวลาเราเข้าไปยัง /admin/products จะทำให้ปุ่มนี้ ยัง active ดังนั้น เพื่อไม่ให้ปุ่ม active
ถ้าอยู่ใน path ย่อยของ /admin ก็ให้ใส่ directive ข้างต้นเข้าไปด้วย
สำหรับปุ่มสุดท้ายที่แสดงจะเป็นปุ่ม logout เพื่อยกเลิกหรือล้างค่าการล็อกอิน เมื่อกดล็อกเอา ก็จะลิ้งค์กลับไปที่หน้า
ล็อกอิน เพื่อขอสิทธิ์การเข้าใช้งานใหม่ก่อน
ในไฟล์ admin-center.component.html นี้เรามีการใช้งาน router outlet ที่เป็น child ซ้อนเข้ามาอีก ดังนั้น เมื่อเราคลิก
ที่เมนู /admin/products หรือ /admin/users ข้อมูลของ path ทั้งสองก็จะมาแสดงใน outlet นี้
ต่อไฟล์เป็นส่วนของไฟล์ admin-center.component.ts อย่างที่กล่าวข้างต้นว่า เราจะมีการเรียกใช้คำสั่ง logout ดังนั้น
เราจึงต้องคำสั่งในส่วนนี้เพิ่มเข้ามา ดังนี้
ไฟล์ admin-center.component.ts
import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { AuthService } from '../auth.service'; @Component({ //selector: 'app-admin-center', templateUrl: './admin-center.component.html', styleUrls: ['./admin-center.component.css'] }) export class AdminCenterComponent implements OnInit { constructor( private authService:AuthService, private router:Router) { } // คำสั่ง สำหรับเรียกให้ AuthService ทำคำสั่งล็อกเอาท์อีกที logout() { this.authService.logout(); this.router.navigate(['/login']); // หลังจากล็อกอาท์ให้ไปที่หน้าล็อกอิน } ngOnInit() { } }
ตัวอย่างหน้า Admin ที่จะแสดงเมื่อทำการล็อกอินแล้ว จะได้ผลลัพธ์เป็นดังนี้
สามารถดูตัวอย่างผลลัพธ์ได้จาก demo ด้านล่าง เพิ่มเติม ตอนนี้ได้รู้จัก และทำความเข้าใจกับการใช้งาน guard ในส่วน
ของ canActivate และ canActivateChild ไปแล้ว เนื้อหาในตอนหน้า เราจะมาดูในส่วนที่เหลือกันต่อ รอติดตาม