Angular2以組件化的視角來看待web應用,使用Angular2開發的web應用,就是一棵組件樹。組件大致分為兩類:一類是如list、table這種通放之四海而皆準的萬用群組件,一類是專為業務開發的業務組件。實際開發中大部分時間我們都需要處理業務組件。對於SPA應用來說,一個通用的問題就是如何控制頁面的切換,解決這個問題的通用方法就是利用路由器來實現。
路由配置
現在我們先撇開Angular2來看看通用的路由器模型。通常來講SPA應用需要路由配置資訊:
[ { 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)。一個路由狀態代表了一份組件布置資訊。 現在我們換一個視角來看這份配置:
在這棵配置樹中,每一個節點就是一個路由,它對應了一個組件。
路由狀態
在路由樹這種視角下,每一個路由狀態就是配置樹的一棵子樹。下圖中的路由狀態下,最終被啟用的組件是ConversationCmp:
導航
路由器的首要任務就是控制在不同路由狀態之間導航以及更新群組件樹。如下圖所示,當我們導航到另一個頁面時,路由狀態也會發生改變,隨之頁面上顯示的組件也跟隨變化。
到此為止路由器的基本模型已經介紹完畢,下面我們來看一下Angular2中的路由模型。
Angular2路由處理流程
Angular2對待一個URL的處理流程為:
1.應用重新導向
2.識別路由狀態
3.應用哨兵與傳遞資料
4.啟用對應組件
重新導向
假設我們訪問的地址是:http://hostname/inbox/33/message/44。路由器首先根據配置規則:
{ 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); }}
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。