Внедрить провайдера в другого провайдера
Допустим, у нас есть Компонент под названием 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