I am developing part of an application where we have some custom built generic components. One of these component is a GenericForm
which we use for easily creating forms, and another component is ClientAutocomplete
, which is a field for searching existing clients, with an embedded button that opens a dialog with a GenericForm
for quickly creating a new client if it doesn't exist.
The GenericFormComponent
takes in an array of field configs, in which they specify the type of field amongst other things. I would like this GenericFormComponent
to be able to reuse the ClientAutocompleteComponent
, but that creates a circular dependency:
GenericFormModule
-> ClientAutocompleteModule
-> ClientCreationFormModule
-> GenericFormModule
The issue would be solved if the ClientCreationFormModule
could import the GenericFormModule
without it then having to import the ClientAutocompleteModule
again, since it will never be used in the ClientCreationFormComponent
. My first idea was to create an alternate GenericFormModule
, that imported everything BUT the ClientAutocompleteModule
, and import that, but that would mean exporting the component in two Angular modules, which does not compile. Even if it did, it technically would break the GenericFormComponent
, but because in that case we would not use the client autocomplete field, the ClientAutocompleteComponent
would never actually be used, so it would not cause trouble.
Is there a way to achieve this?
GenericFormModule
// ...
@NgModule({
declarations: [
GenericFormComponent,
],
imports: [
// ...
AutocompleteClientModule,
],
exports: [GenericFormComponent],
})
export class GenericFormModule { }
GenericFormComponent
<form *ngIf="formGroup" [formGroup]="formGroup" class="form">
<div class="is-flex is-flex-wrap-wrap">
<ng-container *ngFor="let field of config; let index = index; trackBy: indexTrackFn">
<ng-container [ngSwitch]="field.type">
<!-- ... -->
<ng-container *ngSwitchCase="genericInputType.MULTIPLE_INPUT_AUTOCOMPLETE">
<!-- ...other field types -->
</ng-container>
<ng-container *ngSwitchCase="genericInputType.USER_AUTOCOMPLETE">
<!-- ...field content -->
</ng-container>
<ng-container *ngSwitchCase="genericInputType.PHONE">
<!-- ...field content -->
</ng-container>
<ng-container *ngSwitchCase="genericInputType.CLIENT_AUTOCOMPLETE">
<app-autocomplete-client></app-autocomplete-client>
</ng-container>
<ng-container *ngSwitchDefault>
</ng-container>
</ng-container>
</ng-container>
</div>
</form>
ClientAutocompleteModule
// ...
@NgModule({
declarations: [
ClientAutocompleteComponent,
],
imports: [
// ...
ClientCreationFormModule,
],
exports: [
ClientAutocompleteComponent,
],
})
export class ClientAutocompleteModule { }
ClientAutocompleteComponent
<input-autocomplete
(onClickPrefix)="openDialog()"> <!-- This opens a dialog with the ClientCreationFormComponent -->
</input-autocomplete>
ClientCreationFormModule
// ...
@NgModule({
declarations: [
ClientCreationFormComponent,
],
imports: [
// ...
GenericFormModule,
],
})
export class ClientCreationFormModule {
}
ClientCreationFormComponent
<!-- this instance of the GenericFormComponent is guaranteed to never use the ClientAutocomplete field -->
<app-generic-form [formGroup]="formGroup" [config]="formConfig"></app-generic-form>
The code snippets provided are not a complete MRE for brevity's sake, but I think it's enough to illustrate the situation
You need to declare the dependencies in the GenericFormModule
and add them to the exports array also. Then the module will use what it needs from the exports
array, since the contents of the exports
array are visible to other modules.
@NgModule({
declarations: [
GenericFormComponent,
ClientAutocompleteComponent, // <- notice!
],
imports: [], // <- notice!
exports: [
GenericFormComponent,
ClientAutocompleteComponent, // <- notice!
],
})
export class GenericFormModule { }
By doing this the circular dependency is broken and just import GenericFormModule
in any module and it should work fine.
// ...
@NgModule({
declarations: [], // <- notice!
imports: [
GenericFormModule, // <- notice!
ClientCreationFormModule,
],
exports: [], // <- notice!
})
export class ClientAutocompleteModule { }
// ...
@NgModule({
declarations: [
ClientCreationFormComponent,
],
imports: [
GenericFormModule,
],
})
export class ClientCreationFormModule {}