如何为ViewContainerRef加载的Angular组件实现CanDeactivate守卫
问题解答与设计建议
1. CanDeactivate守卫的实现位置
你的路由仅将ParentComponent配置为路由组件,Angular的CanDeactivate守卫只会作用于路由中定义的组件,因此必须在ParentComponent中实现CanComponentDeactivate接口,让守卫能调用它的canDeactivate方法。子组件直接实现接口无效,因为它们不是路由级别的组件,守卫不会主动触发子组件的对应方法。
2. 获取当前子组件的IsDirty状态
需要在ParentComponent中保存当前加载的子组件实例,通过实例调用子组件的canDeactivate方法来获取状态,步骤如下:
- 在
ParentComponent中添加属性存储当前子组件实例 - 修改
loadComponent方法,创建组件后保存实例,并清理旧组件 - 在
ParentComponent的canDeactivate方法中,委托当前子组件处理逻辑
额外设计建议
- 统一子组件接口:确保所有动态加载的子组件严格实现
CanComponentDeactivate接口,避免类型错误 - 组件销毁清理:每次加载新组件前调用
this.container.clear()销毁旧组件,防止内存泄漏 - 异步逻辑支持:子组件的
canDeactivate可以返回Promise<boolean>,处理弹出确认对话框等异步场景,Angular守卫会自动等待结果 - 订阅清理:在
ParentComponent的ngOnDestroy中取消路由参数订阅,避免内存泄漏 - 修正代码错误:你的
Child2Component.ts中类名写错为Child1Component,需修正为Child2Component
修改后的关键代码示例
ParentComponent.ts
import { Component, OnInit, ViewChild, ViewContainerRef, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; import { ComponentModels } from '../../models/component-models'; import { CanComponentDeactivate } from '../../guards/can-deactivate.guard'; @Component({ selector: 'app-parent', templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent implements OnInit, OnDestroy, CanComponentDeactivate { @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef; currentChildComponent: CanComponentDeactivate | null = null; private paramMapSubscription?: Subscription; constructor( private readonly route: ActivatedRoute, private readonly router: Router ) {} ngOnInit() {} ngAfterViewInit() { this.paramMapSubscription = this.route.paramMap.subscribe((params) => { const id = params.get('id'); if (id && ComponentModels[id]) { this.loadComponent(ComponentModels[id]); } }); } loadComponent(component: any) { this.container.clear(); const componentRef = this.container.createComponent(component); this.currentChildComponent = componentRef.instance as CanComponentDeactivate; } canDeactivate(): boolean | Promise<boolean> { return this.currentChildComponent?.canDeactivate() ?? true; } ngOnDestroy() { this.paramMapSubscription?.unsubscribe(); } }
Child2Component.ts
import { CanComponentDeactivate } from '../../guards/can-deactivate.guard'; export class Child2Component implements CanComponentDeactivate { isDirty: boolean = false; conditionX: boolean = true; conditionY: boolean = true; canDeactivate(): boolean | Promise<boolean> { if (this.isDirty && this.conditionX && this.conditionY) { // 示例:浏览器原生确认对话框 return confirm('您有未保存的更改,确定要离开吗?'); // 若使用自定义模态框,可返回Promise: // return this.modalService.openConfirmDialog().then(result => result); } return true; } }
内容的提问来源于stack exchange,提问作者Mark U
相关产品推荐
相关产品推荐

