我设计了我的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;
  }
}

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;
  }
}

推荐答案

当我们运行createcomponent时,我们将获得实例访问权限,所以您只需从实例访问canDeactivate函数并验证脏状态.

父 node 是调用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!
  }
}

Typescript相关问答推荐

如何在Jest中嘲笑location.back()方法调用进行Angular 组件测试?

如何将绑定到实例的类方法复制到类型脚本中的普通对象?

从typescript中的对象数组中获取对象的值作为类型'

类型脚本类型字段组合

STypescript 件的标引签名与功能签名的区别

带switch 的函数的返回类型的类型缩小

TypeScrip:逐个 case Select 退出noUncheck kedIndexedAccess

如何消除在Next.js中使用Reaction上下文的延迟?

TypeScrip:来自先前参数的参数中的计算(computed)属性名称

有没有可能产生输出T,它在视觉上省略了T中的一些键?

如何从输入中删除值0

对象类型的TypeScrip隐式索引签名

ANGLE订阅要么不更新价值,要么不识别价值变化

TypeScrip-如何自动推断UNION变量的类型

作为函数参数的联合类型的键

在tabel管理区域react js上设置特定顺序

TypeScript中的这些条件类型映射方法之间有什么区别?

具有枚举泛型类型的 Typescript 对象数组

为什么 TypeScript 类型条件会影响其分支的结果?

递归地排除/省略两种类型之间的公共属性