You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular15中CanDeactivateFn替代旧Guard的实现方法求助

Angular 15 升级后 CanDeactivate Guard 迁移实现方案

核心问题说明

Angular 14+ 弃用了类式的 CanDeactivate 守卫,官方推荐使用函数式的 CanDeactivateFn 配合 inject() 实现。原类式守卫中的实例状态(如 currentUrl)需要迁移到单例服务中维护,因为函数式守卫是无状态的纯函数。


迁移步骤

1. 创建状态管理服务

将原守卫中的 currentUrl 状态移至单例服务,用于跨守卫和组件共享状态:

// deactivate-state.service.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class DeactivateStateService {
  currentUrl: string = '';
}

2. 重构为函数式 CanDeactivate 守卫

保留原业务逻辑,改用 CanDeactivateFn 类型,通过 inject() 获取状态服务:

// can-deactivate.guard.ts
import { CanDeactivateFn, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { DeactivateStateService } from './deactivate-state.service';
import { inject } from '@angular/core';

// 保留原组件接口,确保组件实现规范不变
export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

export const canDeactivateGuard: CanDeactivateFn<CanComponentDeactivate> = (
  component: CanComponentDeactivate,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState?: RouterStateSnapshot
) => {
  const stateService = inject(DeactivateStateService);

  // 原逻辑:当前URL匹配且不是'/datm/0'时直接放行
  if (stateService.currentUrl === currentState.url && stateService.currentUrl !== '/datm/0') {
    return true;
  }
  // 组件不存在时直接放行
  if (!component) {
    return true;
  }

  // 调用组件的canDeactivate方法,无实现则默认放行
  const isCanDeactivate = component.canDeactivate ? component.canDeactivate() : of(true);

  // 处理布尔值、Promise、Observable三种返回类型,确保状态正确更新
  if (typeof isCanDeactivate === 'boolean') {
    if (isCanDeactivate) {
      stateService.currentUrl = currentState.url;
    }
    return isCanDeactivate;
  } else {
    return new Observable<boolean>(observer => {
      Promise.resolve(isCanDeactivate).then(result => {
        if (result) {
          stateService.currentUrl = currentState.url;
        }
        observer.next(result);
        observer.complete();
      });
    });
  }
};

3. 更新路由配置

将原类式守卫替换为函数式守卫:

// routing.ts
{
  path: PathNames.exampleComponent,
  component: ExampleComponent,
  canDeactivate: [canDeactivateGuard] // 直接使用函数引用
}

4. 修改组件中的状态调用

原组件中注入类式守卫的逻辑改为注入状态服务:

// example.component.ts
import { DeactivateStateService } from './deactivate-state.service';

constructor(private deactivateStateService: DeactivateStateService) {}

// 原修改currentUrl的逻辑替换为:
if (value) {
  this.deactivateStateService.currentUrl = '';
}

关键注意点

  • 函数式守卫必须通过 inject() 获取依赖,不能在函数参数中注入
  • 原组件的 CanComponentDeactivate 接口无需修改,组件仍需实现该接口并提供 canDeactivate 方法
  • 状态服务使用 providedIn: 'root' 确保全局单例,避免状态不一致

内容的提问来源于stack exchange,提问作者Karthiga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:14:51