Внедрение зависимостей в действии
Потребителю внедренной услуги не нужно знать, как ее создать. Задача инфраструктуры DI - создавать и кэшировать зависимости. Потребителю просто нужно сообщить структуре DI, какие зависимости ему нужны.
Команда Angular CLI ng new <имя_проекта> поможет вам начать работу. Когда вы запускаете эту команду, CLI устанавливает необходимые пакеты Angular npm и другие зависимости в новое рабочее пространство с корневой папкой с именем project_name. Он также создает следующие рабочие файлы и файлы стартового проекта:
В следующем примере показано, что AppComponent объявляет свою зависимость от LoggerService и UserContext.
constructor(logger: LoggerService, public userContext: UserContextService) {
userContext.loadUser(this.userId);
logger.logInfo('AppComponent initialized');
}
Демонстрация в реальном времени:
См. Pen src / app / app.component.ts от w3resource ( @ w3resource ) в CodePen .
UserContext, в свою очередь, зависит как от LoggerService, так и от UserService, другой службы, которая собирает информацию о конкретном пользователе.
@Injectable({
providedIn: 'root'
})
export class UserContextService {
constructor(private userService: UserService, private loggerService: LoggerService) {
}
}
Демонстрация в реальном времени:
Смотрите Pen user-context.service.ts (инъекция) от w3resource ( @ w3resource ) на CodePen .
Когда Angular создает AppComponent, структура DI создает экземпляр LoggerService и начинает создавать UserContextService. UserContextService также нуждается в LoggerService, который уже имеется в платформе, поэтому платформа может предоставить тот же экземпляр. UserContextService также требуется UserService, который фреймворк еще не создал. UserService не имеет дальнейших зависимостей, поэтому платформа может просто использовать new для создания экземпляра класса и предоставления экземпляра конструктору UserContextService.
Родительскому AppComponent не нужно знать о зависимостях зависимостей. Объявите, что необходимо в конструкторе (в данном случае LoggerService и UserContextService), и инфраструктура разрешит вложенные зависимости.
Ограничить область обслуживания поддеревом компонента
В приложении Angular есть несколько инжекторов, расположенных в древовидной иерархии, параллельной дереву компонентов. Каждый инжектор создает отдельный экземпляр зависимости. Тот же самый экземпляр вводится везде, где этот инжектор предоставляет эту услугу. Конкретная услуга может предоставляться и создаваться на любом уровне иерархии инжекторов, что означает, что может быть несколько экземпляров сервиса, если он предоставляется несколькими инжекторами.
Зависимости, предоставляемые корневым инжектором, могут быть внедрены в любой компонент в любом месте приложения. В некоторых случаях вы можете захотеть ограничить доступность службы для определенного региона приложения. Например, вы можете разрешить пользователям явно выбирать использование службы вместо того, чтобы корневой инжектор предоставлял ее автоматически.
Вы можете ограничить область применения внедренной службы ветвью иерархии приложения, предоставив эту услугу в подчиненном компоненте для этой ветви. В этом примере показано, как сделать другой экземпляр HeroService доступным для HeroesBaseComponent, добавив его в массив поставщиков декоратора @Component () подкомпонента.
@Component({
selector: 'app-unsorted-heroes',
template: `<div *ngFor="let hero of heroes">{{hero.name}}</div>`,
providers: [HeroService]
})
export class HeroesBaseComponent implements OnInit {
constructor(private heroService: HeroService) { }
}
Демонстрация в реальном времени:
Обратитесь к перу src / app / sorted-heroes.component.ts (отрывок HeroesBaseComponent) по w3resource ( @ w3resource ) в CodePen .
Когда Angular создает HeroesBaseComponent, он также создает новый экземпляр HeroService, который виден только этому компоненту и его дочерним элементам, если таковые имеются.
Вы также можете предоставить HeroService другому компоненту в другом месте приложения. Это привело бы к другому экземпляру службы, живущей в другом инжекторе.
Примеры таких ограниченных синглетонов HeroService приведены в сопровождающем примере кода, включая HeroBiosComponent, HeroOfTheMonthComponent и HeroesBaseComponent. Каждый из этих компонентов имеет свой собственный экземпляр HeroService, управляющий собственной независимой коллекцией героев.
Несколько экземпляров службы (песочница)
Иногда требуется несколько экземпляров службы на одном уровне иерархии компонентов.
Хорошим примером является служба, которая хранит состояние для своего экземпляра компонента-компаньона. Вам нужен отдельный экземпляр службы для каждого компонента. Каждый сервис имеет свое рабочее состояние, изолированное от сервиса и состояния другого компонента. Это называется песочницей, потому что каждый экземпляр службы и компонента имеет свою собственную песочницу для воспроизведения.
В этом примере HeroBiosComponent представляет три экземпляра HeroBioComponent.
@Component({
selector: 'app-hero-bios',
template: `
<app-hero-bio [heroId]="1"></app-hero-bio>
<app-hero-bio [heroId]="2"></app-hero-bio>
<app-hero-bio [heroId]="3"></app-hero-bio>`,
providers: [HeroService]
})
export class HeroBiosComponent {
}
Демонстрация в реальном времени:
Смотрите перо rgjRNy от w3resource ( @ w3resource ) на CodePen .
Каждый HeroBioComponent может редактировать биографию одного героя. HeroBioComponent использует HeroCacheService для извлечения, кэширования и выполнения других операций сохранения этого героя.
@Injectable()
export class HeroCacheService {
hero: Hero;
constructor(private heroService: HeroService) {}
fetchCachedHero(id: number) {
if (!this.hero) {
this.hero = this.heroService.getHeroById(id);
}
return this.hero;
}
}
Смотрите Pen JqEzdp от w3resource ( @ w3resource ) на CodePen .
Три экземпляра HeroBioComponent не могут использовать один и тот же экземпляр HeroCacheService, поскольку они будут конкурировать друг с другом, чтобы определить, какого героя следует кэшировать.
Вместо этого каждый HeroBioComponent получает свой собственный экземпляр HeroCacheService, перечисляя HeroCacheService в своем массиве поставщиков метаданных.
@Component({
selector: 'app-hero-bio',
template: `
<h4>{{hero.name}}</h4>
<ng-content></ng-content>
<textarea cols="25" [(ngModel)]="hero.description"></textarea>`,
providers: [HeroCacheService]
})
export class HeroBioComponent implements OnInit {
@Input() heroId: number;
constructor(private heroCache: HeroCacheService) { }
ngOnInit() { this.heroCache.fetchCachedHero(this.heroId); }
get hero() { return this.heroCache.hero; }
}
Смотрите Pen MdJxyJ от w3resource ( @ w3resource ) на CodePen .
Родительский HeroBiosComponent привязывает значение к heroId. ngOnInit передает этот идентификатор службе, которая выбирает и кэширует героя. Геттер для имущества героя извлекает кэшированного героя из сервиса. Шаблон отображает это свойство с привязкой к данным.
Квалифицируйте поиск зависимостей с помощью декораторов параметров
Когда класс требует зависимости, эта зависимость добавляется в конструктор в качестве параметра. Когда Angular необходимо создать экземпляр класса, он вызывает платформу DI для обеспечения зависимости. По умолчанию структура DI ищет поставщика в иерархии инжекторов, начиная с локального инжектора компонента и, если необходимо, поднимаясь вверх по дереву инжекторов, пока не достигнет корневого инжектора.
- Первый инжектор, настроенный с провайдером, предоставляет зависимость (экземпляр или значение службы) в конструктор.
- Если в корневом инжекторе не найден поставщик, структура DI выдает ошибку.
Существует несколько опций для изменения поведения поиска по умолчанию с использованием декораторов параметров для служебных параметров конструктора класса.
Создайте зависимость @Optional и ограничьте поиск с помощью @Hostlink
Зависимости могут быть зарегистрированы на любом уровне в иерархии компонентов. Когда компонент запрашивает зависимость, Angular запускается с инжектора этого компонента и подходит к дереву инжектора, пока не найдет первого подходящего поставщика. Angular выдает ошибку, если не может найти зависимость во время этой прогулки.
В некоторых случаях вам нужно ограничить поиск или учесть отсутствующую зависимость. Вы можете изменить поведение поиска Angular с помощью квалифицирующих декораторов @Host и @Optional для служебного параметра конструктора компонента.
- Декоратор свойства @Optional сообщает Angular возвращать ноль, когда он не может найти зависимость.
- Декоратор свойства @Host останавливает поиск вверх по компоненту хоста. Хост-компонент обычно является компонентом, запрашивающим зависимость. Однако когда этот компонент проецируется в родительский компонент, этот родительский компонент становится хостом. Следующий пример охватывает этот второй случай.
Эти декораторы могут использоваться по отдельности или вместе, как показано в примере.
@Component({
selector: 'app-hero-contact',
template: `
<div>Phone #: {{phoneNumber}}
<span *ngIf="hasLogger">!!!</span></div>`
})
export class HeroContactComponent {
hasLogger = false;
constructor(
@Host() // limit to the host component's instance of the HeroCacheService
private heroCache: HeroCacheService,
@Host() // limit search for logger; hides the application-wide logger
@Optional() // ok if the logger doesn't exist
private loggerService: LoggerService
) {
if (loggerService) {
this.hasLogger = true;
loggerService.logInfo('HeroContactComponent can log!');
}
}
get phoneNumber() { return this.heroCache.hero.phone; }
}
См. Pen XwpGja от w3resource ( @ w3resource ) в CodePen .
Функция @Host (), украшающая свойство конструктора heroCache, гарантирует, что вы получите ссылку на службу кэширования из родительского объекта HeroBioComponent. Angular выдает ошибку, если родителю не хватает этого сервиса, даже если его включает компонент выше в дереве компонентов.
Вторая функция @Host () украшает свойство конструктора loggerService. Единственный экземпляр LoggerService в приложении предоставляется на уровне AppComponent. Хост HeroBioComponent не имеет собственного провайдера LoggerService.
Angular выдает ошибку, если вы не украсили свойство @Optional (). Когда свойство помечено как необязательное, Angular устанавливает для loggerService значение null, а остальная часть компонента адаптируется.
Если вы закомментируете декоратор @Host (), Angular перемещается вверх по дереву предков инжектора, пока не найдет регистратор на уровне AppComponent. Логика логгера включается и герой отображает обновления с "!!!" маркер, чтобы указать, что регистратор был найден.
Если вы восстанавливаете декоратор @Host () и комментируете @Optional, приложение выдает исключение, когда оно не может найти требуемый регистратор на уровне компонента хоста.
Предыдущая: Рабочая область и структура файла проекта
Next: Конфигурация углового рабочего пространства
Новый контент: Composer: менеджер зависимостей для PHP , R программирования