iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
1
Modern Web

Angular 深入淺出三十天系列 第 26

[Angular 深入淺出三十天] Day 25 - 路由總結(一)

我覺得路由是 Angular 裡很重要的一部分,也是一個初學者比較不容易理解的部分,所以希望透過這七天的小小練習,會讓大家對路由有個初步的認識。

不過分了七天講是比較破碎了一點,而且也有些東西想再補充,就一併做個總結吧!


什麼是路由?

  • 在網址列中輸入 URL 後,會導頁到對應的頁面。
  • 點擊頁面上的連結後,會導頁到對應的頁面。
  • 點擊瀏覽器的上一頁/下一頁的按鈕之後,會根據歷史紀錄前後導頁。

Angular 的路由做了什麼?

  • 負責重新配置頁面中應該顯示哪些 Component
  • 負責儲存頁面中 Component 的路由狀態
    • 路由狀態定義了在某個路由的時候應該顯示哪些 Component
    • 路由狀態最重要的就是記錄路徑與 Component 之間的關係

啟用路由追蹤記錄的功能

app-routing.module.ts 裡的 RouterModule.forRoot 函式裡加入 { enableTracing: true } 的參數。

如:

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

路由策略

還記得我們在路由(一)的時候有說過什麼是路由嗎?

拿 iT 邦幫忙的的網站來舉例好了,當我們在瀏覽器的網址列輸入 ithelp.ithome.com.tw 並按下 Enter 之後,就會看到 iT 邦幫忙的網站。

然後如果我們要到其他地方,比如說「即時通知中心」。除了點擊「即時通知」的連結之外,我們其實可以直接在 iT邦幫忙的網址後面加上 /notifications ,像是這樣: ithelp.ithome.com.tw/notifications

上述這樣的方式其實就有點像是 PathLocationStrategy 這個路由策略,不過 PathLocationStrategy 這個路由策略的技術是建構在 history.pushState 之上,讓我們可以在路由狀態改變的時候,既能將其加入到瀏覽器的歷史裡,又不會真的向伺服器送出請求。

但是這個策略如果是用在低於 IE10 的瀏覽器上的話,還是會真的送出請求,導致頁面發生錯誤。而且這樣的路由策略會讓路由比較容易跟真正需要發出請求的路由混淆。

所以我們可以透過以下方式將預設使用的 PathLocationStrategy 改為使用 HashLocationStrategy :

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

HashLocationStrategy 這個路由策略是採用 HTML 4 的標準 Hash 網址格式, IE 6 以上都能夠正常瀏覽,所以敬請放心使用。

不過官方是強烈推薦使用預設的 PathLocationStrategy 啦!主要是因為官方覺得這樣的 URL 更容易被使用者理解,且以後如果要使用 Server-Side Rendering 機制的話,可以無痛設定。

路由連結

傳統頁面的超連結是透過使用 <a href=""></a> 完成的。

但在 Angualr 裡加了路由設定之後,我們可以透過以下幾種方式在我們的 <a></a> 裡加入超連結:

  1. 用屬性綁定的方式傳入字串
<ul>
  <li><a [routerLink]="'/home'">Home</a></li>
  <li><a [routerLink]="'/about'">About</a></li>
</ul>

2.用屬性綁定的方式傳入字串陣列

<ul>
  <li><a [routerLink]="['/home']">Home</a></li>
  <li><a [routerLink]="['/about']">About</a></li>
</ul>
  1. 用屬性名稱的方式並直接給予路由名稱
<ul>
  <li><a routerLink="/home">Home</a></li>
  <li><a routerLink="/about">About</a></li>
</ul>

除此之外,我們還可以透過以下幾種方式來使用 routerLinkActive,讓我們能夠很輕鬆地在導航路由時,在對應的 <a></a> 上加上我們所指定的樣式類別名稱:

  1. 用屬性綁定的方式傳入字串
<ul>
  <li>
    <a 
      [routerLink]="'/home'"
      [routerLinkActive]="'active'"
    >Home</a>
  </li>
</ul>
  1. 用屬性綁定的方式傳入字串陣列
<ul>
  <li>
    <a 
      [routerLink]="'/home'"
      [routerLinkActive]="['active']"
    >Home</a>
  </li>
</ul>
  1. 用屬性名稱的方式並直接給予類別名稱
<ul>
  <li>
    <a 
      [routerLink]="'/home'"
      routerLinkActive="active"
    >Home</a>
  </li>
</ul>

routerLinkActive 其實很厲害,假設我們今天的路由結構是這樣:

Imgur

所以當我們的路由是 /products 的時候,就會變成這樣:

Imgur

那當路由是 /products/b 的時候呢?

會是這樣嗎?

Imgur

不,其實會變成這樣:

Imgur

因為 routerLinkActive 在比對路由的時候,會是這樣比對的:

  • 當路由是 /products 的時候,比對 /products 連結:成功。
  • 當路由是 /products/b 的時候,比對 /products 連結:成功;比對 /products/b 連結:成功。

所以 routerLinkActive 預設是會套用到上層路由的。

不過如果我們今天不想要連父層也一起套用的話,我們可以在父層的連結上多增加 [routerLinkActiveOptions]="{exact: true}" 的設定即可,像是:

<ul>
  <li>
    <a
      [routerLink]="'news'"
      routerLinkActive="active"
    >News</a>
  </li>
  <li>
    <a
      [routerLink]="'products'"
      [routerLinkActive]="'active'"
      [routerLinkActiveOptions]="{exact: true}"
    >Products</a>
  </li>
  <li>
    <a
      [routerLink]="['products', 'a']"
      [routerLinkActive]="'active'"
    >Product A</a>
  </li>
</ul>

Router Service

當我們的應用程式中加入了路由機制之後,會有個名為 Rotuer 的 Service 可以用。

使用方式:

export class LoginComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit() {
  }

}

注入這個 Service 後,我們可以從這個 Service 之中拿到所有的路由資訊、目前套用的路由與路由狀態。

除此之外,還可以透過使用這個 Service 來導頁,導頁的方式大致如下:

  1. 使用 navigateByUrl 函式並傳入字串
this.router.navigateByUrl('home');
  1. 使用 navigate 函式並傳入字串陣列
this.router.navigate(['home']);
  1. 使用 navigate 函式並傳入字串陣列,且加上 { relativeTo: '當前路由' } 的參數表示要使用相對路徑的方式導頁
export class LoginComponent implements OnInit {

  constructor(
    private router: Router,
    private route: ActivatedRoute // 注入當前路由
  ) { }

  ngOnInit() {
  }

}
this.router.navigate(['..', 'home'], {
    relativeTo: this.route
});

後面的參數物件裡的屬性除了 relativeTO 之外,還有很多很好用的參數,請直接參考官方文件的說明。

路由參數

有時候我們會需透過路由傳遞參數,而傳遞參數的方法有兩種:

  1. 使用 Query String 表示法

網址格式: http://localhost:4200/products?id=101

程式寫法:

this.router.navigate(['products'], {
    queryParams: {
      id: 101
    }
});

Template 寫法:

<a [routerLink]="['products']" [queryParams]="{ id: 101 }">Prodcuts</a>

取得參數的方式:

export class ProductsComponent implements OnInit {

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
  
      // 第一種方式(較推薦)
      this.route.queryParams.subscribe((queryParams) => {
        console.log(queryParams['id']);
      });
      
      // 第二種方式
      console.log(this.route.snapshot.queryParams['id']);
  
  }

}
  1. 使用 matrix URL notation 表示法

網址格式:http://localhost:4200/products;id=101

this.router.navigate(['products', { id: 101 }]);

抑或是

<a [routerLink]="['products', { id: 101 }]">Products</a>

取得參數的方式:

export class ProductsComponent implements OnInit {

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
  
      // 第一種方式
      console.log(this.route.params['id']);
      
      // 第二種方式
      console.log(this.route.snapshot.params['id']);
  
  }

}

由於要總結的事情有點多,所以還是分成兩天好了!

今天就先到這邊,明天繼續!!


上一篇
[Angular 深入淺出三十天] Day 24 - 路由(七)
下一篇
[Angular 深入淺出三十天] Day 26 - 路由總結(二)
系列文
Angular 深入淺出三十天33

尚未有邦友留言

立即登入留言