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