到目前為止,我們都是只有用 Component 來設定我們的路由。但在實際應用上,通常我們會將相關的功能包裝成一個一個的 NgModule ,而每個 NgModule,也其實都可以有自己的 RoutingModule。
舉例來說,我們先產生一個含有路由的 NgModule:
ng generate module feature --routing
並產生相對應的 Component:
ng generate component feature
接著設定路由:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FeatureComponent } from './feature.component';
const routes: Routes = [
{
path: 'feature',
component: FeatureComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class FeatureRoutingModule { }
有發現這邊跟 AppRoutingModule 有個地方不太一樣嗎?
在 AppRoutingModule 的裝飾器裡, RouterModule 是使用 forRoot
的函式:
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
但在 FeatuerRouting 裡, RouterModule 卻是使用 forChild
的函式。
這是一個滿重要的區別,整個系統只有 AppRoutingModule 才會使用 forRoot
,其他的子路由模組都是使用 forChild
。
不過如果都是用 Angular CLI 來產生元件的話,倒是不用擔心這個。
接著我們先將 FeatureModule 引入到 AppModule 裡,並將其放在 AppRoutingModule 的上面,像是這樣:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
// Routing Module
import { AppRoutingModule } from './app-routing.module';
// Module
import { FeatureModule } from './feature/feature.module';
// Component
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { LoginComponent } from './login/login.component';
import { LayoutComponent } from './layout/layout.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
LoginComponent,
LayoutComponent
],
imports: [
BrowserModule,
FeatureModule, // 放在這裡
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
接著再到瀏覽器上輸入 http://localhost:4200/#/feature
看看,應該就能夠看到 feature works!
的字樣:
不過這邊要特別留意的是,我們含有路由設定的子功能模組一定要放在 AppRoutingModule 的上面。
為什麼呢?
還記得我在路由(二)的時候有提到過路由在查找的時候是有順序性的嗎?
AppRoutingModule 裡有個萬用路由,如果我們把其他含有路由模組的功能模組擺在它之後的話,那我們永遠都別想進到放在它之後的子功能模組裡了。
不信?那我們來試試看:
imports: [
BrowserModule,
AppRoutingModule,
FeatureModule
]
來看看位置對調之後的結果:
你看,怎麼樣都進不去吧。
那為什麼會這樣咧?從資料面來看就知道了。
首先我們先打開 home.component.ts
檔,然後注入一個名為 Router
的 Service:
export class HomeComponent implements OnInit {
constructor(private router: Router) { }
ngOnInit() {
}
}
這個名為 Router
Service 是 Angular 的路由機制所提供的,之後會再跟大家介紹它。現在只是需要透過它取得我們目前所設定的路由,看看實際上的資料大概會長怎樣。
然後我們在 ngOnInit
的函式裡加入以下的程式碼:
console.log(this.router.config);
如此便能在控制台裡看到我們印出來的資料:
你看!是不是跟我說的一樣?! 含有 feature
路由設定的 FeatureModule 因為在 AppRoutingModule 才引入的關係,導致其路由被放到萬用路由後面。
我們把 FeatureModule 跟 AppRoutingModule 的位置交換回來之後再看一次:
交換回來之後就不會被放在萬用路由底下了,這就是為什麼萬用路由要放在 routes
陣列裡最後一個,以及 AppRoutingModule 為什麼要放在 imports
陣列裡最後一個的原因。
有了這樣的概念之後,未來才不會有怎麼設路由卻發現怎麼樣都進不去的問題。
那今天就先到這裡,接下來要介紹的是在實務上更為實用的功能噢!
敬請期待!!
Hi 作者大人,
請問我照著您說的要注意在app.module.ts
中引入FeatureModule
的位置,為何我的localhost:4200/#/feature
還是redirect回login那頁?
我還嘗試把之前萬用的route砍掉,但一樣還是redirect回login....
請問我有不小心遺漏了什麼重要的觀念嗎?
Hi thuartlynn,
你可能要在 RouterModule
裡加上 enableTracing: true
的參數讓系統把 log 印出來才會知道。
我先猜是不是你沒有 enableHash: true
所導致的?
如果你沒有 enableHash: true
的話, localhost:4200/#/feature
=> 這個 URL 就不用 #
,只要 localhost:4200/feature
就好。
如果不是這個問題的話,可能要看一下你 AppRoutingModule
裡的設定才知道囉
Hi 作者大人,
我今早重開資料夾,重新ng serve後,一切就正常了...您說的那些我都有保留著,新增那個feature的module後就沒動過,剛log出來也是正確的urlAfterRedirects: '/feature',昨天這邊怎樣都是'/login',所以....昨天真的不知道是哪兒出問題了...
真的超級感謝你,還是很快速的回答了我...
Hi thuartlynn,
別客氣,人剛好在而已,大部分時間都是不在的XDDD
Hi 作者大人,
XD ,好在你就這麼剛好在,謝謝你之前寫的這一系列,讓我快速了解了Angular~感謝你
Hi thuartlynn,
這是我的榮幸,也非常開心我的文章有幫到你^^
給你一個大大的讚~
您好,
想詢問一下,在routing.module 設定的 「useHash: true」
可不可以不要加這個呢?, 因為網站一般好像不太會看到「#」
例如:https://example.com/#/about (useHash)
https://example.com/about (可以是這個嗎)
自己這邊遇到的狀況是,
不用 useHash 的話,打包上架的網站
如果是使用點擊的方式,可以順利導向頁面;
但是自己手動改網址,就會變成 404 了 (╯︿╰)||
Hi memo, 當然是看你的需求囉!
這其實是有歷史背景的XDDD