iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Modern Web

Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站系列 第 13

Day 13 Angular Routing – 作品集加上「詳情頁」

  • 分享至 

  • xImage
  •  

今日目標

  • 認識 Angular Router 與 RouterModule
  • 在專案中設定基礎路由(首頁、作品詳情)
  • 從 Projects 區塊加上連結 → /projects/:id
  • 在 Project Detail 元件裡讀取路由參數、顯示對應資料

基礎概念(Routing 是什麼?)

在傳統網頁,每次換頁都會整個重新載入;但在 SPA(Single Page Application)裡,Routing 是前端框架模擬「換頁」,實際上只是切換元件。

Angular Router 提供:

  • RouterModule.forRoot(routes) → 定義路由表
  • <router-outlet> → 畫面插槽,顯示目前路由對應的元件
  • [routerLink] → 模板中的導覽連結
  • ActivatedRoute → 在元件裡讀取當前路由參數

實作步驟

1) 在 AppModule 啟用 Router

編輯 app.module.ts

import { RouterModule, Routes } from '@angular/router';

// 定義路由表
const routes: Routes = [
  { path: '', component: ProjectsComponent },            // 預設首頁顯示作品集
  { path: 'projects/:id', component: ProjectDetailComponent }, // 動態路由
  { path: '**', redirectTo: '' }                         // 兜底:無效網址回首頁
];

@NgModule({
  declarations: [
    AppComponent,
    // ...其他元件
    ProjectDetailComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(routes),
    FormsModule,
    ReactiveFormsModule,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}


2) 建立 ProjectDetail 元件

用 CLI 新增:

ng g c components/project-detail

檔案結構:

src/app/components/project-detail/
  ├─ project-detail.component.ts
  ├─ project-detail.component.html
  └─ project-detail.component.scss


3) 修改 Projects 區塊:用 RouterLink 導向詳情

projects.component.html

<section id="projects" class="container section">
  <h2>作品集 Projects</h2>
  <div class="project-grid">
    <article class="card" *ngFor="let project of projects; let i = index; trackBy: trackByTitle">
      <h3>{{ project.title }}</h3>
      <p class="muted">{{ project.tech }}</p>
      <p>{{ project.desc }}</p>
      <!-- 使用 routerLink 取代 href -->
      <a class="btn small" [routerLink]="['/projects', i]">查看詳情</a>
    </article>
  </div>
</section>

這裡我們用 i(index)當作簡單的 id。未來可以換成資料庫 ID。


4) 在 ProjectDetailComponent 顯示單一專案

project-detail.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ProjectsDataService } from '../../services/projects-data.service';
import { Project } from '../../models/project.model';

@Component({
  selector: 'app-project-detail',
  templateUrl: './project-detail.component.html',
  styleUrls: ['./project-detail.component.scss']
})
export class ProjectDetailComponent implements OnInit {
  project?: Project;

  constructor(
    private route: ActivatedRoute,
    private projectsSvc: ProjectsDataService
  ) {}

  ngOnInit(): void {
    // 從網址讀取參數
    const id = Number(this.route.snapshot.paramMap.get('id'));
    const all = this.projectsSvc.getAll();
    this.project = all[id]; // 簡化處理:直接用 index
  }
}

project-detail.component.html

<div class="container section" *ngIf="project">
  <h2>{{ project.title }}</h2>
  <p class="muted">{{ project.tech }}</p>
  <p>{{ project.desc }}</p>
  <a class="btn" [href]="project.link" target="_blank">前往 Demo</a>
  <br /><br />
  <a routerLink="/" class="btn btn-outline">返回首頁</a>
</div>


5) 在 AppComponent 放 <router-outlet>

修改 app.component.html

<app-header></app-header>
<router-outlet></router-outlet>
<app-footer></app-footer>


成果

  • 網址 / → 顯示作品集(多個卡片)
  • 點「查看詳情」→ 導向 /projects/0/projects/1
  • 詳情頁會讀取路由參數,顯示對應專案完整資訊
  • 按「返回首頁」→ 回到 /

小心踩雷(常見誤用 → 正確做法)

  1. 忘了在 AppModule 匯入 RouterModule
    • ❌ 只寫 <router-outlet> → 會報錯
    • ✅ 要 imports: [RouterModule.forRoot(routes)]
  2. 還在用 <a href="...">
    • <a href="/projects/0"> → 會整個 reload
    • <a [routerLink]="['/projects', 0]"> → SPA 內部切換
  3. 路由參數型別錯誤
    • 路由參數預設是字串 → 若要用數字,記得 Number(...)
  4. 忘了兜底路由
    • 若沒設定 *,輸入錯誤網址會白畫面
    • 建議加上 redirect 或 NotFound 頁面

下一步(Day 14 預告)

我們要進一步學 Router 的巢狀結構與 Lazy Loading

  • 讓 Projects 詳情頁有「子路由」(例如 /projects/0/info/projects/0/gallery
  • 學會 children<router-outlet> 巢狀使用
  • 介紹 Lazy Loading:只在需要時才載入模組,提升效能

上一篇
Day 12 抽離資料到 Service – 認識 Angular 的相依性注入(DI)
下一篇
Day 14 Angular 巢狀路由與 Lazy Loading – 作品詳情子頁面
系列文
Angular、React、Vue 三框架實戰養成:從零打造高品質前端履歷網站15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言