Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular passing dynamic entry components into module and then passing them again into another module

I'm making a modal component for a component library. I made a 3rd party modal library that I'm using within my component library. A main feature is being able to pass a component via a service and dynamically adding it to the modal.

My modal lib has a static method that allows you to add your component to the module's entry components. It looks like:

export class A11yModalModule {
  static withComponents(components: any[]) {
    return {
      ngModule: A11yModalModule,
      providers: [{
        provide: ANALYZE_FOR_ENTRY_COMPONENTS,
        useValue: components,
        multi: true
      }]
    };
  }
}

Cool, that works. I can pass components into it when I import the module like this: A11yModalModule.withComponents([ModalContentComponent])

My problem occurs when I abstract this out another level. So now instead of 2 modules I have 3. I need to pass a component like I did above from the lib consumer's module, to my component module, and then into the modal module.

How can I pass components from the lib module to the modal module?


I think I'm getting close. Here are my 3 modules

// app module
@NgModule({
  declarations: [AppComponent, ModalContentComponent],
  imports: [
    LibModalModule.withComponents([ModalContentComponent])
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }



// lib module
@NgModule({
  imports: [CommonModule],
  declarations: [LibModal],
  providers: [LibModalService],
  exports: []
})
export class LibModalModule {
  static withComponents(components: any[]) {
    return {
      ngModule: LibModalModule,
      imports: [CommonModule, A11yModalModule.withComponents(components)]
    };
  }
}



// a11y modal module
@NgModule({
  imports: [CommonModule],
  declarations: [ModalComponent],
  exports: [],
  providers: [ModalService, DomService],
  entryComponents: [ModalComponent]
})
export class A11yModalModule {
  static withComponents(components: any[]) {
    return {
      ngModule: A11yModalModule,
      providers: [{
        provide: ANALYZE_FOR_ENTRY_COMPONENTS,
        useValue: components,
        multi: true
      }]
    };
  }
}
like image 287
Kolby Avatar asked Mar 26 '18 20:03

Kolby


People also ask

Is entryComponents deprecated?

Entry components are deprecated, for more information, see entryComponents deprecation in the Deprecated APIs and features. An entry component is any component that Angular loads imperatively, (which means you're not referencing it in the template), by type.

Can we have multiple NgModule in Angular?

Yes you can split your application into modules.

Can we declare a component in two modules in Angular?

You can use same directives/components in multiple modules without errors.


1 Answers

withComponents method should return ModuleWithProviders object which is just wrapper around a module that also includes the providers.

It can't have imports property or something else because it doesn't understand those properties. Here's an excerpt from angular source code that is responsible from reading metadata from ModuleWithProviders:

else if (importedType && importedType.ngModule) {
  const moduleWithProviders: ModuleWithProviders = importedType;
  importedModuleType = moduleWithProviders.ngModule;
  if (moduleWithProviders.providers) {
    providers.push(...this._getProvidersMetadata(
        moduleWithProviders.providers, entryComponents,
        `provider for the NgModule '${stringifyType(importedModuleType)}'`, [],
            importedType));
    }
}

As we can see angular compiler takes providers from the object that will returned in withComponents method.

So, in order to merge your modules you can either use your approach(provide ANALYZE_FOR_ENTRY_COMPONENTS in LibModalModule.withProviders) or reuse A11yModalModule.withComponents like:

@NgModule({
  imports: [CommonModule, A11yModalModule],
  providers: [LibModalService],
  exports: []
})
export class LibModalModule {
  static withComponents(components: any[]) {
    return {
      ngModule: LibModalModule,
      providers: A11yModalModule.withComponents(components).providers
    };
  }
}

(Tested with AOT)

Also A11yModalModule has to be imported in LibModalModule if we want its providers to be included in our root module injector (And i suppose you're going to use ModalService and DomService that are declated in A11yModalModule). The reason of this is that angular includes all providers from transitive module in root module injector.

See also:

  • Avoiding common confusions with modules in Angular

  • What you always wanted to know about Angular Dependency Injection tree

like image 178
yurzui Avatar answered Oct 23 '22 20:10

yurzui