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