提供者的类型
- 类 : useClass
- 值 : useValue
- 工厂: useFactory
- 别名: useExisting
useClass
提供类的实例,使用类提供者
providers :[{ provide: ProductService, useClass: ProductService }]
简写
令牌和类名一起的快捷写法
providers: [ProductService]
这是我们开发很正常这样写的
创建一个服务
@Injectable({
providedIn: 'root',// 依赖根目录
})
export class TestService {
sayHello() {
console.log(`From TestService --> Hello`);
}
}
// 使用
constructor(private readonly testService: TestService) {
testService.sayHello();
}
useValue
提供简单值时,使用
模块配置
providers :[ {provide:'USE_FAKE', useValue: true}]
或者分开独立写一个文件
const APP_CONFIG = {
serviceURL: "www.serviceUrl.comapi",
IsDevleomentMode: true
};
providers: [{ provide: AppConfig, useValue: APP_CONFIG }]
冻结配置不让它修改
const APP_CONFIG = Object.freeze({
serviceURL: "www.serviceUrl.comapi",
IsDevleomentMode: true
});
providers: [{ provide: AppConfig, useValue: APP_CONFIG }]
useExisting 别名
的意思是使用已经注册的类型注入到这里(别名),比如下面示例意思是将 ApiService
起个叫 OtherApiService
的别名
正常使用
providers: [
{provide: API_URL, useExisting: TestService}
]
页面使用
constructor(
@Inject(API_URL) private other: TestService
) {
other.getLog();
总而言之,Angular使用现有对象,而不是在useExisting选项中创建新对象。
useFactory
您可以在其构造函数中插入需要参数的服务,或者可以从函数返回的任何东西。
注入参数化服务
import { Injectable } from '@angular/core';
@Injectable()
export class RandomProvider {
public modulo = 1;
constructor(modulo: number) { //Instanciating the class requires parameter
this.modulo = modulo
}
getRandomNumber(): number {
return this.modulo*Math.random();
}
}
注入
NgModule ({
...
providers: [
{
provide: RandomProvider,
useFactory(){
return new RandomProvider(4);
}
}]
...
})
使用
constructor(
private random:RandomProvider
) {
console.log(random.getRandomNumber());
注入一个构造值
NgModule ({
...
providers: [
{
provide: 'RANDOM_ARRAY',
useFactory(){
return [
Math.random(),
Math.random(),
Math.random()
];
}
}]
...
})
使用
constructor( @Inject('RANDOM_ARRAY') private threeRandomValues: number[] ) {
}
如果工厂函数本身具有依赖项,则可以在deps
属性中声明它们。
NgModule ({
...
providers: [{
provide: 'TRUE_RANDOM_SOURCE',
deps: [HttpClient], //here we declare dependencies of our factory
useFactory(httpClient: HttpClient) { // our factory needs an injectable (HttpClient)
return this.httpClient.get('http://api.example.com/randomval'); //fake URL - does not really work
}
}]
...
})
案例
@NgModule({
// ...
providers: [{
provide: ProductService,
useFactory: () => {
let logger = new LoggerService()
let dev = Math.random() > 0.5
if (dev) {
return new ProductService(logger)
} else {
return new AnotherProductService(logger)
}
}
}, LoggerService]
})
// logger.service.ts,一个简单的打印日志的服务
import { Injectable } from '@angular/core'
@Injectable()
export class LoggerService {
constructor() { }
log (messgae: string) {
console.log(messgae)
}
}
这里有一点需要注意,如果多个组件共用这个服务,那么生成的实例都是相同的,因为工厂方法创建的对象是一个单例对象,工厂方法只会在创建第一个对象的时候被调用一次,然后在整个应用当中所有被注入的服务的实例都是同一个对象
问题一
在方法内部,我们手动实例化了一个 new LoggerService()
,意味着工厂方法与这个类是一种强耦合的关系,而我们又声明了 LoggerService
的提供器,所以我们可以采用下面这种方式来解耦,即利用第三个参数 deps
来声明工厂方法所依赖的参数
@NgModule({
// ...
providers: [{
provide: ProductService,
useFactory: (logger: LoggerService) => {
let dev = Math.random() > 0.5
if (dev) {
return new ProductService(logger)
} else {
return new AnotherProductService(logger)
}
},
deps: [LoggerService]
}, LoggerService]
})
这样一来就不需要我们手动的去实例化(new LoggerService()
),这时的 Angular
将会使用 LoggerService
这个提供器来实例化一个 LoggerService
的实例,并将其注入到 ProductService
的工厂方法的参数当中
问题二
我们是根据一个随机数来决定实例化哪个对象,这样测试是可以的,但是在发布的时候就不适用了,通常在这种情况下,我们可以使用一个变量来决定调用哪个方法,然后同样的在 deps
当中进行依赖声明,然后在当作参数的时候传递进去
@NgModule({
// ...
providers: [
{
provide: ProductService,
useFactory: (logger: LoggerService, isDev) => {
if (isDev) {
return new ProductService(logger)
} else {
return new AnotherProductService(logger)
}
},
deps: [LoggerService, 'IS_DEV_ENV']
},
LoggerService,
{
provide: 'IS_DEV_ENV',
useValue: false
}
]
})
同样的,可以不单一的注入一个固定的值,也是可以注入一个对象,方便维护
@NgModule({
// ...
providers: [
{
provide: ProductService,
useFactory: (logger: LoggerService, appConfig) => {
if (appConfig.isDev) {
return new ProductService(logger)
} else {
return new AnotherProductService(logger)
}
},
deps: [LoggerService, 'APP_CONFIG']
},
LoggerService,
{
provide: 'APP_CONFIG',
useValue: {
isDev: false
}
}
]
})
我们举个小案例方便深入理解下
providers: [
{
provide: 'api', useValue: {
value: true
}
}
]
我们在a中修改这个值
@Inject('api') private api:object
api.value=false;
我们在b中查询到这个值被修改了