3

I need to pass providers to a Lazy loaded module. Context: I have a module that access an API and returns some configs to be rendered in the components. This is the Module:

@NgModule({
  declarations: [AuxTablesComponent, AuxTablesListingComponent],
  providers: [AuxTablesService, AuxTableDataResolverGuard],
  imports: [CommonModule, AuxTablesRoutingModule, SharedModule]
})
export class AuxTablesModule {}

Imported as Lazy on another module:

const routes: Routes = [
      {
        path: AIRPORTS_ROUTES.AUX_TABLES.route,
        loadChildren: () => import('./aux-tables/aux-tables.module').then(m => m.AuxTablesModule)
      }
      ...
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class AirportsRoutingModule {}

This works as a charm. But now I need the same exactly Module, with the same views and behaviour in another Module, but I need to change the providers responsible for fetching the data from another API. So I would like to change the AuxTablesService and AuxTableDataResolverGuard.

I was trying to something like this: Importing it into another module, in this case the TrainsModule

const routes: Routes = [
      {
        path: TRAINS.AUX_TABLES.route,
        loadChildren: () => import('./aux-tables/aux-tables.module').then(m => m.AuxTablesModule
        .forChild(newApiProvider, newResolverGuard))
      }
      ...
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TrainsRoutingModule {}

And prepare the AuxTablesModule to receive custom providers like this:

@NgModule({
  declarations: [AuxTablesComponent, AuxTablesListingComponent],
  providers: [AuxTablesService, AuxTableDataResolverGuard],
  imports: [CommonModule, AuxTablesRoutingModule, SharedModule]
})
export class AuxTablesModule {
  static forChild(auxTableApiService: AuxTablesService, auxTableGuardResolver: Resolve<IAuxTable>) {
    return {
      ngModule: AuxTablesModule,
      providers: [
        {
          provide: AuxTablesService,
          useClass: auxTableApiService
        },
        {
          provide: AuxTablesService,
          useClass: auxTableGuardResolver
        }
      ]
    };
  }
}

But when angular tries to render the forChild lazy module, i get this error:

core.js:6014 ERROR Error: Uncaught (in promise): Error: No NgModule metadata found for '[object Object]'.

Debbuing angular, the return Object is not a contructor and cannot be "factoried" as a module nor finding the meta data as @ngModule on it. Does, anyone knows how to such a thing? Like provide custom classes to lazy loaded modules?

  • I think you are looking for FactoryProviders: https://stackoverflow.com/a/60304854/6455844 – Max K Feb 21 '20 at 22:18
  • Nope :/, I could change the provider to be a factoryProvider, It would be fine, the issue is that importing as a LazyLoaded module, I can't instance the module with any sort of configurations. – Gustavo Vieira Feb 21 '20 at 22:53
  • I got a workaround, but doesn't seem to be on the right design pattern. I've remove the providers from the module that I want to lazy load. And pass the providers on the calling modules of the lazy load module, so basically , the lazyloaded module looks for the provider and doesn't find it, so I gets it from the calling module. But doesn't seem right, I really would like my module to have its own providers, and be able to be lazylodaded overwriting the original ones when needed. – Gustavo Vieira Feb 21 '20 at 22:59

1 Answers1

0

Well, I got a workaround but doesn't seem right. I remove the providers from the module I want to lazy load.

It got like this:

@NgModule({
  declarations: [AuxTablesComponent, AuxTablesListingComponent],
  imports: [CommonModule, AuxTablesRoutingModule, SharedModule]
})
export class AuxTablesModule {}

Then I passed the providers to each module that is lazy importing it:

@NgModule({
  declarations: [
   ...
  ],
  providers: [
    {
      provide: AuxTablesService,
      useClass: TrainsAuxTablesService
    },
    {
      provide: AuxTableDataResolverGuard,
      useClass: TrainAuxTableResolverGuard
    }
    ...
  ],
  imports: [...],
})
export class TrainsModule {}

and:

@NgModule({
  declarations: [
   ...
  ],
  providers: [
    {
      provide: AuxTablesService,
      useClass: AirPortsAuxTablesService
    },
    {
      provide: AuxTableDataResolverGuard,
      useClass: AirPortsAuxTableResolverGuard
    }
    ...
  ],
  imports: [...],
})
export class AirPortsModule {}

So, the lazy loaded module looks for the providers on it self, but it can't find. So it looks for its parent module, and find it, so it can be instanced with its parent injecting the providers.

But I really would like to have my module with its own providers, and be overwritten when lazy loaded.