Компоненты Angular предназначены для использования в качестве пользовательского интерфейса и не более того. Они отображают данные и обеспечивают взаимодействие с пользователем, реагируют на щелчки и ввод данных пользователем. Логика приложения должна выполняться в сервисах. Когда вам нужна служба в компоненте, вы обычно не создаете экземпляр самостоятельно, используя new. Вы помечаете службу как injectable и добавляете ее в качестве параметра в конструктор компонента. Angular Dependency Injection (DI) позаботится о создании экземпляра и внедрит его за вас.

Обычно об этом процессе не думают слишком много. Когда вы создаете сервис для получения данных с сервера, вам нужен только один его экземпляр, и для вас не имеет значения, где он находится. Но иногда вам нужно, чтобы службы были доступны только внутри модуля или даже компонента и его дочерних элементов. Возможно, вам потребуется внедрить разные экземпляры службы в разные компоненты. Вот почему хорошее понимание того, как работает Angular DI, является важным качеством для Angular-разработчика.

Форсунки

Инжекторы отвечают за создание экземпляров классов обслуживания и внедрение их в компоненты, которые их запрашивают. Сервисы - это одиночные экземпляры внутри области инжектора, может быть не более одного экземпляра сервиса.

Инжекторы созданы Angular. Инжектор root создается в процессе начальной загрузки. Angular также будет создавать инжекторы для компонентов, каналов или директив, но они останутся пустыми, если вы не объявите массив провайдеров в их декораторах. У каждого модуля с отложенной загрузкой также есть собственный инжектор.

Форсунки передаются по наследству. Когда вы запрашиваете услугу в компоненте, Angular будет искать в инжекторе компонента, затем в одном из его родителя, затем в родительском родительском ... и т. Д., Пока он либо не найдет его, либо не исчерпает предков. Если он не нашел его в инжекторах элементов, Angular начинает искать в инжекторах модулей, пока не найдет поставщика (или не закончит инжекторы).

Провайдеры

Инжекторы создают экземпляры и внедряют их, но вам нужно указать им как создавать эти экземпляры. Когда вы внедряете службу в компонент, вы даете DI Token ближайшему инжектору. По умолчанию токеном является класс обслуживания TestService в приведенном ниже примере. У инжектора есть карта token-provider, токен - это ключ. Поставщиком услуги обычно является сам класс:

В этом примере служба предоставляется только в TestComponent, и мы говорим Angular использовать класс TestService для создания экземпляра при внедрении TestService. Это настолько распространенное поведение, что Angular дает вам ярлык для него:

providers: [TestService]

Это также означает, что мы можем указать инжектору использовать какой-либо другой класс при внедрении TestService. В следующем примере мы говорим инжектору вернуть экземпляр HelloWorldService при запросе TestService.

Результатом будет:

Иерархия форсунок

Инжектор корня

Возможно, вы не так регулярно предоставляете услуги в своих компонентах. По умолчанию, когда вы создаете службу с помощью Angular cli, служба предоставляется в инжекторе root.

В этом случае нам не нужно предоставлять обслуживание в инжекторе компонента. Экземпляр службы будет создан в инжекторе root и внедрен в наш компонент. Этот экземпляр будет доступен везде в приложении.

Нам не нужно предоставлять услугу где-либо еще, но мы можем.

Элементные форсунки

Несмотря на то, что служба может быть введена инжектором root, это не обязательно. Мы можем решить создать экземпляр нашей службы для этого компонента (и его дочерних элементов). Для этого нам нужно определить провайдера, как мы это делали ранее.

Мы предоставили сервис TestService в TestComponent. Инжектор был создан для компонента и создаст экземпляр TestService при внедрении сервиса.

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

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

В этом примере мы снова увидим:

Еще одна важная вещь, которую следует отметить, - это то, что экземпляры сервисов не живут дольше, чем инжекторы, которые их создали. Это означает, что если вы предоставляете службу в компоненте, экземпляр службы, которую использует компонент, умрет вместе с компонентом. Вы можете подумать об этом, когда данные необходимо сохранить.

Модуль-инжектор

Приложения обычно организованы в разные модули. Поскольку по умолчанию службы предоставляются в инжекторе root, они доступны из любого места в приложении, даже в других модулях. Например, если у вас есть Module1, в котором вы определили службу TestService, предоставленную в root, вы можете использовать эту службу в модуле Module2. Поскольку служба внедряется инжектором root, будет только один ее экземпляр.

Если вы предоставляете службу в модуле Module1 вместо root, служба по-прежнему будет доступна в Module2. Это может быть удивительно, но если Module1 загружен с нетерпением, он импортируется в AppModule. Инжекторы модулей сглаживают поставщиков всего импорта, поэтому TestService будет предоставлен в AppModule.

С другой стороны, если Module1 загружается лениво, он не импортируется в AppModule. Предоставление услуги в модуле сделает ее доступной только внутри него.

Если вы предоставляете службу только в компоненте в Module1, служба будет предоставляться только в инжекторе компонента, ни в Module1, ни в root. В двух последних случаях попытка внедрить службу в компонент в Module2 приведет к ошибке Нет поставщика:

Вы должны явно предоставить службу, будь то в Module2 или в компоненте, в который вы пытаетесь внедрить ее, или непосредственно в инжекторе root.

Angular DI обычно позволяет поддерживать небольшое количество экземпляров службы, не обращая на это слишком много внимания. В большинстве случаев вам не о чем беспокоиться. Но может возникнуть необходимость в более глубоком понимании этого механизма, когда вы работаете со многими модулями, с отложенной загрузкой или когда вам нужны разные экземпляры вашей службы в ваших компонентах.

Angular предоставляет еще несколько инструментов для еще большей гибкости. Возможно, вы захотите проверить модификаторы (@Optional, @SkipSelf, @Self.…) Для получения дополнительной информации.

В этой статье мы рассмотрели только сервисный случай. DI может сделать больше. Например, вы можете указать значения вместо классов. Вы также можете создать свой собственный токен DI и вставлять элементы вручную с помощью аннотации @Inject. Для получения более подробной информации об этом или о внедрении зависимостей в целом, я рекомендую вам прочитать документацию по Angular.