如何为ViewContainerRef加载的Angular组件实现CanDeactivate守卫?

问题描述 投票:0回答:1

我设计了带有路由到同一组件的按钮的 Angular 页面,称为

ParentComponent
。每个按钮都会传递不同的查询参数。
ParentComponent
有一个
ViewContainerRef
,它根据按钮传递的查询参数以编程方式加载子组件。例如,如果“cmp1”作为参数传递,它将加载
Child1Component
。如果传递“cmp2”,它将加载
Child2Component
。我在
ComponentModels
常量类中定义了此映射。

现在,每个子组件都有一个

IsDirty
状态(以及其他内部条件),当该状态为 True 时,当用户尝试离开或导航离开页面时,应该显示一个确认对话框。原则上,如果我实现一个
CanDeactivate
守卫就可以实现这个机制,但我不知道把它放在哪里。

我的问题是:

  1. 哪个组件应该实现
    CanDeactivate
    防护?它应该在
    ParentComponent
    还是子组件中?据我了解,
    CanDeactivate
    仅适用于路由模块中定义的那些组件。就我而言,路由器模块中仅定义了
    ParentComponent
  2. 如果
    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;
  }
}
angular typescript routes components guard
1个回答
0
投票

当我们运行创建组件时,我们将获得实例访问权限,因此您可以从实例访问

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!
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.