Injectable angular что это
Вполне вероятна ситуация, когда мы захотим использовать один сервис в другом сервисе. Например, в прошлой теме был создан сервис для работы с данными. Что если нам необходимо логгировать все операции с данными. Для логгирования определим новый сервис. Для этого добавим в папку src/app новый файл log.service.ts со следующим содержимым:
export class LogService < write(logMessage:string)< console.log(logMessage); >>
Для логгирования в сервисе определен метод write , который выводит некоторое сообщение на консоль.
Теперь используем этот сервис. Для этого изменим код в файле data.service.ts :
import from "@angular/core"; import from "./log.service"; @Injectable() export class DataService < private data: string[] = [ "Tom", "Bob", "Sam"]; constructor(private logService: LogService)<>getData(): string[] < this.logService.write("операция получения данных"); return this.data; >addData(name: string) < this.data.push(name); this.logService.write("операция добавления данных"); >>
Чтобы указать, что сервис может использовать другие сервисы, к классу сервиса применяется декоратор Injectable . Данный декоратор гарантирует, что встроенный механизм внедрения зависимостей сможет создать объект этого класса и передать в него в качестве зависимости другой объект (другой сервис или компонент).
Существует общая рекомендации от разработчиков Angular применять Injectable к любому классу сервиса, хотя в прошлой теме мы могли использовать сервис в компоненте без применения к сервису декоратора Injectable. Однако в практическом плане данный декоратор необходим, если внедряемый сервис сам имеет некоторые зависимости, как в примере с сервисом DataService выше.
И также в случае с DataService сервис LogService также надо зарегистрировать в списке провайдеров AppComponent:
И несмотря на то, что напрямую LogService не используется в компоненте AppComponent, но он используется в DataService, который вызывается в AppComponent.
И при выполнении операций с данными в консоли браузера мы сможем увидеть работу сервиса LogService.
Angular: полное руководство для «Внедрения зависимостей»

Об одной из важнейшей функциональностей фреймворка Angular «Внедрение зависимостей» расскажу я, Александр Желнин, full-stack разработчик, архитектор Департамента информационных технологий Россельхозбанка и автор YouTube-канала.
Мне захотелось представить на конкретных примерах все возможные варианты регистрации зависимостей и их подключения в классах. А также поделиться трудностями, с которыми можно столкнуться, и рассказать об опыте применения «Внедрения зависимостей».
На просторах рунета о «Внедрения зависимостей» написано преступно мало, в основном затрагивается, только «Внедрение сервисов». A «Внедрение зависимостей» — это не только сервисы, но и другие сущности, о которых по какой-то причине не говорят. С помощью «Внедрение зависимостей» вы можете передавать настройки, например, URL к back-end сервису и многое другое. Так же в зависимости от типа сборки можно подменять зависимости с учётом особенностей, например, для тестирования или «параноидального» логирования.
Немного теории по теме

Начнём с такого набора принципов, который называется SOLID. Каждая буква из этого названия — это первая буква соответствующего принципа. Обратим внимание на последний принцип Dependency Inversion «Инверсия зависимостей», который говорит о том, что объект не должен создавать зависимости внутри себя, а должен получать эти зависимости, например, в конструкторе. Ниже приведу примеры без инверсии зависимостей и с инверсией зависимостей. Начнем с примера без инверсии:
class CarOptions < public static read(id: number): CarOptions < return new CarOptions(); >> class CarComponent < public id: number = 1; public options?: CarOptions; constructor() < // Тут зависимость создаём в самом классе. // Более того сама зависимость может создавать зависимости и т.д. this.options = new CarOptions(); // или this.options = CarOptions.read(this.id); >>
В этом примере в классе машина создаёт зависимость на опции машины. У классического подхода без «Инверсии зависимостей» есть недостатки, например, повышается сложность такого класса, потому что класс должен знать, как и откуда получать зависимости.
Рассмотрим пример с «Инверсией зависимостей»:
class Car < constructor(public carOptions: CarOptions) < >>
Принцип «Инверсия зависимостей» даёт ряд неоспоримых преимуществ:
- упрощает взаимосвязи класса: класс получает зависимости в конструкторе
- юнит-тестирование класса упрощается: тестирование происходит только класса, а не его зависимостей
К минусам при «голом» использовании принципа «Инверсия зависимостей» можно отнести то, что объекты класса могут создаваться множество раз, и писать код для передачи зависимостей — трудозатратно.
new Car(new CarOptions(/* тут аргументы*/) /*, а тут ещё зависимости, которых может быть много. */)
Механизм, позволивший автоматизировать процесс создания объектов с учётом зависимостей, называется Dependency Injection «Внедрения зависимостей».
Для понимания: принцип «Инверсии зависимостей» и механизм «Внедрения зависимостей» — это разные понятия и в первом случае — это паттерн, а во втором — реализация, позволяющая нам использовать этот принцип на практике.
В Angular механизм «Внедрение зависимостей» работает по принципу:
- Во-первых, создаётся механизм регистрации зависимостей, который называется DI Container
- Во-вторых, создание объекта переносится на фабрику классов, называемую Injector
В итоге фабрика создаёт объекты, передавая зависимости, которые получает из контейнера зависимостей.
injector.get('ключ, например тип')
В качестве зависимостей можем использовать такие типы как:
- Сервисы (выделил в отдельный пункт, потому что сервисы часто получают другие сервисы, в качестве зависимостей и т.д.)
- Значения
- Скалярные
- Объекты
- Функции
Такое разделение условное, и служит лишь примером.
Как же регистрируется зависимости?
Для ответа на этот вопрос необходимо понять, что существуют несколько уровней иерархии для регистрации зависимостей:

Зарегистрировать зависимость можно на любом из уровней, представленном на схеме. Для понимания, каждый элемент схемы — это уровень. В итоге получаем иерархию регистраций зависимостей. При получении зависимости фабрика если не находит зависимость на текущем уровне, переходит вверх по иерархии, пока не найдёт необходимую зависимость.
Пояснения по схеме:
Первый уровень — это уровень платформы. В своей практике я не регистрирую зависимости на этом уровне, в этом нет необходимости, но такая возможность присутствует.
Большинство зависимостей регистрируются в модулях. Общие зависимости регистрируются в корневом модуле, и если его не переименовывали, то он называется AppModule.
Перейдём непосредственно к регистрации зависимостей.
В Angular зависимости можно зарегистрировать 2-мя возможными способами:


Давайте подробно рассмотрим оба способа.
Регистрация в providers
В простейшем случае регистрация зависимости, выглядит так:
providers: [ OptionsService, /* Другие провайдеры */ ]В более сложном случае необходимо указывать ключ и реализацию экземпляра зависимости:

Для начала разберём какие варианты «ключей» бывают:
providers: [ // регистрация < provide: 'API_URL', /* тут будет вариант подстановки зависимости */ >, /* Другие провайдеры */ ]Этот вариант плох тем, что строковый ключ легко могут использовать в подключаемой библиотеке, в результате чего будет конфликт. Настоятельно не рекомендую применять этот вариант на практике, чтобы потом не тратить время на поиск ошибки.
providers: [ // регистрация < provide: OptionsService, /* тут будет вариант подстановки зависимости */ >, /* Другие провайдеры */ ]Если хотим делать несколько реализаций, то в качестве ключа можем использовать класс. Но при одной реализации лучше применять упрощённую регистрацию, которая описана ниже в примере UseClass.
/** Объявление токена */ export const API_URL_TOKEN = new InjectionToken('API_URL'); // регистрация providers: [ < provide: API_URL_TOKEN, /* тут будет вариант подстановки зависимости */ >, /* Другие провайдеры */ ]Использование токена в качестве ключа наиболее предпочтительно. Строковый ключ рекомендую всегда заменять на токен.
С вариантами ключей разобрались, перейдём к вариантам подстановки зависимости. Angular поддерживает такие варианты как useClass, useValue, useFactory, useExisting.
Подробно разберём каждый из этих вариантов:
Это самый простой вариант, который заключается в том, что для реализации указывается класс.
providers: [ < provide: OptionsService, useClass: OptionsService >, /* Другие провайдеры */ ]Пример ниже это упрощённая запись примера выше. И если хотите зарегистрировать сервис без вариантов, то сокращённая запись предпочтительнее.
providers: [ OptionsService, /* Другие провайдеры */ ]В этом варианте подставляем конкретный экземпляр значения, которое может быть любым типом данных.
providers: [ // число < provide: 'VALUE_NUMBER', useValue: 1 >, // текст < provide: 'VALUE_STRING', useValue: 'Текстовое значение' >, // функция < provide: 'VALUE2_FUNCTION', useValue: () => < return 'что-то' >>, // объект < provide: 'VALUE2_OBJECT', useValue: < id: 1, name: 'имя' >>, // массив < provide: 'VALUE2_ARRAY', useValue: [1, 2, 3] >>, // и т.д. /* Другие провайдеры */ ]С помощь регистрации значений часто регистрируют конфигурационные значения. Например, Angular содержит файлы environment, в которых хранятся конфигурационные значения в зависимости от типа сборки, но к этим файлам нет доступа из подключаемых библиотек. В своей практике часто беру значение из environment и регистрирую это значение в контейнере, после чего к конфигурации получает доступ библиотека. В примере ключ сделал строкой исключительно в демонстрационных целях, в реальных проекта используйте токены.
Это вариант, в котором функция регистрируется как результат. Функция выполняет роль фабрики, возвращающей значение зависимости.
providers: [ < provide: 'VALUE', useFactory: () => < return 'что-то' >>, /* Другие провайдеры */ ]Вариант useFactory отличается от варианта useValue c функцией тем, что когда возвращается функция в useValue, потом с этой функцией необходимо работать как с функцией, а с фабрикой получаем значение, с которым и работаем, и нет повторных вызовов функции.
Для работы фабрики часто необходимо получать зависимости, поэтому предусмотрен механизм передачи зависимостей в функцию фабрики.
Хочу привести «реальный» пример, которой заключается в том, что необходимо получать настройки, например, с back-end, а потом зарегистрировать эти настройки в качестве зависимости.
/** Интерфейс конфигурации */ export interface ISettings < /** URL к API для некоторого сервиса My */ apiUrlMy: string; >/** Токен конфигурации */ export const SETTINGS_TOKEN = new InjectionToken>('SETTINGS_TOKEN'); /** Токен для получения URL API */ export const API_URL_MY_TOKEN = new InjectionToken>('API_URL_MY_TOKEN'); providers: [ < provide: SETTINGS_TOKEN, useFactory: (http: HttpClient): Observable=> http .get('/assets/settings.json') .pipe(shareReplay()), deps: [HttpClient] >, < provide: API_URL_MY_TOKEN, useFactory: (injector: Injector) =>injector.get(SETTINGS_TOKEN).pipe(map(s => s.apiUrlMy)), deps: [Injector] >, /* Другие провайдеры */ ]В представленном примере хотелось бы обратить внимание на свойство deps, которое осуществляет передачу зависимостей в фабрику.
Этот вариант наиболее непонятный для новичка. Суть useExisting заключается в том, что выбирается уже существующая зависимость.
providers: [ < provide: 'CarService1', useClass: CarService>, < provide: 'CarService2', useExisting: 'CarService1' >, /* Другие провайдеры */ ]Сразу отвечу на первый же вопрос – почему мы не должны написать код так:
providers: [ < provide: 'CarService1', useClass: CarService >, < provide: 'CarService2', useClass: CarService >, /* Другие провайдеры */ ]Этот вариант регистрация зависимости создаст нам два экземпляра CarService. Что может доставить много не удобств при отладке, т.к. сервис часто хранит состояние, в результате чего произойдёт так называемый сайд-эффект.
Расскажу почему ещё так важен useExisting

При работе с компонентом экземпляр компонента регистрируется в «Контейнере зависимостей», таким образом легко получить доступ для родительского компонента.
/** Родительский компонент "Машина" */ @Component(< selector: 'car-di', template: `car-di works!
` >) export class CarComponent < constructor() < >> /** Дочерний компонент "Колёса машины" демонстрирует через DI получаем доступ к родительскому компоненту */ @Component(< selector: 'wheels-di', template: `wheels works!
` >) export class WheelsComponent < /** * Конструктор, в котором получаем DI аргументы * @param car Родительский компонент "Машина" */ constructor(public car: CarComponent) < >> /** Директива демонстрирует доступ к родительским компонентам по средствам DI */ @Directive(< selector: '[ad-car]' >) export class CarDirective < /** * Конструктор, в котором получаем DI аргументы * @param wheels Родительский компонент "Колёса" * @param car Родительский ->Родительский компонент "Колёса" */ constructor(wheels: WheelsComponent, car: CarComponent) < >>Разработчики часто пишут универсальные директивы, которые можно использовать в разных компонентах. Поэтому необходимо получить доступ к компоненту по универсальному ключу, а дальше работать с базовым интерфейсом компонента. Для этого нужно использовать регистрацию зависимости useExisting c реализацией forwardRef
/** Общий интерфейс */ export interface IUniversal < /** Марка */ name: string; /** Масса */ weight: number; >/** Токен для роботы в DI */ export const UNIVERSAL_TOKEN = new InjectionToken('UNIVERSAL_TOKEN'); /** Дочерний компонент "Колёса машины" демонстрирует регистрацию зависимости для специального токена, в качестве зависимости выступает сам компонент */ @Component(< selector: 'wheels-universal-di', template: `wheels works!
`, providers:[ < provide: UNIVERSAL_TOKEN, useExisting: forwardRef(() =>WheelsComponent), multi: true > ] >) export class WheelsComponent implements IUniversal < /** Марка, от интерфейса IUniversal */ public name = 'no-name'; /** Масса, от интерфейса IUniversal */ public weight = 10; /** * Конструктор, в котором получаем DI аргументы * @param car Родительский компонент "Машина" */ constructor(public car: CarComponent) < >> /** Получаем доступ к родительскому компоненту используя базовый интерфейс. Соответственно эта директива может работать со всеми компонентами, реализующих UNIVERSAL_TOKEN */ @Directive(< selector: '[ad-universal]' >) export class UniversalDirective < /** * Конструктор * @param universal объект с базовой реализацией интер */ constructor(@Inject(UNIVERSAL_TOKEN) universal: IUniversal) < >>forwardRef позволяет обратиться к ещё не зарегистрированной зависимости. Из примера понятно, что в коде декоратора сам компонент ещё не был зарегистрирован в контейнере зависимостей.
Такая же реализация помогает решить задачу получения события загрузки компонента. Ссылка на видео: https://youtu.be/097plGqjP0U
Кроме того, самая часто встречающая реализация — это ngModel. Это тема для отдельной статьи.
Регистрация нескольких зависимостей с одинаковым ключом
Так же хотелось бы обратить внимание на дополнительное свойство multi. Приведу пример, который часто может быть необходим:
Если не указывать свойство multi, то в результате бы работал только HttpInterceptorService2. Свойство multi даёт нам возможность, чтобы одна зависимость не переписывала другу, если ключ совпадает, а накапливала зависимости в массиве.
И если получить зависимость по ключу, то в результате будет массив зависимостей.
const interceptors = injector.get(HTTP_INTERCEPTORS); // interceptors = [экземпляр HttpInterceptorService1, экземпляр HttpInterceptorService2]Регистрация providedIn
Такой способ даёт возможность зависимости зарегистрировать саму себя. Зависимость может зарегистрировать себя либо в виде сервиса, помеченного декоратором @Injectable,либо при определении токена InjectionToken.
Таким способом можно зарегистрировать зависимость на уровнях:
- ‘platform’
- ‘root’
- ‘any’
- или указать конкретный тип для регистрации, например, компонент или модуль
@Injectable() export class OptionsService < >@Injectable(< providedIn: AppModule >) export class OptionsService < >@Injectable(< providedIn: 'any' >) export class OptionsService
‘any’ является особым уровнем регистрации. Этот уровень позволяет создавать отдельный экземпляр зависимости для каждого «лениво загружаемого модуля» (lazy-loaded module)
Обязательно при регистрации токена необходимо указать фабрику
export const API_URL_MY_TOKEN = new InjectionToken('API_URL_MY_TOKEN', < providedIn: 'root', factory: () =>'http://localhost/test:5000' >);Управление доступом «Внедрения зависимостей» занимаются специальные декораторы
Этот декоратор будет брать зависимость только этого же компонента/директивы/модуля, в котором требуется получить зависимость.

@Component(< selector: 'car-di', template: `` // На уровне компонента providers: [OptionsService] >) export class CarComponent < constructor(@Self() public options: OptionsService)<>>Если зависимость не будет зарегистрирована в этом же компоненте, то получим ошибку.
export class CarComponent < constructor(@Optional() public options: OptionsService) < >>Если зависимость OptionsService не найдена, то options === null никаких ошибок сгенерировано не будет. Так же этот декоратор можно применять с любыми другими декораторами уровня доступа.
Этот декоратор пропускает зарегистрированную зависимость у самого компонента и ищет зависимость выше по иерархии.

export class CarComponent < constructor(@SkipSelf() public options: OptionsService) < >>
- @Host()
@Directive(< selector: '[ad-car]' >) export class CarDirective < constructor(@Host() public options: OptionsService) < >>Специально пример с декоратором, чтобы пояснить чем отличается от @Self().
@Host() указывает, что искать нужно у родительского компонента
Хотелось бы добавить из своего опыта то, что специализированные декораторы применяются редко, сам чаще всего применяю декоратор @Optional. Но возможность иметь полный контроль добавляет плюсов в копилку Angular.
В качестве постскриптума. Механизм «Внедрения зависимостей» в Angular достаточно объёмный, но если разобраться — ничего сложного в нём нет. В этой статье показаны все варианты регистрации и внедрения зависимостей, приведены примеры кода, которые надеюсь раскроют принципы «Внедрения зависимостей». Если возникли вопросы — задавайте в комментариях.
- Блог компании РСХБ.цифра (Россельхозбанк)
- Angular
- TypeScript
Как работает внедрение зависимостей в Angular
Angular позволяет создавать компоненты, каналы, директивы и многое другое. Для настройки необходимых зависимостей Angular предоставляет технологию под названием Dependency Injection (внедрение зависимостей).
Есть два механизма — провайдер зависимостей (dependency provider) и потребитель зависимостей (dependency consumer). Взаимодействие между ними возможно благодаря абстрактному объекту, называемому инжектором (injector).
Добавление декоратора Injectable к классу показывает, что он доступен для внедрения и может быть внедрен.
@Injectable()
class OneService <>Теперь OneService действует как зависимость, которая может быть внедрена в несколько мест.
Где она может быть внедрена?
- На уровне компонента.
- На уровне модуля приложения.
P. S. Рассмотрим уровень модуля отдельно (в условиях отложенной загрузки).
На уровне компонента можно просто добавить провайдеры в декоратор компонента:
@Component( selector: 'app-list',
template: '. ',
providers: [oneService]
>)
class TestComponent <>На уровне модуля аналогично предоставляем зависимость в провайдеры в декораторе NgModule :
@NgModule( selector: 'app-list',
template: '. ',
providers: [oneService]
>)
class TestComponent <>Из компонента также можно непосредственно добавить зависимость на корневом уровне, т.е. с помощью встроенного модуля, используя providedIn:’root’ .
На данный момент это наиболее часто используемый вариант:
@Injectable( providedIn: 'root'
>)
class TestService <>После добавления зависимости в провайдеры можно использовать ее и внедрять в элементы там, где это требуется.
Самый распространенный способ внедрения зависимости — добавление ее в конструктор соответствующего компонента, где нужно использовать эту зависимость.
constructor(private loggerService:LoggerService)
Ранее я упомянул об абстрактном объекте — инжекторе, управляющим взаимодействием между потребителями и провайдерами.
Посмотрим, как это происходит.
Повторное использование экземпляра
Мы добавили зависимость в конструктор, чтобы использовать ее в компоненте. Поэтому, когда компонент инстанцируется, Angular проверяет, существует ли уже экземпляр запрашиваемой зависимости. Если он существует, то Angular продолжает работу с ним. Если нет, инжектор создаст необходимый экземпляр.
P. S. Для проверки наличия экземпляров используется восходящий подход к разрешению, который будет продемонстрирован ниже.
Экземпляр относительно определенного уровня
Обсудим еще один момент, связанный с экземпляром. Рассмотрим пример, когда зависимость внедряется на уровне компонента.
Предположим, у нас два компонента, и оба они требуют одну и ту же зависимость, которую мы обеспечили на уровне каждого компонента.
Соответственно у этих двух компонентов будут созданы разные экземпляры.
А когда мы предоставляем зависимость на корневом уровне, т. е. на уровне модуля приложения, экземпляр, созданный для зависимости, остается неизменным во всем приложении.
Теперь следует ответить на вопрос: почему мы определяем экземпляр в модуле приложения, а не в компоненте приложения?
Дело в том, что в соответствии с иерархией модулей Angular модуль приложения находится выше, чем компонент приложения.
Примечание: над модулем приложения (App Module) находится модульный инжектор (Module injector), настраиваемый модулем платформы (Platform Module), а еще выше — нулевой инжектор (Null Injector), на котором все заканчивается. За более подробной информацией можно обратиться к официальной документации.
Область применения провайдера
Как ограничить область применения провайдера?
В модуле с динамической загрузкой инжектор в корне делает все упомянутые провайдеры доступными в начале работы приложения.
Теперь предположим, что на определенном маршруте мы отложено загружаем модуль, и он требует соответствующие зависимости, которые указаны в его массиве провайдеров, т.е. провайдеров для конкретного модуля. Но ведь корневой компонент не знает об этом.
Поэтому в отношении отложено загружаемого модуля дочерний инжектор заполняется в соответствии с корневым инжектором и полностью удовлетворяет требования сервисов, указанных в массиве провайдеров этого модуля.
Созданный отложено загруженный модуль имеет свой экземпляр.
Он может быть реализован следующим образом:
import < Injectable >from ‘@angular/core’;@Injectable( providedIn: ‘any’,
>)
export class SomeService <>Помните, мы обсуждали внедрение сервисов на уровне компонента? Это называется ограничением области применения провайдеров на уровне компонента.
Теперь вы можете спросить: если я внедрил одну и ту же зависимость (сервис) на уровне модуля приложения и на уровне компонента, то как Angular узнает, какой экземпляр использовать?
Дело в том, что есть набор правил разрешения, которым следует Angular во избежание любого типа конфликта.
Разрешение происходит в два этапа.
1. ModuleInjector. Он может быть настроен одним из двух способов с помощью:
- свойства @Injectable() providedIn ;
- массива провайдеров @NgModule() .
2. ElementInjector. Предоставление сервиса в декораторе @Component() с помощью провайдеров или свойства viewProviders настраивает ElementInjector .
Когда компонент объявляет зависимость, Angular прежде всего пытается удовлетворить эту зависимость с помощью своего ElementInjector . Если это не удается, то запрос передается вверх к ElementInjector родительского компонента, пока Angular не найдет инжектор, способный обработать запрос, или не исчерпает иерархию родительских компонентов ElementInjector .
Для разрешения запроса используется восходящий подход.
Если Angular не удалось найти провайдера ни в одной иерархии ElementInjector , т.е. в провайдерах компонентного уровня, он возвращается к элементу, в котором возник запрос, и проводит поиск в иерархии ModuleInjector . Если и после этого провайдер не будет найден, Angular выдаст ошибку.
- Избавляемся от рендеринга в Angular: только функциональность и никакого рендера
- Платформы Аngular в деталях. Часть 3. Визуализация Angular-приложений в терминале
- Protractor мертв, да здравствует Cypress!
Читайте нас в Telegram, VK и Дзен
Dependency injection¶
Dependency Injection — широко распространенный паттерн проектирования (сокращенно DI), который позволяет создавать объект, использующий другие объекты. При этом изменения в определении используемых объектов никак не влияют на создаваемый объект.
Ядро Angular имеет свою собственную реализацию паттерна Dependency Injection и без него создать приложение было бы просто невозможно. Самый простой пример DI в Angular — это использованием компонентом сервиса, чаще всего для получения данных.
Для того чтобы созданный сервис мог быть использован компонентом или другим сервисом, его объявление должно предваряться декоратором @Injectable() . Поскольку сервисы создаются именно для стороннего использования, то рекомендуется всегда использовать декоратор.
Все сервисы регистрируются Injector-ом, который является частью механизма DI в Angular. Причем в приложении может быть несколько injector-ов одновременно.
Самый главный — root injector. Он регистрирует все сервисы, которые определяются на уровне модулей. Дочерние injector-ы создаются в том случае, если есть хотя бы один сервис, который определен только в пределах компонента. Причем если сервисы создаются в двух разных компонентах, то будут созданы два injector-а, даже если это один и тот же сервис.
Когда компоненту требуется сервис, то его поиск начинается с самого нижнего injector-а и далее вверх по иерархии, то есть сначала проверяется уровень самого компонента.
Если сервис был найден на одном из нижних уровней, то дальнейший поиск не осуществляется. Если же поиск вообще не даст никаких результатов, то будет сгенерирована ошибка.
Декораторы @SkipSelf() и @Optional()¶
Немного выше уже упоминались два нюанса, связанных с работой Angular Dependency Injection. Первая из них это прекращение поиска, если запрошенный сервис определен на уровне компонента. Но что если вам понадобится, например, обратиться из этого компонента к двум сервисам одновременно: локальному и глобальному?
Здесь поможет использование декоратора @SkipSelf() . Если указать его в конструкторе перед нужным сервисом, то локальный injector будет исключен из поиска.
1 2 3 4 5 6 7 8 9 10 11 12
@Component( selector: 'deposits', templateUrl: './deposits.component.html', styleUrls: ['./deposits.component.scss'], >) export class DepositsComponent constructor( private localDepositsService: DepositsService, @SkipSelf() private rootDepositsService: DepositsService ) <> >Таким образом, если указать два одинаковых сервиса в одном компоненте, но перед одним из них поставить @SkipSelf() , то удастся получить доступ к локальному и глобальному экземплярам одновременно.
Назначение декоратора @Optional() весьма простое. В случае отсутствия необходимого сервиса во всех Angular injector не будет сгенерировано исключение, а в переменную, которая должна была стать экземпляром, просто запишется null .
Angular Providers¶
Основная задача Angular Dependency Injection — снабжение сервисами компонентов, директив и других сущностей. При создании экземпляров запрашиваемых сервисов injector полностью полагается на значения свойства providers (указывается у декораторов @NgModule() и @Component() ) или на значение объекта, передаваемого @Injectable() .
Если не указать сервис в providers и ничего не передать @Injectable() , то Angular injector не будет знать о его существовании.
Самый простой способ зарегистрировать сервис в приложении.
@Injectable( providedIn: 'root' >) export class DepositsService <>