Внедрить провайдера в другого провайдера

Допустим, у нас есть Компонент под названием Comp и два поставщика @Injectable, которые называются P1 и P2.

P1 нужен экземпляр P2. P1 впрыскивается в Comp.

Это работает отлично, если я объявляю обоих провайдеров на Comp следующим образом:

@Component ({
    providers: [P1, P2]
})
export class Comp { ... }

Теперь я хотел бы объявить, что P1 нужен P2 непосредственно внутри P1:

@Component ({
    providers: [P1]
})
export class Comp { ... }


@Injectable(/** Inject P2 here **/)
export class P1 { ... }

Как этого добиться?

Ответ 1

Фактически, инжекторы могут быть настроены только на уровне компонентов или при начальной загрузке приложения.

Когда вы устанавливаете поставщиков на уровне компонентов, все классы, участвующие в обработке, будут иметь доступ к этим поставщикам: подкомпонентам, услугам. Но вы не можете настроить провайдеров для самих сервисов.

Если вы настраиваете провайдеров на уровне начальной загрузки (при вызове функции bootstrap), все элементы в приложении смогут использовать этих провайдеров.

Фактически, инжектор зависимостей Angular2 использует иерархические инжекторы. Это означает, что если провайдер не найден на уровне, он будет искать на уровне выше и так далее.

Вот краткий обзор всех этих элементов и их связей:

Application
(providers defined in bootstrap)
     |
AppComponent
(providers defined in the providers attribute)
     |
ChildComponent
(providers defined in the providers attribute)
  getData()     --- Service1 --- Service2

Чтобы иметь возможность использовать Service2 в Service1, соответствующий поставщик должен быть найден в дереве поставщиков.

В таком приложении у нас есть три инжектора:

  • Инжектор приложения, который можно настроить с помощью второго параметра функции bootstrap
  • Инжектор AppComponent который можно настроить с помощью атрибута providers этого компонента. Он может "видеть" элементы, определенные в инжекторе приложения. Это означает, что если поставщик не найден в этом поставщике, он будет автоматически искать этот родительский инжектор. Если в последнем случае не найдено, будет выдана ошибка "поставщик не найден".
  • Инжектор ChildComponent который будет следовать тем же правилам, что и AppComponent. Чтобы внедрить элементы, включенные в цепочку внедрения, выполняемую для компонента, провайдеры будут искать сначала в этом инжекторе, затем в AppComponent и, наконец, в прикладном.

Этот ответ может дать вам более подробную информацию об иерархических инжекторах:

Ответ 2

У вас есть два варианта:

  • Как уже упоминалось в другом ответе, вы должны разместить свои службы в своем массиве providers, как вы это делали:

    providers: [P1, P2]
    

    И в конструкторе P1 просто введите его:

    export class P1 {
        constructor(private p2: P2){}
    }
    
  • Если вы не хотите помещать его в массив providers, вы можете сделать это в методе bootstrap, который загружает ваш основной компонент:

    bootstrap(AppComponent, [P2]);
    

И снова просто введите его так же в P1

Я не рекомендую второй, и создатели Angular 2 тоже не рекомендуют его. Если вы хотите, чтобы глобальная служба доступна повсюду (в других службах и компонентах просто поместите ее в массив providers корневого компонента)

Источник: https://angular.io/docs/ts/latest/guide/dependency-injection.html

Нам нужно настроить инжектор, зарегистрировав поставщиков, которые создают необходимые нам услуги в нашем приложении. Мы объясним, какие поставщики услуг будут позже в этой главе. Прежде чем мы это сделаем, рассмотрим пример регистрации провайдера при загрузке:

// Injecting services in bootstrap works but is discouraged

bootstrap(AppComponent, [HeroService]);

Инжектор теперь знает о нашем HeroService. Экземпляр нашего HeroService будет доступен для инъекций по всему нашему приложению.

Конечно, мы не можем не задаться вопросом о том, что комментарий говорит нам не сделайте это так. Это будет работать. Это просто не лучшая практика. Опция bootstrap provider предназначена для настройки и переопределения Angular собственные предварительно зарегистрированные службы.

Предпочтительный подход заключается в регистрации поставщиков приложений в компонентов приложения. Потому что HeroService будет использоваться внутри площадь героев - и нигде больше - идеальное место для зарегистрируйте его в HeroesComponent верхнего уровня.

Ответ 3

Injectables Поддержка зависимостей Инъекция таким же образом, что и компоненты, посредством встраивания конструктора.

@Injectable()
export class P1 {
    constructor(private p2: P2){}
}

Вы можете объявить его как поставщика, доступным для всего приложения, добавив его в bootstrap

bootstrap(MyAppComponent, [P1, P2, ...]);

Ответ 4

Существует открытая проблема для этого

https://github.com/angular/angular/issues/5622

Не похоже, чтобы это было реализовано: -/

В настоящее время поставщики могут быть объявлены только в компонентах/директивах/каналах и в bootstrap(). Также ограничение с поставщиками в компонентах/директивах/трубах заключается в том, что они не могут быть переопределены в глобальной конфигурации.

Ответ 5

Вы можете сделать это следующим образом:

import {Injectable, Inject} from 'angular2/core';
import {Http, Response, Headers, RequestOptions} from 'angular2/http';
import {Observable} from 'rxjs/Rx'; 
import {P2} from '../p2/p2.service'; // path to P2

@Injectable(/** Inject P2 here **/)
export class P1 { 
  private _p2Service: P2

  constructor(private http: Http) {
    this._p2Service = new P2(this.http);
  }

  getProcessedP2Data() {
    ... create your Rx obserwable here, read async data from P2, prcess them, and return obserwable (subscriable) object
  }
}

Вам также, вероятно, потребуется создать свой собственный RxJS, чтобы возвращать обработанные данные из P2. Подробнее здесь: https://egghead.io/lessons/rxjs-creating-an-observable