我正在开发Angular 7应用程序,该应用程序可以管理实体,例如汽车和学生。
可以通过以下树来描述应用程序组件:
汽车
汽车/ CreateCar(对话)
学生们
学生/ CreateStudent(对话)
在CreateCar对话框中创建汽车时,用户应该能够使用CreateStudent对话框创建并分配一个新学生作为Car的所有者。
同样,在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似乎没有显示编译警告)
最佳答案
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
调用方法,则可以声明接口类型。关键是将接口保存在单独的文件中。如果从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门户网站中发生,因为我们经常需要打开对话框的服务,然后对话框出于其他原因需要使用同一服务。我已经发生过很多次了。