您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何理解Angular服務,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
組件不應該直接獲取或保存數據,它們不應該了解是否在展示假數據。 它們應該聚焦于展示數據,而把數據訪問的職責委托給某個服務。
你將創建一個 HeroService
,應用中的所有類都可以使用它來獲取英雄列表。 不要使用 new
來創建此服務,而要依靠 Angular 的依賴注入機制把它注入到 HeroesComponent
的構造函數中。
服務是在多個“互相不知道”的類之間共享信息的好辦法。 你將創建一個 MessageService
,并且把它注入到兩個地方:
HeroService
中,它會使用該服務發送消息。
MessagesComponent
中,它會顯示其中的消息。
HeroService
使用 Angular CLI 創建一個名叫 hero
的服務。
ng generate service hero |
該命令會在src/app/hero.service.ts
中生成HeroService
類的骨架。HeroService
類的代碼如下:
src/app/hero.service.ts (new service)
import { Injectable } from '@angular/core' ; @Injectable ({ providedIn: 'root' , }) export class HeroService { constructor() { } } |
注意,這個新的服務導入了 Angular 的 Injectable 符號,并且給這個服務類添加了 @
Injectable()
裝飾器。 它把這個類標記為依賴注入系統的參與者之一。HeroService
類將會提供一個可注入的服務,并且它還可以擁有自己的待注入的依賴。 目前它還沒有依賴,但是很快就會有了。
@
Injectable()
裝飾器會接受該服務的元數據對象,就像 @
Component()
對組件類的作用一樣。
HeroService
可以從任何地方獲取數據:Web 服務、本地存儲(LocalStorage)或一個模擬的數據源。
從組件中移除數據訪問邏輯,意味著將來任何時候你都可以改變目前的實現方式,而不用改動任何組件。 這些組件不需要了解該服務的內部實現。
這節課中的實現仍然會提供模擬的英雄列表。
導入 Hero
和 HEROES
。
import { Hero } from './hero' ; import { HEROES } from './mock-heroes' ; |
添加一個 getHeroes
方法,讓它返回模擬的英雄列表。
getHeroes(): Hero[] { return HEROES; } |
HeroService
在要求 Angular 把 HeroService
注入到 HeroesComponent
之前,你必須先把這個服務提供給依賴注入系統。稍后你就要這么做。 你可以通過注冊提供商來做到這一點。提供商用來創建和交付服務,在這個例子中,它會對 HeroService
類進行實例化,以提供該服務。
現在,你需要確保 HeroService
已經作為該服務的提供商進行過注冊。 你要用一個注入器注冊它。注入器就是一個對象,負責在需要時選取和注入該提供商。
默認情況下,Angular CLI 命令 ng generate service
會通過給 @
Injectable 裝飾器添加元數據的形式,用根注入器將你的服務注冊成為提供商。
如果你看看 HeroService
緊前面的 @
Injectable()
語句定義,就會發現 providedIn 元數據的值是 'root':
@Injectable ({ providedIn: 'root' , }) |
@
Injectable
({ providedIn: 'root',})
當你在頂層提供該服務時,Angular 就會為 HeroService
創建一個單一的、共享的實例,并把它注入到任何想要它的類上。 在 @
Injectable 元數據中注冊該提供商,還能允許 Angular 通過移除那些完全沒有用過的服務來進行優化。
要了解關于提供商的更多知識,參見提供商部分。 要了解關于注入器的更多知識,參見依賴注入指南。
現在 HeroService
已經準備好插入到 HeroesComponent
中了。
這是一個過渡性的代碼范例,它將會允許你提供并使用 HeroService
。此刻的代碼和最終代碼相差很大。
HeroesComponent
打開 HeroesComponent
類文件。
刪除 HEROES
的導入語句,因為你以后不會再用它了。 轉而導入 HeroService
。
src/app/heroes/heroes.component.ts (import HeroService)
import { HeroService } from '../hero.service' ; |
把 heroes
屬性的定義改為一句簡單的聲明。
heroes: Hero[]; |
HeroService
往構造函數中添加一個私有的 heroService
,其類型為 HeroService
。
constructor( private heroService: HeroService) { } |
這個參數同時做了兩件事:1. 聲明了一個私有 heroService
屬性,2. 把它標記為一個 HeroService
的注入點。
當 Angular 創建 HeroesComponent
時,依賴注入系統就會把這個 heroService
參數設置為 HeroService
的單例對象。
創建一個函數,以從服務中獲取這些英雄數據。
getHeroes(): void { this .heroes = this .heroService.getHeroes(); } |
ngOnInit
中調用它你固然可以在構造函數中調用 getHeroes()
,但那不是最佳實踐。
讓構造函數保持簡單,只做初始化操作,比如把構造函數的參數賦值給屬性。 構造函數不應該做任何事。 它當然不應該調用某個函數來向遠端服務(比如真實的數據服務)發起 HTTP 請求。
而是選擇在 ngOnInit 生命周期鉤子中調用 getHeroes(),之后交由 Angular 處理,它會在構造出 HeroesComponent 的實例之后的某個合適的時機調用 ngOnInit。
ngOnInit() { this .getHeroes(); } |
刷新瀏覽器,該應用仍運行的一如既往。 顯示英雄列表,并且當你點擊某個英雄的名字時顯示出英雄詳情視圖。
HeroService.getHeroes()
的函數簽名是同步的,它所隱含的假設是 HeroService
總是能同步獲取英雄列表數據。 而 HeroesComponent
也同樣假設能同步取到 getHeroes()
的結果。
this .heroes = this .heroService.getHeroes(); |
這在真實的應用中幾乎是不可能的。 現在能這么做,只是因為目前該服務返回的是模擬數據。 不過很快,該應用就要從遠端服務器獲取英雄數據了,而那天生就是異步操作。
HeroService
必須等服務器給出響應, 而 getHeroes()
不能立即返回英雄數據, 瀏覽器也不會在該服務等待期間停止響應。
HeroService.getHeroes()
必須具有某種形式的異步函數簽名。
它可以使用回調函數,可以返回 Promise
(承諾),也可以返回 Observable
(可觀察對象)。
這節課,HeroService.getHeroes()
將會返回 Observable
,因為它最終會使用 Angular 的 HttpClient.get
方法來獲取英雄數據,而 HttpClient.get()
會返回 Observable
。
HeroService
Observable
是 RxJS 庫中的一個關鍵類。
在稍后的 HTTP 教程中,你就會知道 Angular HttpClient 的方法會返回 RxJS 的 Observable
。 這節課,你將使用 RxJS 的 of()
函數來模擬從服務器返回數據。
打開 HeroService
文件,并從 RxJS 中導入 Observable
和 of
符號。
src/app/hero.service.ts (Observable imports)
import { Observable, of } from 'rxjs' ; |
把 getHeroes
方法改成這樣:
getHeroes(): Observable<Hero[]> { return of(HEROES); } |
of(HEROES)
會返回一個Observable<Hero[]>
,它會發出單個值,這個值就是這些模擬英雄的數組。
在 HTTP 教程中,你將會調用 HttpClient.get<Hero[]>()
它也同樣返回一個 Observable<Hero[]>
,它也會發出單個值,這個值就是來自 HTTP 響應體中的英雄數組。
HeroesComponent
中訂閱HeroService.getHeroes
方法之前返回一個 Hero[]
, 現在它返回的是 Observable<Hero[]>
。
你必須在 HeroesComponent
中也向本服務中的這種形式看齊。
找到 getHeroes
方法,并且把它替換為如下代碼(和前一個版本對比顯示):
heroes.component.ts (Observable)
getHeroes(): void { this .heroService.getHeroes() .subscribe(heroes => this .heroes = heroes); } |
heroes.component.ts (Original)
getHeroes(): void { this .heroes = this .heroService.getHeroes(); } |
Observable.subscribe()
是關鍵的差異點。
上一個版本把英雄的數組賦值給了該組件的 heroes
屬性。 這種賦值是同步的,這里包含的假設是服務器能立即返回英雄數組或者瀏覽器能在等待服務器響應時凍結界面。
當 HeroService
真的向遠端服務器發起請求時,這種方式就行不通了。
新的版本等待 Observable
發出這個英雄數組,這可能立即發生,也可能會在幾分鐘之后。 然后,subscribe
函數把這個英雄數組傳給這個回調函數,該函數把英雄數組賦值給組件的 heroes
屬性。
使用這種異步方式,當 HeroService
從遠端服務器獲取英雄數據時,就可以工作了。
在這一節,你將
添加一個 MessagesComponent
,它在屏幕的底部顯示應用中的消息。
創建一個可注入的、全應用級別的 MessageService
,用于發送要顯示的消息。
把 MessageService
注入到 HeroService
中。
當 HeroService
成功獲取了英雄數據時顯示一條消息。
MessagesComponent
使用 CLI 創建 MessagesComponent
。
ng generate component messages |
CLI 在 src/app/
messages 中創建了組件文件,并且把 MessagesComponent
聲明在了 AppModule
中。
修改 AppComponent
的模板來顯示所生成的 MessagesComponent
:
/src/app/app.component.html
<h2>{{title}}</h2> <app-heroes></app-heroes> <app-messages></app-messages> |
你可以在頁面的底部看到來自的 MessagesComponent
的默認內容。
MessageService
使用 CLI 在 src/app
中創建 MessageService
。
ng generate service message |
打開MessageService
,并把它的內容改成這樣:
/src/app/message.service.ts
import { Injectable } from '@angular/core' ; @Injectable ({ providedIn: 'root' , }) export class MessageService { messages: string[] = []; add(message: string) { this .messages.push(message); } clear() { this .messages = []; } } |
該服務對外暴露了它的 messages 緩存,以及兩個方法:add()
方法往緩存中添加一條消息,clear()
方法用于清空緩存。
HeroService
中重新打開 HeroService
,并且導入 MessageService
。
/src/app/hero.service.ts (import MessageService)
import { MessageService } from './message.service' ; |
修改這個構造函數,添加一個私有的 messageService
屬性參數。 Angular 將會在創建 HeroService
時把 MessageService
的單例注入到這個屬性中。
constructor( private messageService: MessageService) { } |
這是一個典型的“服務中的服務”場景: 你把 MessageService
注入到了 HeroService
中,而 HeroService
又被注入到了 HeroesComponent
中。
HeroService
中發送一條消息修改 getHeroes
方法,在獲取到英雄數組時發送一條消息。
getHeroes(): Observable<Hero[]> { // TODO: send the message _after_ fetching the heroes this .messageService.add( 'HeroService: fetched heroes' ); return of(HEROES); } |
HeroService
中顯示消息MessagesComponent
可以顯示所有消息, 包括當 HeroService
獲取到英雄數據時發送的那條。
打開 MessagesComponent
,并且導入 MessageService
。
/src/app/messages/messages.component.ts (import MessageService)
import { MessageService } from '../message.service' ; |
修改構造函數,添加一個 public 的 messageService
屬性。 Angular 將會在創建 MessagesComponent
的實例時 把 MessageService
的實例注入到這個屬性中。
constructor( public messageService: MessageService) {} |
這個messageService
屬性必須是公共屬性,因為你將會在模板中綁定到它。
Angular 只會綁定到組件的公共屬性。
MessageService
把 CLI 生成的 MessagesComponent
的模板改成這樣:
src/app/messages/messages.component.html
<div *ngIf= "messageService.messages.length" > <h3>Messages</h3> <button class = "clear" (click)= "messageService.clear()" >clear</button> <div *ngFor= 'let message of messageService.messages' > {{message}} </div> </div> |
這個模板直接綁定到了組件的 messageService
屬性上。
*
ngIf 只有在有消息時才會顯示消息區。
*
ngFor 用來在一系列 <div>
元素中展示消息列表。
Angular 的事件綁定把按鈕的 click
事件綁定到了 MessageService.clear()
。
當你把 最終代碼 某一頁的內容添加到 messages.component.css
中時,這些消息會變得好看一些。
刷新瀏覽器,頁面顯示出了英雄列表。 滾動到底部,就會在消息區看到來自 HeroService
的消息。 點擊“清空”按鈕,消息區不見了。
你的應用應該變成了這樣 在線例子 / 下載范例。本頁所提及的代碼文件如下。
如果你想直接在 stackblitz 運行本頁中的例子,請單擊鏈接:https://stackblitz.com/github/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services
本頁中所提及的代碼如下:https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services
對應的文件列表和代碼鏈接如下:
文件名 | 源代碼 |
---|---|
src/app/hero.service.ts | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/hero.service.ts |
src/app/heroes/heroes.component.ts | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/heroes/heroes.component.ts |
src/app/messages/messages.component.ts | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/messages/messages.component.ts |
src/app/messages/messages.component.html | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/messages/messages.component.html |
src/app/messages/messages.component.css | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/messages/messages.component.css |
src/app/app.module.ts | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/app.module.ts |
src/app/app.component.html | https://github.com/cwiki-us-angular/cwiki-us-angular-tour-of-hero-services/blob/master/src/app/app.component.html |
你把數據訪問邏輯重構到了 HeroService
類中。
你在根注入器中把 HeroService
注冊為該服務的提供商,以便在別處可以注入它。
你使用 Angular 依賴注入機制把它注入到了組件中。
你給 HeroService
中獲取數據的方法提供了一個異步的函數簽名。
你發現了 Observable
以及 RxJS 庫。
你使用 RxJS 的 of()
方法返回了一個模擬英雄數據的可觀察對象 (Observable<Hero[]>
)。
在組件的 ngOnInit
生命周期鉤子中調用 HeroService
方法,而不是構造函數中。
你創建了一個 MessageService
,以便在類之間實現松耦合通訊。
HeroService
連同注入到它的服務 MessageService
一起,注入到了組件中。
上述內容就是如何理解Angular服務,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。