angular4 (2)依赖注入

2018-04-12  本文已影响0人  mumumuu

如果一个对象A需要依赖对象B,那么对象A不需要明确实例化对象B,B会由外部机制注入进来。


1.png

<1>提供器声明在模块中

1.新建一个项目

ng new projectname

2.新建组件product1

ng g component product1

3.生成服务service

ng g service shared/product
//生成一个名叫product的服务,放在了shared文件夹下

ps:
若报错

too many symbolic links encountered

解决办法:

删除node_modules文件夹, 重新npm install

4.目录结构

2.jpg
5.编写服务product.service.ts
import { Injectable } from '@angular/core';

/*
  @Injectable装饰器作用:
  让ProductService能够将别的服务注入到它的构造函数constructor中
*/
@Injectable()
export class ProductService {

  constructor() { }
  //3.声明getProduct()方法,返回Product对象
  getProduct():Product {
    //4.返回--这里直接new了一个对象
    return new Product(1,"iphone7",8799,"手机");
  }
}
// 1.定义一个商品信息类product
export class Product {
    // 2.构造函数:定义里面的字段
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public desc:string
    ){}
}

6.修改模块声明--app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { Product1Component } from './product1/product1.component';

// 2.引入
import { ProductService } from './shared/product.service';

@NgModule({
  declarations: [
    AppComponent,
    Product1Component
  ],
  imports: [
    BrowserModule
  ],
  //1.提供器--添加声明
  providers: [ProductService],
  bootstrap: [AppComponent]
})
export class AppModule { }

7.改写组件--product1.component.ts

import { Component, OnInit } from '@angular/core';
//4.引入Product,ProductService
import { Product,ProductService } from '../shared/product.service';

@Component({
  selector: 'app-product1',
  templateUrl: './product1.component.html',
  styleUrls: ['./product1.component.css']
})
export class Product1Component implements OnInit {
    //1.声明Product类型的组件用来接收从服务中获取到的数据
    product: Product;

    //2.注入器:构造函数中通过依赖注入,声明token是ProductService的一个服务
    constructor(private productService:ProductService) { }

    ngOnInit() {
        //3.使用服务的getProduct()方法获取数据,赋给本地product变量
        this.product = this.productService.getProduct();
    }

}

8.修改组件的模版,用于展示信息--product1.component.html

<div>
    <h4>商品详情</h4>
    <h4>id:{{product.id}}</h4>
    <h4>名称:{{product.title}}</h4>
    <h4>价格:{{product.price}}</h4>
    <h4>描述:{{product.desc}}</h4>
</div>

9.修改主组件模版,来显示商品组件--app.component.html

<div>
  <div>依赖注入例子</div>
  <div>
    <app-product1></app-product1>
  </div>
</div>

<2>提供器声明在组件中

上面的例子中,提供器是声明在app.module.ts中的,提供器也可以声明在组件中,下面是例子。
1.声明第二个组件

ng g component product2

2.生成另一个服务

ng g service shared/anotherProduct

3.编写anotherProduct服务--another-product.service.ts

import { Injectable } from '@angular/core';
//4.import ProductService,Product
import { ProductService,Product } from './product.service';

@Injectable()
//1.实现ProductService,让AnotherProductService和ProductService有相同方法
export class AnotherProductService implements ProductService{
  //2.实现ProductService中的getProduct()方法
  getProduct():Product {
    //3.同样返回一个商品对象
    return new Product(2,"huawei",3000,"华为");
  }
  constructor() { }
}

4.修改模块声明--app.module.ts

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';

//2.引入
import { Product2Component } from './product2/product2.component';

@NgModule({
  declarations: [
    AppComponent,
    Product1Component,
    //1.引入
    Product2Component
  ],
  imports: [
    BrowserModule
  ],
  providers: [ProductService],
  bootstrap: [AppComponent]
})
export class AppModule { }

5.编写product2.component.ts
构造器部分与product1.component.ts相同,唯一不同的是多声明了一个providers

import { Component, OnInit } from '@angular/core';
import { Product,ProductService } from '../shared/product.service';
//4.引入AnotherProductService
import { AnotherProductService } from '../shared/another-product.service';

@Component({
  selector: 'app-product2',
  templateUrl: './product2.component.html',
  styleUrls: ['./product2.component.css'],
  //2.在组件中声明providers
  providers:[{
    //3.不同的是使用的是AnotherProductService服务
    provide:ProductService,useClass:AnotherProductService
  }]
})

export class Product2Component implements OnInit {
    //1.与produce1相同
    product: Product;
    constructor(private productService:ProductService) { }

    ngOnInit() {
        this.product = this.productService.getProduct();
    }

}

6.编写product2.component.html
与product1模版相同

<div>
    <h4>商品详情</h4>
    <h4>id:{{product.id}}</h4>
    <h4>名称:{{product.title}}</h4>
    <h4>价格:{{product.price}}</h4>
    <h4>描述:{{product.desc}}</h4>
</div>

7.编写app.component.html

<div>
  <div>依赖注入例子</div>
  <div>
    <app-product1></app-product1>
    <!-- 引入product2组件 -->
    <app-product2></app-product2>
  </div>
</div>
提供器作用规则
1.当提供其声明在模块中时(app.module.ts),是对所有模块可见的,所有模块都可以注入
2.当一个提供器声明在组件中时,例如(product2.component.ts),这个提供器只对声明它的组件及
  其子组件可见
3.当声明在组件中的提供器和模块中的提供器具有相同的token时,组件中的提供器,
  例如(product2.component.ts)会覆盖声明在模块中的提供器
4.一般情况下应优先将提供器声明在模块中(app.module.ts),除非某提供器必须对其他组件不可见时
  才声明在组件中

<3>服务之间如何互相注入

1.生成新的服务logger

ng g service shared/logger

2.编写logger.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class LoggerService {

  constructor() { }
  //1.方法log(),接收一个string类型的message
  log(message:string) {
    //2.打印
    console.log(message);
  }
}

3.编写product.service.ts--在其构造函数中注入logger服务

import { Injectable } from '@angular/core';
//2.引入
import { LoggerService } from './logger.service';

@Injectable()
export class ProductService {
  //1.注入logger服务
  constructor(private logger:LoggerService) { }
  getProduct():Product {
    //3.调用logger中的log方法
    this.logger.log("log message");
    return new Product(1,"iphone7",8799,"手机");
  }
}
export class Product {
    constructor(
        public id:number,
        public title:string,
        public price:number,
        public desc:string
    ){}
}

4.app.module.ts模块中声明logger提供器--让logger能被别的服务注入

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';

// 2.引入
import { LoggerService } from './shared/logger.service';

@NgModule({
  declarations: [
    AppComponent,
    Product1Component,
    Product2Component
  ],
  imports: [
    BrowserModule
  ],
  //1.提供器--添加声明LoggerService
  providers: [ProductService,LoggerService],
  bootstrap: [AppComponent]
})
export class AppModule { }

5.结果--控制台中

3.jpg

PS:
1.只有声明了@Injectable()装饰器的服务才能注入其他服务,建议所有服务都声明@Injectable()装饰器。
2.组件能够注入服务的原因是声明了@Component装饰器,而这个装饰器是@Injectable()装饰器的子类

<4>使用工厂提供器和值声明提供器

1.修改product2.component.ts
删除以上例子中product2组件中的providers声明,使product1和product2共用模块中app.module.ts声明的service

删除
  providers:[{
    //3.不同的是使用的是AnotherProductService服务
    provide:ProductService,useClass:AnotherProductService
  }]

2.修改app.module.ts的提供器声明

把providers: [ProductService,LoggerService],
             bootstrap: [AppComponent]
改成
providers: [{
    provide:ProductService,
    //工厂提供器
    useFactory:() => {
      let logger = new LoggerService();
    //设置随机数来判断使用哪个服务
      let flag = Math.random() > 0.5;
      if(flag){
        return new ProductService(logger);
      }else{
        return new AnotherProductService(logger);
      }
    }
  },LoggerService],
  bootstrap: [AppComponent]
})

3.修改another-product.service.ts

import { Injectable } from '@angular/core';
import { ProductService,Product } from './product.service';
import { LoggerService } from './logger.service';

@Injectable()
export class AnotherProductService implements ProductService{
  getProduct():Product {
    return new Product(2,"huawei",3000,"华为");
  }

  //因为实现了ProductService,所以与ProductService的构造函数一样
  constructor(public logger:LoggerService) { }
}

4.修改product.service.ts

把logger改为public
constructor(public logger:LoggerService) { }

5.结果
根据随机数的不同,实现的服务不一样

4.jpg 5.jpg

PS:
在这个例子中produce1和product2组件使用的服务永远是相同的,因为工厂方法对象是一个单例对象,它只会在创建第一个需要注入的对象时被调用一次,然后在整个应用中,所有被注入的ProductService的实例,都是同一个对象

问题1
在工厂方法中手工实现了一个LoggerService,意味着这个工厂方法与LoggerService类是紧密耦合在一起的

let logger = new LoggerService();

解决
在product的工厂方法useFactory中去使用LoggerService提供器,修改代码:

providers: [{
    provide:ProductService,
    //2.把logger作为参数传进去,
    useFactory:(logger:LoggerService) => {
      let flag = Math.random() > 0.5;
      if(flag){
        return new ProductService(logger);
      }else{
        return new AnotherProductService(logger);
      }
    },
    //1.声明第三个参数deps,用来声明工厂方法所依赖的参数
    //3.在deps里添加LoggerService,表明需要依赖LoggerService 
    deps:[LoggerService]
  },LoggerService],

问题2
当前实例化哪个对象使用flag随机数来判断的,在真实的项目中可能会依赖一个变量来判断,这个变量可能在不同环境和变量是不一样的
解决
变量也能像服务一样进行依赖注入

providers: [{
    provide:ProductService,
    // 2.加上第二个参数,这个参数自定义命名,对应的是 deps中的第二个参数
    useFactory:(logger:LoggerService,IsT) => {
      if(IsT){
        return new ProductService(logger);
      }else{
        return new AnotherProductService(logger);
      }
    },
    //3.添加提供器的token:Is_True
    deps:[LoggerService,"Is_True"]
  },LoggerService,
  // 1.第三个提供器,声明了一个token是一个字符串,注入的就是明确的值flase
  {
    provide:"Is_True",useValue:false
  }],

除了用具体值来定义一个提供器,也可以用一个对象来定义

providers: [{
    provide:ProductService,
    useFactory:(logger:LoggerService,IsT) => {
      // 2.取对象中的isDev的值
      if(IsT.isDev){
        return new ProductService(logger);
      }else{
        return new AnotherProductService(logger);
      }
    },
    deps:[LoggerService,"Is_True"]
  },LoggerService,
  {
    //1.把Is_True改为对象,修改useValue
    provide:"Is_True",useValue:{isDev:true}
  }],
上一篇下一篇

猜你喜欢

热点阅读