依賴注入是重要的程序設計模式。 Angular 有本身的依賴注入框架,離開了它,幾乎無法構建 Angular 應用。 它使用得很是普遍,以致於幾乎每一個人都會把它簡稱爲 DI。
//注入器 Constructor(private productService: ProductService ){ }
通常是在組件或者是服務中寫入端代碼,若是一個組件或者服務中的 constructor 注入爲空。 就表明着這個組件或者服務沒有注入任何的服務。
//提供器寫法一 Providers:[ProductService] //提供器寫法二 Providers:[{provide: ProductService, userClass:ProductService}] //提供器寫法三 Providers:[{ provide: ProductService, userClass:AnotherProductService }] //提供器寫法四 Providers:[{ provide: ProductService , userFactory: () => { /*新建服務的方法*/ } }]
寫法一:使用這種方法是默認的寫法(也是最精簡的寫法),直接提供一個 ProuctService。 寫法二:使用這種方法和默認的寫法是一個意思。 provide: ProductService就是提供的是 ProductService服務。 userClass:ProductService 就是咱們new 這個服務對象的時候,new的是 ProductService。 寫法三:使用這種方式,就是提供器提供的是 ProductService,可是 new 服務對象的時候, new 的是 AnotherProductService。 寫法四:使用工廠模式建立提供器
在代碼中找注入的方法: 1.在具體的組件或者服務中的 constructor 方法中找注入的服務 2.根據 1 中注入的服務找 提供器 3.跟據2 中的提供器找到對應的注入服務類 例: 注入器爲 :Constructor(private productService: ProductService ){ } 就去找對應的 providers(提供器) 若是提供器爲:Providers:[ProductService] 那麼注入的就是 ProductService 若是提供器爲:Providers:[{ provide: ProductService, userClass:AnotherProductService }] 那麼注入的就是 AnotherProductService
一方面,NgModule 中的提供商是被註冊到根注入器。這意味着在 NgModule 中註冊的提供商能夠被整個應用訪問。 另外一方面,在應用組件中註冊的提供商只在該組件及其子組件中可用。
@Injectable() 標識一個類能夠被注入器實例化。 一般,在試圖實例化沒有被標識爲@Injectable()的類時,注入器會報錯。 官方建議: 建議:爲每一個服務類都添加 @INJECTABLE() 建議:爲每一個服務類都添加@Injectable(),包括那些沒有依賴嚴格來講並不須要它的。由於: 面向將來: 沒有必要記得在後來添加依賴的時候添加 @Injectable()。 一致性:全部的服務都遵循一樣的規則,不須要考慮爲何某個地方少了一個。 "注意": 老是使用@Injectable()的形式,不能只用@Injectable。 若是忘了括號,應用就會神不知鬼不覺的報錯!
目的:在新建的工程中將數據從Service中注入到component中,而且在界面上面展現出來 1.新建一個工程: ng new di 2.新建 product1 組件: ng g c product1 3.新建 product 服務(在shared 路徑下面新建 product 服務): ng g service shared/product 或者 ng g s shared/product
修改代碼 produc.service.ts:css
/* 增長 class Product, 以及返回 Product對象供外部調用的方法 getProduct() getProduct方法須要返回一個 Product 。若是須要讓外部訪問到當前的 Service ,就須要加上一個註解 @Injectable() */ import { Injectable } from '@angular/core'; @Injectable() export class ProductService { constructor() { } getProduct(): Product { return new Product(1, "IPhone X", "最牛逼的全面屏手機", 8388); } } export class Product{ constructor( public id: number, public name: string, public desc: string, public price: number ){} }
修改 product1.component.tshtml
/* 在當前的 Product 類中增長 變量 product 以及 注入 ProductService,在初始化的鉤子中 調用 ProductService 的 getProduct 方法,返回一個 Product */ 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.htmlbootstrap
<!-- 修改界面,用於界面展現 --> <div> <div>商品編碼:{{product.id}}</div> <div>商品名稱:{{product.name}}</div> <div>商品描述:{{product.desc}}</div> <div>商品價格:{{product.price}}</div> </div>
修改 app.conponent.html設計模式
<!-- 將 product.html 加入到 當前界面 --> <h1> 依賴注入的例子 </h1> <div> <app-product1></app-product1> </div>
修改 app.modules.tsapp
/*添加 Product.service.ts 到 providers 中,在這個地方注入是叫作 「從根組件中注入」,而後全部的均可以訪問到。*/ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { Product1Component } from './product1/product1.component'; import {ProductService} from "app/shared/product.service"; @NgModule({ declarations: [ AppComponent, Product1Component ], imports: [ BrowserModule, FormsModule, HttpModule ], providers: [ProductService], bootstrap: [AppComponent] }) export class AppModule { }
圖示:框架
在具體的某個組件中去注入服務,而不是經過"根模塊"去注入。 因爲在具體的組件中去注入服務,這樣子能夠"覆蓋根模塊的注入",從而使本身的子模塊擁有該服務,這樣子就能夠作服務的多級注入。 目的:我新建一個 product2 組件,而後在 product2 組件中注入 anotherProductService 1.新建一個組件 product2 ng g c product2 2.在 shared目錄下新建一個service anotherProduct ng g s shared/anotherProduct
修改 another-product.service.tsdom
/* 實現 ProductService 服務,共同的擁有一個返回 Product 的方法,這個地方 用不用 implements 實現 ProductService都不影響,在 component中的提供器上面 使用這個服務 */ import { Injectable } from '@angular/core'; import {Product, ProductService} from "./product.service"; @Injectable() export class AnotherProductService implements ProductService{ constructor() { } getProduct(): Product { return new Product(2, "小米 MIX2", "小米最牛逼的全屏手機,陶瓷機身", 3600); } }
修改product2.conponent. tside
/* 在 Component 語法糖中添加 服務的提供器 provide, 這樣子作會覆蓋根模塊中注入的服務。而且在 Component中注入的服務就能夠不用在 app.modules.ts 中的 provide中添加 */ 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(); } }
修改product2.component.htmlthis
<!-- 用於展現數據 --> <div> <div>商品編碼:{{product.id}}</div> <div>商品名稱:{{product.name}}</div> <div>商品描述:{{product.desc}}</div> <div>商品價格:{{product.price}}</div> </div>
修改 app.component.html編碼
<!-- 將 product2 的組件添加到 根頁面中,讓頁面展現 product2 的數據 --> <h1> 依賴注入的例子 </h1> <div> <app-product1></app-product1> <hr> <app-product2></app-product2> </div>
圖示:
上面咱們是介紹了怎麼去在 組件中注入服務,其實在"服務"中能夠"注入服務" 新建服務 logger.service.ts ng g s shared/logger
修改 logger.service.ts
/* 增長一個打印日誌的方法 */ import { Injectable } from '@angular/core'; @Injectable() export class LoggerService { constructor() { } log(msg: string){ console.log(msg); } }
修改app.module.ts
/*增長 loggerService的服務到提供器中*/ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { Product1Component } from './product1/product1.component'; import {ProductService} from "app/shared/product.service"; import { Product2Component } from './product2/product2.component'; import {LoggerService} from "./shared/logger.service"; @NgModule({ declarations: [ AppComponent, Product1Component, Product2Component ], imports: [ BrowserModule, FormsModule, HttpModule ], providers: [ProductService, LoggerService], bootstrap: [AppComponent] }) export class AppModule { }
修改another-product.service.ts
/*增長 logger 服務到當前的 服務中,調用的時候就打印日誌*/ import { Injectable } from '@angular/core'; import {Product, ProductService} from "./product.service"; import {LoggerService} from "./logger.service"; @Injectable() export class AnotherProductService implements ProductService{ constructor(private logger: LoggerService) { } getProduct(): Product { this.logger.log("product2 getProduct() 方法被調用"); return new Product(2, "小米 MIX2", "小米最牛逼的全屏手機,陶瓷機身", 3600); } }
圖示:
說明: 有的時候,咱們須要動態建立某一個依賴值,由於它所須要的信息直到最後一刻才能肯定。而後根據這個依賴值去建立咱們所須要的類。 當遇到這樣子的狀況,就可使用工廠模式。
修改 product2.component.ts
/* 目的:是爲了經過工廠模式注入,而不是在 組件 Component中注入 去掉在 @component 語法糖中的提供器 providers,讓product2 和 product1 共用一個提供器 */ import { Component, OnInit } from '@angular/core'; import {Product, ProductService} from "../shared/product.service"; @Component({ selector: 'app-product2', templateUrl: './product2.component.html', styleUrls: ['./product2.component.css'] }) export class Product2Component implements OnInit { product: Product; constructor(private productService: ProductService) { } ngOnInit() { this.product = this.productService.getProduct(); } }
圖示:
修改 app.module.ts
/* 修改app.module.ts 中的提供器,修改成使用工廠模式啓動。 須要說明的是,由於在 AnotherProductService 中的構造器注入了 LoggerService 服務,須要在這個地方傳入一個logger對象進去。 咱們也能夠將工廠模式的這段代碼寫入到單獨的一個ts中。(見官方的例子) */ providers: [{ provide:ProductService, useFactory: () => { let logger = new LoggerService(); let random = Math.random(); let flag = random < 0.5; logger.log("生成的隨機數爲: "+random); if(flag){ return new ProductService(); }else{ return new AnotherProductService(logger); } } }, LoggerService]
完整的 app.module.ts 的代碼
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { Product1Component } from './product1/product1.component'; import {ProductService} from "app/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, FormsModule, HttpModule ], providers: [{ provide:ProductService, useFactory: () => { let logger = new LoggerService(); let random = Math.random(); let flag = random < 0.5; logger.log("生成的隨機數爲: "+random); if(flag){ return new ProductService(); }else{ return new AnotherProductService(logger); } } }, LoggerService], bootstrap: [AppComponent] }) export class AppModule { }
圖示:
修改 provides 的寫法,增長 deps 節點,這個節點就是提供 工廠模式傳遞參數,而且能夠把數據傳入進去。 在providers 中增長 {provide: "IS_DEV_ENV", useValue: false}, 就是在 provider 中增長一個變量爲 IS_DEV_ENV,值爲 false。 經過 useFactory 的方法傳入參數進去,就能夠直接使用這個參數的值
providers: [{ provide:ProductService, useFactory: (logger: LoggerService, is_dev) => { if(is_dev){ return new ProductService(); }else{ return new AnotherProductService(logger); } }, deps:[LoggerService, "IS_DEV_ENV"] }, LoggerService, {provide:"IS_DEV_ENV", useValue :false} ]
providers: [{ provide:ProductService, useFactory: (logger: LoggerService, config) => { if( config.isDev ){ return new ProductService(); }else{ return new AnotherProductService(logger); } }, deps:[LoggerService, "APP_CONFIG"] }, LoggerService, { provide:"APP_CONFIG", useValue :{isDev: false} } ]
我是一步一步編寫代碼,邊整理博客,按照上面的順序,應該是能夠把例子跑起來。若是須要本例子的代碼,能夠聯繫我。