我正在开发Angular 7应用程序,该应用程序可以管理实体,例如汽车和学生。
应用程序组件可以通过以下树来描述:
在 CreateCar 对话框中创建汽车时,用户应该能够使用 CreateStudent 对话框创建并分配一个新的学生作为Car的所有者。< / p>
类似地,在 CreateStudent 对话框中创建Student时,用户应该能够使用 CreateCar 对话框创建并分配新汽车作为Student的属性。
在编译时,Angular会显示:“检测到循环依赖中的警告”,并且我知道这会发生。
我尝试搜索模式来解决此问题,例如共享服务,但是似乎没人能工作。
编辑:
两个对话框的构造函数的相关部分:
constructor(
private readonly matDialog: MatDialog
) {
}
在CreateStudent对话框中,用于打开CreateCar对话框的方法:
createCar(): void {
this.matDialog
.open(CreateCarDialogComponent)
.afterClosed().subscribe((car: Car) => {
// Do something with car
});
}
在CreateCar对话框中,用于打开CreateStudent对话框的方法:
createStudent(): void {
this.matDialog
.open(CreateStudentDialogComponent)
.afterClosed().subscribe((student: Student) => {
// Do something with student
});
}
有什么解决建议吗?
谢谢
编辑2:
演示在这里 https://stackblitz.com/edit/angular-bbfs8k
(Stackblitz似乎没有显示编译警告)
答案 0 :(得分:1)
MatDialog
不需要直接引用组件声明。您只需要传递一个ComponentType<any>
参数即可打开一个对话框。因此,我们可以使用Angular依赖关系注入器解决循环依赖关系(由TypeScript触发)。
创建一个名为create-card-token.ts
的文件并定义一个注入令牌。
export const CREATE_CAR_TOKEN: InjectionToken<ComponentType<any>> =
new InjectionToken<ComponentType<any>>('CREATE_CAR_TOKEN');
在您的模块中,将上述令牌的值定义为提供者。在此定义用于MatDialog
的组件。
@NgModule({
....
providers: [
{provide: CREATE_CAR_TOKEN, useValue: CreateCarComponent}
]
}) export class MyModule {}
现在您可以在CarComponent
中注入此令牌,并使用它来打开对话框。
@Component({...})
export class CarComponent {
public constructor(@Inject(CREATE_CAR_TOKEN) private component: ComponentType<any>,
private matDialog: MatDialog) {}
public createCar() {
this.matDialog
.open(this.component)
.afterClosed().subscribe((car: Car) => {
// Do something with car
});
}
}
这将解决循环依赖性,因为CarComponent
永远不需要知道CreateCarComponent
的类型老化。相反,它仅知道已注入ComponentType<any>
,并且MyModule
定义了将使用的组件。
还有另一个问题。上面的示例使用any
作为将要创建的组件类型。如果需要访问对话框实例并直接从CarComponent
调用方法,则可以声明 interface 类型。关键是将 interface 保留在单独的文件中。如果您从CreateCarComponent
文件中导出接口,则会回到具有循环依赖项的状态。
例如;
export interface CreateCarInterface {
doStuff();
}
然后您更新令牌以使用界面。
export const CREATE_CAR_TOKEN: InjectionToken<ComponentType<CreateCarInterface>> =
new InjectionToken<ComponentType<CreateCarInterface>>('CREATE_CAR_TOKEN');
然后您可以像这样从汽车部件拨打doStuff()
:
@Component({...})
export class CarComponent {
public constructor(@Inject(CREATE_CAR_TOKEN) private component: ComponentType<CreateCarInterface>,
private matDialog: MatDialog) {}
public createCar() {
const ref = this.matDialog.open(this.component);
ref.componentInstance.doStuff();
}
}
然后您可以在CreateCarComponent
中实现该接口。
@Component({..})
export class CreateCarComponent implements CreateCarInterface {
public doStuff() {
console.log("stuff");
}
}
MatDialog
和CDK门户网站经常发生这类循环引用,因为我们经常需要在对话框中 open 打开服务,然后对话框需要使用相同的服务由于其他原因。我已经发生过很多次了。
答案 1 :(得分:0)
建议:
答案 2 :(得分:0)
create-car-dialog.component.ts
导入create-student-dialog.component.ts
,后者导入create-car-dialog.component.ts
。
create-student-dialog.component.ts
导入create-car-dialog.component.ts
,后者导入create-student-dialog.component.ts
。
这就是错误消息的含义,并且存在循环依赖项。您将需要以某种方式纠正此问题。
即您需要停止创建的该导入圈子。也许您需要第三个组件来导入这两个组件。可能有很多方法可以做到这一点。
答案 3 :(得分:0)
带有正向类引用(forwardRef)的中断圆度
类声明的顺序在TypeScript中很重要。在定义好类之前,您不能直接引用它。
这通常不是问题,特别是如果您遵守建议的每个文件一个类的规则。但是有时循环引用是不可避免的。当类“ A”指代“ B”而“ B”指代“ A”时,您处于困境。首先必须定义其中一个。
Angular forwardRef()函数创建一个间接引用,Angular以后可以解析。
Parent Finder示例充满了无法破坏的循环类引用。
当类像其提供程序数组中的AlexComponent一样对其自身进行引用时,您将面临这个难题。 providers数组是@Component()装饰器函数的一个属性,该函数必须出现在类定义上方。
使用forwardRef中断圆度。
providers: [{ provide: Parent, useExisting: forwardRef(() => AlexComponent) }],
答案 4 :(得分:0)
我不使用该材料,但是我发现使用ng-bootstrap的解决方案,您可以采用相同的想法并将其应用于该材料。
基本上,我在模态中定义了一个名为backComponent的变量,当我请求打开模态时,我定义了哪个是backComponent,在ng-boostrap中将是这样的:
open() {
const createStudent = this.modalService.open(CreateStudentDialogComponent);
createStudent.componentInstance.backComponent = CreateCarDialogComponent;
}
当我需要返回上一个组件时,例如使用按钮返回模态时,我使用以下backComponent返回: