基础
大多数带路由的应用都要在@H_502_7@index.html的@H_502_7@<head>标签下先添加一个@H_502_7@<base>元素,来告诉路由器该如何合成导航用的 URL 。
如果@H_502_7@app文件夹是该应用的根目录,那就把@H_502_7@href的值设置为下面这样:
<base href="/">
另外如果app不是根路径则需要在<head>中使用
<script>document.write('<base href="' + document.location + '" />');</script>
路由配置
首选方案是用带有“路由数组”的 provideRouter 工厂函数( [provideRouter(routes)] )来启动此应用。
myApp.routes.ts
import {RouterConfig,provideRouter} from"@angular/router";
import {LoginComponent} from"./login/login.component";
import {HomeComponent} from"./shared/default/home.component";
/**
*Created by Administrator on 2016-07-22.
*/
const routes:RouterConfig =[
{path:'',component:HomeComponent},
{path:'login',component:LoginComponent}
]
export const myAppRouterProviders=[
provideRouter(routes)
]
RouterConfig 是一个 路由 数组,它会决定如何导航。 每个 Route 会把一个 URL 的 path 映射到一个组件。
path 中 不能用斜线 / 开头。路由器会为我们解析和生成 URL ,以便在多个视图间导航时,可以自由使用相对路径和绝对路径。
第三个路由中的 :id 是一个路由参数的令牌 (Token) 。比如 /hero/42 这个 URL 中,“ 42 ”就是 id 参数的值。此 URL 对应的 HeroDetailComponent 组件将据此查找和展现 id 为 42 的英雄。
我们将把这份配置数组传给 provideRouter() 函数,它返回一个经过配置的 Router 服务提供商(以及别的东西)。
最后,我们通过 appRouterProviders 数组导出这个提供商,以便我们以后将来在 main.ts 中简单的注册路由器依赖。目前我们还没有注册任何别的提供商,但很快就会这么做了!
路由使用
有了这份配置,当本应用在浏览器中的 URL 变为 /heroes 时,路由器就会匹配到 path 为 heroes 的 Route ,并在宿主视图中的 RouterOutlet 中显示 HeroListComponent 组件。
现在,我们已经有了配置好的一些路由,还找到了渲染它们的地方,但又该如何导航到它呢?固然,从浏览器的地址栏直接输入 URL 也能做到,但是大多数情况下,导航是某些用户操作的结果,比如点击一个 A 标签。
我们把一个 RouterLink 指令添加到这个 A 标签上,并把该指令绑定到一个能返回路由链接数组(链接参数数组 )的模板表达式上。 路由器最终会把此数组解析成一个 URL 和一个组件视图。
一个模板中只能有一个未命名的 <router-outlet> 。 但路由器可以支持多个 命名的 插座( outlet )。
RouterLink 绑定
在插座上方的 A 标签中,有一个绑定 RouterLink 指令的属性绑定 ,就像这样:[routerLink]="[...]" 。我们从路由库中导入了 RouterLink 。
等号( = )右侧的模板表达式返回一个链接参数数组 。
链接参数数组中存放导航时所需的那些要素:
指向目标组件的路由中的 path 属性
可选的路由参数和查询参数,它们会被编入到该路由的 URL 中
这个例子中的数组都只有一个字符串参数,也就是我们以前配置过的路由中的 path 部分。目前还没有用到路由参数。
注意:RouterLink 和 RouterOutlet是 ROUTER_DIRECTIVES集合中的指令。记住把它们加入 @Component元数据的 directives数组中。
import{ROUTER_DIRECTIVES} from "@angular/router";
…..
directives:[ROUTER_DIRECTIVES]
进阶
把应用组织为一些特性区
典型的应用中有多个特性区 ,每个区都是一个“功能岛”,它们有自己的工作流、实现一个特定的业务目标。
我们可以继续把文件全添加到 app/ 目录中。但那么做不太现实,并且最终将无法维护。因此,把每个特性区都放进自己的目录中会更好一些。
第一步: 创建一个独立的 app/heroes/ 文件夹,并在其中添加属于英雄管理 特性区的文件。
第二步: 为每个特性区创建它自己的路由配置文件。
import {RouterConfig} from"@angular/router";
import { ChangePasswordComponent} from"../../system/changepassword/changepassword.component";
import {AdminMenuComponent} from"../adminmenu/adminmenu";
/**
*Created by Administrator on 2016-07-22.
*/
export const topNavRoutes:RouterConfig=[
{path:"userNav",component:AdminMenuComponent},
{path:'changepassword/:id',component:ChangePasswordComponent}
];
记住此处需要export这个路由表
第三步:把特性区的路由合并到应用程序的路由中。
import {RouterConfig,provideRouter} from"@angular/router";
import {LoginComponent} from"./login/login.component";
import {HomeComponent} from"./shared/default/home.component";
import {topNavRoutes} from"./shared/topnav/topnav.routes";
import {loginRoutes} from"./login/login.routes";
/**
*Created by Administrator on 2016-07-22.
*/
export const routes:RouterConfig =[
{path:'',redirectTo:'home',pathMatch: 'full'},
{path:'home',
...loginRoutes,
...topNavRoutes
]
export const myAppRouterProviders=[
provideRouter(routes)
]
这里的provideRouter即是将配置的路由表注入到应用中,这样在整个应用中都可以使用这个路由表。
这里注意通过“…”会将特性区配置的路由表扁平化到app路由配置中
另外,通常情况我们需要对根路径做一个重定向处理。由“/”定向到“/home”中
这样做的结果是, app.routes.ts 文件不用再了解特性区的具体知识,比如组件细节和路由细节等等。这样,当我们要为特性区加入更多的组件和路由,该文件将不用做任何变化。这真是为每个特性区单独创建路由配置的关键性优点,这集中体现了模块化开发的思路。
命令式地导航
首先需要在组件的构造函数中注入Router,然后使用router进行路由导航
import {Router} from"@angular/router";
…..
constructor(private router:Router){
}
无参数导航
this.router.navigate(['/'])
带参数导航
this.router.navigate(['/menu,menu.id]);
路由传值
路由传值有两种方式:
一种是通过路由参数,如http://localhost:63342/URS.Web/home/menu/12
对应路由配置{path:"menu/:id",component:MenuComponent},使用方式this.router.navigate(['/menu',menu.id]);
参数接受方式使用ActivatedRoute 服务
this.sub = this.route.params.subscribe(params=> {
let id = +params['id'];
…..
})
另一种为查询参数,如http://localhost:63342/URS.Web/login?url=/userNav&code=002
对应的路由配置{path:"login",component:LoginComponent},使用方式
this.router.navigate(['/login'],{queryParams: { url: '/userNav',code:’002’} });
接受方式使用Router服务
this.sub = this.router
.routerState
.queryParams
.subscribe(params => {
this.nextUrl = params['url'];
this.code=params[‘code’];
});
这两种方式各有各的好处,但通常情况下如果路由有明确的规则情况下使用路由参数,如list组件导向detail组件,通常会传递id到detail组件,这种方式可以在路由规则中加入:id,这样限制detail组件访问方式必须加入id。
而对于不明确的参数或者参数较为复杂的情况下使用查询参数的方式更为适合。
子路由
在前面的介绍中已经可以实现路由的扁平化,可以做到模块间的路由配置由该模块自身来维护。但在前面的介绍中会发现所有的组件最终显示在app所定义的@H_502_7@RouterOutlet中,这样并不利于模块的展示。
在angular2中可以定义子路由,并且子路由的加载会在父路由所定义的RouterOutlet中,这样就能够由模块自己来维护自身的加载了。
通常情况下需要先定义一个路由组件,该路由组件主要是定义了路由导航及子路由的加载位置,如下
import {Component} from"@angular/core";
import {ROUTER_DIRECTIVES} from"@angular/router";
@Component({
moduleId:module.id,
templateUrl:'admin.component.html',
directives:[ROUTER_DIRECTIVES]
})
export class AdminComponent{
}
这个AdminComponent是一个路由组件,很简单并没有多少内容,只是定义了模板而已,注意这里使用了moduleId相对路径的方式。下面看看模板里的内容
<div>
<ol class="breadcrumb">
<li class="active"><a[routerLink]="['/admin']">admin</a></li>
<li><a[routerLink]="['/admin/users']">UserManager</a></li>
<li><a[routerLink]="['/']">RoleManager</a></li>
</ol>
</div>
<div>
<router-outlet></router-outlet>
</div>
这个模板中可以看到定义了路由的导航及子路由加载的routeroutlet区域,下面看看子路由的配置。
import {RouterConfig} from"@angular/router";
import {AdminComponent} from"./admin.component";
import {UserRoutes} from "../users/users.router";
import {AdminIndexComponent} from"./adminindex.component";
/**
*Created by Administrator on 2016-07-25.
*/
export const AdminRoutes:RouterConfig=[
{path:'',redirectTo:'admin',pathMatch:'full'},
{path:'admin',component:AdminComponent,
children:[
{path:'',component:AdminIndexComponent},
...UserRoutes
]
}
]
在这个路由配置中首先对该区域的跟路径做了重定向,并指向了前面创建的路由组件,接下来配置子路由,在子路由中首先定义了一个缺省的显示组件,默认是放在首位的。这个例子中当我们的路由是/admin/时会默认在该组件定义的routeroutlet处加载AdminIndexComponent组件。下面再次使用了“…”,将Users这个特性区路由加入到子路由中,这样就行成了一个路由树。
这个例子实际是一个管理菜单的导航,实现AdminComponent将其他应用区域如User,Role等等功能模块加入到该路由的子路由中。
路由守卫
到目前为止我们的路由一直畅行无阻,没有任何拦截,但实际中我们需要根据用户的状态、权限等禁止某些路由的访问,这里就要使用angular2提供的路由守卫服务了。
Angular2路由器支持两种守卫:
用 CanActivate 来处理导航到 某路由的情况。
用 CanDeactivate 来处理从当前路由离开 的情况。
这样我们只需在路由配置中加入这两种守卫,并将这两种守卫的方式以服务的方式注入进来就可实现对路由的限制。
先看路由配置
export const UserRoutes:RouterConfig=[
{path:'',redirectTo:'users',
{path:'users',component:UsersComponent,
children:[
{path:'adduser',component:AddUserComponent,canActivate:[AuthGuard]},
{path:'edituser',component:EditUserComponent,canActivate: [AuthGuard]},
{path:'deluser',component:DelUserComponent,
{path:'',component:UserListComponent}
]
}
]
在这个路由配置中可以看到在子路由中加入了canActivate路由守卫,其中'adduser','edituser','deluser'将会受限访问,是否能够导航该路由则要看AuthGuard这个服务所返回的值。下面看看该服务。
import { Injectable } from '@angular/core';
import { CanActivate,Router } from'@angular/router';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implementsCanActivate {
constructor(private authService: AuthService,private router: Router) {}
canActivate() {
if (this.authService.isLogin) { return true; }
this.router.navigate(['/login']);
return false;
}
}
这个服务实现了CanActivate并且设计为可注入的,并在构造函数中注入了AuthService用来与后台连接获取用户状态或权限,同时注入路由服务。在该实例中简单的进行用户登录状态的验证,如果未登录在返回false并导航到登录页面否则继续前面所需的路由。
最后在我们的app路由配置中将AuthGuard注入到系统中。
App.routes.ts
import {RouterConfig,provideRouter} from"@angular/router";
import {Home} from"./shared/home/home.component";
import {AdminRoutes} from"./system/admin/admin.routes";
import {AuthService} from"./shared/auth/auth.service";
import {AuthGuard} from"./shared/auth/auth.guard";
/**
*Created by Administrator on 2016-07-24.
*/
export const routes:RouterConfig=[
{path:'',component:Home},
...AdminRoutes
]
export const authProviders = [AuthGuard,AuthService];
export const AppRouteProvider=[
provideRouter(routes),
authProviders
]
总结
在实际项目开发中采用模块化开发,每个模块都要实现自己的路由,在app路由中只需通过扁平化几个总的模块路由即可,便于整个项目的路由规划。可以通过子路由的方式合并子模块路由,这样能保证模块开发相对独立,降低耦合度。按照前面所提的规范组织项目目录文件结构也能极大方便后期扩展维护。