[從 0 開始的 Angular 生活]No.38 實作一個 Angular Router 切換元件頁面(一)

前言

今天是進入公司的第三天,為了能盡快投入專案與成為團隊可用的戰力,我正在努力啃官方文件學習 Angular 的知識,所以這一篇文章主要是記錄我如何閱讀官方文件後,實作這個非常基本的、帶導航的網頁應用。

需求

需求大概是這樣的:

  • 開一個新的 Angular 專案,並且一開始選擇加入 Router 功能
    • 根元件是 AppComponent ,然後下方有三個子元件分別是
      • page1
      • page2
      • page3
    • 可以在 AppComponent 內點擊連結切換到這三個頁面

參考文件:

建立環境

輸入 ng new ngRouterDemo 建立新專案,並直接選擇要使用 Router 。

觀察檔案結構

這次選擇加入 Router 後,發現 app 資料夾內多了 app-routing.module.ts

1
2
3
4
5
6
7
8
9
10
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

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

然後 app.module.ts 中也把 app-routing.module.ts 這隻檔案給引入了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

對照一下有無加入 Router 的部分

app.module

除此之外就是 app 資料夾內多了 app-routing.module.ts 這隻檔案。

運行結果

沒有什麼明顯的變化。

分別建立三個元件

輸入指令建立本次範例用的元件

  • page1
  • page2
  • page3

ng g c page1

配置路由

因為是選擇使用 Router 的模式,所以 Angular CLI 預設幫我們加入了 router-outlet 標籤,這代表路由切換後的畫面都會在這個標籤裡面呈現。

以下引用自官方說明:
RouterOutlet 是一個來自路由模組中的指令,它的用法類似於元件。 它扮演一個佔位符的角色,用於在範本中標出一個位置,路由器將會把要顯示在這個出口處的元件顯示在這裡。
有了這份配置,當本應用在瀏覽器中的 URL 變為 /heroes 時,路由器就會匹配到 path 為 heroes 的 Route,並在宿主檢視中的 RouterOutlet 之後顯示 HeroListComponent 元件。

因為要點擊連結後透過路由配置切換到該元件,所以必須先設置路由器連結 (Router links):

對 Appcomponent 進行 Template 上的調整

1
2
3
4
5
6
7
8
9
10
11
12
13
<h1>點擊以下連結切換元件</h1>
<ul>
<li>
<a routerLink="/page1" routerLinkActive="active">Page1</a>
</li>
<li>
<a routerLink="/page2" routerLinkActive="active">Page2</a>
</li>
<li>
<a routerLink="/page3" routerLinkActive="active">Page3</a>
</li>
</ul>
<router-outlet></router-outlet>

  • RouterLink
    • a 標籤上的 RouterLink 指令讓路由器得以控制這個 a 元素,這裡的導航路徑是固定的,因此可以把一個字串賦給 routerLink(”一次性”繫結)。
    • 這後面接的就是實際上網址列顯示的路徑
  • RouterLinkActive
    • 在每個 a 標籤上,你會看到一個 RouterLinkActive 的屬性繫結,像是 routerLinkActive="..."
    • 等號右邊可以填入包含一些用空格分隔的 CSS 類名,當這個連結啟用時,路由器將會把它們加上去
      • 並在處於非活動狀態時移除

為了方便辨識效果,所以也加入 .active 的樣式吧

1
2
3
.active{
color: red;
}

這邊要注意的是 CSS 樣式要寫在 Appcomponent 內。

註冊路由器與路由定義

要使用路由的話,必須要把要使用的元件 import 進來,並且在 routes 陣列內配置它們,陣列內傳入一個物件,而物件內可以傳入參數:

  • path - 切換到這個元件的路徑
  • component - 切換的元件名稱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';

const routes: Routes = [
{ path: 'page1', component: Page1Component },
{ path: 'page2', component: Page2Component },
{ path: 'page3', component: Page3Component },
];

@NgModule({
imports: [
RouterModule.forRoot(routes, { enableTracing: true })
],
exports: [RouterModule]
})
export class AppRoutingModule { }

當我們定義好路由陣列 routes 並把它傳給 RouterModule.forRoot() 方法後:

  • 它會返回一個模組,其中包含配置好的 Router 服務提供商,以及路由庫所需的其它提供商。

一旦啟動了應用 Router 就會根據當前的瀏覽器 URL 進行首次導航。

存檔,試著運行看看。

page1

page2

這樣基礎的 Angular 路由範例就完成了!

而因為我們有在 RouterModule.forRoot() 把 enableTracing 打開,所以當切換路由時時可以看到一些額外訊息:

設置萬用字元與預設路由

雖然我們基礎的範例完成了但還不夠好,因為:

  • 如果在網址列隨意輸入會跳出錯誤
    • 而使用者是這麼白目
  • 需要設定一組預設路由,也就是使用者初次進入網頁時會顯示的畫面

設置萬用字元

新增一個萬用字元路由來攔截所有無效的 URL 並處理它們。 萬用字元路由的 path 是兩個星號(**),它會匹配任何 URL。 當路由器匹配不上以前定義的那些路由時,它就會選擇這個路由。

萬用字元路由可以導航到自訂的 “404 Not Found” 元件,也可以重定向到一個現有路由。

特別要注意的是:
路由器使用先匹配者優先的策略來選擇路由,萬用字元路由是路由配置中最沒有特定性的那個,因此務必確保它是配置中的最後一個路由。

因此修改 app-routing.module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';

const routes: Routes = [
{ path: 'page1', component: Page1Component },
{ path: 'page2', component: Page2Component },
{ path: 'page3', component: Page3Component },
{ path: '**', component: Page1Component },
];

@NgModule({
imports: [
RouterModule.forRoot(routes, { enableTracing: true })
],
exports: [RouterModule]
})
export class AppRoutingModule { }

胡亂輸入路徑

像這樣,永遠確保萬用字元在最後一組路由就可以了,而且也不會跳錯誤。

設置預設路由

與設置萬用字元時差不多,由於路由是有順序性的,因此應該其放在萬用字元路由的前一個。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1/page1.component';
import { Page2Component } from './page2/page2.component';
import { Page3Component } from './page3/page3.component';

const routes: Routes = [
{ path: 'page1', component: Page1Component },
{ path: 'page2', component: Page2Component },
{ path: 'page3', component: Page3Component },
{ path: '', redirectTo: 'page2', pathMatch: 'full' },
{ path: '**', component: Page1Component },
];

@NgModule({
imports: [
RouterModule.forRoot(routes, { enableTracing: true })
],
exports: [RouterModule]
})
export class AppRoutingModule { }

為了方便辨識,我將預設路由設置為 page2 ,也就是預期當使用者初次進入網站時會看到這個畫面。

重定向路由需要一個 pathMatch 屬性,來告訴路由器如何用 URL 去匹配路由的路徑,否則路由器就會報錯。

在本範例中路由器應該只有在完整的 URL 等於 ‘’ 時才選擇 Page2Component 元件,因此要把 pathMatch 設定為 ‘full’。

可以觀察下方的 log ,發現一開始如果網址都沒輸入時,會自動跳轉到 page2

順序錯誤的場合

前面提到路由器使用先匹配者優先的策略來選擇路由,所以順序很重要,如果把萬用字元的順序稍微挪動,如:

1
2
3
4
5
6
7
const routes: Routes = [
{ path: 'page1', component: Page1Component },
{ path: 'page2', component: Page2Component },
{ path: 'page3', component: Page3Component },
{ path: '**', component: Page1Component },
{ path: '', redirectTo: 'page2', pathMatch: 'full' },
];

路由順序調換

因為匹配到的路由會變成萬用字元的路由,因此就不會跳轉到 page2 了。

小結

透過實作這個非常基本的、帶導航的網頁應用學到了如何:

  • 載入路由庫
  • 在根元件的 Template 中新增一個導覽列,導覽列中有一些 A 標籤、routerLink 指令和 routerLinkActive 指令
  • 在根元件的 Template 中新增一個 router-outlet 指令,頁面將會被顯示在那裡
  • 用 RouterModule.forRoot 配置路由器模組
  • 使用萬用字元路由來處理無效路由
  • 當應用在空路徑下啟動時,導航到預設路由

  • GitHub - 範例原始碼

之後會嘗試實作路由守衛的部分。

0%