Angular 預設是一個單頁應用程式(SPA),為了實現不同頁面內容的切換,提供了強大的路由功能。透過路由,使用者可以根據網址(URL)的變化,動態顯示不同的元件,就像在多個頁面之間切換一樣,但實際上不需要重新載入整個網頁。這就是 client-side routing 的特點。
建立路由#
可以在 app.routes.ts 中透過建立 Routes 陣列來定義路由設定,每個路由物件包含以下主要的屬性
path:路由的路徑,對應 URL 的一部分component:當路由匹配時要顯示的元件
export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'tasks', component: TasksComponent }]建立完路由後,需要在 main.ts 中使用 provideRouter 來註冊路由設定,這樣應用程式才能識別並處理路由。
bootstrapApplication(AppComponent, { providers: [provideRouter(routes)]})最後需要在應用程式的根元件模板中加入 router-outlet,來顯示根據路由設定所對應的元件內容。路由切換時,會在這個標籤內動態載入對應的元件。
需在元件中引入 RouterOutlet,才能在模板中使用
<router-outlet>標籤。
import { RouterOutlet } from '@angular/router';@Component({ ... imports: [RouterOutlet] ...})<router-outlet />路由導航#
使用 router-link 指令來建立導航連結,點擊後會改變 URL 並觸發路由切換。
需要引入 RouterLink 指令,才能在模板中使用
routerLink屬性。
import { RouterLink } from '@angular/router';@Component({ ... imports: [RouterLink] ...})<a routerLink="/tasks">任務列表</a>使用 .. 來導航到上一層父路由
queryParams:用來設定查詢參數
<!-- 實際網址會變成 /users?ref=dashboard --><a [routerLink]="['..']" [queryParams]="{ ref: 'dashboard' }">返回使用者列表</a>routerLinkActive 指令,用來為當前路由匹配的連結添加 CSS 類別,通常用於標示當前頁面。
需要引入 RouterLinkActive 指令,才能在模板中使用
routerLinkActive屬性。
import { RouterLinkActive } from '@angular/router';@Component({ ... imports: [RouterLinkActive] ...})<a routerLink="/tasks" routerLinkActive="active">任務列表</a>需要在元件中使用程式導航時,可使用 Router 來直接導航到指定的路由。
navigate:Router的方法,用來導航到指定的路由。
export class SomeComponent { private router = inject(Router);
goToTasks() { this.router.navigate(['/tasks']); }}若是提交表格後,想要禁止使用者使用瀏覽器的返回按鈕回到前一頁,可以使用 replaceUrl 選項來取代當前的歷史紀錄條目。
this.router.navigate(['/'], { replaceUrl: true });動態路由#
可以在路由路徑中使用 :parameterName 來定義動態路由參數。
:後面的參數名稱,可以自訂,但要與取得參數時使用的名稱一致。
{ path: 'tasks/:taskId', <domain>/tasks/1 component: TaskDetailComponent}在使用動態路由時,繫結連結需要傳入參數值時,可以使用字串拼接或陣列語法。
<a [routerLink]="'/tasks/' + task.id">{{ task.title }}</a><a [routerLink]="['/tasks', task.id]">{{ task.title }}</a>而在元件中取得路由參數或查詢參數有兩種方式:
- 使用 input 可以取得路由參數或查詢參數
需要在 provideRouter 時使用
withComponentInputBinding,來啟用 input 綁定路由參數的功能。
bootstrapApplication(AppComponent, { providers: [provideRouter(routes,withComponentInputBinding())]})export class TaskDetailComponent { taskId = input<string>(); // 取得路由參數 taskId query = input<string | null>(); // 取得查詢參數 query // 例如: /tasks/1?query=angular}- 使用
ActivatedRoute服務
paramMap:用來取得路由參數的 Observable 物件,可觀察路由參數的變化。
export class TaskDetailComponent { private activatedRoute = inject(ActivatedRoute);
this.activatedRoute.paramMap.subscribe({ next: (params) => { // 取得路由參數 taskId conole.log(params.get('taskId')); // 取得查詢參數 query console.log(params['query']); } });}巢狀路由#
可以在路由設定中定義子路由,來建立巢狀的路由結構。
{ path: 'user/:userId', component: UserComponent, children: [ { path: 'tasks, component: TaskDetailComponent } ]}也需要在父元件的模板中加入 <router-outlet>,來顯示子路由對應的元件。
<h2>任務列表</h2><router-outlet></router-outlet>在巢狀路由中路徑是相對於父路由的,所以在內部中導航時,可不需要加上父元件的路徑。
<!-- 不需要加上父元件的路徑 --><a [routerLink]="[task]">{{ task.title }}</a>若需要巢狀路由繼承父路由的參數的話,可以在 provideRouter 時使用 withRouterConfig 來設定 paramsInheritanceStrategy 路由引數的繼承策略,這樣才可以使用 input 的方式來取得父路由的參數。
emptyOnly:只有當路由本身具有空路徑時,才會繼承父路由的引數。always:無論路由本身是否具有空路徑,都會繼承父路由的引數。
bootstrapApplication(AppComponent, { providers: [provideRouter(routes,withComponentInputBinding(),withRouterConfig({ paramsInheritanceStrategy: 'always' }))]})結論#
今天介紹了 Angular 路由的基本概念與使用方式,包含建立路由、導航、動態路由以及巢狀路由等。明天會繼續介紹進階的路由相關功能。