
本文共 15771 字,大约阅读时间需要 52 分钟。
路由基础知识
路由相关对象介绍
新建一个项目
ng new router --routing
并使用webstrom 打开
发现--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 也会导入这个模块
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 ], providers: [], bootstrap: [AppComponent]})export class AppModule { }
app.component.html 下面也会多一个插座
为了展示功能 先创建两个组件
ng g component home
ng g component product
修改 home.component.html
这里是主页组件
product.component.html
这是是商品信息组件
修改 app.component.html
主页商品详情
运行查看效果
选择商品的时候上面地址会自动变成
加个按钮 使用控制器跳转
主页商品详情
修改 app.component.ts
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']); }}
效果同routerLink
当用户使用不存在的路径
创建一个404组件
ng g component code404
修改 code404.component.html
页面不存在
修改 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 >> ActivatedRoute.queryParams[id]
在路由的路径中传递数据
{path:/product/:id} >> /product/1 ActivatedRoute.params[id]
在路由配置中传递数据
{path:/product,comonent:ProductCompomemt,data[{isProd:true}]} >> ActivatedRoute.data[0][isProd]
下面我们继续修改demo 查看效果
app.component.html
主页商品详情
product.component.ts
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 { public productId: number; constructor(private routeInfo: ActivatedRoute) { } ngOnInit() { this.productId = this.routeInfo.snapshot.queryParams['id']; }}
这是是商品信息组件
商品Id是{ {productId}}
下面第二种方式
app.component.html
主页商品详情
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/:id', component: ProductComponent}, {path: '**', component: Code404Component},];@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule]})export class AppRoutingModule {}
product.component.ts
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 { public productId: number; constructor(private routeInfo: ActivatedRoute) { } ngOnInit() { this.productId = this.routeInfo.snapshot.params['id']; }}
在写第三种方式的时候 我们要明确两个概念 参数快照 和参数订阅
修改 app.component.ts
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', 2]); }}
然后操作 网页
上面 主页 商品详情(p) 商品详情(button) 我们 称为1 2 3
点击 1 点击 3 效果正常
点击 1 点击2 效果正常
点击 1 点击2 点击3 效果如上图所示
这是因为product.component.ts中的
ngOnInit() { this.productId = this.routeInfo.snapshot.params['id']; }
使用了参数快照
我们改成参数订阅试一下
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 { public productId: number; constructor(private routeInfo: ActivatedRoute) { } ngOnInit() { this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']); }}
我们会发现 即使 点击 1 点击2 点击3 就可以了 效果就没问题了
重定向路由
在用户访问一个特定的地址时,将其重定向到另一个指定的地址、
www.aaa.com >> www.aaa.com/productswww.aaa.com/x >> www.aaa.com/y
修改路由配置
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' compenent:YyyComponent}]}
当访问home 路径的时候 还是会展现Home组件的模版 但是
router-outlet会显示Xxx组价模版
同理 访问home/yyy的时候 还是会展现Home组件的模版 但是
router-outlet会显示Yyy组价模版
现在我们来一步一步来实现子路由
新建两个组件
ng g component productDescng g component sellerInfo
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 {}
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 { public sellerId: number; constructor(private routeInfo: ActivatedRoute) { } ngOnInit() { this.sellerId = this.routeInfo.snapshot.params['id']; }}
seller-info.component.html
销售员ID是{ {sellerId}}
product-desc.component.html
这是一个牛叉的商品
product.component.html
这是是商品信息组件
商品Id是{ {productId}}
商品描述销售员信息
子路由不仅仅可以嵌套两层 随着需求的复杂 可以无限往下嵌套
辅助路由
配置分为三步{path: 'xxx',component: XxxComponent,outlet: 'aux'}{path: 'yyy',component: YxxComponent,outlet: 'aux'} Xxx Yyy
辅助路由案例 整体思路
在app 组件的模板上 再定义一个插座来显示聊天面板
单独开发一个聊天的插件,只显示在新定义的插座上
通过路由参数控制新插座是否显示聊天面板
ng g component chat
chat.component.html
.chat{ background: aquamarine; height: 100px; width: 30%; float: left; box-sizing: border-box;}
home.component.html
.home{ background: red; height: 100px; width: 70%; float: left; box-sizing: border-box;}
product.component.html
product.component.css
.product{ background: yellow; height: 100px; width: 70%; float: left; box-sizing: border-box;}
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
主页商品详情开始聊天结束聊天
其中 primary:'home' 是为了 不论每次切换到chat组件时候 主页都显示home
路由守卫
使用场景
- 只有当用户已经登录并拥有某些权限时才能进入某些路由。
- 一个由多个表单组件组成的向导,例如注册流程,用户只有在当前路由的组件中填写了满足需求的信息才可以导航到下一路由。
- 当用户未执行保存操作而试图离开当前导航时提醒用户。
可以使用钩子来完成这些操作
有三种路由守卫
- CanActivate:处理导航到某路由的情况。
- CanDeactivate:处理当前路由离开的情况。
- Resolve:在路由激活之前获取路由数据。
下面我们继续修改案例 只有当用户已经登录并拥有某些权限时才能进入产品路由
在app目录下 新建一个guard 文件夹 并新建一个 login.guard.ts
import {CanActivate} from '@angular/router';export class LoginGuard implements CanActivate { canActivate() { const loggedIn: boolean = Math.random() < 0.5; if (!loggedIn) { console.log('用户未登录'); } return loggedIn; }}
修改路由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';import {UnsavedGuard} from './guard/Unsaved.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 {}
这样就只有百分之50的概率进入商品路由了
我们再新建一个 CanDeactivate:处理当前路由离开的情况。
guard 文件下 新建 Unsaved.guard.ts
import {CanDeactivate} from '@angular/router';import {ProductComponent} from '../product/product.component';export class UnsavedGuard implements CanDeactivate{ canDeactivate(component: ProductComponent) { return window.confirm('你还没有保存,确定要离开么?'); }}
修改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';import {UnsavedGuard} from './guard/Unsaved.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], canDeactivate: [UnsavedGuard], }, {path: '**', component: Code404Component},];@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [LoginGuard, UnsavedGuard],})export class AppRoutingModule {}
当从商品离开到别的组件的时候 就会弹出对话框
下面使用Resolve守卫
在guard文件下 创建product.resolve.ts
代码如下
///import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';import {Product} from '../product/product.component';import {Observable} from 'rxjs';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, 'iPhoneX'); } else { this.router.navigate(['/home']); return undefined; } }}
product.component.html
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 { public productId: number; public productName: string; constructor(private routeInfo: ActivatedRoute) { } ngOnInit() { 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) { }}
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';import {UnsavedGuard} from './guard/Unsaved.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: [LoginGuard, UnsavedGuard, ProductResolve],})export class AppRoutingModule {}
我们之前命名的1 2 3 还记得么
点击1 的时候 主页 点击2 的时候 进的是id 1 所以能进去 显示苹果7 点击3 的时候 进去的是id 2 所以显示失败 进入了主页
发表评论
最新留言
关于作者
