iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
1
Modern Web

從零開始的點餐系統,Google好棒棒系列 第 22

[Day22] Angular 主要概念 - 路由

路由在網頁是非常常見的功能,在 Angular 也有屬於自己的路由系統 (Angular 真的好多功能都是內建!)。Angular 路由系統是藉由 component 的替換,來決定當下畫面要顯示什麼。Angular 是屬於 SPA (Single Page Application),所以換頁的動作會在前端完成,在這裡並不會跟伺服器端請求。

路由功能演練

建立項目清單功能的 component

接續上次的程式碼,我們需要另外的 component 讓我們可以換頁,如下圖所示,目標是希望點擊管理菜單後(藍色框框),可以轉跳到項目清單的頁面,但這裡先不真的實作項目清單的細節。

所以我的在之前建立的 product module 的資料夾下,輸入以下指令,建立需要的資料夾與 component (這些資料夾的用處可參考之前的文章)

cd ./src/app/product 
mkdir containers components services models
touch index
ng g c ./containers/product-list

設定路由路徑

由於我們預計項目清單的功能可能會越來越複雜,所以在這裡設計這個模組會是懶載入。首先開啟 product-routing.module.ts 檔案,設定路由的路徑 (第 5 行),以及對應的 component (第 6 行),與設定提供給當前路由的 data 物件的 Observable (第 7 行,用來顯示在 header 的標題,後面會再提到)

// product-routing.module.ts 
// ...省略
const routes: Routes = [
  {
    path: '',
    component: ProductListComponent,
    data: { moduleName: '項目清單' },
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class ProductRoutingModule {}

在 merchant-routing.module.ts 裡也是一樣的設置方式

// merchant-routing.module.ts 
// ...省略
const routes: Routes = [
  {
    path: '',
    component: MerchantListComponent,
    data: { moduleName: '商家菜單' },
  },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
export class MerchantRoutingModule {}

而在 app-routing.module.ts 就比較不一樣了。在 AppComponent 我們會先載入 LayoutComponent (第 6 行),在 LayoutComponent 裡面去置換 merchant module 與 product module 裡的 component,這裡的語法是懶載入的寫法,兩個 module 的路由在剛剛已經定義好了(第 9~12 行與第 16~18 行),此外,第 20、23 行是萬用字元路徑,就是當沒有匹配路徑時,會重新導向到指定路徑。

因為懶載入的關係,先前匯入 AppModule 的 MerchantModule 可以拿掉了

// app-routing.module.ts 
// ...省略
const routes: Routes = [
 {
   path: '',
   component: LayoutComponent,
   children: [
     {
       path: 'merchant',
       loadChildren: () =>
         import('./merchant/merchant.module').then(
           (mod) => mod.MerchantModule
         ),
     },
     {
       path: 'product/:merchantId',
       loadChildren: () =>
         import('./product/product.module').then((mod) => mod.ProductModule),
     },
     { path: '**', redirectTo: 'merchant' },
   ],
 },
 { path: '**', redirectTo: '' },
];
@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule],
})
export class AppRoutingModule {}

設定 router-outlet

當路徑都設好後,接下來要改 html 的部分,就是利用 router-outlet tag 在 html 內排版。我們先修改 app.component.html,這裡到時候會帶入剛剛設定的 LayoutComponent

<app-nav class="mat-elevation-z6"></app-nav>
<router-outlet></router-outlet>

所以,我們在往 layout.component.html 設定,在這裡也是利用 router-outlet 加到在 html 內,另外還用 Template reference variables 存入 outlet 物件(第 6 行),與用 activate 事件綁定,當路由在轉換時會呼叫 setModuleName (第 7 行)

<div class="sidenav-inner-content">
  <app-header [headText]="moduleName"></app-header>
  <main class="sidenav-body-content">
    <app-sidenav></app-sidenav>
    <router-outlet
      #routerOutlet="outlet"
      (activate)="setModuleName(routerOutlet)"
    ></router-outlet>
  </main>
  <app-footer></app-footer>
</div>

setModuleName 方法,會傳來把 outlet 物件裡的 data (剛剛在上面設定 product 與 merchant 路由路徑時帶的 data 物件)
,指定給 moduleName 屬性,由於 HeaderComponent 有屬性綁定 moduleName,所以當畫面轉換時, Header 會依 data 的值一起改變。

// layout.component.ts
// ...省略
export class LayoutComponent implements OnInit {
 moduleName;
 constructor() {}

 ngOnInit(): void {}

 setModuleName(outlet: RouterOutlet): void {
   const moduleName = outlet.activatedRouteData.moduleName;
   this.moduleName = moduleName;
 }
}

這裡的 LayoutModule 之前沒匯入 RouterModule,因為會用到router-outlet 所以需要匯入。

設定 routerLink,觸發轉跳頁面

接下來要修改 merchant-item.component.html,在管理菜單的按鈕加上routerLink,並提供相對於現在位置要到達到路徑(第 5 行)。5這樣就大功告成了。

<!-- ...省略 -->
  <mat-card-actions>
    <a
      mat-stroked-button
      [routerLink]="['..', 'product', merchant.id]"
      (click)="$event.stopPropagation()"
    >
      管理菜單
    </a>
    <button
      mat-icon-button
      class="card-delete-btn"
      (click)="deleteMerchant(merchant.id)"
    >
      <mat-icon>delete</mat-icon>
    </button>
  </mat-card-actions>
 <!-- ...省略 -->

結語

今天說完了 router 的概念了,也是非常簡單的說明而已,事實上還有很多很深的實作,不過目前的專案這樣算是夠用了。完整的範例程式碼。下一篇會講用 Angular 的 http client 來發送 request。


上一篇
[Day21] Angular 主要概念 - 表單功能
下一篇
[Day23] Angular 主要概念 - http 呼叫
系列文
從零開始的點餐系統,Google好棒棒30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言