Angular 依赖注入
Angular 依赖注入
依赖注入:Dependency Injection 简称DI
控制反转:Inversion of Control 简称IOC
var product = new Product();
createShipment(product);
var product = new MockProduct();
createShipment(product);
通过手工的实例化对象。
依赖注入的好处
@NgModule({
providers: [ProductService]
= providers: [{provide: ProductService, useClass: ProductService}]
= providers: [{provide: ProductService, useClass: AnotherProductService}]//这样切换项目对于ProductComponent不需要修改。
})
export class AppModule {}
@Component({
...省略组件配置
})
export class ProductComponent {
product: Product;
constructor(productService: ProductService) {//需要ProductService的Token,即上方provide对应的值。
this.product = productService.getProduct();
}
}
依赖注入
- 注入器
constructor(private productService: ProductService){}
- 提供器
providers: [ProductService]
,providers: [{provide: ProductService, useClass: ProductService}]
还可以使用工厂方法返回实例providers: [{provide: ProductService, userFactory: () => {...}}]
生成组件和服务,分别使用指令:ng g component product1
和ng g service shared/product
首先定义商品信息类
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor() {
}
}
export class Product {
constructor(public id: number, public title: string, public price: number, public desc: string) {
}
}
并且编写getProduct()
方法
getProduct(): Product {
return new Product(0, 'Iphone7', 5999, '最新款手机');
}
然后去修改模块的声明app.module.ts
,添加ProductService
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';
import {ProductService} from './shared/product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component
],
imports: [
BrowserModule
],
providers: [ProductService],
bootstrap: [AppComponent]
})
export class AppModule { }
修改Product1Component
并且注入ProductService
import {Component, OnInit} from '@angular/core';
import {Product, ProductService} from '../shared/product.service';
@Component({
selector: 'app-product1',
templateUrl: './product1.component.html',
styleUrls: ['./product1.component.css']
})
export class Product1Component implements OnInit {
product: Product;
constructor(private productService: ProductService) {
}
ngOnInit() {
this.product = this.productService.getProduct();
}
}
跟着修改页面模版内容,展示获取的product
内容。
<div>
<h1>商品详情</h1>
<h2>名称:{{product.title}}</h2>
<h2>价格:{{product.price}}</h2>
<h2>描述:{{product.desc}}</h2>
</div>
提供器的作用域
提供器除了可以声明在模块中,也可以声明在组件中。我们在创建第二个组件和另外一个服务,分别使用指令:ng g component product2
和ng g service shared/anotherProduct
我们使用AnotherProductService
实现ProductService
并实现其中方法
import { Injectable } from '@angular/core';
import {Product, ProductService} from './product.service';
@Injectable({
providedIn: 'root'
})
export class AnotherProductService implements ProductService{
constructor() { }
getProduct(): Product {
return new Product(0, 'Iphone9', 6999, '最新款手机');
}
}
然后修改product2.component.ts
,直接复制product1.component.ts
代码即可,不同的地方是在组件的级别使用providers
import {Component, OnInit} from '@angular/core';
import {Product, ProductService} from '../shared/product.service';
import {AnotherProductService} from '../shared/another-product.service';
@Component({
selector: 'app-product2',
templateUrl: './product2.component.html',
styleUrls: ['./product2.component.css'],
providers: [{
provide: ProductService, useClass: AnotherProductService
}]
})
export class Product2Component implements OnInit {
product: Product;
constructor(private productService: ProductService) {
}
ngOnInit() {
this.product = this.productService.getProduct();
}
}
跟着修改页面模版内容,展示获取的product
内容,其实与product1
组件页面模版一样。
<div>
<h1>商品详情</h1>
<h2>名称:{{product.title}}</h2>
<h2>价格:{{product.price}}</h2>
<h2>描述:{{product.desc}}</h2>
</div>
这样product1
是获取ProductService
注入的信息,而product2
是获取AnotherProductService
注入的信息。
总结:
- 当一个提供器声明在模块上,它是对所有组件可见的,所有组件都是可以使用的。
- 当一个提供器声明在组件中,它只对组件以及其子组件可见,其他组件不可以注入它。
- 当模块中的提供器和组件中的提供器重名时,声明在组件中的提供器会覆盖声明在模块中的提供器,这时使用的是声明在组件中的提供器。
- 一般情况下,优先将服务器供器声明在模块中。
@Injectable()
决定着其构造函数中能不能注入别的服务。只有声明为@Injectable()
的服务才可以注入其它的服务。
服务之间互相注入
首先生成新的服务ng g service shared/logger
这个服务比较简单,即输出日志到控制台。
import {Injectable} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class LoggerService {
constructor() {
}
log(message: string) {
console.log(message);
}
}
将LoggerService
注入到ProductService
中去。
product.service.ts
import {Injectable} from '@angular/core';
import {LoggerService} from './logger.service';
@Injectable({
providedIn: 'root'
})
export class ProductService {
constructor(private logger: LoggerService) {
}
getProduct(): Product {
this.logger.log('getProduct方法被调用');
return new Product(0, 'Iphone7', 5999, '最新款手机');
}
}
export class Product {
constructor(public id: number, public title: string, public price: number, public desc: string) {
}
}
想要注入LoggerService
,必须在模块中声明这个提供器。
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';
import {ProductService} from './shared/product.service';
import { Product2Component } from './product2/product2.component';
import {LoggerService} from './shared/logger.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
providers: [ProductService, LoggerService],
bootstrap: [AppComponent]
})
export class AppModule { }
为什么组件没有@Injectable()
装饰器也能注入服务呢。其实@Component
是其的子类。
使用工厂和值声明提供器
修改模版中提供器的声明
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {Product1Component} from './product1/product1.component';
import {ProductService} from './shared/product.service';
import {Product2Component} from './product2/product2.component';
import {LoggerService} from './shared/logger.service';
import {AnotherProductService} from './shared/another-product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
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],
bootstrap: [AppComponent]
})
export class AppModule {
}
但此时ProductService
依赖于LoggerService
,做一些调整
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {Product1Component} from './product1/product1.component';
import {ProductService} from './shared/product.service';
import {Product2Component} from './product2/product2.component';
import {LoggerService} from './shared/logger.service';
import {AnotherProductService} from './shared/another-product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
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],
bootstrap: [AppComponent]
})
export class AppModule {
}
使用值做判断
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {Product1Component} from './product1/product1.component';
import {ProductService} from './shared/product.service';
import {Product2Component} from './product2/product2.component';
import {LoggerService} from './shared/logger.service';
import {AnotherProductService} from './shared/another-product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
providers: [{
provide: ProductService,
useFactory: (logger: LoggerService, isDev) => {
let dev = Math.random() > 0.5;
if (isDev) {
return new ProductService(logger);
} else {
return new AnotherProductService(logger);
}
},
deps: [LoggerService, 'IS_DEV_ENV']
}, LoggerService, {
provide: 'IS_DEV_ENV', useValue: false
}],
bootstrap: [AppComponent]
})
export class AppModule {
}
也可以使用值对象
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {Product1Component} from './product1/product1.component';
import {ProductService} from './shared/product.service';
import {Product2Component} from './product2/product2.component';
import {LoggerService} from './shared/logger.service';
import {AnotherProductService} from './shared/another-product.service';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
providers: [{
provide: ProductService,
useFactory: (logger: LoggerService, appConfig) => {
let dev = Math.random() > 0.5;
if (appConfig.isDev) {
return new ProductService(logger);
} else {
return new AnotherProductService(logger);
}
},
deps: [LoggerService, 'APP_CONFIG']
}, LoggerService, {
provide: 'APP_CONFIG', useValue: {isDev: false}
}],
bootstrap: [AppComponent]
})
export class AppModule {
}
注入器的层级关系
应用级注入器=>主组件注入器=>子组件注入器
app.module.ts=>app.component.ts=>product.component.ts
会根据构造函数的参数注入进来。
手工注入方式,实际过程中要避免使用这种写法。
import {Component, Injector, OnInit} from '@angular/core';
import {Product, ProductService} from '../shared/product.service';
import {AnotherProductService} from '../shared/another-product.service';
@Component({
selector: 'app-product2',
templateUrl: './product2.component.html',
styleUrls: ['./product2.component.css'],
// providers: [{
// provide: ProductService, useClass: AnotherProductService
// }]
})
export class Product2Component implements OnInit {
product: Product;
private productService: ProductService;
constructor(private injector: Injector) {
this.productService = injector.get(ProductService);
}
ngOnInit() {
this.product = this.productService.getProduct();
}
}