我正在开发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似乎没有显示编译警告)

angular - 2个组件之间的 Angular 圆依赖性-LMLPHP

最佳答案

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门户网站中发生,因为我们经常需要打开对话框的服务,然后对话框出于其他原因需要使用同一服务。我已经发生过很多次了。

10-06 11:48