Angular2以組件化的視角來看待web應用,使用Angular2開發的web應用,就是一棵組件樹。組件大體分爲兩類:一類是如list、table這種通放之四海而皆準的通用組件,一類是專爲業務開發的業務組件。實際開發中大部分時間咱們都須要處理業務組件。對於SPA應用來講,一個通用的問題就是如何控制頁面的切換,解決這個問題的通用方法就是利用路由器來實現。web
如今咱們先撇開Angular2來看看通用的路由器模型。一般來說SPA應用須要路由配置信息:bootstrap
[ { path: '', pathMatch: 'full', redirectTo: '/inbox' }, { path: ':folder', children: [ { path: '', component: ConversationsCmp }, { path: ':id', component: ConversationCmp, children: [ { path: 'messages', component: MessagesCmp }, { path: 'messages/:id', component: MessageCmp } ] } ] }, { path: 'compose', component: ComposeCmp, outlet: 'popup' }, { path: 'message/:id', component: PopupMessageCmp, outlet: 'popup' } ]
這個配置信息定義了應用的潛在路由狀態(Router State)。一個路由狀態表明瞭一份組件佈置信息。 如今咱們換一個視角來看這份配置:async
在這棵配置樹中,每個節點就是一個路由,它對應了一個組件。ide
在路由樹這種視角下,每個路由狀態就是配置樹的一棵子樹。下圖中的路由狀態下,最終被激活的組件是ConversationCmp:組件化
路由器的首要任務就是控制在不一樣路由狀態之間導航以及更新組件樹。以下圖所示,當咱們導航到另外一個頁面時,路由狀態也會發生改變,隨之頁面上顯示的組件也跟隨變化。fetch
到此爲止路由器的基本模型已經介紹完畢,下面咱們來看一下Angular2中的路由模型。this
Angular2對待一個URL的處理流程爲:url
在全部步驟以前路由器要進行url格式解析,這部份內容不是本文重點,請你們自行查看其它資料。spa
假設咱們訪問的地址是:http://hostname/inbox/33/message/44。路由器首先根據配置規則:3d
{ path: ‘’, pathMatch: ‘full’, redirectTo: ‘/inbox’ }
來判斷是否須要重定向,若是咱們的url是http://hostname/此時,就是重定向到http://hostname/inbox,根據配置規則:folder,這時候被激活的組件就是ConversationComp。但如今咱們的url是http://hostname/inbox/33/message/44,因此不會發生重定向。
接下來路由器會爲這個URL分發一個路由狀態。根據配置規則
{ path: ':folder', children: [ { path: '', component: ConversationsCmp }, { path: ':id', component: ConversationCmp, children: [ { path: 'messages', component: MessagesCmp }, { path: 'messages/:id', component: MessageCmp } ] } ] }
/inbox/33/message/44首先匹配:folder,對應組件爲ConversationCmp,然後進入子配置,'message/:id',MessageCmp組件被激活。
根據上圖的狀態樹,咱們能夠看出MessageCmp與ConversationCmp對應的路由狀態。與此同時一個被稱爲激活路由(ActivatedRoute)的對象將被建立,並能夠在MessageCmp訪問到,經過ActivatedRoute咱們能夠拿到它的routerState屬性,經過路由狀態咱們能夠拿到具體參數如id對應的44。今後也能夠看出拿到父級參數id(33)就必須訪問父級的路由狀態。
ngOnInit() { this.sub = this.router.routerState.parent(this.route).params.subscribe(params => { this.parentRouteId = +params["id"]; }); }
哨兵的做用是判斷是否容許應用在不一樣狀態間進行切換,好比:若是用戶沒有登錄就不容許進入Message頁面。哨兵能夠用來判斷是否容許進入本路由狀態,是否容許離開本路由狀態。下例中的CanActivate用來判斷是否容許進入,這個服務類須要繼承CanActivate接口。
import { AuthGuard } from '../auth-guard.service'; const adminRoutes: Routes = [ { path: 'admin', component: AdminComponent, canActivate: [AuthGuard], children: [ { path: '', children: [ { path: 'crises', component: ManageCrisesComponent }, { path: 'heroes', component: ManageHeroesComponent }, { path: '', component: AdminDashboardComponent } ], } ] } ]; export const adminRouting: ModuleWithProviders = RouterModule.forChild(adminRoutes);
import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { canActivate() { console.log('AuthGuard#canActivate called'); return true; } }
哨兵內容涉及到另外一個部分知識,因此我會把他放到下一篇文章中。
Angular2的路由器容許咱們在進入組件中拿到除當前路由參數以外的其餘信息。在路由配置中使用resolve屬性指定一個數據分發器。
[ { path: ':folder', children: [ { path: '', component: ConversationsCmp, resolve: { conversations: ConversationsResolver } } ] } ]
數據分發器須要繼承DataResolver接口:
@Injectable() class ConversationsResolver implements DataResolver { constructor(private repo: ConversationsRepo, private currentUser: User) {} resolve(route: ActivatedRouteSnapshot, state: RouteStateSnapshot): Promise<Conversation[]> { return this.repo.fetchAll(route.params['folder'], this.currentUser); } }
還須要把這個數據分發器加入到module的Providers中:
@NgModule({ //... providers: [ConversationsResolver], bootstrap: [MailAppCmp] }) class MailModule { } platformBrowserDynamic().bootstrapModule(MailModule);
然後咱們在組件中就能夠經過ActivatedRoute來訪問分發數據了。
@Component({ template: ` <conversation *ngFor="let c of conversations | async"></conversation> ` }) class ConversationsCmp { conversations: Observable<Conversation[]>; constructor(route: ActivatedRoute) { this.conversations = route.data.pluck('conversations'); } }
此時路由器根據路由狀態來實例化組件並把他們放到合適的路由組出發點上。
@Component({ template: ` ... <router-outlet></router-outlet> ... <router-outlet name="popup"></router-outlet> ` }) class MailAppCmp { }
如‘/inbox/33/message/44(popup:compose)’,首先實例化ConversationCmp放到主<router-outlet>中,而後實例化MessageCmp放到name爲popup的<Router-outlet>中。
如今路由器對URL的解析過程完畢。可是若是用戶想從MessageCmp中跳轉到別的路由狀態該如何作呢?Angular2提供了兩種方式。
一種是經過router.navigate方法來導航:
@Component({...}) class MessageCmp { private id: string; constructor(private route: ActivatedRoute, private router: Router) { route.params.subscribe(_ => this.id = _.id); } openPopup(e) { this.router.navigate([{outlets: {popup: ['message', this.id]}}]).then(_ => { // navigation is done }); } }
一種是利用router-link方式:
@Component({ template: ` <a [routerLink]="['/', {outlets: {popup: ['message', this.id]}}]">Edit</a> ` }) class MessageCmp { private id: string; constructor(private route: ActivatedRoute) { route.params.subscribe(_ => this.id = _.id); } }
參考資料:
http://stackoverflow.com/questions/34500147/angular-2-getting-routeparams-from-parent-component