Я разработал свою страницу Angular с кнопками, которые ведут к тому же компоненту, который называется ParentComponent. Каждая из этих кнопок передает разные параметры запроса. У ParentComponent есть ViewContainerRef, который программно загружает дочерний компонент в зависимости от параметра запроса, передаваемого кнопками. Например, если в качестве параметра передан «cmp1», будет загружен Child1Component. Если передан «cmp2», он загрузится Child2Component. Я определил это отображение в классе констант ComponentModels.
Теперь у каждого из дочерних компонентов есть состояние IsDirty (и другие внутренние условия), которое, если оно истинно, должно отображать диалоговое окно подтверждения, когда пользователь пытается покинуть страницу или уйти со страницы. В принципе, этого механизма можно добиться, если реализовать 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 {}
РодительскийКомпонент.html
<h1>Parent View</h1>
<div #container></div>
РодительскийКомпонент.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;
}
}
Компонент-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;
}
}





Мы получим доступ к экземпляру при запуске компонента create, поэтому вы можете просто получить доступ к функции 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!
}
}