Angular 5中CanDeactivate仅触发一次及自定义PrimeNG模态框问题
嘿,我来帮你搞定Angular 5里CanDeactivate守卫只触发一次的问题,结合你用PrimeNG p-dialog的场景,咱们一步步理清楚:
问题根源分析
为啥守卫只触发一次呢?大概率是因为你返回的Observable/Promise没有在每次导航时重新创建,或者模态框的状态没重置到位,导致后续导航时守卫直接复用了之前的结果,没重新走判断逻辑。比如第一次弹出模态框后,Observable没有正确完成或者状态没有重置,下次导航就不会再触发新的判断流程了。
解决方案
我们需要确保每次触发CanDeactivate时,都生成新的Observable来处理模态框的确认/取消操作,同时正确管理模态框的显示状态和回调逻辑。
1. 重构守卫服务代码
修改你的DeactivateUnsavedChangesService,保证每次canDeactivate调用都返回全新的Observable,并且通过Subject来接收模态框的操作结果:
import { Injectable } from '@angular/core'; import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, Subject } from 'rxjs'; // 替换成你实际的组件类型,方便类型校验 import { TestPageComponent } from './test-page.component'; @Injectable() export class DeactivateUnsavedChangesService implements CanDeactivate<TestPageComponent> { canDeactivate( component: TestPageComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot ): Observable<boolean> | boolean { // 先判断组件是否存在未保存的测试状态(这里假设组件有对应的判断方法) if (!component.hasUnsavedTestChanges()) { return true; // 没有未保存内容,直接允许导航 } // 每次都创建新的Subject,避免复用旧的Observable const confirmSubject = new Subject<boolean>(); // 调用组件的方法打开模态框,并传入回调函数接收结果 component.openUnsavedConfirmDialog((result: boolean) => { confirmSubject.next(result); confirmSubject.complete(); // 完成Observable,防止内存泄漏 }); return confirmSubject.asObservable(); } }
2. 组件内处理模态框逻辑
在你的测试页面组件里,实现打开模态框的方法,并且绑定按钮的点击事件来触发回调:
import { Component } from '@angular/core'; @Component({ selector: 'app-test-page', templateUrl: './test-page.component.html', styleUrls: ['./test-page.component.css'] }) export class TestPageComponent { isConfirmDialogVisible = false; private dialogCallback: (result: boolean) => void; // 业务逻辑:判断是否有未保存的测试内容 hasUnsavedTestChanges(): boolean { // 这里替换成你的实际判断逻辑,比如测试已启动且未保存 return this.testIsStarted && !this.testIsSaved; } // 打开模态框,接收守卫传来的回调 openUnsavedConfirmDialog(callback: (result: boolean) => void): void { this.dialogCallback = callback; this.isConfirmDialogVisible = true; } // 确认离开的按钮事件 onConfirmLeave(): void { this.isConfirmDialogVisible = false; this.dialogCallback(true); // 告诉守卫允许导航 } // 取消离开的按钮事件 onCancelLeave(): void { this.isConfirmDialogVisible = false; this.dialogCallback(false); // 告诉守卫阻止导航 } }
3. 模板中配置PrimeNG的p-dialog
在组件的HTML模板里,绑定模态框的显示状态和按钮事件:
<p-dialog header="未保存提示" [(visible)]="isConfirmDialogVisible" [modal]="true" [closable]="false" [resizable]="false"> <p>您的测试尚未保存,确定要离开当前页面吗?</p> <p-footer> <button pButton type="button" label="取消" (click)="onCancelLeave()"></button> <button pButton type="button" label="确定离开" class="p-button-danger" (click)="onConfirmLeave()"></button> </p-footer> </p-dialog>
4. 路由中正确配置守卫
确保守卫服务被正确注册到路由和模块中:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { TestPageComponent } from './test-page.component'; import { DeactivateUnsavedChangesService } from './deactivate-unsaved-changes.service'; const routes: Routes = [ { path: 'test', component: TestPageComponent, canDeactivate: [DeactivateUnsavedChangesService] } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [DeactivateUnsavedChangesService] // 确保守卫被注入 }) export class TestRoutingModule { }
关键注意事项
- 每次
canDeactivate调用都要创建新的Subject,绝对不能复用同一个Observable,否则第一次触发后后续导航会直接用旧结果。 - 模态框关闭后一定要调用
confirmSubject.complete(),避免内存泄漏。 - 组件里的
dialogCallback要在每次打开模态框时重新赋值,确保每次导航的回调都是最新的。
这样修改后,每次用户尝试导航离开时,都会触发守卫并弹出模态框,不会再出现只触发一次的问题啦~
内容的提问来源于stack exchange,提问作者dangerisgo
相关产品推荐
相关产品推荐

