Иерархия инжекторов. Примеры использования.
Возможность настройки одного или нескольких провайдеров на разных уровнях открывает полезные возможности. Рассмотрим некоторые из них:
Изоляция сервиса
Архитектурные соображения могут побудить вас ограничить доступ к сервису пределами компонента, которому он принадлежит. Например, компонент VillainsListComponent
, который отображает список злодеев, данные приходят из VillainsService
.
Если бы вы предоставили VillainsService
в корневом AppModule (где вы зарегистрировали HeroesService
), это сделало бы VillainsService
видимым везде, включая компоненты героев.
Вместо этого, можно объявить VillainsService
в метаданных провайдеров VillainsListComponent
@Component({ standalone: true, selector: 'app-villains-list', templateUrl: './villains-list.component.html', providers: [ VillainsService ], imports: [ NgFor, AsyncPipe ] })
Объявляя VillainsService
в VillainsListComponent
, сервис становится доступным только в VillainsListComponent
и его дереве подкомпонентов.
Редактирование из нескольких мест
Многие приложения позволяют пользователям работать сразу с несколькими задачами. Например, в приложении для подготовки налоговых деклараций сотрудник может работать с несколькими налоговыми декларациями, переключаясь между ними в течение дня.
Чтобы продемонстрировать этот сценарий, представьте внешний компонент HeroListComponent
, который отображает список супергероев.
Чтобы открыть налоговую декларацию героя, сотрудник кликает на имя героя, и открывает компонент для редактирования этой декларации. Каждая выбранная налоговая декларация героя открывается в своем компоненте, и одновременно могут быть несколько деклараций.
Каждый компонент декларации имеет следующие характеристики:
- Является собственной сессией редактирования налоговой декларации.
- Может изменять налоговую декларацию без влияния на декларацию в другом компоненте.
- Имеет возможность сохранять изменения в своей налоговой декларации или отменять их.
Предположим, что HeroTaxReturnComponent
содержит логику для управления изменениями и их восстановления. Для одной налоговой декларации героя это была бы простая задача. Однако в реальном мире, с богатой моделью данных налоговой декларации, управление изменениями становится сложной задачей. Вы могли бы делегировать это управление вспомогательному сервису, как это делается в данном примере.
HeroTaxReturnService
кэширует одну налоговую декларацию героя, отслеживает изменения этой декларации и может сохранять или восстанавливать ее. Также он делегирует задачи к сервису-синглтону HeroService
, который получает через DI.
// src/app/hero-tax-return.service.ts import { Injectable } from '@angular/core'; import { HeroTaxReturn } from './hero'; import { HeroesService } from './heroes.service'; @Injectable() export class HeroTaxReturnService { private currentTaxReturn!: HeroTaxReturn; private originalTaxReturn!: HeroTaxReturn; constructor(private heroService: HeroesService) { } set taxReturn(htr: HeroTaxReturn) { this.originalTaxReturn = htr; this.currentTaxReturn = htr.clone(); } get taxReturn(): HeroTaxReturn { return this.currentTaxReturn; } restoreTaxReturn() { this.taxReturn = this.originalTaxReturn; } saveTaxReturn() { this.taxReturn = this.currentTaxReturn; this.heroService.saveTaxReturn(this.currentTaxReturn).subscribe(); } }
Использование HeroTaxReturnService
в компоненте HeroTaxReturnComponent
может выглядеть следующим образом:
// src/app/hero-tax-return.component.ts import { Component, EventEmitter, Input, Output } from '@angular/core'; import { HeroTaxReturn } from './hero'; import { HeroTaxReturnService } from './hero-tax-return.service'; import { FormsModule } from '@angular/forms'; @Component({ standalone: true, selector: 'app-hero-tax-return', templateUrl: './hero-tax-return.component.html', styleUrls: [ './hero-tax-return.component.css' ], providers: [ HeroTaxReturnService ],
Налоговая декларация для редактирования поступает через свойство @Input()
, которое реализовано с использованием геттеров и сеттеров. Сеттер инициализирует собственный экземпляр HeroTaxReturnService
. Геттер всегда возвращает то, что сервис сообщает о текущем состоянии героя. Компонент также запрашивает у сервиса сохранение и восстановление этой налоговой декларации.
Это не сработает, если сервис является синглтоном на уровне всего приложения. Каждый компонент будет делиться одним и тем же экземпляром сервиса, и каждый компонент будет перезаписывать налоговую декларацию, принадлежащую другому герою.
Чтобы предотвратить это, настройте инжектор на уровне компонента HeroTaxReturnComponent
для использования сервиса.
// src/app/hero-tax-return.component.ts (providers) providers: [ HeroTaxReturnService ],
Компонент HeroTaxReturnComponent
имеет собственного провайдера сервиса HeroTaxReturnService
. Напомним, что у каждого экземпляра компонента есть свой собственный инжектор. Объявление сервиса на уровне компонента гарантирует, что каждый экземпляр компонента получит собственный экземпляр сервиса. Это гарантирует, что ни одна налоговая декларация не будет перезаписана.
Специализированные провайдеры
Еще одна причина объявить сервис повторно на другом уровне состоит в том, чтобы заменить более специализированную реализацию этого сервиса в дереве компонентов.
Например, рассмотрим компонент Car
, который включает информацию об услугах и зависит от других сервисов для предоставления более подробной информации о машине. Корневой инжектор, обозначенный как (A), использует общие провайдеры для получения дополнительной информации о CarService
и EngineService
.
Под капотом каждый компонент настраивает свой собственный инжектор с одним или более провайдерами (или без них), определенными для этого компонента самого по себе.
Когда вы разрешаете экземпляр Car на самом глубоком компоненте (C), его инжектор создает: