路由是 Angular 应用程序的核心,它加载与所请求路由相关联的组件,以及获取特定路由的相关数据。这容许咱们经过控制不一样的路由,获取不一样的数据,从而渲染不一样的页面。html
yarn add @angular/router
# OR
npm i --save @angular/router
以上命令执行后,将会自动下载 @angular/router
模块到 node_modules
文件夹中。正常的angular项目通常不须要进行此步,在构建想的时候已经将此包下载。node
咱们须要作的最后一件事,是将 <base>
标签添加到咱们的 index.html
文件中。路由须要根据这个来肯定应用程序的根目录。例如,当咱们转到 http://example.com/page1
时,若是咱们没有定义应用程序的基础路径,路由将没法知道咱们的应用的托管地址是 http://example.com
仍是 http://example.com/page1
。npm
这件事操做起来很简单,只需打开项目中的 index.html
文件,添加相应的 <base>
标签,具体以下:数组
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Mostlove</title> 6 <base href="/"> 7 8 <meta name="viewport" content="width=device-width, initial-scale=1"> 9 <link rel="icon" type="image/x-icon" href="favicon.ico"> 10 </head>
以上配置信息告诉 Angular 路由,应用程序的根目录是 /
。若是没有此处的配置Angular将没法进行路由功能。app
1.路由相关配置less
路由类设置 dom
/*路由基本模型*/
/*导入RouterModule,Routes类型*/
import { RouterModule, Routes } from '@angular/router'; import { LoginComponent } from "./login/login.component"; /*定义路由const表示不可改变*/ const routers: Routes = [ /* path:字符串,表示默认登入, path为路径 /login component:组件 component:组件 pathMatch:为字符串默认为前缀匹配 "prefix"; "full" 为彻底匹配。 redirectTo:指向为路径,既path outlet:字符串,路由目标,面对多个路由的状况 children:Routes 子路由相关 */ { path: '', component: LoginComponent }, // path:路径 /detail/1 :id表明参数相关 { path: 'detail/:id', component: LoginComponent }, // 懒加载子模块, 子模块须要配置路由设置启动子组件,若是这样设置了路由,须要在子模块中再定义路由 { path: 'other', loadChildren:"./demo/demo.module#demoModule" }, // 重定向,路径为** 表示不能识别的路径信息,重定向到相关路径下 { path: '**', pathMatch: 'full', redirectTo: '' } ]; /*将路由设置导出,子模块中的路由使用 forChild 而不是 forRoot*/ export const appRouter = RouterModule.forRoot(routers);
ngModule设置
编辑器
@NgModule({ declarations: [ ...... ], imports: [ ...... appRouter ] })
组件模板设置
ide
<router-outlet></router-outlet>
2.多路由处理
函数
{ path: 'news', outlet: 'let1', component: }, { path: 'news', outlet: 'let2', component: }, //模板中NewsComponentNews2Cmponent
<router-outlet name="let1"></router-outlet> <router-outlet name="let2"></router-outlet>
访问 /news/
时同时加载 NewsComponent
和 News2Cmponent
两个组件
3.路有连接以及组件中调用路由方法使用
<a routerLink="/detail/1" routerLinkActive="active">detail</a>
<a [routerLink]="['/detail', news.id]">{{news.title}}</a>
<a [routerLink]="[{ outlets: { let2: ['news'] } }]">Contact</a>
routerLinkActive="active"
即在本路由激活时添加样式 .active
或者:
this.router.navigate(['/detail', this.news.id]) this.router.navigate([{ outlets: { let2: null }}]);
其中:navigateByUrl 方法指向完整的绝对路径
4.路由守卫(适用于后台管理等须要登陆才能使用的模块)
import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; @Injectable() export class AuthService implements CanActivate { canActivate() { // 这里判断登陆状态, 返回 true 或 false return true; } }
在路由配置中的设置
{ path: '', component: LoginComponent, canActivate:[LoginComponent] },
5.退出守卫(适合于编辑器修改后的保存提示等场景)
import { Injectable } from '@angular/core'; import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; // CanDeactivateComponent 是定义的接口,见下段代码 import { CanDeactivateComponent } from './can-deactivate.omponent'; @Injectable() export class DeacService implements CanDeactivate<CanDeactivateComponent> { canDeactivate( canDeactivateComponent: CanDeactivateComponent, activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot: RouterStateSnapshot ) { // 目标路由和当前路由 console.log(activatedRouteSnapshot); console.log(routerStateSnapshot); // 判断并返回 return canDeactivateComponent.canDeactivate ? canDeactivateComponent.canDeactivate() : true } }..
// 接口组件, 返回 true 或 false 如表单发生改变则调用对话框服务
export interface CanDeactivateComponent { canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; }
路由配置
{
path: ..., canDeactivate: [DeacService], component: ... }
模块中添加服务
providers: [ DeactivateGuardService ]
要使用路由,咱们须要在 AppModule
模块中,导入 RouterModule
。具体以下:
1 mport { BrowserModule } from '@angular/platform-browser'; 2 import { NgModule } from '@angular/core'; 3 import { FormsModule } from '@angular/forms'; 4 import { HttpModule } from '@angular/http'; 5 import { RouterModule } from "@angular/router";
此时咱们的路由还不能正常工做,由于咱们还未配置应用程序路由的相关信息。RouterModule
对象为咱们提供了两个静态的方法:forRoot()
和 forChild()
来配置路由信息。
RouterModule.forRoot() 方法用于在主模块中定义主要的路由信息,经过调用该方法使得咱们的主模块能够访问路由模块中定义的全部指令。接下来咱们来看一下如何使用 forRoot()
:
1 @NgModule({ 2 declarations: [ 3 AppComponent, 4 NavigationComponent, 5 SideComponent, 6 // DisplayComponent 7 ], 8 imports: [ 9 BrowserModule, 10 FormsModule, 11 HttpClientModule, 12 HttpModule, 13 RouterModule.forRoot(ROUTES), 14 // SideModule 15 ],
咱们经过使用 const
定义路由的配置信息,而后把它做为参数调用 RouterModule.forRoot()
方法,而不是直接使用 RouterModule.forRoot([...])
这种方式,这样作的好处是方便咱们在须要的时候导出 ROUTES
到其它模块中。
RouterModule.forChild() 与 Router.forRoot() 方法相似,但它只能应用在特性模块中。
友情提示:根模块中使用 forRoot()
,子模块中使用 forChild()
这个功能很是强大,由于咱们没必要在一个地方(咱们的主模块)定义全部路由信息。反之,咱们能够在特性模块中定义模块特有的路由信息,并在必要的时候将它们导入咱们主模块。
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { Routes, RouterModule } from '@angular/router'; 4 5 export const ROUTES: Routes = []; 6 7 @NgModule({ 8 imports: [ 9 CommonModule, 10 RouterModule.forChild(ROUTES) 11 ], 12 // ... 13 }) 14 export class ChildModule {}
经过以上示例,咱们知道在主模块和特性模块中,路由配置对象的类型是同样的,区别只是主模块和特性模块中需调用不一样的方法,来配置模块路由。接下来咱们来介绍一下如何配置 ROUTES
对象。
咱们定义的全部路由都是做为 ROUTES
数组中的对象。首先,为咱们的主页定义一个路由:
1 import { Routes, RouterModule } from '@angular/router'; 2 3 import { HomeComponent } from './home/home.component'; 4 5 export const ROUTES: Routes = [ 6 { path: '', component: HomeComponent } 7 ];
示例中咱们经过 path
属性定义路由的匹配路径,而 component
属性用于定义路由匹配时须要加载的组件。
友情提示:咱们使用 path: ''
来匹配空的路径,例如:https://yourdomain.com
配置完路由信息后,下一步是使用一个名为 router-outlet
的指令告诉 Angular 在哪里加载组件。当 Angular 路由匹配到响应路径,并成功找到须要加载的组件时,它将动态建立对应的组件,并将其做为兄弟元素,插入到 router-outlet
元素中。
在咱们 AppComponent
组件中,咱们能够在任意位置插入 router-outlet
指令:咱们如今已经创建了应用程序的主路由,咱们能够进一步了解路由的其它配置选项。
到目前为止咱们已经介绍的内容只是一个开始 ,接下来咱们来看看其它一些选项和功能。
若是路由始终是静态的,那没有多大的用处。例如 path: ''
是加载咱们 HomeComponent
组件的静态路由。咱们将介绍动态路由,基于动态路由咱们能够根据不一样的路由参数,渲染不一样的页面。
例如,若是咱们想要在我的资料页面根据不一样的用户名显示不一样的用户信息,咱们可使用如下方式定义路由:
1 import { HomeComponent } from './home/home.component'; 2 import { ProfileComponent } from './profile/profile.component'; 3 4 export const ROUTES: Routes = [ 5 { path: '', component: HomeComponent }, 6 { path: '/profile/:username', component: ProfileComponent } 7 ];
这里的关键点是 :
,它告诉 Angular 路由,:username
是路由参数,而不是 URL 中实际的部分。
友情提示:若是没有使用 :
,它将做为静态路由,仅匹配 /profile/username
路径
如今咱们已经创建一个动态路由,此时最重要的事情就是如何获取路由参数。要访问当前路由的相关信息,咱们须要先从 @angular/router
模块中导入 ActivatedRoute
,而后在组件类的构造函数中注入该对象,最后经过订阅该对象的 params
属性,来获取路由参数,具体示例以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { ActivatedRoute } from '@angular/router'; 3 4 @Component({ 5 selector: 'profile-page', 6 template: ` 7 <div class="profile"> 8 <h3>{{ username }}</h3> 9 </div> 10 ` 11 }) 12 export class SettingsComponent implements OnInit { 13 username: string; 14 constructor(private route: ActivatedRoute) {} 15 ngOnInit() { 16 this.route.params.subscribe((params) => this.username = params.username); 17 } 18 }
介绍完动态路由,咱们来探讨一下如何建立 child routes
。
实际上每一个路由都支持子路由,假设在咱们 /settings
设置页面下有 /settings/profile
和 /settings/password
两个页面,分别表示我的资料页和修改密码页。
咱们可能但愿咱们的 / settings
页面拥有本身的组件,而后在设置页面组件中显示 / settings/profile
和 / settings/password
页面。咱们能够这样作:
1 import { SettingsComponent } from './settings/settings.component'; 2 import { ProfileSettingsComponent } from './settings/profile/profile.component'; 3 import { PasswordSettingsComponent } from './settings/password/password.component'; 4 5 export const ROUTES: Routes = [ 6 { 7 path: 'settings', 8 component: SettingsComponent, 9 children: [ 10 { path: 'profile', component: ProfileSettingsComponent }, 11 { path: 'password', component: PasswordSettingsComponent } 12 ] 13 } 14 ];
在这里,咱们在 setttings
路由中定义了两个子路由,它们将继承父路由的路径,所以修改密码页面的路由匹配地址是 /settings/password
,依此类推。
接下来,咱们须要作的最后一件事是在咱们的 SettingsComponent
组件中添加 router-outlet
指令,由于咱们要在设置页面中呈现子路由。若是咱们没有在 SettingsComponent
组件中添加 router-outlet
指令,尽管 /settings/password
匹配修改密码页面的路由地址,但修改密码页面将没法正常显示。具体代码以下:
1 import { Component } from '@angular/core'; 2 3 @Component({ 4 selector: 'settings-page', 5 template: ` 6 <div class="settings"> 7 <settings-header></settings-header> 8 <settings-sidebar></settings-sidebar> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class SettingsComponent {}
另外一个颇有用的路由功能是 component-less
路由。使用 component-less
路由容许咱们将路由组合在一块儿,并让它们共享路由配置信息和 outlet。
例如,咱们能够定义 setttings
路由而不须要使用 SettingsComponent
组件:
1 import { ProfileSettingsComponent } from './settings/profile/profile.component'; 2 import { PasswordSettingsComponent } from './settings/password/password.component'; 3 4 export const ROUTES: Routes = [ 5 { 6 path: 'settings', 7 children: [ 8 { path: 'profile', component: ProfileSettingsComponent }, 9 { path: 'password', component: PasswordSettingsComponent } 10 ] 11 } 12 ];
此时, /settings/profile
和 /settings/password
路由定义的内容,将显示在 AppComponent
组件的 router-outlet
元素中。
咱们也能够告诉路由从另外一个模块中获取子路由。这将咱们谈论的两个想法联系在一块儿 - 咱们能够指定另外一个模块中定义的子路由,以及经过将这些子路由设置到特定的路径下,来充分利用 component-less
路由的功能。
让咱们建立一个 SettingsModule
模块,用来保存全部 setttings
相关的路由信息:
1 import { NgModule } from '@angular/core'; 2 import { CommonModule } from '@angular/common'; 3 import { Routes, RouterModule } from '@angular/router'; 4 5 export const ROUTES: Routes = [ 6 { 7 path: '', 8 component: SettingsComponent, 9 children: [ 10 { path: 'profile', component: ProfileSettingsComponent }, 11 { path: 'password', component: PasswordSettingsComponent } 12 ] 13 } 14 ]; 15 16 @NgModule({ 17 imports: [ 18 CommonModule, 19 RouterModule.forChild(ROUTES) 20 ], 21 }) 22 export class SettingsModule {}
须要注意的是,在 SettingsModule
模块中咱们使用 forChild()
方法,由于 SettingsModule
不是咱们应用的主模块。
另外一个主要的区别是咱们将 SettingsModule
模块的主路径设置为空路径 ('')。由于若是咱们路径设置为 /settings
,它将匹配 /settings/settings
,很明显这不是咱们想要的结果。经过指定一个空的路径,它就会匹配 /settings
路径,这就是咱们想要的结果。
那么 /settings
路由信息,须要在哪里配置?答案是在 AppModule
中。这时咱们就须要用到 loadChildren
属性,具体以下:
export const ROUTES: Routes = [ { path: 'settings', loadChildren: './settings/settings.module#SettingsModule' } ];
须要注意的是,咱们没有将 SettingsModule
导入到咱们的 AppModule
中,而是经过 loadChildren
属性,告诉 Angular 路由依据 loadChildren
属性配置的路径去加载 SettingsModule
模块。这就是模块懒加载功能的具体应用,当用户访问 /settings/**
路径的时候,才会加载对应的 SettingsModule
模块,这减小了应用启动时加载资源的大小。
另外咱们传递一个字符串做为 loadChildren
的属性值,该字符串由三部分组成:
须要导入模块的相对路径
#
分隔符
导出模块类的名称
了解完路由的一些高级选项和功能,接下来咱们来介绍路由指令。
除了 router-outlet
指令,路由模块中还提供了一些其它指令。让咱们来看看它们如何与咱们以前介绍的内容结合使用。
为了让咱们连接到已设置的路由,咱们须要使用 routerLink
指令,具体示例以下:
1 <nav> 2 <a routerLink="/">Home</a> 3 <a routerLink="/settings/password">Change password</a> 4 <a routerLink="/settings/profile">Profile Settings</a> 5 </nav>
当咱们点击以上的任意连接时,页面不会被从新加载。反之,咱们的路径将在 URL 地址栏中显示,随后进行后续视图更新,以匹配 routerLink
中设置的值。
友情提示:咱们也能够将 routerLink
的属性值,改为数组形式,以便咱们传递特定的路由信息
若是咱们想要连接到动态的路由地址,且该地址有一个 username
的路由变量,则咱们能够按照如下方式配置 routerLink
对应的属性值:
<a [routerLink]="['/profile', username]"> Go to {{ username }}'s profile. </a>
在实际开发中,咱们须要让用户知道哪一个路由处于激活状态,一般状况下咱们经过向激活的连接添加一个 class 来实现该功能。为了解决上述问题,Angular 路由模块为咱们提供了 routerLinkActive
指令,该指令的使用示例以下:
<nav> <a routerLink="/settings" routerLinkActive="active">Home</a> <a routerLink="/settings/password" routerLinkActive="active">Change password</a> <a routerLink="/settings/profile" routerLinkActive="active">Profile Settings</a> </nav>
经过使用 routerLinkActive
指令,当 a
元素对应的路由处于激活状态时,active
类将会自动添加到 a
元素上。
最后,咱们来简单介绍一下 Router API。
咱们能够经过路由还提供的 API 实现与 routerLink
相同的功能。要使用 Router API,咱们须要在组件类中注入 Router
对象,具体以下:
1 import { Component } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Our app</h3> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class AppComponent { 14 constructor(private router: Router) {} 15 }
组件类中注入的 router
对象中有一个 navigate()
方法,该方法支持的参数类型与 routerLink
指令同样,当调用该方法后,页面将会自动跳转到对应的路由地址。具体使用示例以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Our app</h3> 9 <router-outlet></router-outlet> 10 </div> 11 ` 12 }) 13 export class AppComponent implements OnInit { 14 constructor(private router: Router) {} 15 ngOnInit() { 16 setTimeout(() => { 17 this.router.navigate(['/settings']); 18 }, 5000); 19 } 20 }
若以上代码成功运行,用户界面将在 5 秒后被重定向到 /settings
页面。这个方法很是有用,例如当检测到用户还没有登陆时,自动重定向到登陆页面。
另外一个使用示例是演示页面跳转时如何传递数据,具体以下:
1 import { Component, OnInit } from '@angular/core'; 2 import { Router } from '@angular/router'; 3 4 @Component({ 5 selector: 'app-root', 6 template: ` 7 <div class="app"> 8 <h3>Users</h3> 9 <div *ngFor="let user of users"> 10 <user-component 11 [user]="user" 12 (select)="handleSelect($event)"> 13 </user-component> 14 </div> 15 <router-outlet></router-outlet> 16 </div> 17 ` 18 }) 19 export class AppComponent implements OnInit { 20 users: Username[] = [ 21 { name: 'toddmotto', id: 0 }, 22 { name: 'travisbarker', id: 1 }, 23 { name: 'tomdelonge', id: 2 } 24 ]; 25 26 constructor(private router: Router) {} 27 28 handleSelect(event) { 29 this.router.navigate(['/profile', event.name]); 30 } 31 }
Angular 路由的功能很是强大,既可使用指令方式也可使用命令式 API,但愿本文能够帮助你尽快入门,若要进一步了解路由详细信息,请访问 - Angular Router 官文文档。
navigate()
方法外还有没有其它方法能够实现页面导航?Angular Router API 为咱们提供了 navigate()
和 navigateByUrl()
方法来实现页面导航。那为何会有两个不一样的方法呢?
使用 router.navigateByUrl()
方法与直接改变地址栏上的 URL 地址同样,咱们使用了一个新的 URL 地址。然而 router.navigate()
方法基于一系列输入参数,产生一个新的 URL 地址。为了更好的区分它们之间的差别,咱们来看个例子,假设当前的 URL 地址是:
/inbox/11/message/22(popup:compose)
当咱们调用 router.navigateByUrl('/inbox/33/message/44')
方法后,此时的 URL 地址将变成 /inbox/33/message/44
。但若是咱们是调用 router.navigate('/inbox/33/message/44')
方法,当前的 URL 地址将变成 /inbox/33/message/44(popup:compose)
。