Angular Route导航
路由基础知识
路由相关对象介绍
名称 | 简介 |
---|---|
Routes | 路由配置,保存着哪个URL对应展示哪个组件,以及在哪个RouterOutlet中展示组件。 |
RouterOutlet | 在Html中标记路由内容呈现位置的占位符指令。 |
Router | 负责在运行时执行路由的对象,可以通过调用其navigate()和navigateByUrl()方法来导航到一个指定的路由。 |
RouterLink | 在Html中声明路由导航用的指令。 |
ActivatedRoute | 当前激活的路由对象,保存着当前路由的信息,如路由地址,路由参数等。 |
新建路由项目
使用angular-cli
新建项目。
ng new router --routing
新生成一个带有app-routing.module.ts
路由模块的项目。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
app.module.ts
模块中会导入AppRoutingModule
模块。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule <<=== add here
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
再生成home
和product
组件。
ng g component home
ng g component product
修改新建组件内容
home.component.html
这里是主页组件
product.component.html
这里是商品信息组件
添加路由配置
修改app-routin.module.ts
路由配置文件。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'product', component: ProductComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
修改app.component.html
页面模版。
主页
商品详情
routerLink
参数是数组类型,以便传递参数。
运行项目
ng serve --open
运行项目。
修改app.component.html
给页面模版添加一个商品详情按钮。
主页
商品详情
其中在app.component.ts
实现toproductDetails()
方法
import {Component} from '@angular/core';
import {Router} from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'router';
constructor(private router: Router) {
}
toProductDetails() {
this.router.navigate(['product']);
}
}
这里方法中使用Router
的navigate
方法实现路由跳转。
这里routeLink
是通过前端页面实现跳转,而navigate
是通过后台组件实现跳转。
实现访问不存在页面跳转
创建code404组件,实现默认跳转页面。
ng g component code404
code404.component.html
页面模版内容。
404 Not Found!
路由配置,修改app-routing.module.ts
,添加页面不存时显示页面。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'product', component: ProductComponent},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
路由器使用先匹配者优先选择原则,所以通配符路由要放在路由配置的最后面。
路由时传递数据
第一种方式
/product?id=1&name=2 => ActivateRoute.queryParams[id]
第二种方式
{path: /product/:id} => /product/1 => ActivateRoute.params[id]
第三种方式
{path: /product, component: ProductComponent, data: [{isProd: true}]} => ActivateRoute.data[0][isProd]
修改app.component.html
页面模版。
主页
商品详情
点击页面商品详情
链接,URL跳转变为http://localhost:4200/product?id=1
。
下面来看看怎么在商品详情组件中接受这个传递参数。
首先在商品详情组件product.component.ts
中注入ActivatedRoute
组件,然后在其中声明一个productId
来接收传进来的id
,如第一种方式所示,使用queryParams
获取传递值。
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
this.productId = this.routeInfo.snapshot.queryParams['id'];
}
}
页面上展示ID值。
这里是商品信息组件
商品ID是: {{productId}}
修改app-routing.module.ts
路由配置中的path
属性使其可以携带参数。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'product/:id', component: ProductComponent},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
然后修改app.component.html
路由链接的参数来传递数据。
主页
商品详情
点击页面商品详情
链接,URL跳转变为http://localhost:4200/product/1
如第二种方式,修改商品详情组件product.component.ts
,将queryParams
改为params
即可。
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
this.productId = this.routeInfo.snapshot.params['id'];
}
}
点击商品详情链接页面上展示ID值为1。
什么是参数快照?什么是参数订阅?
参数快照就是snapshot
,这里如果切换商品详情页面,虽然URL会来回切换,但是ID的显示值并不会改变。所以采用参数订阅方式。
参数订阅,subscribe
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
// this.productId = this.routeInfo.snapshot.params['id'];
this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);
}
}
重定向路由
在用户访问一个特定的地址时,将其重定向到另一个指定的地址。
www.aaa.com => www.aaa.com/products
www.aaa.com/x => www.aaa.com/y
修改app-routing.module.ts
,添加重定向路由。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'product/:id', component: ProductComponent},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
子路由
{path: 'home', component: HomeComponent}
{path: 'home', component: HomeComponent,
children: [
{
path: '', component: XxxComponent
},
{
path: '/yyy', component: YyyComponent
}
]}
新建商品描述组件和商品销售员组件。
ng g component product-desc
和ng g component seller-info
修改product-desc.component.html
模版页面内容。
这是一个商品描述页面
修改seller-info.component.html
模版页面内容,并传递sellerId
。
销售员ID是:{{sellerId}}
修改seller-info.component.ts
控制器内容,获取路由传递参数。
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-seller-info',
templateUrl: './seller-info.component.html',
styleUrls: ['./seller-info.component.css']
})
export class SellerInfoComponent implements OnInit {
private sellerId: number;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
this.sellerId = this.routeInfo.snapshot.params['id'];
}
}
修改app-routing.module.ts
,添加商品子路由配置。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{
path: 'product/:id', component: ProductComponent,
children: [
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
]
},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
然后修改product.component.html
模版页面,添加路由选项。
这里是商品信息组件
商品ID是: {{productId}}
商品描述
销售员信息
这里./
表示当前页面下面的路由选项这里就是http://localhost:4200/product/1/seller/99
。
辅助路由
{path: 'xxx', component: XxxComponent, outlet: "aux"}
{path: 'yyy', component: YyyComponent, outlet: "aux"}
Xxx
Yyy
新建聊天组件
ng g component chat
修改模版页面内容
修改app-routing.module.ts
,添加辅助路由配置。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'chat', component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
{
path: 'product/:id', component: ProductComponent,
children: [
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
]
},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
然后修改app.component.html
路由链接的参数来传递数据。
主页
商品详情
开始聊天
结束聊天
如果想同时指定这时主路由的跳转,还请修改为
开始聊天
路由守卫
只有当用户已经登录并拥有某些权限时才能进入某些路由。
一个由多个表单组件组成的向导,例如注册流程,用户只有在当前路由的组件中填写了满足要求的信息才可以导航到下一个路由。
当用户未执行保存操作而试图离开当前导航时提醒用户。
CanActivate
:处理导航到某路由的情况。
CanDeactivate
:处理从当前路由离开的情况。
Resolve
:在路由激活之前获取路由数据。
添加自定义路由守卫login.guard.ts
import {CanActivate} from '@angular/router';
export class LoginGuard implements CanActivate {
canActivate() {
let loginIn: boolean = Math.random() < 0.5;
if (!loginIn) {
console.log('用户未登录');
}
return loginIn;
}
}
修改app-routing.module.ts
,添加路由守卫配置。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
import {LoginGuard} from './guard/login.guard';
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'chat', component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
{
path: 'product/:id', component: ProductComponent,
children: [
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
], canActivate: [LoginGuard]
},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [LoginGuard]
})
export class AppRoutingModule {
}
并且通过在providers
添加LoginGuard
来实现依赖注入。LoginGuard
实现CanActivate
方法。
CanDeactivate
同理。
添加product.resolve.ts
,如果productId
值为1则创建新对象传递进商品详情页面,否则跳转到主页。
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {Product} from '../product/product.component';
import {Injectable} from '@angular/core';
@Injectable()
export class ProductResolve implements Resolve {
constructor(private router: Router) {
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | Product {
let productId: number = route.params['id'];
if (productId == 1) {
return new Product(1, 'Iphone7');
} else {
this.router.navigate(['/home']);
}
}
}
修改app-routing.module.ts
,添加reslove
路由守卫配置。
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
import {LoginGuard} from './guard/login.guard';
import {ProductResolve} from './guard/product.resolve';
const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'chat', component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
{
path: 'product/:id', component: ProductComponent,
children: [
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
], resolve: {
product: ProductResolve
}
},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [ProductResolve]
})
export class AppRoutingModule {
}
resolve
中参数是一个对象,product是想传递进去参数的名字,然后用ProductResolve
来生成。
在商品信息组件中在声明productName
product.component.ts
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private productId: number;
private productName: string;
constructor(private routeInfo: ActivatedRoute) {
}
ngOnInit() {
// this.productId = this.routeInfo.snapshot.params['id'];
this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);
this.routeInfo.data.subscribe((data: { product: Product }) => {
this.productId = data.product.id;
this.productName = data.product.name;
});
}
}
export class Product {
constructor(public id: number, public name: string) {
}
}
将传递进来product
对象的id
和name
赋予我本地的productId
和productName
属性。
修改模版页面product.component.html