- 什么是依赖注入?
依赖注入(DI)是一种设计模式,也有相应的框架,比如InversifyJS
在这里介绍Angular自己的DI框架,它会在实例化该类时向其提供该类所生命的依赖项
带修饰符的参数
在ts中,一个类的参数如果带上修饰符,那个参数就变成了类的实例属性
class Mobile{
constructor(readonly name:string = '小米'){}
logName(){
console.log(this.name)
}
}
上面的name有修饰符,那么它就是Mobile类的实例属性,等同于:
class Mobile{
readony name:string
constructor(){
this.name = '小米'
}
logName(){
console.log(this.name)
}
}
实现过程
- 创建服务
ng g s hero
被@Injectable装饰的类,就是一个可以被注入的类,也称为服务(为其他服务、组件、指令或管道提供服务)
import {Injectable} from '@angular/core'
import {HEROS} from './mock-heroes'
@Injectable({
providedIn:'root'
})
export class HeroService {
constructor(){}
getHeroes(){
return HEROS;
}
}
- 使用服务(例如:使用Renderer2操作DOM元素)
在构造函数中直接声明,Angular框架帮我们完成注入(开箱即用),默认是单例模式(A、B模块同时注入时,得到的是同一个)
import {Renderer2} from '@angular/core'
constructor(private rd2:Renderer2)
@ViewChildren('img') imgs:QueryList<ElementRef>
ngAterViewInit():void{
this.imgs.forEach(item=>{
this.rd2.setStyle(item.nativeElement,'height','100px')
//设置属性时为this.rd2.setProperty()
})
}
- 提供服务的地方
- 在服务本身的@Injectable()的装饰器中
- 在NgModule的@NgModule()的装饰器中,providers数组,或import对应模块
ngOnInit(){
const injector = Injector.create({
providers:[
{
provide:Product,
//useClass:Product
useFactory:() => {
return new Product("xxx")
},
deps:[]
//如果还依赖其他的内容,需要在deps数组中提供
},
//在这里写也可以
{
provide:PurchaseOrder,
useClass:PurchaseOrder,
deps:[Product]
}
//如果provide和useClass的名字相同,也可以这样
//PurchaseOrder
]
})
}
- 在组件的@Component()装饰器中
@Component({
selector:'',
templateUrl:'',
styleUrls:'[]',
changeDetection:ChangeDetectionStragegy.OnPush,
providers:[HeroService]
})
通常,在使用的时候不需要这么麻烦,只需要放在module.ts中的providers就可以
替代类
useClass和provide可以是不一样的类
providers:[
{provide:LoggerService, useClass:BetterLoggerService}
]
值提供者
对于很简单的值,没必要把它做成一个类,可用useValue提供简单的值
providers:[{provide:Logger,useValue:'simpleValue'}]
非类令牌
上面的每个provide都是一个类,那么也可以用其它数据类型作为令牌
providers:[{provide:'httpApi', useValue:'123.com'}]
- 注入方式
class AppComponent{
constructor(@Inject('httpApi') readonly api){}
}
InjectionToken
当知道要注入的是什么类型时,可以用InjectionToken
interface AppConfig {
apiEndpoint : string;
title : string
}
无法用AppConfig作为令牌
[
{
provide:AppConfig,
useValue:{
apiEndPoint:'api.heros.com',
title:'Dependency Injection'
}
}
]
但又想要限制值的类型,可以借助InjectionToken
import { InjectionToken } from '@angular/core'
//参数是该令牌的一个描述,可选择
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config')
[
{
provide:APP_CONFIG,
useValue:{
apiEndPoint:'api.heros.com',
title:'Dependency Injection'
}
}
]
注入方式
class AppComponent{
constructor(@Inject(APP_CONFIG) config: Appconfig){
this.title = config.title
}
}
工厂提供者
import { Injectable } from '@angular/core';
import {UserService} from './user.service';
import {LoggerService} from './logger.service';
@Injectable()
export class UserLoggerService extends LoggerService {
constructor(private userService: UserService, extra: string) {
super();
console.log('UserLoggerService', extra);
}
log(message: string) {
const name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`);
}
}
{
provide: UserLoggerService,
useFactory(userServe: UserService) {
return new UserLoggerService(userServe, 'factory msg');
},
deps: [UserService] // 依赖其它服务的话,要列在这里
}
网友评论