Получить инжектор из модуля в угловых 8

Проблема:

Я настраиваю ленивую загрузку для не маршрутизируемого модуля в угловых. На 7 версии я использовал NgModuleFactoryLoader и это функция load лениво загрузить модуль и получить первую точку входа в модуль (сервис в случае выхода)

this.loader.load('path-to-module')
  .then(factory => {
    const module = factory.create(this._injector);
    return module.injector.get(EntryService);
  });

Но в Angular 8 NgModuleFactoryLoader устарела, поэтому вместо этого я должен загрузить модуль следующим образом:

import('path-to-module')
  .then(m => m.MyModule)
  .then(myModule => {
    ...
});

Проблема здесь в том, что я не могу восстановить фабрику и получить здесь провайдера в новой ленивой загрузке (одна из идей IVY - никаких фабрик).

Что я уже пробовал:

Первое решение (работа только с JIT-компилятором, который нам не подходит, так как я использую AOT-компилятор для prod)

import('path-to-module')
  .then(m => m.MyModule)
  .then(myModule => {
    return this._compiler.compileModuleAsync(myModule)
      .then(factory => {
        const module = factory.create(this._injector);
        return module.injector.get(EntryService);
      });
});

Второе решение (грязное и не полностью проверено. Используется ngInjectorDef которая является новой функцией IVY и еще не имеет описанного API):

import('path-to-module')
  .then(m => m.MyModule)
  .then(myModule => {
    const providers = myModule['ngInjectorDef'].providers; // Array<Providers>
    ... find EntryService in providers
});

ngInjectorDef - это свойство класса статического модуля, которое добавляется angular и имеет свойства фабрики, поставщиков и импорта.

Источники:

1 ответ

Я бы не сказал, что доступ к ngInjectorDef собственность "грязный хак". Да, это нигде не задокументировано, потому что, как сказал Игорь Минар, Ivy является предпросмотром предварительного просмотра в Angular 8. Но многие частные функции уже упоминались в некоторых статьях Виктора Савкина, например directiveInject,

Вы не должны искать свою услугу в providers свойство. Представьте, что есть более 20 провайдеров, а также EntryService имя будет минимизировано в производстве что-то вроде t или же k,

Если вы используете плющ - есть частная функция под названием createInjector, который принимает конструктор модуля в качестве аргумента.

@Injectable()
export class EntryService {
  public logFromEntryService(): void {
    console.log('Logging from EntryService...');
  }
}

@NgModule({
  providers: [EntryService]
})
export class EntryModule {
  public static getEntryService: () => EntryService = null;

  constructor(injector: Injector) {
    EntryModule.getEntryService = () => injector.get(EntryService);
  }
}

Предположим, у вас есть такой код, давайте использовать динамический импорт для загрузки этого EntryModule:

import { ɵcreateInjector as createInjector } from '@angular/core';

export class AppComponent {
  constructor(private injector: Injector) {
    this.loadEntryModule();
  }

  private async loadEntryModule(): Promise<void> {
    const { EntryModule } = await import('./entry.module');
    createInjector(EntryModule, this.injector);
    EntryModule.getEntryService().logFromEntryService();
  }
}

createInjector используется для создания экземпляров EntryModuleпосле мгновенного getEntryService статический метод не равен null,

Мы могли бы также выставить injector собственность, как:

public static injector: Injector = null;

constructor(injector: Injector) {
  EntryModule.injector = injector;
}

Это может рассматриваться как сервисный локатор, который является своего рода анти-паттерном. Но до вас!

Другие вопросы по тегам