Динамическая конфигурация модуля / сервиса и AOT

Мне нужно, чтобы некоторые службы Angular были настроены динамически, в зависимости от переключателя времени выполнения. За несколько дней до AOT я заставил его работать, используя следующий код:

@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]
        },

      ]
    };
  }
}

Затем в моем AppModule я бы импортировал это как MyModule.forRoot(myConfig).

Когда я обновил CLI и Angular, он больше не компилируется, потому что его нельзя статически проанализировать. Я понимаю, почему, но все еще не уверен, как это правильно решить.

Не злоупотреблял ли я этим forRoot() подходом в первую очередь? Как вы пишете модули, чтобы в зависимости от переключателя времени выполнения они производили разные службы?


person Konrad Garus    schedule 11.02.2017    source источник


Ответы (1)


Я нашел один способ добиться этого: выставить конфигурацию через поставщика, а затем внедрить ее в «статическую» фабричную функцию. Приведенный выше код будет выглядеть так:

// 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]
        },

      ]
    };
  }
}

Это работает и все такое, но мне все равно было бы очень интересно узнать, действительно ли это хорошая идея, или я делаю что-то ужасно неправильное и должен использовать совершенно другой подход к решению этой проблемы.


Нашел другое решение:

  1. Используйте среду Angular CLI.
  2. Создайте абстрактные классы / интерфейсы для сервисов с разными реализациями или зависимостями для разных сред.
  3. Экспортируйте нужный тип из каждого файла среды (кто сказал, что это должен быть только простой объект JS?).
  4. В определении поставщика модуля импортируйте из среды.
  5. Во время компиляции среды CLI будут правильно связывать все необходимое.

Дополнительная информация и образец проекта: мой блог.

person Konrad Garus    schedule 11.02.2017
comment
Думаю, это единственный вариант. 1 замечание: начиная с версии 4 вы должны использовать InjectionToken вместо OpaqueToken. - person mat3e; 13.07.2017