Angular6學習筆記8: 服務(Service)(1)

wujiayucn發表於2018-08-23

服務(Service)

繼學習筆記7,可以使用主從元件,現在繼續學習(服務)Service;

問題:為什麼需要服務?


因為:元件不應該直接獲取或儲存資料,它們不應該瞭解是否在展示假資料。 它們應該聚焦於展示資料,而把資料訪問的職責委託給某個服務。


這次將建立一個 HeroService,應用中的所有類都可以使用它來獲取英雄列表。 不要使用 new 來建立此服務,而要依靠 Angular 的依賴注入機制把它注入到 HeroesComponent 的建構函式中

服務是在多個“互相不知道”的類之間共享資訊的好辦法。

1.建立HeroService

使用 Angular CLI 建立一個名叫 hero 的服務。

ng generate service hero
wjydeMacBook-Pro:demo wjy$ ng generate service hero
CREATE src/app/hero.service.spec.ts (362 bytes)
CREATE src/app/hero.service.ts (133 bytes)

 執行完這個命令以後,開啟src/app/hero.service.ts 中生成 HeroService,

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

@Injectable({
  providedIn: 'root'
})
export class HeroService {

  constructor() { }
}

@Injectable() 服務

這個新的服務匯入了 Angular 的Injectable  符號,並且給這個服務類新增了 @Injectable() 裝飾器。 它把這個類標記為依賴注入系統的參與者之一。HeroService 類將會提供一個可注入的服務,並且它還可以擁有自己的待注入的依賴。 目前它還沒有依賴.

@Injectable() 裝飾器會接受該服務的後設資料物件,就像 @Component() 對元件類的作用一樣。

2.在HeroService中獲取hero的資料

HeroService 可以從任何地方獲取資料:Web 服務、本地儲存(LocalStorage)或一個模擬的資料來源。

從元件中移除資料訪問邏輯,意味著將來任何時候你都可以改變目前的實現方式,而不用改動任何元件。

在HeroService中匯入 Hero 和 HEROES,並寫一個獲取hero列表的方法:getHeroes()

import {Injectable} from '@angular/core';
import {Hero} from './hero';
import {HEROES} from './mock-heroes';

@Injectable({
  providedIn: 'root'
})
export class HeroService {

  constructor() {
  }

  getHeroes(): Hero[] {
    return HEROES;
  }
}

3.提供(provide) HeroService

在要求 Angular 把 HeroService 注入到 HeroesComponent 之前,你必須先把這個服務提供給依賴注入系統。通過註冊提供商來做到這一點。提供商用來建立和交付服務,在這個例子中,它會對 HeroService 類進行例項化,以提供該服務,需要確保 HeroService 已經作為該服務的提供商進行過註冊。 你要用一個注入器註冊它。注入器就是一個物件,負責在需要時選取和注入該提供商。預設情況下,Angular CLI 命令 ng generate service 會通過給 @Injectable 裝飾器新增後設資料的形式,為該服務把提供商註冊到根注入器上。如果你看看 HeroService 緊前面的 @Injectable() 語句定義,就會發現 providedIn 後設資料的值是 'root',當你在頂層提供該服務時,Angular 就會為 HeroService 建立一個單一的、共享的例項,並把它注入到任何想要它的類上。 在 @Injectable 後設資料中註冊該提供商,還能讓 Angular 可以通過移除那些完全沒有用過的服務,來進行優化。

注意:如果需要,可以在不同的層次上註冊提供商 —— 在 HeroesComponent 中、在 AppComponent 中,或在 AppModule 中。 比如,可以通過附加 --module=app 引數來告訴 CLI 要自動在模組級提供該服務。

ng generate service hero --module=app

4.修改 HeroesComponent

開啟 HeroesComponent 類檔案。

刪除 HEROES 的匯入語句,因為你以後不會再用它了。 轉而匯入 HeroService,把 heroes 屬性的定義改為一句簡單的宣告。

import {Component, OnInit} from '@angular/core';
import {Hero} from '../hero';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm',
  };
  heroes: Hero[];
  selectedHero: Hero;

  constructor() {
  }

  ngOnInit() {
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

5.注入 HeroService

往建構函式中新增一個私有的 heroService,其型別為 HeroService

constructor(private heroService: HeroService) { }

新增 getHeroes()

getHeroes(): void {
  this.heroes = this.heroService.getHeroes();
}

6.在 ngOnInit 中呼叫getHeroes()

a.在構造方法中呼叫(不推薦)

在建構函式呼叫:getHeroes();

constructor(private heroService: HeroService) {
    this.getHeroes();
  }

這樣也會達到之前應用的效果,如圖

b.在 ngOnInit 生命週期鉤子中呼叫

在ngOnInit 呼叫:getHeroes();

ngOnInit() {
    this.getHeroes();
  }

這樣也會達到之前應用的效果,如圖

兩者的比較:

讓建構函式保持簡單,只做初始化操作,比如把建構函式的引數賦值給屬性。 建構函式不應該做任何事它肯定不能呼叫某個函式來向遠端服務(比如真實的資料服務)發起 HTTP 請求。

而是選擇在 ngOnInit 生命週期鉤子中呼叫 getHeroes(),之後交由 Angular 處理,它會在構造出 HeroesComponent 的例項之後的某個合適的時機呼叫 ngOnInit。

 

相關文章