Как динамически добавлять провайдеров в инжектор в Angular 2?
Каждый компонент может указывать новых провайдеров, используя его свойство "провайдеров" в ComponentMetadata.
Есть ли способ указать провайдеров динамически, скажем, из конструктора компонента?
2 ответа
Я использовал useFactory, чтобы определить, какой класс будет использоваться для обеспечения. Делюсь за кого беспокойство.
В компоненте ts
@Component({
selector: 'app-chart',
templateUrl: './chart.component.html',
styleUrls: ['./chart.component.scss'],
providers: [
{ provide: DateTimeAdapter, useClass: MomentDateTimeAdapter },
{ provide: OWL_DATE_TIME_FORMATS, useValue: CUSTOM_FORMATS },
{ provide: OwlDateTimeIntl, deps: [SettingService],
useFactory: (settingsService) => settingsService.getLanguage()
}
]
})
В службе ts получить экземпляр класса
@Injectable()
export class SettingService {
public getLanguage(){
return this.translate.currentLang == "ko" ? new KoreanIntl() : new DefaultIntl;
}
}
Ниже приведены 3 более широких шага, которые необходимо выполнить для реализации динамических провайдеров. Обратите внимание, что я прокомментировал многие части кода, чтобы мы сосредоточились на основном ответе. Если вы хотите увидеть подробный шаг, обратитесь к этому угловому уроку.
Шаг 1:- Создать коллекцию провайдеров
Создайте коллекцию, и вы можете использовать метод push для динамического добавления объектов DI.
var providerscoll:any = [];
providerscoll.push({ provide: "1", useClass: DialogLogger });
providerscoll.push({ provide: "2", useClass: ConsoleLogger });
Шаг 2:- Предоставить коллекцию провайдеров в "NgModule" .
Пожалуйста, смотрите синтаксис квадратной скобки.
@NgModule({
// code removed for clarity
providers: [providerscoll]
})
export class MainModuleLibrary { }
Шаг 3:- Получить объект Injector в конструкторе
Получите объект инжектора в конструкторе, используя DI, и затем вы можете искать, используя метод "Get" и токен. Таким образом, если вы предоставите "1", вы получите что-то, а если "2", вы получите что-то.
// code removed for clarity
import { Injector } from '@angular/core';
// code removed for clarity
export class CustomerComponent {
constructor(public injector: Injector){
this.logger = this.injector.get("2");
}
}
Я сделал это в начальной загрузке.
bootstrap(AppComponent,[
provide( RequestOptions, { useClass: DefaultRequestOptions } ),
provide(Http, { useFactory:
function(backend, defaultOptions) {
return new Http(backend, defaultOptions); },
deps: [XHRBackend, RequestOptions]}),
]);
Я предполагаю, что это можно сделать и в компоненте:
https://angular.io/docs/ts/latest/api/http/Http-class.html
Вы делаете это динамическим, добавляя решения в фабричную функцию вместо того, чтобы просто возвращать тот же объект.
Есть один способ создать компонент, используя ViewContainerRef
, где разрешено проходить injector
поэтому я предполагаю, что это должно быть возможно, но оно ограничено динамическим созданием компонентов:
Создать инжектор:
static create(options: {providers: StaticProvider[], parent?: Injector, name?: string}): Injector;
// @param injector The injector to use as the parent for the new component.
abstract createComponent<C>(
componentFactory: ComponentFactory<C>, index?: number, injector?: Injector,
projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;
Псевдокод:
class FooComponent {
constructor(
private readonly injector: Injector,
private readonly viewContainer: ViewContainerRef){
const customInjector = this.injector.create({ providers: [FooService], parent: injector });
this.viewContainer.createComponent(componentFactory, 0, customInjector );
...
}
}
Или аналогично используйте Portal из Angular CDK.