我设计了带有路由到同一组件的按钮的 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;
}
}
组件模型.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;
}
}
当我们运行创建组件时,我们将获得实例访问权限,因此您可以从实例访问
canDeactivate
函数并验证脏状态。
父级是 canDeactivate 被调用的地方,因为它是路由中指定的组件!
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;
componentRef: ComponentRef<any>; // <- changed here!
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.componentRef = this.container.createComponent(component); // <- changed here!
}
canDeactivate(): boolean | Promise<boolean> {
return this.componentRef?.instance?.canDeactivate(); // <- changed here!
}
}