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
}
}
解决方法
下面的解决方案允许嵌套调用,其中组件类型 DialogYesNoComponent
的 Dialog
可以打开组件类型 DialogWarningComponent
的 Dialog
,反之亦然。
例子
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) { }
}
我的 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.json 禁用循环依赖警告。不幸的是,此选项不适用于每个文件。
angular.json
....
"defaults": {
....
"build": {
"showCircularDependencies": false
}
}
解决方法
下面的解决方案允许嵌套调用,其中组件类型 DialogYesNoComponent
的 Dialog
可以打开组件类型 DialogWarningComponent
的 Dialog
,反之亦然。
例子
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) { }
}