PHP Ionic Angular HTML5 AJAX Javascript CSS MySQL jQuery Forum


ใช้งาน Guard สำหรับ Asynchronous Routing ใน Angular ตอนที่ 11

14 December 2017 By Ninenik Narkdee
canload preloading asynchronous routing angular router

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ canload preloading asynchronous routing angular router



เนื้อหาต่อไปนี้ จะเป็นตอนสุดท้ายเกี่ยวกับการใช้งาน routing เราจะมาดูในเรื่องของการกำหนด route
แบบ asynchronous ซึ่งจะช่วยเพิ่มประสิทธิภาพการทำงานให้ app  ทั้งนี้ก็เพราะว่า เมื่อ app มีขนาดใหญ่ขึ้น 
การโหลด module ของการใช้งานส่วนต่างๆ ก็จะมีผลให้มีการทำงานที่หนักหรือใช้เวลาโหลดนานขื้น 
    การใช้งานแบบ asynchronous จะทำให้เรากำหนดการโหลดเฉพาะ module แบบ lazy load ได้ 
หรือก็คือโหลด module เมื่อมีการเรียกใช้งาน อย่างกรณีเช่น หน้า admin นั้น จะมีผู้ใช้แค่ไม่กี่คน หรือคนเดียว 
ที่สามารถที่จะล็อกอินเข้าไปใช้งานได้ ดังนั้น การโหลด module ในส่วนของ admin หลังจากมีการล็อกอินแล้ว 
ก็จะช่วยให้ app ของเราทำงาน ได้เร็วขึ้น เพราะหากเป็นผู้ใช้ทั่วไป ที่ไม่ได้เข้าใข้งานในส่วนของ admin ตัว app ก็จะโหลดเฉพาะส่วนที่จะใช้งานเท่านั้น
 
    ข้อดีของการใช้งานแบบ lazy load 
  •     เราสามารถโหลดส่วนการใช้งานพิเศษเฉพาะเมื่อมีการร้องขอจากผู้ใช้ได้
  •     ช่วยลดเวลาในการโหลดหรือใช้งาน app สำหรับผู้ใช้ทั่วไปได้
  •     ถึงมีการเพิ่มการใช้งาน lazy load เข้ามา ก็ไม่ทำให้ขนาดของไฟล์โดยรวมเพิ่มขึ้นตามไปด้วย
 
จะเห็นว่า เพื่อรองรับการใช้งานแบบ lazy load สำหรับ app เราจึงควรสร้างส่วนต่างๆ ออกเป็น module แยกย่อย
อย่างที่เราได้ทำให้ดูเป็นตัวอย่างมาแล้ว เมื่อแต่ละส่วนถูกแยกออกเป็น module ก็จะทำให้เราสะดวกขึ้นในการ
กำหนดหรือเรียกใช้งานแบบ lazy load
    ย้อนกลับมาพูดถึง AdminModule เมื่อเรามีการเปิดใช้งาน app ตัว module นี้ก็จะถูกโหลดเข้ามาใช้งานพร้อมกับ
module อื่นๆ แม้ว่าผู้ใช้นั้นๆ จะไม่ได้เป็น admin หรือ user เฉพาะก็ตาม เราจะเห็นว่า AdminModule นี้เหมาะที่จะทำเป็น 
lazy load
    แต่ในบาง module เช่น AppModule นั้น จำเป็นต้องโหลดมาใช้งานในตอนเริ่มต้นเสมอ หรือบาง module ที่เมื่อเปิด app
เข้ามาแล้า มีการแสดงข้อมูลของ module นั้นๆ เลย ก็ไม่เหมาะที่จะใช้งานแบบ lazy load ทั้งนี้ก็เพื่อให้การทำงาน
ของ app เป็นไปอย่างราบรื่นและเหมาะสม เพราะถ้าเปิดมาแล้ว ต้องรอโหลดในส่วนของเนื้อหาเริ่มต้นอีก ก็จะทำให้ได้รับ
ประสบการณ์การใช้งาน app ที่ไม่ดีเท่าที่ควร

 

การกำหนด Lazy Loading ให้กับ Route

    เราจะมาทำตัวอย่างในส่วนของ AdminModule ให้เราเปิดไฟล์ admin-routing.module.ts แก้ไข path
admin เป็น path ว่าง
 
ไฟล์ admin-routing.module.ts บางส่วน
 
const adminRoutes: Routes = [
  {
    path:'',  // แก้ไขส่วนนี้จากเดิม path:'admin' เป็น path:''
    component:AdminComponent,
    canActivate: [AuthGuardService], 
    children:[
      {
        path:'',
        component:AdminCenterComponent,
        canActivateChild: [AuthGuardService],
        children:[
          {
            path:'users',
            component:ManageUsersComponent
          },
          {
            path:'products',
            component:ManageProductsComponent
          }
        ]
      }
    ]
  }
];
 
การกำหนด path เป็นค่าว่างนั้น router จะมองเสมือนเป็นกลุ่มของ route ผู้ใช้ยังสามารถเข้าใช้งานผ่าน path /admin 
โดยยังเรียกใช้งานผ่าน AdminComponent ที่มีการใช้งาน route ย่อยด้านในได้เหมือนเดิม โดยต้องไปกำหนด
ค่าเพิ่มเติมในไฟล์ app-routing.module.ts ซึ่งเป็นส่วนจัดการ routes หลัก
    
ต่อไปให้มากำหนดในไฟล์ app-routing.module.ts โดยกำหนด path admin โดยมีการใช้งาน loadChildren property
เพื่อกำหนดให้ AdminModule จะถูกดึงมาใช้งานทีหลัง ดังนี้
 
ไฟล์ app-routing.module.ts บางส่วน
 
const appRoutes: Routes = [
  {
    path: 'contact',
    component: ContactComponent,
    outlet: 'popup'
  },  
  {
    path: 'admin',
    loadChildren: 'app/admin/admin.module#AdminModule',
  },  
  { path: 'home', component: HomeComponent },
  { path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  { path: '**', component: PagenofoundComponent }
];
 
บรรทัดที่ highlight คือส่วนที่เราเพิ่มเข้ามา โดยมีการกำหนด path ของไฟล์ admin.module.ts จาก root
เป็น app/admin/admin.module แล้วคั่นด้วยเครื่องหมาย # และตามด้วยชื่อ module เป็น AdminModule
 
จากนั้นให้เราแก้ไข AppMoudle ในไฟล์ app.module.ts โดยให้ตัดในส่วนของการ import AdminModule ในส่วน
ด้านบน และ ตัดในส่วนของ AdminModule ใน NgModule import array ดังนี้
 
ไฟล์ app.module.ts บรรทัดที่ comment คือส่วนที่ต้องตัดออก
 
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
  
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { ProductModule } from './product/product.module';
import { UserModule } from './user/user.module';
//import { AdminModule } from './admin/admin.module';
import { LoginModule } from './login/login.module';
import { GuestbookModule } from './guestbook/guestbook.module';
 
import { HomeComponent } from './home/home.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { ContactComponent } from './contact/contact.component';

import { DialogService } from './dialog.service';
  
  
@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    PagenofoundComponent,
    ContactComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    ProductModule,
    UserModule,
   // AdminModule,
    LoginModule,
    GuestbookModule,
    AppRoutingModule
  ], 
  providers: [
    DialogService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
 
ในตอนนี้ เมื่อมีการเปิด app ขึ้นมาใช้งาน จะไม่มีการโหลดไฟล์ และ AdminModule เข้ามาใช้งานในตอนเริ่มต้น
แต่ถ้ามีการเปลี่ยน path ไปยัง /admin ตัว loadChildren property ที่เรากำหนด จะทำการโหลดไฟล์ และ
AdminModule เข้ามาใช้งาน โดยจะทำเพียงครั้งเดียว ในครั้งแรกที่เรียก path มาใช้งานเท่านั้น เมื่อมีการใช้งานแล้ว
ส่วนของ routes ย่อยต่างๆ ที่กำหนดใน admin-routing.module.ts ก็จะสามารถเรียกใช้งานได้
    จะเห็นว่า ถ้าไม่ได้เรียกใช้งาน /admin path ตัว app ก็จะไม่มีการโหลดส่วนของ AdminModule มาใช้งานนั่นเอง
 
    สมมติถ้าเป็นผู้ใช้ทั่วไป ไม่ได้เป็น admin รู้ path ของส่วน admin เช่น รู้ /admin path เมื่อมีการเข้าไปยัง path นั้น
app ก็จะโหลด AdminModule มาใช้งานในทันทีด้วย ดังนั้นเราจะดูวิธีการจำกัดการโหลด module ผ่านการใช้งาน guard
ตัวสุดท้าย นั้นก็คือ CanLoad Guard

 

การใช้งาน CanLoad Guard

    อย่างที่สมมติเหตุการณ์ไปข้างต้น ถ้ารู้ /admin path และเมื่อเรียก path นั้นขึ้นมา ถึงแม้จะมีการเปลี่ยนไปหน้าล็อกอิน
เพื่อขอสิทธิ์การเข้าใช้งานหน้า admin แต่ตัว AdminModule ก็ถูกโหลดมาใช้งานแล้ว และเพื่อจำกัดการโหลด module
โดย module จะโหลดก็ต่อเมื่อผ่านการตรวจสอบสิทธิ์การเข้าใช้งานหรือเป็น admin ที่ล็อกอินแล้วเท่านั้น เราจึงนำในส่วน
ของ canload guard มาใช้งาน ดังนี้
 
ให้เราเปิดไฟล์ auth-guard.service.ts แล้วแก้ไขเพิ่มส่วนของการใช้งาน CanLoad 
 
ไฟล์ auth-guard.service.ts
 
import { Injectable } from '@angular/core';
// import ส่วนที่จะใช้งาน guard เช่น CanActivate, CanActivateChild เป็นต้นมาใช้งาน
import {
  CanActivate, Router,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  NavigationExtras,
  CanActivateChild,
  CanLoad,Route
} 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);
  }

  // กำหนด guard ในส่วนของการใช้งานกับ canLoad ส่วนนี้จะใช้กับ path ที่มีการโหลด module 
  // แบบ asynchronous หรือ lazy load ผ่าน loadChildren property
  canLoad(route: Route): boolean {
    // ใช้ Route ในการอ้างอิงหรือดึงข้อมูล url
    let url = `/${route.path}`;
    // จะโหลด module เมื่อมีค่าเป็น true หรือเป็นผู้ใช้ admin ที่ล็อกอินแล้วเท่านั้น
    return this.checkLogin(url); 
  }

  // ฟังก์ชั่นเช็คสถานะการล็อกอิน รับค่า url ที่ผู้ใช้พยายามจะเข้าใช้งาน
  checkLogin(url: string): boolean {
    // ถ้าตรวจสอบค่าสถานะการล็อกอินแล้วเป็น true ก็ให้คืนค่า true กลับอกไป
    if (this.authService.isLoggedIn) { return true; }
    
    // แต่ถ้ายังไม่ได้ล็อกอิน ให้เก็บ url ที่พยายามจะเข้าใช้งาน สำหรับไว้ลิ้งค์เปลี่ยนหน้า
    this.authService.redirectUrl = url; // redirectUrl เป็นตัวแปรที่อยู่ใน authService

    // จำลองค่า session id
    let sessionId = 123456789;

    // กำหนด ค่าเพิ่มเติมให้กับ NavigationExtras ที่จะมีการส่งค่าไปพร้อมกับ url 
    // ประกอบไปด้วย query params และ fragment
    let navigationExtras: NavigationExtras = {
      queryParams: { 'session_id': sessionId },
      fragment: 'anchor'
    };

    // ลิ้งค์ไปยังหน้าล็อกอิน เพื่อล็อกอินเข้าใช้งานก่อน มีการส่งค่า query params
    this.router.navigate(['/login'],navigationExtras);
    return false; // คืนค่า false กรณียังไม่ได้ล็อกอิน
  }    

}
 
บรรทัดที่ 9 import  CanLoad และ Route มาใช้งาน โดยตัว Route ที่ import เข้ามานั้น จะใช้สำหรับดึงข้อมูล
หรือเรียกใช้ข้อมูลเกี่ยวกับ route จะเห็นได้จากการอ้างอิง url ที่จะเข้าใช้งาน ผ่าน /${route.path} ในบรรทัดที่ 42
บรรทัดที่ 40 - 45 ทำคำสั่งเมื่อ canload ถุกเรียกใช้งาน โดยเก็บ url ที่ผู้ใช้พยายามเข้าใช้งานไว้ และเมื่อผู้ใช้
ล็อกอินเข้าใช้งานสำเร็จ ก็จะคืนค่า true ออกมา ทำให้ app ทำการโหลด AdminModule เข้ามาใช้งาน
    
ต่อไปให้กำหนดการเรียกใช้งาน canLoad property ในไฟล์ app-routing.module.ts ดังนี้
 
ไฟล์ app-routing.module.ts
 
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
 
import { HomeComponent } from './home/home.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { ContactComponent } from './contact/contact.component';

import { CanDetactivateGuardService } from './can-detactivate-guard.service';
import { AuthGuardService } from './auth-guard.service';
 
const appRoutes: Routes = [
  {
    path: 'contact',
    component: ContactComponent,
    outlet: 'popup'
  },  
  {
    path: 'admin',
    loadChildren: 'app/admin/admin.module#AdminModule',
    canLoad: [AuthGuardService]
  },  
  { path: 'home', component: HomeComponent },
  { path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  { path: '**', component: PagenofoundComponent }
];
 
@NgModule({
  imports: [
    RouterModule.forRoot(
      appRoutes,
      { enableTracing: false } // <-- debugging purposes only set true
    )
  ], 
  exports: [
    RouterModule
  ],
  providers:[
    CanDetactivateGuardService,
    AuthGuardService
  ]
})
export class AppRoutingModule { }
 
เรา import AuthGuardService เข้ามาใช้งานในบรรทัดที่ 9 และ 42 และเรียกใช้งาน AuthGuardService ทำงาน
เมื่อ canLoad property ถูกเรียกใช้งาน ในบรรทัดที่ 20 
    การทำงานในบรรทัดที่ 20 อธิบายอย่างง่ายก็คือจะมีการโหลดหรือจะสามารถโหลดไฟล์ และ module ชื่อ 
AdminModule ได้ ก็ต่อเมื่อ ทำคำสั่ง canload() ผ่าน AuthGuardService ที่เราสร้างขึ้น การทำงานของคำสั่ง
canload() ก็คือถ้าผู้ใช้งานล็อกอิน ให้คืนค่ากลับมาเป็น true ก็จะทำการโหลดไฟล์ และ module มาใช้งาน เป็นต้น
 
 

การใช้งาน Preloading หรือ Background loading

    เราได้รู้แล้วว่า AppModule จะโหลดทันทีที่ app เริ่มทำงาน และ AdminModule จะโหลดที่หลังแบบ lazy load
เมื่อผู้ใช้เรียกใช้งานผ่าน /admin path แต่สำหรับ preloading นั้น จะต่างออกไป ลักษณะการทำงานก็จะอยู่กึ่งกลาง
ระหว่างรูปแบบการโหลด module ของ AppModule และ AdminModule
    ลองพิจารณาในส่วนของ ProductModule ประกอบ เราจะพบว่า module product นั้น ไม่ใช้ module แรกที่ผู้ใช้งาน
เห็นหรือไม่ใช่ module แรกที่มีการแสดงทันทีที่ app เปิดขึ้นมา เราสามารถที่จะทำการ preloading module นี้โด้
การ preloading ก็คือทำการโหลด module นี้เบื้องหลังให้เสร็จทัน ก่อนที่ผู้ใช้จะคลิกไปยัง path ของ /products 
หรือพูดแบบง่ายๆ ก็คือการทยอยโหลดส่วนต่างๆเข้ามาทีหลัง เหมาะสำหรับกรณีใช้งานกรณีต้องโหลด app ผ่าน 
การใช้งานอินเทอร์เน็ตที่่ค่อนข้างช้า หรือสัญญาณไม่เสถียร
 
เราสามารถกำหนดการใช้งาน preloading ผ่าน route ได้ใน 2 เงื่อนไขดังนี้คือ
    1. ใช้งาน preloading กับ module ที่ใช้งานแบบ lazy load ทุกตัว ทั้งหมด
    2. ไม่ใช้งาน preloading กับ module แต่ให้ใช้เป็นแบบ lazy load แทน (ปกติค่าเริ่มต้นจะเป็นค่านี้)
 
อย่าสับสนระหว่าง preloading กับ lazy load ทบทวนย้ำอีกครั้ง 
    lazy loading ก็คือ module ที่กำหนดแบบ lazy load จะมีการโหลด module นั้นมาทีหลัง โดยจะโหลด module
ก็ต่อเมื่อมีการเรียกไปยัง path ของ module นั้นๆ ส่วน preloading ก็คือ module ที่กำหนดแบบ lazy load เช่นกัน
แต่โหลดเองอัตโนมัติก่อนที่ผู้ใช้งานจะเรียกไปยัง path ของ module นั้น
    ดังนั้นถ้าเราจะใช้งาน preloading เราก็ต้องกำหนดการใช้งานให้กับ module ที่กำหนดแบบ lazy load ทั้งหมดนั่นเอง
ก่อนทดสอบการใช้งานแบบ preloading กับ module lazy load ทั้งหมด จะขอบปรับให้เรียกใช้งานส่วนของ ProductModule
ให้เป็นแบบ lazy load อีก module หนึ่ง วิธีการก็คล้ายกับในส่วนของการกำหนดใน admin แต่ใน ProductModule นี้
เราไม่ต้องใช้งาน canload guard
 
ไฟล์ app-routing.module.ts บางส่วน
 
const appRoutes: Routes = [
  {
    path: 'contact',
    component: ContactComponent,
    outlet: 'popup'
  },  
  {
    path: 'admin',
    loadChildren: 'app/admin/admin.module#AdminModule',
    canLoad: [AuthGuardService]
  },  
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule'
  },    
  { path: 'home', component: HomeComponent },
  { path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  { path: '**', component: PagenofoundComponent }
];
 
เรามีการใช้งาน lazy load สำหรับ AdminModule และ ProductModule
ตอนนี้เราได้ 2 module ที่ใช้งานแบบ lazy load และเราจะทำการกำหนดให้มีการ preloading หรือโหลด
module อัตโนมัติ โดยทำงานแบบ background หรืออยู่เบื้องหลัง โดยปรับไฟล์ app-routing.module.ts
เรียกใช้งาน PreloadAllModules ดังนี้
 
ไฟล์ app-routing.module.ts 
 
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
 
import { HomeComponent } from './home/home.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { ContactComponent } from './contact/contact.component';

import { CanDetactivateGuardService } from './can-detactivate-guard.service';
import { AuthGuardService } from './auth-guard.service';
 
const appRoutes: Routes = [
  {
    path: 'contact',
    component: ContactComponent,
    outlet: 'popup'
  },  
  {
    path: 'admin',
    loadChildren: 'app/admin/admin.module#AdminModule',
    canLoad: [AuthGuardService]
  },  
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule'
  },    
  { path: 'home', component: HomeComponent },
  { path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  { path: '**', component: PagenofoundComponent }
];
 
@NgModule({
  imports: [
    RouterModule.forRoot(
      appRoutes,
      { 
        enableTracing: false,
        preloadingStrategy: PreloadAllModules 
      } 
    )
  ], 
  exports: [
    RouterModule
  ],
  providers:[
    CanDetactivateGuardService,
    AuthGuardService
  ]
})
export class AppRoutingModule { }
 
บรรทัดที่ 2 เรามีการ import PreloadAllModules เข้ามาใช้งาน และเรียกใช้ผ่านรูปแบบการกำหนดค่าเพิ่มเติม
ในบรรทัดที่ 40 เป็น preloadingStrategy: PreloadAllModules 
 
จากการกำหนดการใช้งาน preloading ข้างต้น AdminModule และ ProductModule จะมีการโหลดแบบ preloading
แต่เนื่องจาก AdminModule มีการกำหนด Canload เข้าไป จีงทำให้มีการโหลดเฉพาะ ProductModule ส่วน AdminModule
ก็จะโหลดแบบ lazy load มาทีหลังเมื่อผู้ใช้ล็อกอินผ่านระบบ admin แล้วเท่านั้น ถ้าเราต้องการให้ AdminModule ใช้งานแบบ
preloading เราก็สามารถตัดในส่วนของ การกำหนด canload ออกไปได้
 
 

การใช้งาน Preloading แบบกำหนดเอง

    ในกรณีที่เราไม่ต้องการ preloading module ที่ใช้งานแบบ lazy load ทั้งหมดด้วยการกำหนด PreloadAllModules ใน
preloadingStrategy ข้างต้น เราสามารถเลือกที่จะทำการ preloading สำหรับเลือก module ที่ต้องการ ตามความเหมาะสม
ที่เราเห็นควรเองได้ ด้วยวิธีดังนี้
    ให้เราสร้างไฟล์ service สำหรับกำหนด preloadingStrategy แบบกำหนดเองใน root app ด้วยคำสั่ง
 
C:\projects\simplerouter>ng g service selective-preloading-strategy
 
แล้วกำหนดโค้ดในไฟล์ selective-preloading-strategy.service.ts ดังนี้
 
ไฟล์ selective-preloading-strategy.service.ts
 
import { Injectable } from '@angular/core';
import 'rxjs/add/observable/of';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class SelectivePreloadingStrategyService {
  public preloadedModules: string[] = [];

  constructor() { }

  preload(route: Route, load: () => Observable<any>): Observable<any> {
    // เช็ค route ที่มีการกำหนด data และ data preload
    if (route.data && route.data['preload']) {
      // เพื่อ route path ที่จะใช้งาน  ไว้ใน preloaded module array
      this.preloadedModules.push(route.path);

      // แสดง route path ใน console
      console.log('Preloaded: ' + route.path);

      return load(); // โหลด module นั้น
    } else {
      return Observable.of(null); // คืนค่า null
    }
  }  

}
 
จากนั้นให้เราเอา SelectivePreloadingStrategyService นี้ไปใช้งานใน app-routing.module.ts ดังนี้
 
ไฟล์ app-routing.module.ts
 
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
 
import { HomeComponent } from './home/home.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { ContactComponent } from './contact/contact.component';

import { CanDetactivateGuardService } from './can-detactivate-guard.service';
import { AuthGuardService } from './auth-guard.service';

import { SelectivePreloadingStrategyService } from './selective-preloading-strategy.service';
 
const appRoutes: Routes = [
  {
    path: 'contact',
    component: ContactComponent,
    outlet: 'popup'
  },  
  {
    path: 'admin',
    loadChildren: 'app/admin/admin.module#AdminModule',
    canLoad: [AuthGuardService]
  },  
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule',
    data: { preload: true }
  },    
  { path: 'home', component: HomeComponent },
  { path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  { path: '**', component: PagenofoundComponent }
];
 
@NgModule({
  imports: [
    RouterModule.forRoot(
      appRoutes,
      { 
        enableTracing: false,
        preloadingStrategy: SelectivePreloadingStrategyService 
      } 
    )
  ], 
  exports: [
    RouterModule
  ],
  providers:[
    CanDetactivateGuardService,
    AuthGuardService,
    SelectivePreloadingStrategyService
  ]
})
export class AppRoutingModule { }
 
บรรทัด 11 เรา import SelectivePreloadingStrategyService เข้ามาใช้งาน
และกำหนดการใช้งานในบรรทัดที่ 43 
ส่วนบรรทัดที่ 27 นั้น เรามีการกำหนดส่วนของ data โดยให้ ค่า preload เป็น true ค่า preload ที่เรากำหนดในส่วน
ของ data ใน product path นี้ เป็นการบอกว่า เราต้องการให้ทำการ preloading ให้กับ ProductModule
โดย SelectivePreloadingStrategyService จะไล่ดูการกำหนดค่าของ route path ที่มีการกำหนดค่า preload ดังกล่าว
ก็จะทำการ preloading module นั้นๆ ในตัวอย่างเราทำการ preloading เฉพาะ ProductModule เท่านั้น
สำหรับบรรทัดที่ 53 เรากำหนด SelectivePreloadingStrategyService ไว้ใน providers array เพื่อจะเรียกใช้ service
นี้ใน app โดยเราลองให้แสดงค่า preloading module ที่เราได้เลือกในหน้า home ผ่าน home.compoennt.ts ดังนี้
 
ไฟล์ home.component.html
 
<p>
  home works!
</p>
Preloaded Modules
<ul>
  <li *ngFor="let module of modules">{{ module }}</li>
</ul>
 
ไฟล์ home.component.ts
 
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute }       from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { SelectivePreloadingStrategyService } from '../selective-preloading-strategy.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  public modules: string[];

  constructor(
    private route: ActivatedRoute,
    private preloadStrategy: SelectivePreloadingStrategyService
  ) {
    
  }

  ngOnInit() {
    this.modules = this.preloadStrategy.preloadedModules;
  }

}
 
ดูผลลัพธ์ได้ที่ demo 1 ด้านล่าง
 
ก่อนจบเนื้อหาการใช้งาน routing ขอเพิ่มอีกส่วนคือการย้าย path หนึ่งไปยังอีก path
ตัวอย่างเช่น เราต้องการเปลี่ยนจาก /users เปลี่ยนเไปเป็น /members เราสามารถใช้งาน การกำหนด
redirectTo เพื่อให้เปลี่ยนเส้นทางไปยัง path ใหม่ที่ต้องการได้ดังนี้
 
ไฟล์ user-routing.module.ts บางส่วน
 
const userRoutes: Routes = [
  {
    path:'users',
    component:UserCenterComponent,
    children:[
      {
        path: '',
        component: UserListComponent,
        children:[
          {
            path:':id',
            component:UserDetailComponent,
            resolve: {
              user: UserDetailResolverService
            }            
          }
        ] // end children
      }
    ] // end children
  }
];
 
เปลี่ยนเป็น
 
const userRoutes: Routes = [
  {
    path:'users',redirectTo:'members'
  },
  {
    path:'members',
    component:UserCenterComponent,
    children:[
      {
        path: '',
        component: UserListComponent,
        children:[
          {
            path:':id',
            component:UserDetailComponent,
            resolve: {
              user: UserDetailResolverService
            }            
          }
        ] // end children
      }
    ] // end children
  }
];
 
สังเกตการณ์ทำงานการย้าย path ที่ demo 1 คลิกส่วนของ menu user 
การย้าย path ด้วยวิธีข้างต้น ทำให้เราสะดวก ไม่ต้องไปทำการแกไขการกำหนด path ในไฟล์ที่เกี่ยวข้อง
โดยเฉพาะถ้า app มีขนาดใหญ่ขึ้น 
 
ตอนนี้เราได้รู้จักการใชงานต่างๆ มากมายเกี่ยวกับ Routing และ Naviagtion ใน Angular อาจมีรายละเอียดปลีกย่อย
บ้างบางอย่างที่เราต้องไปดูเพิ่มเติม แต่ในส่วนการใช้งานหลักๆ ของบทความก็ค่อนข้างครอบคลุม
เนื้อหาในตอนหน้า เราจะมาดูในเรื่องของการใช้งาน httpClient ส่วนที่ค่อนข้างสำคัญในการใช้งานข้อมูล และการเชื่อม
โยงกับฝั่ง backend หรือฝั่ง server รอติดตาม
 










เนื้อหาที่เกี่ยวข้อง



Tags:: canload preloading angular router asynchronous routing






อย่าลืมกด Like กด Share เป็นกำลังใจ ในการสร้างบทความใหม่ๆ น่ะครับ


URL สำหรับอ้างอิง