Angular-路由

路由与导航

在用户使用应用程序时,Angular 的路由器能让用户从一个视图导航到另一个视图

概览

AngularRouter(即“路由器”)把浏览器中的 URL 看做一个操作指南, 据此导航到一个视图,并可以把参数传给支撑视图的相应组件,帮它决定具体该展现哪些内容。
路由器还在浏览器的历史日志中记录下这些活动,这样浏览器的前进和后退按钮也能照常工作。

基础知识

base href元素,来告诉路由器该如何合成导航用的 URL
需要在index.html<head> 标签下先添加一个 <base> 元素。
src/index.html

<base href="/">

从路由库中导入

Angular 的路由器是一个可选的服务,它用来呈现指定的 URL 所对应的视图。 它并不是 Angular 核心库的一部分,而是在它自己的 @angular/router 包中。

src/app/app.module.ts

import { RouterModule, Routes } from '@angular/router';

配置

路由器需要先配置才会有路由信息。 下面的例子创建了五个路由定义,并用 <font size=5>RouterModule.forRoot</font> 方法来配置路由器, 并把它的返回值添加到 AppModuleimports 数组中。

src/app/app.module.ts

const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'hero/:id',      component: HeroDetailComponent },
  {
    path: 'heroes',
    component: HeroListComponent,
    data: { title: 'Heroes List' }
  },
  {
    path:'portal',
    loadChildren: './settings/settings.module#SettingsModule'
  }
  { path: '',
    redirectTo: '/heroes',
    pathMatch: 'full'
  },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [
    RouterModule.forRoot(
      appRoutes,
      { enableTracing: true } // <-- debugging purposes only
    )
    // other imports here
  ],
  ...
})
export class AppModule { }

这里的路由数组 appRoutes 描述如何进行导航。 把它传给 RouterModule.forRoot 方法并传给本模块的 imports 数组就可以配置路由器。
每个 Route 都会把一个 URLpath 映射到一个组件
注意path 不能以斜杠(/)开头,可以以(../)开头。

第二个路由中:id 是一个路由参数的令牌(Token)。比如 /hero/42 这个 URL 中,“42”就是 id 参数的值。

第三个路由中data 属性用来存放于每个具体路由有关的任意信息。该数据可以被任何一个激活路由访问,并能用来保存诸如 页标题、面包屑以及其它静态只读数据。

第四个路由中我们没有将 ettingsModule导入到我们的 AppModule 中,而是通过 loadChildren 属性来告诉 Angular 路由依据 loadChildren 属性配置的路径去加载 SettingsModule 模块。这就是模块懒加载功能的具体应用,当用户访问 /settings/** 路径的时候,才会加载对应的 SettingsModule 模块,这减少了应用启动时加载资源的大小。
另外我们传递一个字符串作为 loadChildren 的属性值,该字符串由三部分组成:
(1)需要导入模块的相对路径
(2)# 分隔符
(3)导出模块类的名称

第五个路由中的空路径('')表示应用的默认路径,当 URL 为空时就会访问那里,因此它通常会作为起点。 这个默认路由会重定向URL /heroes,并显示 HeroesListComponent

最后一个路由中的 ** 路径是一个通配符。当所请求的 URL 不匹配前面定义的路由表中的任何路径时,路由器就会选择此路由。 这个特性可用于显示“404 - Not Found”页,或自动重定向到其它路由。

这些路由的定义顺序是刻意如此设计的。路由器使用先匹配者优先的策略来匹配路由,所以,具体路由应该放在通用路由前面。在上面的配置中,带静态路径的路由被放在了前面,后面是空路径路由,因此它会作为默认路由。而通配符路由被放在最后面,这是因为它能匹配上每一个 URL,因此应该只有在前面找不到其它能匹配的路由时才匹配它。

如果你想要看到在导航的生命周期中发生过哪些事件,可以使用路由器默认配置中的 enableTracing 选项。它会把每个导航生命周期中的事件输出到浏览器的控制台。 这应该只用于调试。你只需要把 enableTracing: true 选项作为第二个参数传给 RouterModule.forRoot()方法就可以了。

路由数组

Routes是路由配置数组。每个都有以下属性:

  • path 是路由匹配的路径。
  • pathMatch 是指定匹配策略的字符串。pathMatch:'full'表示完全匹配
  • matcher定义了路径匹配并取代自定义策略pathpathMatch
  • component 是组件类型。
  • redirectTo 是将替换当前匹配段的url片段。
  • outlet 是组件应放入的插座的名称。
  • canActivate控制是否允许进入路由。。
  • canActivateChild等同 canActivate,只不过针对是所有子路由。。
  • canDeactivate控制是否允许离开路由。
  • canLoad控制是否允许延迟加载整个模块。
  • data是提供给组件的附加数据,被激活路由访问。
  • resolve是用于查找数据解析器的DI令牌的映射。
  • children 是子路由定义的数组。
  • loadChildren是对延迟加载子路由的引用。

注意:路由守卫对于权限控制非常便利,当然其粒度当然只能在页面层级。倘若需要对按钮粒度也只能利用指令的方式,而二者的结合可以极大的改善权限控制埋点的代码量。

RouterModule.forChild()

RouterModule.forChild()Router.forRoot() 方法类似,但它只能应用在特性模块中。

  • 友情提示:根模块中使用 forRoot(),子模块中使用 forChild()

这个功能非常强大,因为我们不必在一个地方(我们的主模块)定义所有路由信息。反之,我们可以在特性模块中定义模块特有的路由信息,并在必要的时候将它们导入我们主模块。RouterModule.forChild() 的使用方法如下:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';

export const ROUTES: Routes = [
  { 
    path: 'settings', 
    component: SettingsComponent,
    ///settings 设置页面下有 /settings/profile 和 /settings/password 两个页面
    children: [
      { path: 'profile', component: ProfileSettingsComponent },
      { path: 'password', component: PasswordSettingsComponent }
    ]
  }
];


@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(ROUTES)
  ],
  // ...
})
export class ChildModule {}

通过以上示例,我们知道在主模块和特性模块中,路由配置对象的类型是一样的,区别只是主模块和特性模块中需调用不同的方法,来配置模块路由。

路由出口

RouterOutlet 是一个来自路由模块中的指令,它的用法类似于组件。 它扮演一个占位符的角色,用于在模板中标出一个位置,路由器将会把要显示在这个出口处的组件显示在这里。

<router-outlet></router-outlet>
<!-- Routed components go here -->

有了这份配置,当本应用在浏览器中的 URL 变为 /heroes 时,路由器就会匹配到 pathheroesRoute,并在宿主视图中的RouterOutlet之后显示 HeroListComponent 组件。

  • 多个路由区域
1.路由配置
const routes: Routes = [
{ path: 'news', 
  component: NewsComponent,
  outlet:'let1'
}
{ path: 'news', 
  component: News2Cmponent,
  outlet:'let2'
}]

2.html点击链接
<a routerLink = "[{ outlets: { let1: ['news'] } }]"></a>
<a routerLink = "[{ outlets: { let2: ['news'] } }]"></a
3.html路由出口
<router-outlet name="let1"></router-outlet>
<router-outlet name="let2"></router-outlet>

即访问 /news/ 时同时加载 NewsComponentNews2Cmponent 两个组件

路由器链接

现在,你已经有了配置好的一些路由,还找到了渲染它们的地方,但又该如何导航到它呢?固然,从浏览器的地址栏直接输入 URL 也能做到,但是大多数情况下,导航是某些用户操作的结果,比如点击一个 A 标签。

考虑下列模板:

src/app/app.component.html

<h1>Angular Router</h1>
<nav>
  <a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
  <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>

a 标签上的 <font size=5>RouterLink</font> 指令让路由器得以控制这个 a 元素。 这里的导航路径是固定的,因此可以把一个字符串赋给 routerLink(“一次性”绑定)。
routerLink第一个路径片段可以以 /./../ 开头:

  • 如果以 / 开头,路由将从根路由开始查找

  • 如果以 ./ 开头或没有使用 / ,则路由将从当前激活路由的子路由开始查找

  • 如果以 ../ 开头,路由往上一级查找

如果需要更加动态的导航路径,那就把它绑定到一个返回链接参数数组的模板表达式。 路由器会把这个数组解析成完整的 URL
例如使用 ['/team', teamId, 'user', userName, {details: true}] 数组,意味着我们想要生成一个链接到 /team/11/user/bob;details=true

  • ts中跳转写法
import { Router } from '@angular/router';
// ...
constructor(private router: Router) {}

// ...

this.router.navigate(['/detail', this.news.id])
this.router.navigate([{ outlets: { let2: null }}]);

navigateByUrl 方法指向完整的绝对路径

路由链接的激活状态

<a routerLink="/user/bob" routerLinkActive="active">Bob</a>

RouterLinkActive 指令:当 URL 地址是 /user/user/bob 时,当前的 RouterState 为活动状态,active 类将会被添加到 <a> 标签上。如果 URL 发生变化,则 active类将自动从 <a> 标签上移除。

路由链接的激活状态会向下级联到路由树中的每个层级,所以,父子路由链接可能会同时激活。
只有当 URL 与当前 URL 精确匹配时才会激活,可以把 [routerLinkActiveOptions] 绑定为 { exact: true } 表达式。

路由器状态

路由器的当前状态(RouterState):在导航时的每个生命周期成功完成时,路由器会构建出一个 ActivatedRoute 组成的
你可以在应用中的任何地方用 Router 服务及其 routerState 属性来访问当前的 RouterState 值。

RouterState 中的每个 ActivatedRoute 都提供了从任意激活路由开始向上或向下遍历路由树的一种方式,以获得关于父、子、兄弟路由的信息。

  class MyComponent {
    constructor(router: Router) {
      const state: RouterState = router.routerState;
      const snapshot: RouterStateSnapshot = state.snapshot;
      const root: ActivatedRouteSnapshot = snapshot.root;
      const child = root.firstChild;
      const id: Observable<string> = child.params.map(p => p.id);
      //...
    }
  }

激活的路由

该路由的路径参数可以通过注入进来的一个名叫ActivatedRoute路由服务来获取。 它有一大堆有用的信息,包括:

属性 说明
url 路由路径的 Observable 对象,是一个由路由路径中的各个部分组成的字符串数组。
data 一个 Observable,其中包含提供给路由的 data 对象。也包含由解析守卫(resolve guard)解析而来的值。
paramMap 一个 Observable,其中包含一个由当前路由的必要参数和可选参数组成的map对象。用这个 map可以获取来自同名参数的单一值或多重值。
queryParamMap 一个 Observable,其中包含一个对所有路由都有效的查询参数组成的map对象。 用这个 map 可以获取来自查询参数的单一值或多重值。
fragment 一个适用于所有路由的 URLfragment(片段)的 Observable
outlet 要把该路由渲染到的 RouterOutlet 的名字。对于无名路由,它的路由名是 primary,而不是空串。
routeConfig 用于该路由的路由配置信息,其中包含原始路径。
parent 当该路由是一个子路由时,表示该路由的父级 ActivatedRoute
firstChild 包含该路由的子路由列表中的第一个 ActivatedRoute
children 包含当前路由下所有已激活的子路由。
//获取路由参数
private route: ActivatedRoute,
this.username = this.route
      .queryParamMap
      .pipe(map(params => this.username = params.username));

路由事件

在每次导航中,Router 都会通过 Router.events 属性发布一些导航事件。这些事件的范围涵盖了从开始导航到结束导航之间的很多时间点。下表中列出了全部导航事件:

路由器事件 说明
NavigationStart 本事件会在导航开始时触发。
RouteConfigLoadStart 本事件会在 Router 惰性加载 某个路由配置之前触发。
RouteConfigLoadEnd 本事件会在惰性加载了某个路由后触发。
RoutesRecognized 本事件会在路由器解析完 URL,并识别出了相应的路由时触发
`GuardsCheckStart 本事件会在路由器开始Guard` 阶段之前触发。
ChildActivationStart 本事件会在路由器开始激活路由的子路由时触发。
ActivationStart 本事件会在路由器开始激活某个路由时触发。
GuardsCheckEnd 本事件会在路由器成功完成了 Guard 阶段时触发。
ResolveStart 本事件会在 Router 开始解析(Resolve)阶段时触发。
ResolveEnd 本事件会在路由器成功完成了路由的解析(Resolve)阶段时触发。
ChildActivationEnd 本事件会在路由器激活了路由的子路由时触发。
ActivationEnd 本事件会在路由器激活了某个路由时触发。
NavigationEnd 本事件会在导航成功结束之后触发。
NavigationCancel 本事件会在导航被取消之后触发。 这可能是因为在导航期间某个路由守卫返回了 false
NavigationError 这个事件会在导航由于意料之外的错误而失败时触发。
Scroll 本事件代表一个滚动事件。

当启用了 enableTracing 选项时,这些事件也同时会记录到控制台中。要想查看对路由导航事件进行过滤的例子,请访问 Angular 中的可观察对象一章的路由器部分

路由守卫

适用于后台管理等需要登录才能使用的模块

  • 创建一个认证服务
// app/auth.service.ts

import { Injectable }     from '@angular/core';
import { CanActivate }    from '@angular/router';

@Injectable()
export class AuthService implements CanActivate {
  canActivate() {
    // 这里判断登录状态, 返回 true 或 false
    return true;
  }
}
  • 添加或修改路由配置
// app/app.router.ts

// 增加 CanActivate
import { CanActivate ... } from '@angular/router';


  // 配置中增加 canActivate 如:
  { path: 'admin', canActivate:[AuthService] ... }

总结一下

该应用有一个配置过的路由器。 外壳组件中有一个 RouterOutlet,它能显示路由器所生成的视图。 它还有一些 RouterLink,用户可以点击它们,来通过路由器进行导航。

下面是一些路由器中的关键词汇及其含义

路由器部件 含义
Router(路由器) 为激活的 URL 显示应用组件。管理从一个组件到另一个组件的导航ts->this.router.navigateByUrl("/protel")
RouterModule 一个独立的 Angular 模块,用于提供所需的服务提供商,以及用来在应用视图之间进行导航的指令。ts->RouterModule.forRoot(Routers数组,ExtraOptions对象)
Routes(路由数组) 定义了一个路由数组,每一个都会把一个 URL 路径映射到一个组件。ts->[(path:'',componet: ***)]
Route(路由) 定义路由器该如何根据 URL 模式(pattern)来导航到组件。大多数路由都由路径和组件类构成。
RouterOutlet(路由出口) 该指令(<router-outlet>)用来标记出路由器该在哪里显示视图。
RouterLink(路由链接) 这个指令把可点击的 HTML 元素绑定到某个路由。点击带有 routerLink 指令(绑定到字符串或链接参数数组)的元素时就会触发一次导航。html-><a [routerLink]="[./order]"></a>
RouterLinkActive(活动路由链接) HTML 元素上或元素内的routerLink变为激活或非激活状态时,该指令为这个 HTML 元素添加或移除 CSS 类。html中
ActivatedRoute(激活的路由) 为每个路由组件提供的一个服务,它包含特定于路由的信息,比如路由参数、静态数据、解析数据、全局查询参数和全局碎片(fragment)。ts中
RouterState(路由器状态) 路由器的当前状态包含了一棵由程序中激活的路由构成的树。它包含一些用于遍历路由树的快捷方法。
链接参数数组 这个数组会被路由器解释成一个路由操作指南。你可以把一个RouterLink绑定到该数组,或者把它作为参数传给Router.navigate方法。
路由组件 一个带有RouterOutletAngular 组件,它根据路由器的导航来显示相应的视图。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • 摘要:在本教程中,Ahmed Bouchefra 介绍了angular路由器(router),以及如何使用它创建客...
    哈维尔23456阅读 3,277评论 0 3
  • 官网链接: angular官网 路由与导航最好是跟着官网写一遍代码,然后来看这个总结,会比较清晰 如何实现一个简单...
    H_DaYan阅读 3,223评论 0 6
  • 路由配置:路由配置是一个Routes类型的数组 例如: 创建根路由模块: 通过调用RouterModule.for...
    我不傻_cyy阅读 2,222评论 0 1
  • 一:路由基础 什么是路由: 在web开发中,路由的概念由来已久,简而言之,就是利用URL的唯一性来指定特定的事物,...
    真的稻城阅读 6,015评论 2 7
  • 一、SPA 单页Web应用(single page web application,SPA),就是只有一张Web页...
    笨蛋小明阅读 881评论 0 3