Получить инжектор из модуля в угловых 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 и имеет свойства фабрики, поставщиков и импорта.
Источники:
- https://netbasal.com/the-need-for-speed-lazy-load-non-routable-modules-in-angular-30c8f1c33093 (отложенная загрузка не маршрутизируемых модулей до угла 8)
- https://herringtondarkholme.github.io/2018/02/19/angular-ivy/ (предварительный просмотр IVY - см. раздел
No NgFactory file anymore
) - https://blog.angularindepth.com/automatically-upgrade-lazy-loaded-angular-modules-for-ivy-e760872e6084 (Описывает различия между отложенной загрузкой в Angular < 8 и Angular 8. Важный раздел - от NgModule до NgModuleFactory с AOT Compiler, в чем сейчас и заключается моя проблема)
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;
}
Это может рассматриваться как сервисный локатор, который является своего рода анти-паттерном. Но до вас!