Angular CLI:嵌套 Material 对话框的误报循环依赖警告?

Angular CLI: False positive circular dependency warning for nested Material Dialogs?

我的 Angular 8 应用程序使用服务 class,该服务包装了 Angular Material 对话框实现并根据许多不同的组件类型呈现对话框。这是它的简化版本:

@Injectable()
export class MyService {
    renderDialogTypeOne() {
        // implementation here calls
        // matDialog.open(TypeOne)
    }

    renderDialogTypeTwo() {
        // implementation here calls
        // matDialog.open(TypeTwo)
    }
}

由于此服务 class 引用它呈现的类型,因此它依赖于它们。但是,其中一种呈现类型(下面的 TypeTwo)也将上述服务注入到其构造函数中,以便它可以启动自己的 TypeOne:

对话框
export class TypeOne {
}

export class TypeTwo {
    contructor(private service: MyService) { }

    showNestedDialog() {
        this.service.renderDialogTypeOne();
    }
}

因此,服务 class 和 TypeTwo 之间似乎存在循环依赖关系。我知道我可以通过将服务 class 分成多个部分并仅引用给定上下文中所需的部分来解决这个问题,但是将 class 纯粹为了解决似乎是不对的编译器警告。

这真的是循环依赖吗?如果是这样,在许多其他情况下是否存在相同的问题,其中两个实体之间存在鸡/蛋关系?

除了禁用 Angular 的循环依赖警告之外,是否有任何合理的解决方案?

Dialog shows that an Injector is used to instantiate the Component to be displayed in the Dialog. 的 Angular Material 源代码。

因此,循环依赖警告似乎是误报。

可以通过更新 angular.json 禁用循环依赖警告。不幸的是,此选项不适用于每个文件。

angular.json

....
  "defaults": {
    ....
    "build": {
      "showCircularDependencies": false
    }
  }

解决方法

下面的解决方案允许嵌套调用,其中组件类型 DialogYesNoComponentDialog 可以打开组件类型 DialogWarningComponentDialog,反之亦然。

例子

import { DialogService, DialogYesNoComponent, DialogWarningComponent } from '...'


export class TypeOne {
  constructor(private dialog_service: DialogService) { }

  showYesNoDialog() {
    const dialog_question = "Would you like to continue?";
    const dialog_ref: MatDialogRef<DialogYesNoComponent> =
      this.dialog_service.open_yes_no_dialog({
        question: dialog_question,
        title: 'Confirm', height: '300px' })
    dialog_ref.afterClosed().subscribe(
      (choice: 'yes' | 'no') => {
        if (choice === 'yes') {
          // Continue
        } else {
          // Open Nested Dialog
          this.showWarningDialog("Stopping the program.");
        }
      }
    )
  }

  showWarningDialog(warning: String) {
    ...
  }
}

对话框服务

import { ElementRef, Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material';

import { DialogWarningComponent } from './dialog-warning/dialog-warning.component';
import { DialogYesNoComponent } from './dialog-yes-no/dialog-yes-no.component';

@Injectable()
export class DialogService {
  constructor(public dialog: MatDialog) { }

  public open_yes_no_dialog({ question, title = 'Confirm', yes_button_first = true,
    has_backdrop = false, height = '250px', width = '350px' }:
    {
      question: string, title?: string, yes_button_first?: boolean, has_backdrop?: boolean,
      height?: string, width?: string
    }): MatDialogRef<DialogYesNoComponent> {

    const dialog_ref = this.dialog.open(DialogYesNoComponent, {
      autoFocus: true,
      backdropClass: 'cdk-overlay-transparent-backdrop',
      closeOnNavigation: true,
      disableClose: false,
      hasBackdrop: has_backdrop,
      height: height,
      width: width,
      data: { question: question, title: title, yes_button_first: yes_button_first }
    })

    return dialog_ref
  }

  public open_warning_dialog() {
    { warning, title = 'Warning',
    has_backdrop = false, height = '250px', width = '350px' }:
    {
      warning: string, title?: string, has_backdrop?: boolean,
      height?: string, width?: string
    }): MatDialogRef<DialogWarningComponent> {

    const dialog_ref = this.dialog.open(DialogWarningComponent, {
      autoFocus: true,
      backdropClass: 'cdk-overlay-transparent-backdrop',
      closeOnNavigation: true,
      disableClose: false,
      hasBackdrop: has_backdrop,
      height: height,
      width: width,
      data: { warning: warning, title: title }
    })

    return dialog_ref
  }
}

DialogYesNoComponent

import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';


export interface YesNoDialogOptions {
  question: string
  title: string
  yes_button_first: boolean
}


@Component({
  selector: 'dialog-yes-no',
  templateUrl: './dialog-yes-no.component.html',
  styleUrls: ['./dialog-yes-no.component.css']
})
export class DialogYesNoComponent {
  constructor(public dialog_ref: MatDialogRef<DialogYesNoComponent>,
    @Inject(MAT_DIALOG_DATA) public options: YesNoDialogOptions) { }
}

DialogWarningComponent

import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';


export interface WarningDialogOptions {
  warning: string
  title: string
}


@Component({
  selector: 'dialog-warning',
  templateUrl: './dialog-warning.component.html',
  styleUrls: ['./dialog-warning.component.css']
})
export class DialogWarningComponent {
  constructor(public dialog_ref: MatDialogRef<DialogWarningComponent>,
    @Inject(MAT_DIALOG_DATA) public options: WarningDialogOptions) { }
}