In Angular, the resolution modifier is used to control how the dependency injector resolves dependencies. There are four resolution modifiers in Angular:
先来给这几个操作符分个类:
@SkipSelf
- 何时开始查找。@Self
,@Host
- 何时停止查找。@Optional
- 找不到时怎么办。
@Optional
这个是最简单了修饰符了,它告诉Angular依赖注入器,如果找不到依赖项,不要抛出错误,而是返回null
。
大家都见过下面这个错误吧,当Angular找不到某个依赖时,就会抛出这个错误:(注意:这种错误一般发生在某个Service没有提供{providedIn: 'root'}
,而且该Service的使用者也没有在providers
数组中提供该Service时。)
1 | ERROR NullInjectorError: R3InjectorError(Environment Injector)[_OrderService -> _OrderService]: |
如果我们在依赖注入的时候加上@Optional()
修饰符,那么Angular就不会抛出错误,而是返回null
。
1 | constructor(private orderService: OrderService) { () |
注意:上面这种写法,在使用orderService
时,需要判断是否为null
,否则仍然会抛出Cannot read properties of null (reading 'fetchData')
的错误。
我们可以针对orderService
使用?(Optional Chaining Operator),就不会抛出错误了:
1 | constructor(private orderService: OrderService) { () |
@Self
@Self
也比较简单,就是告诉Angular只在当前Component或者Directive中搜索某个依赖项,而不会去父级Component或者Directive中搜索。假设有OrderService,且是providedIn: 'root'
的。
1 | providedIn: "root"}) ({ |
如果我们在注入该Service时,加上@Self()
修饰符,那么Angular只会在当前Component或者Directive中搜索该Service,但是当前Component或者Directive中没有提供该Service时,所以就会抛出错误。
1 | constructor(private orderService: OrderService) { () |
注意观察这个错误和上面错误的区别,这个错误的顶端不再是NullInjector
了,因为我们只在当前Component中查找,所以永远不会到达NullInjector
这一步。而NodeInjector
是当前Component的Injector。
1 | core.mjs:7191 ERROR RuntimeError: NG0201: No provider for _OrderService found in NodeInjector. Find more at https://angular.dev/errors/NG0201 |
@Self
和@Optional
是可以配合使用的,为了修复上面的错误,我们可以这样写,这样就不会报错了。
1 | constructor(private orderService: OrderService) { () () |
所以,@Self
修饰符的使用场景到底是啥?请继续往下看。
Multiple Directives in same Dom elements
如果一个DOM元素上应用了多个Directive, 那么当第一个Directive提供了某个Service时,后面的Directive就可以通过@Self
来获取这个Service。
下面的代码,假设dir1对应Directive1,dir2对应Directive2,如果Directive1中的providers数组提供了某个Service,那么Directive2中constructor可以使用@Self
来获取Service。
1 | <p dir1 dir2>Hello, world!</p> |
Directive1中提供了OrderService
1 | // Directive1 |
Directive2中使用@Self
来获取OrderService。
1 | // Directive2 |
为什么这样可以呢?因为dir1
和dir2
位于同一个dom元素-p
标签上,Angular会为这个p标签创建一个Injector,dir1
和dir2
会share这个Injector,所以dir2
可以通过@Self
来获取dir1中提供的OrderService。
@Host
@Host
修饰符告诉Angular依赖注入器,只在当前Component或者Host元素中搜索某个依赖项。
- 当不使用
ng-content
时,Host就是注入依赖的Component。 - 当使用
ng-content
时,Host就是投射内容的Component(也就是ng-content
所在的Component)。see here for details.
@Host的作用体现在使用ng-content
进行内容投射的时候。下面的例子中,我们有一个ProductComponent,它的模板中有ng-content
,我们将ProductDetailComponent投射到ProductComponent中。- 此时,ProductComponent就是ProductDetailComponent的Host。
1 | <!-- product.component.html --> |
然后我们在ProductComponent中提供OrderService,并在ProductDetailComponent中使用@Host
来获取OrderService。
1 | // product.component.ts |
1 | // product-detail.component.ts |
最后在AppComponent
的模板中使用ProductComponent
1 | <!--app.component.html--> |
这时,app-product
就是app-product-detail
的Host
,如果我们在ProductDetailComponent
中使用@Host
来获取OrderService
,那么Angular会首先在ProductDetailComponent
中查找,结果没找到,然后再去ProductComponent
中查找OrderService,结果找到了。
@SkipSelf
@SkipSelf
就是查找依赖时不在当前Component或者Directive中查找,而是跳过当前Component或者Directive,去父级Component或者Directive中查找。
所以,@SkipSelf
修饰符的使用场景到底是啥?