美文网首页
angular4 (2)依赖注入

angular4 (2)依赖注入

作者: mumumuu | 来源:发表于2018-04-12 14:25 被阅读0次

    如果一个对象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}
      }],
    

    相关文章

      网友评论

          本文标题:angular4 (2)依赖注入

          本文链接:https://www.haomeiwen.com/subject/cqgxkftx.html