依赖注入(DependencyInJection 简称DI)依然在线竞拍为例子
控制反转:Inversionof Control 简称:IOC
Varproducr =new Product();
product依赖Product;
依赖注入模式及使用的好处,angular如何实现依赖注入
注入器的层级关系
松耦合和可重用性,提高可测试性
实现依赖注入:
注入器:每个组件都有一个注入器,负责注入组件需要的对象
注入器:
constructor( private productService:ProductService) {...}
提供器:不同的写法
providers:[{provider:ProductService,useClass:ProductService}]
providers:[ProductService]//如果provider和useClass一样可以简写成这样
providers:[{provider:ProductService,useClass:AnotherProductService}]
解释:联动注入器,其实真正的实例化是AnotherProductService,原因是注入器中的ProductService,根据提供器中的ProductService来匹配要注入的对象和提供器的,然后根据useClass提供什么类,就实例化什么类!(简洁一句话就是:真正的注入的是useClass这个类)
provider:[{provide:ProductService,useFactory:()=>{...}}]
解释:就是通过工厂方法返回的实例,然后将返回的实例注入到注入器中
实例演示:
新建一个项目ngnew di
新建组件ng gcomponent product1
新建服务ng gservice shared/product
修改product.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class ProductService {
constructor() { }
//添加的地方--------------------------------
getProduct():Product{
return new Product(0,"iPhone",5899,"最新版")
}
//--------------------------
}
//添加到类
export class Product{
constructor(
public id:number,
public title:string,
public price:number,
public desc:string
){
}
}
在app.module.ts中引入
providers: [ProductService],
修改product1的控制器product1.component.ts
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();
}
}
修改product1.component.html
<div>
<h1>商品详情</h1>
<h2>名称:{{product.title}}</h2>
<h2>价格:{{product.price}}</h2>
<h2>描述:{{product.desc}}</h2>
</div>
修改app.component.html
<div>
<div>
<h1>基本的依赖注入</h1>
</div>
<div>
<app-product1></app-product1>
</div>
</div>
运行程序:结果截图
新建组件ng g component product2
新建服务ng g service shared/anotherProduct
修改another-product.service.ts控制器
import { Injectable } from '@angular/core';
import { ProductService,Product } from './product.service';
@Injectable()
//实现productService接口
export class AnotherProductService implements ProductService{
getProduct(): Product {
return new Product(1,"三星手机",4899,"最新版手机");
}
constructor() { }
}
修改product2.component.ts控制器
import { Component,ProductService } from '../shared/product.service';
import { AnotherProductService} from '../shared/another-product.service';
@Component({
selector: 'app-product2',
templateUrl: './product2.component.html',
styleUrls: ['./product2.component.css'],
//添加一个注入器,让他实例化AnotherProductService类,是another-product.service.ts中的一个类
providers:[{
provide:ProductService,useClass:AnotherProductService
}]
})
export class Product2Component implements OnInit {
product:Product;
constructor(private productService:ProductService) { }
ngOnInit() {
this.product=this.productService.getProduct();
}
}
修改product2.component.html
<div>
<h1>商品详情</h1>
<h2>名称:{{product.title}}</h2>
<h2>价格:{{product.price}}</h2>
<h2>描述:{{product.desc}}</h2>
</div>
修改app.component.html
<div>
<div>
<h1>基本的依赖注入</h1>
</div>
<div>
<app-product1></app-product1>
<app-product2></app-product2>
</div>
</div>
总结注入器的作用域规则
第一点:一个提供器声明在模块时,是对所有组件可见的,所有组件都是可以注入的
第二点:声明在组件内的时候,只对组件和子组件可见其他组件不能注入
第三点:声明在模块的提供器和声明在组件内的提供器时,组件内的可以覆盖模块中的 提供器
第四点:我们应该优先把提供器声明在模块中,只有需要对其他组件不可见时才声明在组件中,但这种情况是非常罕见的。
-------------------------------------------------
product.service.ts控制器
@Injectable()
export class ProductService {
constructor() { }
getProduct():Product{
return new Product(0,"最新版")
}
}
图中的productService可以注入别的服务(通过构造函数将其他服务注入到本服务中),本服务能不能注入到别的服务是看本服务有没有在providers属性中声明决定的,
@Injectable()这个装饰器只要有@Injectable(),服务就能将别的服务注入进来
@Injectable()
export classProductService {
@Injectable()这个装饰器的意思是,其他服务也可以注入到这个服务中,建议给每个服务都添加这个装饰器。
问题:为啥我组件上没有声明这个装饰器也可以注入服务,因为@Component装饰器是@Injectable()装饰器的子类。
而这个服务是否可以注入到其他服务中,是根据他是否声明在app.module.ts中的providers属性中决定的。
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';
@NgModule({
declarations: [
AppComponent,
Product1Component,
Product2Component
],
imports: [
BrowserModule
],
providers: [ProductService],
bootstrap: [AppComponent]
})
export class AppModule { }
服务之间如何互相注入
新建服务ng g service shared/logger
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
constructor() { }
log(message:string){
console.log(message);
}
}
修改 app.module.ts
providers: [ProductService,LoggerService],
修改product.service.ts
import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
@Injectable()
export class ProductService {
constructor(private logger:LoggerService) { }
getProduct():Product{
this.logger.log("getproduct方法被调用");
return new Product(0,"最新版")
}
}
export class Product{
constructor(
public id:number,
public desc:string
){
}
}
运行结果:
重构Auction
1、编写ProductService 包含3个方法:getProducts(),getProduct(id)以及getCommentsForProduct(id);
2、修改路由配置。在从商品列表进入商品详情时不在传输商品名称,修改传输商品ID。
3、注入ProductService并使用其服务。
添加一个服务:ng g service shared/product
修改product.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class ProductService {
//数组
private products:Product[]=[
new Product(1,"第一个商品",1.99,3.5,"我是学习Angular",["电子","物电"]),//ID 商品名称,价格,星级,商品描述,商品类别
new Product(2,"第二个商品",1.5,
new Product(3,"第三个商品",2.5,
new Product(4,"第四个商品",
new Product(5,"第五个商品",
new Product(6,"第六个商品",4.5,"物电"])
];
private@H_837_2403@ comments@H_837_2403@:Comment@H_837_2403@[]=[//商品评论的实体,包括商品评论的信息
new Comment(1,1,"2018年4月23日17:14:08","张三",3,"东西很好"),
new Comment(2,"李四","东西挺好"),
new Comment(3,"王五","东西还行"),
new Comment(4,"赵柳","东西不错")
]
constructor() { }
//第一个方法,返回这个数组信息
getProducts():Product[]{
return this.products;
}
//根据商品的id,返回相应的商品
getProduct(id:number):Product {
return this.products.find((product)=>product.id==id);
}
//根据商品的id,返回所有的评论信息(filter:是过滤)
getCommentsForProdutId(id:number):Comment[]{
return this.comments.filter((comment:Comment)=>comment.productid==id);
}
}
export class Product{
constructor(
public id:number,
public rating:number,
public desc:string,
public categories:Array<string>
){}
}
//商品评论的类
export class Comment{
constructor(public id:number,
public productid:number,
public timestamp:string,
public user:string,
public content:string
){
}
}
解释:主要就是将数据移植到这里来,Product,就是一个实体
修改路由配置:修改app.module.ts(之前传的是商品的名称,现在传的是id,所以修改两处
{第一处:path:'product/:productId',component:ProductDetailComponent}中的prodTitle改成productId 第二处:providers: [ProductService])
//-------------------------------------------------------------------------
部分代码:如下
import { ProductService } from './shared/product.service';
const routeConfig:Routes=[
{path:'',component:HomeComponent},
{path:'product/:productId',component:ProductDetailComponent}
]
修改商品组件的模板上:product.component.html
<!--let product of products绑定后台的数据-->
<div *ngFor="let product ofproducts" class="col-md-4 col-sm-4 col-lg-4">
<div class="thumbnail">
<!-- <img src="http://placehold.it/320X150">-->
<img[src]="imgUrl"><!--属性绑定 -->
<div class="caption">
<h4class="pull-right">{{product.price}}元</h4>
<h4><a [routerLink]="['/product',product.id]">{{product.title}}</a></h4>
<p>{{product.desc}}</p>
</div>
<div>
<app-stars [rating]="product.rating"></app-stars>
<!-- <app-stars>{{product.rating}}</app-stars>-->
</div>
</div>
</div>
商品详情的组件上,修改控制器:product-detail.component.ts
export class ProductDetailComponentimplementsOnInit {
product:Product;
comments:Comment[];
constructor(private routeInfo:ActivatedRoute,
private productService:ProductService) { }
ngOnInit() {
let productId:number=this.routeInfo.snapshot.params["productId"]
//this.productTitle=this.routeInfo.snapshot.params["prodTitle"]
}
}
注入productService并且使用其服务:修改app.module.ts
providers:@H_837_2403@ [ProductService@H_837_2403@],//把服务声明在模块的providers的属性中
然后在商品组件中构造函数中注入productService修改product.component.ts
constructor(private productService:ProductService) { }
详细代码:
import { Component,OnInit } from '@angular/core';
import { ProductService,Product } from '../shared/product.service';
@Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
private products:Product[];
private imgUrl='http://placehold.it/320X150';
constructor(private productService:ProductService) { }
ngOnInit() {//组件实例化的钩子
this.products=this.productService.getProducts();
}
}
然后接着在修改product-detail.component.ts
this.product=this.productService.getProduct(productId);
然后接着修改product-detail.component.html
<div class="thumbnail">
<img src="http://placehold.it/820X230">
<h4 class="pull-right">{{product.price}}</h4>
<h4>{{product.title}}</h4>
<p>{{product.desc}}</p>
<div>
<p class="pull-right">{{comments.length}}</p>
<p>
<app-stars [rating]="product.rating"></app-stars>
</p>
</div>
</div>
<div class="well">
<div class="row" *ngFor="let comment ofcomments">
<hr>
<div class="col-md-12">
<app-stars [rating]="comment.rating"></app-stars>
<span>{{comment.user}}</span>
<span>{{comment.timestamp}}</span>
<p></p>
<p>{{comment.content}}</p>
</div>
</div>
</div>