Angular 4: Service


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中。




沒有留言:

張貼留言

Java Spring Framework 筆記 - Autowiring (2)

這篇記錄透過 Annotation來做到 Spring的 autowiring。