2个组件之间的角度循环依赖

Tom*_*Law 10 dialog circular-dependency angular

我正在开发一个 Angular 7 应用程序,它允许管理实体,例如汽车和学生。

应用程序组件可以用以下树来描述:

  • 汽车
  • 汽车/创建汽车(对话框)
  • 学生们
  • 学生/创建学生(对话框)

CreateCar对话框中创建 Car 时,用户应该能够使用CreateStudent对话框创建并指定一个新学生作为 Car 的所有者。

同样,在CreateStudent对话框中创建 Student 时,用户应该能够使用CreateCar对话框创建和分配一辆新车作为 Student 的属性。

编译时,Angular 显示:“检测到循环依赖项中的警告”,我知道这应该发生。

我试过寻找模式来解决这个问题,例如共享服务,但似乎没有人工作。

编辑:

两个对话框的构造函数的相关部分:

constructor(
  private readonly matDialog: MatDialog
) {
}
Run Code Online (Sandbox Code Playgroud)

在 CreateStudent 对话框中,打开 CreateCar 对话框的方法:

createCar(): void {
  this.matDialog
    .open(CreateCarDialogComponent)
    .afterClosed().subscribe((car: Car) => {
      // Do something with car
    });
}
Run Code Online (Sandbox Code Playgroud)

在 CreateCar 对话框中,打开 CreateStudent 对话框的方法:

createStudent(): void {
  this.matDialog
    .open(CreateStudentDialogComponent)
    .afterClosed().subscribe((student: Student) => {
       // Do something with student
     });
}
Run Code Online (Sandbox Code Playgroud)

关于解决这个问题的任何建议?

谢谢

编辑2:

演示在这里 https://stackblitz.com/edit/angular-bbfs8k

(Stackblitz 似乎没有显示编译警告)

编译警告

Rea*_*lar 13

MatDialog不需要直接引用组件声明。您只需要传递一个ComponentType<any>参数即可打开一个对话框。所以我们可以通过使用 Angular 依赖注入器来解决循环依赖(由 TypeScript 触发)。

创建一个命名的文件create-card-token.ts并定义一个注入令牌。

export const CREATE_CAR_TOKEN: InjectionToken<ComponentType<any>> =
new InjectionToken<ComponentType<any>>('CREATE_CAR_TOKEN');
Run Code Online (Sandbox Code Playgroud)

在您的模块中,将上述令牌的值定义为提供者。您可以在此处定义将用于MatDialog.

@NgModule({
    ....
    providers: [
        {provide: CREATE_CAR_TOKEN, useValue: CreateCarComponent}
    ]
}) export class MyModule {}
Run Code Online (Sandbox Code Playgroud)

在 中,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
            });
     }       
}
Run Code Online (Sandbox Code Playgroud)

这将解决循环依赖,因为CarComponent永远不需要知道CreateCarComponent. 相反,它只知道 aComponentType<any>已被注入,并且MyModule定义了将使用哪个组件。

还有一个问题。上面的示例any用作将要创建的组件类型。如果需要访问对话框实例,并直接从 调用方法CarComponent,则可以声明接口类型。关键是将界面保存在一个单独的文件中。如果您从CreateCarComponent文件中导出接口,您将回到具有循环依赖关系。

例如;

  export interface CreateCarInterface {
       doStuff();
  }
Run Code Online (Sandbox Code Playgroud)

然后更新令牌以使用该接口。

export const CREATE_CAR_TOKEN: InjectionToken<ComponentType<CreateCarInterface>> =
new InjectionToken<ComponentType<CreateCarInterface>>('CREATE_CAR_TOKEN');
Run Code Online (Sandbox Code Playgroud)

然后你可以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();
     }       
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在CreateCarComponent.

@Component({..})
export class CreateCarComponent implements CreateCarInterface {
      public doStuff() {
         console.log("stuff");
      }
}
Run Code Online (Sandbox Code Playgroud)

此类循环引用经常发生MatDialog在 CDK 门户上,因为我们经常需要让一个服务打开对话框,然后该对话框出于其他原因需要使用相同的服务。我已经发生过很多次了。


Mox*_*arm 0

建议:

  • 您可以添加一个标志来指示打开的对话框是父对话框还是子对话框。如果是子项,则打开的对话框不会是创建汽车/子项对话框。它打断了圆圈。
  • 在创建汽车/学生对话框之前,您应该有一个可以打开这两个对话框的主控器(作业管理器)。在对话框中没有打开另一个对话框的选项。