Иерархия инжекторов. Видимость сервисов. Часть 3.
Эта часть описывает, как ограничить область видимости начального и конечного ElementInjector
, используя декораторы видимости @Host()
, @Self()
и @SkipSelf()
.
Видимость инжектированных токенов
Декораторы влияют на то, где начинается и заканчивается поиск токена инъекции в логическом дереве. Для этого разместим декораторы видимости в конструкторе. Чтобы изменить место, где инжектор начинает искать FlowerService
, добавьте @SkipSelf()
к декларации @Inject
для FlowerService
в <app-child>
. Это объявление находится в конструкторе <app-child>
, как показано в child.component.ts
:
constructor(@SkipSelf() public flower : FlowerService) { }
С @SkipSelf()
инжектор <app-child>
не ищет FlowerService
в самом себе. Вместо этого, инжектор начинает поиск FlowerService
в ElementInjector
или <app-root>
, где он не находит ничего. Затем он возвращается к ModuleInjector
<app-child>
и находит значение hibiscus 🌺
, которое доступно, потому что ModuleInjector
<app-child>
и ModuleInjector
<app-root>
объединены в один ModuleInjector
. Таким образом, в пользовательском интерфейсе отображается следующее:
В логическом дереве шаблона это выглядит так:
<app-root ApplicationConfig @Inject(FlowerService) flower=>"🌺"> <#VIEW> <app-child @Provide(FlowerService="🌻")> <#VIEW @Inject(FlowerService, SkipSelf)=>"🌺"> <!-- With SkipSelf, the injector looks to the next injector up the tree --> </#VIEW> </app-child> </#VIEW> </app-root>
Хотя <app-child>
сервис возвратит sunflower 🌻
, в приложении отобразится hibiscus 🌺
, потому что @SkipSelf()
заставляет текущий инжектор пропустить себя и обратиться к своему родителю.
Если добавить @Host()
(в дополнение к @SkipSelf()
) к @Inject
для FlowerService
, результат будет null
. Это потому, что @Host()
ограничивает верхнюю границу поиска <#VIEW>
. Вот как это выглядит в логическом дереве:
<app-root ApplicationConfig @Inject(FlowerService) flower=>"🌺"> <#VIEW> <!-- end search here with null--> <app-child @Provide(FlowerService="🌻")> <!-- start search here --> <#VIEW @Inject(FlowerService, @SkipSelf, @Host, @Optional)=>null> </#VIEW> </app-parent> </#VIEW> </app-root>
Здесь сервисы и их значения одинаковы, но @Host()
останавливает поиск инжектором дальше <#VIEW>
для FlowerService
, поэтому он не находит сервис и возвращает null
.
@SkipSelf() в viewProviders
Текущий <app-child>
предоставляет AnimalService
в массиве viewProviders
со значением dog🐶
. Поскольку инжектор ищет AnimalService
только в ElementInjector
<app-child>
, он никогда не видит whale🐳
.
Как и в примере с FlowerService
, если добавить @SkipSelf()
в конструктор для AnimalService
, инжектор не будет искать AnimalService
в ElementInjector
текущего <app-child>
.
export class ChildComponent { // add @SkipSelf() constructor(@SkipSelf() public animal : AnimalService) { } }
Вместо этого инжектор начнет с ElementInjector
<app-root>
. Помните, что класс <app-child>
возвращает AnimalService
в массиве viewProviders
со значением собаки dog🐶
:
@Component({ standalone: true, selector: 'app-child', … viewProviders: [{ provide: AnimalService, useValue: { emoji: '🐶' } }] ... })
В логическом дереве шаблона это выглядит так:
<app-root ApplicationConfig @Inject(AnimalService=>"🐳")> <#VIEW><!-- search begins here --> <app-child> <#VIEW @Provide(AnimalService="🐶") @Inject(AnimalService, SkipSelf=>"🐳")> <!--Add @SkipSelf --> </#VIEW> </app-child> </#VIEW> </app-root>
С использованием @SkipSelf()
в <app-child>
инжектор начинает поиск AnimalService
в ElementInjector
<app-root>
и находит whale🐳
.
@Host() в viewProviders
Если добавить @Host()
в конструкторе AnimalService
, результат будет dog🐶
, потому что инжектор находит AnimalService
в <#VIEW>
<app-child>
. Вот массив viewProviders
в классе <app-child>
и @Host()
в конструкторе:
@Component({ standalone: true, selector: 'app-child', … viewProviders: [{ provide: AnimalService, useValue: { emoji: '🐶' } }] ... }) export class ChildComponent { constructor(@Host() public animal : AnimalService) { } }
@Host()
заставляет инжектор искать до тех пор, пока он не достигнет края <#VIEW>
.
<app-root ApplicationConfig @Inject(AnimalService=>"🐳")> <#VIEW> <app-child> <#VIEW @Provide(AnimalService="🐶") @Inject(AnimalService, @Host=>"🐶")> <!-- @Host stops search here --> </#VIEW> </app-child> </#VIEW> </app-root>
Добавим массив viewProviders
с hedgehog 🦔
, в app.component.ts
:
@Component({ standalone: true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ], viewProviders: [{ provide: AnimalService, useValue: { emoji: '🦔' } }] ... })
Далее добавим @SkipSelf()
вместе с @Host()
в конструктор сервиса child.component.ts
:
export class ChildComponent { constructor( @Host() @SkipSelf() public animal : AnimalService) { } }
Когда @Host()
и @SkipSelf()
были применены к FlowerService
, который находится в массиве providers
, результатом было значение null
, потому что @SkipSelf()
начинает поиск внутри инжектора <app-child>
, а @Host()
прекращает поиск в <#VIEW>
— где нет FlowerService
. В логическом дереве видно, что FlowerService
виден в <app-child>
, а не в его <#VIEW>
.
<app-root ApplicationConfig @Inject(AnimalService=>"🐳")> <#VIEW @Provide(AnimalService="🦔") @Inject(AnimalService, @Optional)=>"🦔"> <!-- ^^@SkipSelf() starts here, @Host() stops here^^ --> <app-child> <#VIEW @Provide(AnimalService="🐶") @Inject(AnimalService, @SkipSelf, @Host, @Optional)=>"🦔"> <!-- Add @SkipSelf ^^--> </#VIEW> </app-child> </#VIEW> </app-root>
@SkipSelf()
заставляет инжектор начинать поиск AnimalService
в <app-root>
, а не в <app-child>
, откуда происходит запрос, и @Host()
останавливает поиск в <app-root>
<#VIEW>
. Поскольку AnimalService
объявлен с помощью массива viewProviders
, инжектор находит hedgehog 🦔
в <#VIEW>
.