Solve circular dependency by "overloading" modules in Angular

Discussão em 'Angular' iniciado por Ciro García, Março 25, 2025.

  1. Ciro García

    Ciro García Guest

    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

    Continue reading...

Compartilhe esta Página