Dynamic module/service configuration and AOT

孤街醉人 提交于 2019-12-18 13:37:35

问题


I need to have some Angular services configured dynamically, depending on a runtime switch. In days before AOT, I got it to work using the following code:

@NgModule({
  imports: [HttpModule],
  providers: []
})
export class MyModule {
  static forRoot(config: MyConfiguration): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [
        SomeService,
        {
          provide: SomeOtherService,
          useFactory: (some: SomeService, http: Http) => {
            switch (config.type) {
              case 'cloud':
                return new SomeOtherService(new SomethingSpecificForCloud());
              case 'server':
                return new SomeOtherService(new SomethingSpecificForServer());
            }
          },
          deps: [SomeService, Http]
        },

      ]
    };
  }
}

Then in my AppModule I would import this as MyModule.forRoot(myConfig).

As I updated CLI and Angular, this no longer compiles, because it cannot be statically analyzed. I understand why, but I am still not sure what the correct way to solve it is.

Have I abused this forRoot() approach in the first place? How do you write modules so that depending on a runtime switch, they produce different services?


回答1:


I found one way to achieve it: Expose the configuration via a provider, then have injected to a "static" factory function. The code above would look like this:

// Necessary if MyConfiguration is an interface
export const MY_CONFIG = new OpaqueToken('my.config');

// Static factory function
export function someOtherServiceFactory(config: MyConfiguration,some: SomeService, http: Http) {
  switch (config.type) {
    case 'cloud':
      return new SomeOtherService(new SomethingSpecificForCloud());
    case 'server':
      return new SomeOtherService(new SomethingSpecificForServer());
  }
}

@NgModule({
  imports: [HttpModule],
  providers: []
})
export class MyModule {
  static forRoot(config: MyConfiguration): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [
        SomeService,
        { provide: MY_CONFIG, useValue: config },
        {
          provide: SomeOtherService,
          useFactory: someOtherServiceFactory,
          deps: [MY_CONFIG, SomeService, Http]
        },

      ]
    };
  }
}

It works and all, but I would still be very interested in knowing whether this is actually a good idea, or if I'm doing something terribly wrong and should be taking a completely different approach to solving this problem.


I found another solution:

  1. Use Angular CLI environments.
  2. Create an abstract classes/interfaces for the services with different implementations or dependencies for different environments.
  3. Export the right type from each enviromnent file (who said it has to only be a plain JS object?).
  4. In the module provider definition, import from the environment.
  5. At compile time, CLI environments will make the right thing get linked.

More information and sample project at my blog.



来源:https://stackoverflow.com/questions/42176090/dynamic-module-service-configuration-and-aot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!