我设计了我的Angular页面,其中的按钮路由到同一个组件,称为ParentComponent
.每个按钮都传递不同的查询参数.ParentComponent
有一个ViewContainerRef
,它根据按钮传递的查询参数以编程方式加载子组件.例如,如果"cmp1"作为参数传递,它将加载Child1Component
.如果"cmp2"被传递,它将加载Child2Component
.我在一个ComponentModels
常量类中定义了这个映射.
现在,每个子组件都有一个IsDirty
状态(和其他内部条件),当为True时,当用户试图离开或离开页面时,应该会显示一个确认对话框.原则上,如果我实现了CanDeactivate
保护,这个机制就可以实现,但我不知道该把它放在哪里.
我的问题是:
- 哪个组件应该执行
CanDeactivate
守卫?它应该在ParentComponent
或子组件中?据我所知,CanDeactivate
只适用于路由模块中定义的组件.在我的例子中,路由模块中只定义了ParentComponent
. - 如果
ParentComponent
实现了保护,它将如何能够获得加载子组件的IsDirty
状态?每个子组件都有不同的条件来决定它是否真的可以被停用.
我是Angular的新手,所以我不确定我的设计是复杂的还是不正确的.任何意见或建议将不胜感激.谢谢!
MainComponent.html
<p>This is the Main Page</p>
<button (click)="openChild1()">Open Child 1</button>
<button (click)="openChild2()">Open Child 2</button>
MainComponent.ts
export class MainComponent {
constructor(private router: Router) {}
openChild1() {
this.router.navigate(['child', 'cmp1']);
}
openChild2() {
this.router.navigate(['child', 'cmp2']);
}
}
AppRouting.module.ts
import { MainComponent } from './components/main/main.component';
import { ParentComponent } from './components/parent/parent.component';
const routes: Routes = [
{ path: '', component: MainComponent },
{ path: 'child/:id', component: ParentComponent, canDeactivate: [CanDeactivateGuard]},
];
@NgModule({
imports: [CommonModule],
declarations: [],
})
export class AppRoutingModule {}
ParentComponent.html
<h1>Parent View</h1>
<div #container></div>
ParentComponent.ts
import { Component, OnInit } from '@angular/core';
import { ChildComponents } from '../../models/child-components';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
})
export class ParentComponent implements OnInit, {
@ViewChild('container', { read: ViewContainerRef })
container!: ViewContainerRef;
constructor(
private readonly route: ActivatedRoute,
private readonly router: Router
) {}
ngAfterViewInit() {
this.route.paramMap.subscribe((params) => {
let id = params.get('id');
if (id) {
this.loadComponent(ChildComponents[id]);
}
});
}
loadComponent(component: any) {
this.container.createComponent(component);
}
canDeactivate(): boolean | Promise<boolean> {
// should ParentComponent implement canDeactivate?
// how can it get the IsDirty state of the loaded child component?
return true;
}
}
Component-Models.ts
import { Child1Component } from '../components/child1/child1.component';
import { Child2Component } from '../components/child2/child2.component';
export const ComponentModels: { [key: string]: any } = {
cmp1: Child1Component,
cmp2: Child2Component,
};
CanDeactivate.guard.ts
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
export interface CanComponentDeactivate {
canDeactivate: () => boolean | Promise<boolean>;
}
@Injectable({
providedIn: 'root'
})
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(
component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
Child1Component.ts
export class Child1Component implements CanComponentDeactivate {
isDirty: boolean;
constructor() {
}
canDeactivate(): boolean | Promise<boolean> {
//is this correct?
return this.isDirty === true;
}
}
Child2Component.ts
export class Child1Component implements CanComponentDeactivate {
isDirty: boolean;
constructor() {
}
canDeactivate(): boolean | Promise<boolean> {
//is this correct?
return this.isDirty === true && conditionX && conditionY;
}
}