angular4 (2)依賴注入

如果一個對象A需要依賴對象B,那么對象A不需要明確實例化對象B,B會由外部機制注入進來。


1.png

<1>提供器聲明在模塊中

1.新建一個項目

ng new projectname

2.新建組件product1

ng g component product1

3.生成服務(wù)service

ng g service shared/product
//生成一個名叫product的服務(wù),放在了shared文件夾下

ps:
若報錯

too many symbolic links encountered

解決辦法:

刪除node_modules文件夾, 重新npm install

4.目錄結(jié)構(gòu)

2.jpg

5.編寫服務(wù)product.service.ts

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

/*
  @Injectable裝飾器作用:
  讓ProductService能夠?qū)e的服務(wù)注入到它的構(gòu)造函數(shù)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.構(gòu)造函數(shù):定義里面的字段
    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類型的組件用來接收從服務(wù)中獲取到的數(shù)據(jù)
    product: Product;

    //2.注入器:構(gòu)造函數(shù)中通過依賴注入,聲明token是ProductService的一個服務(wù)
    constructor(private productService:ProductService) { }

    ngOnInit() {
        //3.使用服務(wù)的getProduct()方法獲取數(shù)據(jù),賦給本地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.生成另一個服務(wù)

ng g service shared/anotherProduct

3.編寫anotherProduct服務(wù)--another-product.service.ts

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

@Injectable()
//1.實現(xiàn)ProductService,讓AnotherProductService和ProductService有相同方法
export class AnotherProductService implements ProductService{
  //2.實現(xiàn)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
構(gòu)造器部分與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服務(wù)
    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>
提供器作用規(guī)則
1.當(dāng)提供其聲明在模塊中時(app.module.ts),是對所有模塊可見的,所有模塊都可以注入
2.當(dāng)一個提供器聲明在組件中時,例如(product2.component.ts),這個提供器只對聲明它的組件及
  其子組件可見
3.當(dāng)聲明在組件中的提供器和模塊中的提供器具有相同的token時,組件中的提供器,
  例如(product2.component.ts)會覆蓋聲明在模塊中的提供器
4.一般情況下應(yīng)優(yōu)先將提供器聲明在模塊中(app.module.ts),除非某提供器必須對其他組件不可見時
  才聲明在組件中

<3>服務(wù)之間如何互相注入

1.生成新的服務(wù)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--在其構(gòu)造函數(shù)中注入logger服務(wù)

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

@Injectable()
export class ProductService {
  //1.注入logger服務(wù)
  constructor(private logger:LoggerService) { }
  getProduct():Product {
    //3.調(diào)用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能被別的服務(wù)注入

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.結(jié)果--控制臺中

3.jpg

PS:
1.只有聲明了@Injectable()裝飾器的服務(wù)才能注入其他服務(wù),建議所有服務(wù)都聲明@Injectable()裝飾器。
2.組件能夠注入服務(wù)的原因是聲明了@Component裝飾器,而這個裝飾器是@Injectable()裝飾器的子類

<4>使用工廠提供器和值聲明提供器

1.修改product2.component.ts
刪除以上例子中product2組件中的providers聲明,使product1和product2共用模塊中app.module.ts聲明的service

刪除
  providers:[{
    //3.不同的是使用的是AnotherProductService服務(wù)
    provide:ProductService,useClass:AnotherProductService
  }]

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

把providers: [ProductService,LoggerService],
             bootstrap: [AppComponent]
改成
providers: [{
    provide:ProductService,
    //工廠提供器
    useFactory:() => {
      let logger = new LoggerService();
    //設(shè)置隨機數(shù)來判斷使用哪個服務(wù)
      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,"華為");
  }

  //因為實現(xiàn)了ProductService,所以與ProductService的構(gòu)造函數(shù)一樣
  constructor(public logger:LoggerService) { }
}

4.修改product.service.ts

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

5.結(jié)果
根據(jù)隨機數(shù)的不同,實現(xiàn)的服務(wù)不一樣

4.jpg

5.jpg

PS:
在這個例子中produce1和product2組件使用的服務(wù)永遠是相同的,因為工廠方法對象是一個單例對象,它只會在創(chuàng)建第一個需要注入的對象時被調(diào)用一次,然后在整個應(yīng)用中,所有被注入的ProductService的實例,都是同一個對象

問題1
在工廠方法中手工實現(xiàn)了一個LoggerService,意味著這個工廠方法與LoggerService類是緊密耦合在一起的

let logger = new LoggerService();

解決
在product的工廠方法useFactory中去使用LoggerService提供器,修改代碼:

providers: [{
    provide:ProductService,
    //2.把logger作為參數(shù)傳進去,
    useFactory:(logger:LoggerService) => {
      let flag = Math.random() > 0.5;
      if(flag){
        return new ProductService(logger);
      }else{
        return new AnotherProductService(logger);
      }
    },
    //1.聲明第三個參數(shù)deps,用來聲明工廠方法所依賴的參數(shù)
    //3.在deps里添加LoggerService,表明需要依賴LoggerService 
    deps:[LoggerService]
  },LoggerService],

問題2
當(dāng)前實例化哪個對象使用flag隨機數(shù)來判斷的,在真實的項目中可能會依賴一個變量來判斷,這個變量可能在不同環(huán)境和變量是不一樣的
解決
變量也能像服務(wù)一樣進行依賴注入

providers: [{
    provide:ProductService,
    // 2.加上第二個參數(shù),這個參數(shù)自定義命名,對應(yīng)的是 deps中的第二個參數(shù)
    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}
  }],
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容