Как динамически добавлять провайдеров в инжектор в 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.

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