- resolver 可用来预先获取 将要导航去某路由之前 目的路由的数据,以便在组件渲染后,将数据显示出来,防止异步延迟带来的无数据用户体验
- 这里接着官网的案例;
ng g service crisis-center/crisis-detail-resolver // 创建resolver守卫服务 // crisis-detail-resolver.service.ts import { Injectable } from '@angular/core'; import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; import { Observable, of, EMPTY } from 'rxjs'; import { mergeMap, take } from 'rxjs/operators'; import { CrisisService } from './crisis.service'; import { Crisis } from './crisis'; @Injectable({ providedIn: 'root', }) export class CrisisDetailResolverService implements Resolve<Crisis> { constructor(private cs: CrisisService, private router: Router) { } // 在导航到详情路由前,就将数据初始化以保证数据预先加载,提高用户体验(防止数据延迟导致无法即时显示) resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> | Observable<never> { let id = route.paramMap.get('id'); return this.cs.getCrisis(id).pipe( take(1), // 确保这个可观察对象会从getCrisis()中取到第一个值就结束 mergeMap(crisis => { if (crisis) { return of(crisis); } else { // id not found this.router.navigate(['/crisis-center']); return EMPTY; } }) ); } }
- 将该服务导入到该模块路由中(crisis-center-routing.module.ts)
// .... import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; const crisisCenterRoutes: Routes = [ { path: 'crisis-center', component: CrisisCenterComponent, children: [ { path: '', component: CrisisListComponent, children: [ { path: ':id', component: CrisisDetailComponent, resolve: { crisis: CrisisDetailResolverService }}, { path: '', component: CrisisCenterHomeComponent} ]} ] } ]; // ...
- 在crisis-detail.component.ts 中;因为有了这个预先获取数据的服务;只需要从这个路由的data中获取数据(ActivateRoute.data 里包含了预先处理的解析数据)
import { Component, OnInit, Input } from '@angular/core'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; // 导入类 import { Observable } from 'rxjs'; import { Crisis } from '../crisis'; @Component({ selector: 'app-crisis-detail', templateUrl: './crisis-detail.component.html', styleUrls: ['./crisis-detail.component.css'] }) export class CrisisDetailComponent implements OnInit { crisis: Crisis; editName: string; // 服务注入 constructor( private router: Router, private activatedRoute: ActivatedRoute ) { } ngOnInit() { // ActivatedRoute 的 data; 它是一个Observable对象,包含解析守卫resolve 解析的值; this.activatedRoute.data.subscribe((data: { crisis: Crisis }) => { console.log(data); this.editName = data.crisis.name; this.crisis = data.crisis; }) } // 使用相对路径导航,需要提供当前的ActivatedRoute,让路由器知道当前处于路由树的位置 // 使用: 链接参数数组后,添加relativeTo属性,并设置为当前的ActivatedRoute gotoCrises() { let crisisId = this.crisis ? this.crisis.id : null; this.router.navigate(['../', { id: crisisId, foo: 'foo' }], { relativeTo: this.activatedRoute }); // 相对路由跳转; } save() { this.crisis.name = this.editName; this.gotoCrises(); } cancel() { this.gotoCrises(); } }
<!-- // crisis-detail.html --> <h2>crisisES</h2> <div *ngIf="crisis"> <h3>"{{ editName }}"</h3> <div> <label>Id: </label>{{ crisis.id }}</div> <div> <label>Name: </label> <input [(ngModel)]="editName" placeholder="name"/> </div> <p> <button (click)="save()">Save</button> <button (click)="cancel()">Cancel</button> </p> </div>
- 现在可以对详情进行更改操作,但是如果我们离开这个页面,未点击保存事件的值将不会被保存,此时需要提供一个处理未保存的更改功能么,以提高用户体验;
- CanDeactivate 守卫 可以对这个需求提供解决方法;
-
ng generate service dialog // 创建一个对话框服务,该服务处理用户确认或取消操作 // 内容 import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DialogService { constructor() { } // 显示对话框,等待用户操作,返回observable ,当用户最终决定后,它会被解析; confirm(message?: string): Observable<boolean> { const confirmation = window.confirm(message || 'Is it OK?'); return of(confirmation); } }
- 创建一个守卫,以检查组件是否存在canDeactivate()方法,因为守卫可 不需要知道 (任意)组件确认退出的激活状态, 但是它只需要检查该组件是否有一个canDeactivate()方法,并调用这个方法即可;
ng generate guard can-deactivate // 内容 import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { Observable } from 'rxjs'; export interface CanComponentDeactivate { canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean; } @Injectable({ providedIn: 'root', }) export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> { canDeactivate(component: CanComponentDeactivate) { return component.canDeactivate ? component.canDeactivate() : true; } }
- 在crisis-center-routing.module.ts中配置守卫
import { CanDeactivateGuard } from '../can-deactivate.guard'; import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; const crisisCenterRoutes: Routes = [ { path: 'crisis-center', component: CrisisCenterComponent, children: [ { path: '', component: CrisisListComponent, children: [ // tslint:disable-next-line: max-line-length { path: ':id', component: CrisisDetailComponent, canDeactivate: [CanDeactivateGuard], resolve: { crisis: CrisisDetailResolverService }}, { path: '', component: CrisisCenterHomeComponent} ]} ] } ];
- 在crisis-detail.component.ts 中使用canDeactivate
// 处理未保存的更改确认 // canDeactivate 方法可以同步返回,如果没有危机,或者没有未定的修改,它就立即返回 true。但是它也可以返回一个承诺(Promise)或可观察对象(Observable),路由器将等待它们被解析为真值(继续导航)或假值(留下) canDeactivate(): Observable<boolean> | boolean { if (!this.crisis || this.crisis.name === this.editName) { return true; } return this.dialogService.confirm('Discard changes?'); }
- 效果展示