Service可以在不同的component與service中傳遞與共用一些資訊,有了這樣的功能,有些在component上原本透過event binding來實作的功能,改用service來輔助,開發上也會變得簡單與方便許多,且可以達到相同的目的。
1. 建立service
Angular中的service其實只是一般的class,因此在建立時,只需要建立一個單純的class檔就是service。export class LoggingService{}
亦可以透過angular-cli指令 : ng g s serviceName
建出來的檔案就是基本service的雛形。
2. Dependency Injection
調整LoggingService,新增一個可供呼叫的function:export class LoggingService { constructor() { } addLog(message: string) { console.log('message: ' + message); } }
要在Component中使用LoggingService,通常是透過dependency injection的方式來使用,在將LoggingService注入Component前,還需要在要注入的component (或是app.module.ts內)的decorator處告訴component,要使用什麼service,之後才可以成功注入service到component中。
service的注入通常會在component的constructor內,參考下列程式:
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], providers: [LoggingService] }) export class AppComponent implements OnInit { title = 'app works!'; constructor(private loggingService: LoggingService) { } ngOnInit(): void { this.loggingService.addLog('New message'); } }
透過這個方式,就可以在不同的component或是service內共享一些function與data,意思就是A component與B component在service內所使用的data與function都是同一個,藉此可以達到component間的資料共享。
3. Hierarchical Injection
Angular內有Hierarchical Injection,主要是Angular內component在有dependency injection時,各個component找到注入類別的相關方式。
舉例來說,如今有
<componentA>
<componentB>
< componentC></ componentC>
</<componentB>
<componentA>
A是B的parent; B是C的parent。
如今在C 注入UserService時,C會先檢查自己的providers內有沒有定義UserService,如果有,則直接從C內初始化一個UserService使用,如果沒有則往parent B找,如果B有,則使用B內已經注入且初始化過的UserService,如果沒有,則再往B的parent A找,最終會找到app.component.ts內的providers,如果appComponent內也沒有,則會拋出錯誤。
service對特定條件下的component是共用的(有點像是singleton),特定條件是指在providers上加入service的component與其child component,但如果child component也有在providers上加入service,則不在這個特定條件下,該service會以新的實體被建立出來( 即使類別相同,但產生的卻是不同的實體),給這個child component與child-child component使用。
對Angular來說,appComponent是所有component的root,所以在appComponent內定義的service可以說是整個application內共用的。
4. 在Service內注入Service
如果現在新建立一個UserService,裡面儲存使用者資料與提供新增的功能,那麼寫法可以如下:import { LoggingService } from './logging.service'; import { Injectable } from '@angular/core'; @Injectable() export class UserService { users: { name: string, age: number }[] = []; constructor(private loggingService: LoggingService) { } addUser(name: string, age: number) { this.users.push({ name: name, age: age }); this.loggingService.addLog('New user added: ' + name + ' / ' + age); } }
這邊希望在每次新增一個User時,都透過console顯示資訊,所以希望在UserService內使用LoggingService來記錄Log,如果希望可以在特定Service內注入其他Service,則必須為Service加上decorator:@Injectable(),意思是允許在Service內做dependency Injection,如果漏了這個decorator,又注入service,Angular就會拋出錯誤訊息。
5. 透過service讓components互相溝通
service內可以定義像是component內的event,來讓其他component接收並處理,方法很相近,但有點不同。如果今天希望在UserService新增了一位User後,通知另一個component顯示新增的User 姓名與年齡,則可以在UserService內定義一個EventEmitter,在新增使用者後,送出一個emit。
@Injectable() export class UserService { users: { name: string, age: number }[] = []; onUserAdded = new EventEmitter<{ name: string, age: number }>(); constructor(private loggingService: LoggingService) { } addUser(name: string, age: number) { this.users.push({ name: name, age: age }); this.loggingService.addLog('New user added: ' + name + ' / ' + age); this.onUserAdded.emit({ name: name, age: age }); } }
接著在另一個showUserComponent中,透過以下方法來等待UserService的onUserAdded事件觸發:
export class ShowUserComponent implements OnInit { name: string; age: number; constructor(private userService: UserService) { this.userService.onUserAdded.subscribe( (user: { name: string, age: number }) => { this.name = user.name; this.age = user.age; } ); } ngOnInit() { } }
關鍵就在於,在另一個component中,對UserService的onUserAdded做subscribe,傳入的user即為onUserAdded處emit出來的User物件,subscribe內使用的是arrow function,透過這方式,在其他component中,就可以等待UserService內拋出來的事件,對內容做處理,在這邊就是讓從AppComponent新增的User顯示在ShowUserComponent中。
沒有留言:
張貼留言