前言

Router 是 Angular 中負責頁面導航的核心功能。透過 Router,我們可以實現單頁應用(SPA)的頁面切換,而不需要重新加載整個應用程式。

Router 用途

Angular Router 負責管理 Angular Web App 中的不同頁面之間的路由。透過 Router 模組提供了路由的機制,讓 Angular Web App 進行類似傳統網頁應用的導航,但不需要重新加載整個應用程式。

可以通過 RouterModule 進行自訂和訪問。通過設定路由規則可以讓 Angular 在特定的 URL 下載入特定 Component。

// path 為路徑,component 決定要載入的 Component
const routes: Routes = [{ path: "users/:id", component: UserComponent }];

基本路由設定

設定路由

app-routing.module.tsapp.config.ts 中設定路由:

// 傳統 NgModule 方式
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { AboutComponent } from "./about/about.component";
import { UserComponent } from "./user/user.component";

const routes: Routes = [
  { path: "", component: HomeComponent }, // 根路徑
  { path: "about", component: AboutComponent },
  { path: "users/:id", component: UserComponent }, // 動態路由
  { path: "**", redirectTo: "" }, // 404 重新導向
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
// Standalone 方式
import { provideRouter } from "@angular/router";

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter([
      { path: "", component: HomeComponent },
      { path: "about", component: AboutComponent },
      { path: "users/:id", component: UserComponent },
    ]),
  ],
};

使用 Router Outlet

在主要的 Template 中加入 <router-outlet>

<nav>
  <a routerLink="/">Home</a>
  <a routerLink="/about">About</a>
  <a routerLink="/users/1">User 1</a>
</nav>

<router-outlet></router-outlet>

使用 routerLink 指令進行導航:

<!-- 基本使用 -->
<a routerLink="/about">About</a>

<!-- 帶參數 -->
<a [routerLink]="['/users', userId]">User Profile</a>

<!-- 帶 Query Parameters -->
<a [routerLink]="['/search']" [queryParams]="{q: 'angular'}">Search</a>

<!-- Active 樣式 -->
<a routerLink="/about" routerLinkActive="active">About</a>

路由參數

取得路由參數

在 Component 中取得路由參數:

import { ActivatedRoute } from "@angular/router";

export class UserComponent implements OnInit {
  userId: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit(): void {
    // 方式 1:snapshot(快照)
    this.userId = this.route.snapshot.paramMap.get("id");

    // 方式 2:Observable(推薦,支援參數變化)
    this.route.paramMap.subscribe((params) => {
      this.userId = params.get("id");
      this.loadUserData(this.userId);
    });
  }
}

Query Parameters

// 設定 Query Parameters
this.router.navigate(["/search"], { queryParams: { q: "angular", page: 1 } });

// 取得 Query Parameters
this.route.queryParamMap.subscribe((params) => {
  const query = params.get("q");
  const page = params.get("page");
});

程式化導航

使用 Router 服務進行程式化導航:

import { Router } from "@angular/router";

export class MyComponent {
  constructor(private router: Router) {}

  goToAbout(): void {
    this.router.navigate(["/about"]);
  }

  goToUser(id: number): void {
    this.router.navigate(["/users", id]);
  }

  goBack(): void {
    // 使用 Location 服務返回上一頁
    this.location.back();
  }
}

路由守衛(Route Guards)

路由守衛用於控制路由的訪問權限。

CanActivate

保護路由,決定是否可以進入。

import { Injectable } from "@angular/core";
import { CanActivate, Router } from "@angular/router";

@Injectable({ providedIn: "root" })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    }
    this.router.navigate(["/login"]);
    return false;
  }
}

使用守衛:

const routes: Routes = [
  {
    path: "admin",
    component: AdminComponent,
    canActivate: [AuthGuard],
  },
];

CanDeactivate

決定是否可以離開當前路由。

export interface CanComponentDeactivate {
  canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable({ providedIn: "root" })
export class CanDeactivateGuard
  implements CanDeactivate<CanComponentDeactivate>
{
  canDeactivate(
    component: CanComponentDeactivate
  ): boolean | Observable<boolean> {
    return component.canDeactivate();
  }
}

子路由(Child Routes)

建立嵌套路由結構:

const routes: Routes = [
  {
    path: "products",
    component: ProductsComponent,
    children: [
      { path: "", component: ProductListComponent },
      { path: ":id", component: ProductDetailComponent },
      { path: ":id/edit", component: ProductEditComponent },
    ],
  },
];

在父 Component 的 Template 中:

<h2>Products</h2>
<router-outlet></router-outlet>

懶加載(Lazy Loading)

使用懶加載減少初始載入時間:

const routes: Routes = [
  {
    path: "admin",
    loadChildren: () =>
      import("./admin/admin.module").then((m) => m.AdminModule),
  },
  // Standalone 方式
  {
    path: "shop",
    loadComponent: () =>
      import("./shop/shop.component").then((m) => m.ShopComponent),
  },
];

路由事件

監聽路由變化:

import { Router, NavigationStart, NavigationEnd } from '@angular/router';

constructor(private router: Router) {
  this.router.events.subscribe(event => {
    if (event instanceof NavigationStart) {
      console.log('Navigation started');
    }
    if (event instanceof NavigationEnd) {
      console.log('Navigation ended');
    }
  });
}

總結

Angular Router 提供了完整的路由解決方案:

  • 基本路由:設定路徑與 Component 的對應關係
  • 路由參數:支援動態路由和 Query Parameters
  • 程式化導航:使用 Router 服務進行導航
  • 路由守衛:控制路由訪問權限
  • 子路由:建立嵌套路由結構
  • 懶加載:優化應用程式載入效能

掌握 Angular Router 可以讓你建立複雜的單頁應用,提供流暢的使用者體驗。

參考資料