Unit 16 在一個元件樣版中使用多個命名的 router outlets

3 minute read

多個命名 router outlet 的應用情境

頁面中劃分成多個可更新的區域。點擊特定錨點或者按鈕後,更新特定區域的內容。

例如, 底下的頁面有兩個 <router-outlet>。右邊的 <router-outlet> 顯示快訊清單(new list), 左邊的則顯示其它資訊。所以, 我們可以左邊顯示 stock list, 頁面右邊顯示 new list。

多個 router outlet 下的設定

使用多個 router-outlet 的設定程序如下:

  1. 為第二個以後的 router-outlet 取名
  2. 在 router module 中, 在 route 的設定上加上 outlet 屬性
  3. routerLink directive 的動態路徑中加上其它 router-outlet 的名字及在該 outlet 下要導向的路徑。

在說明設定上述這些步驟之前,先說明 Angular 如何顯示具有多個 router-outlet 的路徑。

多個 router outlet 下的路徑表示方式

Angular 會在主要的路徑的結尾使用括號()表示目前其它路徑內容。

假設現在有兩個 router-outlet:

1
2
<router-outlet></router-let>
<router-outlet name="aux"></router-let>

沒有名稱的為預設的 router-outlet, 我們將之稱為 primary outlet. 另一個取名為 aux 的 router-outlet, 將之稱為 secondary outlet.

當畫面在 secondary router-outlet 上未顯示內容時(畫面只顯示 primary-outlet 上的內容),此時瀏覽器上顯示的為主要路徑 /stock/item:

但是,若在 secondary router-outlet 上顯示內容是,瀏覽器的主要路徑上會附加上刮號,刮號內顯示 secondary router-outlet 的名稱以及在該 outlet 上的路徑,所以瀏覽器的完整路徑為 /stock/item(aux:news/list):

若點選 new list 上的第一個快訊標題,會在該原件的 router-outlet 顯示快訊的內容。此時瀏覽器上的完整路徑為 /stock/item(aux:news/detail/0), 表示在 secondary router-let 上目前顯示的內容為 news/detail/:id 路徑下對應的原件 html 內容,在 primary router-outlet 上的為路徑 /stock/item 對應的元件的 html 內容。

router-outlet 的取名

預設的 router-outlet 是不需特別命名,它的名稱為 primary。非預設的 router-outlet 便要取名。使用 <router-outlet>name 屬性為其命名,例如:<router-outlet name="aux">:

router module 的設定

要顯示到非預設的 router-outlet 的 route 要額外使用 outlet 屬性,指定特定路徑要導向的出口(outlet)及在出口顯示的元件的 HTML 內容。

底下的例子中,路徑 new/list 的出口為 <router-outlet name="aux"> 在出口處顯示的元件為 StockNewsListComponent:

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

const routes: Routes = [
    {path: 'news/detail/:id', component: StockNewsDetailComponent},
    {path: 'news/list', 
     component: StockNewsListComponent,
     // 額外新增的屬性,指定路徑要導向的 outlet
     outlet: 'aux',
     children: [{path: "detail/:id", component: StockNewsDetailComponent}]
    }
];

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

當要導向到非預設的 router-outlet 時, routerLink directive 的寫法和導向到預設的 router-outlet 有所差異。

在產生動態路徑(Dynamic Path)的陣列中, 使用 outlets 物件表示要路徑要導向的 router-outlet:

1
{outlets:{outlet_name: 'path', outlet_name2: 'path2', ...}} 

在例子 {outlets: {aux: 'news/list' }} 中,路徑 'news/list' 會將元件在名為 aux 的 router-outlet 上顯示。

注意,outlets 物件中的路徑需要使用相對路徑,不能使用絕對路徑,因爲非預設 router-outlet 的路徑是附加在主要路徑之後的。

如果目前的主要路徑為 /stock/item,那麽點擊 Open News List 連結之後所產生的完整路徑為 /stock/item(aux:news/list)

1
2
3
4
5
6
7
8
<!-- 導向到預設的 router-outlet -->
<a routerLink="/stock/item" routerLinkActive="active-link">Stock Item |</a>
<!-- 導向到命名的 router-outlet (非預設的或 secondary router-outlet) -->
<!-- 注意: outlets 的路徑要使用相對路徑, 相對在目前的路徑 -->
<!-- 此 outlet 的路徑為 current_path(aux:news/list) -->
<a [routerLink]="[{outlets: {aux: 'news/list' }}]" routerLinkActive="active-link">Open News List / </a>
<!-- Close the new list -->
<a [routerLink]="[{outlets: {aux: null }}]" routerLinkActive="active-link">Close New List |</a>

Angular 允許一次的導向在兩個 router-outlet 同時顯示 2 個元件的 view (HTML)。底下的例子可以同時在預設及非預設的 router-outlet 上顯示某個路徑上的元件:

1
2
3
4
5
// create /team/33/(user/11//right:chat)
router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]);

// remove the right secondary node
router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]);

Ref: Usage note in Router.createurltree() @ Angular

Ref: https://angular.io/guide/router-tutorial-toh#displaying-multiple-routes-in-named-outlets

實作練習

Use Case 説明

以 u15 的練習成果為基礎加以修改。

步驟

設定 Feature Module 中的路徑顯示於特定名稱的 router outlet。

開啓 src\app\stock-news\stock-news-routing.module.ts

增加 outlet 屬性到 news/list 的 route 物件:

1
2
3
4
5
6
7
8
9
const routes: Routes = [
    {path: 'news/detail/:id', component: StockNewsDetailComponent},
    {path: 'news/list', 
     component: StockNewsListComponent,
     // 新增的 outlet 屬性
     outlet: 'aux',
     children: [{path: "detail/:id", component: StockNewsDetailComponent}]
    }
];

如此,news/list 路徑下的元件將顯示於名稱為 aux 的 router outlet。

app 元件的頁面中加入第二個 router outlet,並爲其命名。

開啓 src\app\app.component.html

加入第二個 router outlet 並命名爲 aux:

1
<router-outlet name="aux"></router-outlet>

新增加 Open News ListClose News List 兩個超鏈接:

1
2
3
4
5
6
7
<!-- 注意: outlets 的路徑要使用相對路徑, 相對在目前的路徑 -->
  <!-- 此 outlet 的路徑為 current_path(aux:news/list) -->
  <a [routerLink]="[{outlets: {aux: 'news/list' }}]"
    routerLinkActive="active-link">Open News List / </a>
  <!-- Close the new list -->
  <a [routerLink]="[{outlets: {aux: null }}]" 
    routerLinkActive="active-link">Close News List |</a>

當點選 Open News List 會在目前主要路徑下,附加 {aux:news/list}。第二個 router outlet 會顯示該路徑對應的元件的 HTML 内容。

當點選 Close News List, 附加的路徑為 {aux:null},第二個 router outlet 不會顯示任何元件的 HTML 内容。

注意,要使用相對路徑。

Router outlet 的 RWD 排版。

匯入 FlexLayoutModule 到專案中,以利進行 RWD 排版。

開啓 src\app\app.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {FlexLayoutModule} from '@angular/flex-layout'

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    
    StockNewsModule,
    // Added module
    FlexLayoutModule
  ],
  providers: [
    ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

使用 fxFlex directive 為第一及第二個 router outlets 的版面進行 RWD 排版:

1
2
3
4
5
6
7
8
9
10
<div fxLayout="row" fxLayout.lt-sm="column">
  <div fxFlex="1 1 50%" fxFlex.lt-sm="100%">
    <!-- First Router outlet -->
    <router-outlet></router-outlet>
  </div>
  <div fxFlex="1 1 50%" fxFlex.lt-sm="100%">
    <!-- Second router outlet -->
    <router-outlet name="aux"></router-outlet>
  </div>
</div>

完成。

Updated: