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';
复制代码

配置

路由器需要先配置才会有路由信息。 下面的例子创建了五个路由定义,并用 RouterModule.forRoot 方法来配置路由器, 并把它的返回值添加到 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 标签上的 RouterLink 指令让路由器得以控制这个 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 组件,它根据路由器的导航来显示相应的视图。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值