【Angular】サービスとは?「作成方法」や「使い方」などを解説!

この記事では、Angularの「サービス」について、以下の内容をサンプルコードを用いてわかりやすく解説します。

  • Angularの「サービス」とは
  • Angularの「サービス」の使い方
  • @Injectableデコレーター内にprovidedIn: 'root'を記述しない場合のサービスの使い方

Angularの「サービス」とは

Angularの「サービス」とは

Angularのサービスとは、データの取得や計算などの処理をまとめ、コンポーネントなどから利用できるようにしたクラスです。

Angularでは、コンポーネントは画面の表示やユーザーの操作への対応を担当し、データの取得・変更・計算などの処理はサービスにまとめるのが一般的です。このような、アプリケーションの機能や業務上のルールに関わる処理を「ビジネスロジック」と呼びます。

例えば、外部のWeb APIからデータを取得する処理や、取得したデータを使いやすい形に整える処理は、サービスにまとめると管理しやすくなります。コンポーネントからそのサービスを呼び出すことで、画面に関する処理とデータに関する処理を分けられます。

ビジネスロジックをサービスに分けると、コンポーネントのコードが読みやすくなります。また、同じ処理を複数のコンポーネントから利用しやすくなります。

サービスの使用例には、以下のようなものがあります。

  • 本などの商品情報を取得するサービス
    • BookService
  • チケットの予約を行うサービス
    • BookingService

この記事では、簡単な例として、「Hello Angular!」という文字列を返すサービス(MessageService)の作成方法と使い方を説明します。サービスが返した文字列は、コンポーネントを通して画面に表示します。

あわせて読みたい

ビジネスロジックとは、業務やサービスのルールに基づいて、計算・判断などを行う処理のことです。

「ビジネスロジック」については、以下の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

Angularの「サービス」の使い方

Angularの「サービス」を使用する手順を以下に示します。

Angularの「サービス」を使用する手順

  1. サービスを作成する
  2. サービスを編集する
  3. サービスをコンポーネントに注入する

それぞれの手順を順番に説明します。

1. サービスを作成する

まず、Angular CLIを使って、サービスのひな形を作成します。

ターミナルで、Angularプロジェクトのルートディレクトリ(angular.jsonがあるディレクトリ)に移動します。サービスのひな形は、Angular CLIのng generate serviceコマンドで作成できます。この記事では、サービス名にmessageを指定して実行します。

ng generate service [サービス名]
# MessageServiceを作成する例
ng generate service message

このコマンドを実行すると、src/appディレクトリにサービスのファイル(message.service.ts)と、ユニットテスト用のファイル(message.service.spec.ts)が作成されます。ユニットテストは、サービスの処理が想定どおりに動くかを確認するためのテストです。

作成されるmessage.service.tsの基本的な構成は、以下のとおりです。

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

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

  constructor() { }
}

上記のコードでは、@Injectableデコレーターを使うために、@angular/coreからInjectableをインポートしています。

クラス定義(export class MessageService)の前にある@Injectableは、Angularが依存性注入の仕組みでこのクラスのインスタンスを作成するための情報を付けるデコレーターです。「注入」とは、必要なサービスのインスタンスをAngularから受け取ることです。サービスを登録すると、コンポーネントや別のサービスなどに注入して利用できます。

providedIn: 'root'は、サービスをアプリケーション全体で利用できるように登録する設定です。登録先の「ルートインジェクター」は、サービスのインスタンスを管理し、必要な場所に渡す仕組みです。通常、この設定で登録したサービスは、アプリケーション全体で同じインスタンスを共有します。

この記事では、ユニットテスト用のmessage.service.spec.tsは使用しません。今回のサンプルを試すだけであれば、削除しても構いません。

2. サービスを編集する

次に、自動生成されたmessage.service.tsを編集します。以下のように、文字列を返すmessageメソッドを追加します。

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

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  message(): string {
    return 'Hello Angular!';
  }
}

サービスのクラス内には、コンポーネントなどから利用する処理をメソッドとして記述します。上記のmessageメソッドは、呼び出されるとHello Angular!という文字列を返します。: stringは、戻り値が文字列であることを表しています。

今回は仕組みを理解するために、文字列を返すだけの簡単なメソッドを用意しました。実際のアプリケーションでは、Web APIからデータを取得する処理や、データを加工・計算する処理などを記述します。

3. サービスをコンポーネントに注入する

次に、作成したMessageServiceをAppComponentに注入します。以下は、サービスから受け取った文字列を画面に表示する例です。

import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-root',
  template: `{{receiveMessage}}`,
})
export class AppComponent {
  receiveMessage: string | undefined;

  constructor(private messageService: MessageService) { }

  ngOnInit() {
    this.receiveMessage = this.messageService.message();
  }
}

上記のコードでは、ngOnInitでサービスのmessageメソッドを呼び出し、戻り値をreceiveMessageに代入しています。ngOnInitは、コンポーネントの初期化時に一度呼び出されるメソッドです。テンプレートの{{receiveMessage}}によって、その値が画面に表示されます。サービスを注入するための設定は、以下の2つです。

サービスをコンポーネントに注入する手順

  1. MessageServiceをインポートする
  2. コンストラクタにサービス型の引数を追加する

それぞれの手順を順番に説明します。

1. MessageServiceをインポートする

AppComponentのファイルに、以下のインポート文を追加します。

import { MessageService } from './message.service';

AppComponentでサービスを使うために、MessageServiceをインポートします。./message.serviceは、インポート元のファイルの場所を表しています。

2. コンストラクタにサービス型の引数を追加する

コンストラクタに、以下のようにMessageService型の引数を追加します。

constructor(private messageService: MessageService) { }

次に、コンストラクタにMessageService型の引数を追加します。Angularは、この型をもとに登録済みのサービスを探し、インスタンスを渡します。

引数の前にprivateを付けると、受け取ったインスタンスがmessageServiceプロパティに自動的に保存されます。そのため、AppComponentのクラス内では、this.messageService.message()のようにサービスのメソッドを呼び出せます。

privateは、そのプロパティをクラスの外から参照できないようにするアクセス修飾子です。この例のmessageServiceは、AppComponentのクラス内で使用します。

テンプレートからmessageServiceを直接参照したい場合は、privateをpublicに変更できます。publicは、クラスの外からも参照できることを表します。今回の例では、テンプレートにはreceiveMessageを渡しているため、messageServiceはprivateのままで構いません。

以上で準備は完了です。アプリを起動してブラウザーで確認すると、以下のように「Hello Angular!」が表示されます。

Angularの「サービス」の使い方

【補足】以下の2つの書き方は同じ意味

TypeScriptのパラメータプロパティ(Parameter Properties)を使うと、コンストラクタの引数にprivateやpublicなどを付けるだけで、プロパティの宣言と値の代入をまとめて行えます。プロパティの宣言と代入を別々に書く必要がなくなり、コードを短くできます。

以下の例では、コンストラクタで受け取ったMessageServiceのインスタンスを、messageServiceプロパティに保存しています。

constructor(private messageService: MessageService) { }

この書き方は、プロパティの宣言と値の代入を分けた以下のコードと同じ意味です。

private messageService: MessageService;

constructor(messageService: MessageService){
  this.messageService = messageService;
}

@Injectableデコレーター内に「providedIn: 'root'」を記述しない場合

@Injectableデコレーター内にprovidedIn: 'root'を記述すると、サービスをアプリケーション全体で利用できるように登録できます。

providedIn: 'root'を削除した場合は、利用する場所に応じて、モジュールやコンポーネントなどのprovidersにサービスを登録します。削除するだけで利用範囲が限定されるわけではなく、どこに登録するかによって、利用できる範囲やインスタンスを共有する範囲が変わります。

モジュールの中のみでサービスを利用する場合

NgModuleを使う構成では、モジュールのprovidersにサービスを登録できます。MessageServiceサービスクラスとAppModuleモジュールを以下のように記述します。ただし、AppModuleに登録したサービスはアプリケーション全体で利用できるため、「そのモジュール内だけ」に限定されるわけではありません。

MessageServiceサービスクラス

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

@Injectable() //「providedIn: 'root'」を削除
export class MessageService {
  message(): string {
    return 'Hello Angular!';
  }
}

サービス側では、providedIn: 'root'を削除し、@Injectable()だけを記述します。サービスを利用できるようにするための登録は、以下のprovidersで行います。

AppModuleモジュール

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { MessageService } from './message.service'; //MessageServiceサービスクラスをインポート

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [MessageService],  //@NgModuleデコレーターのprovidersパラメータにMessageServiceサービスクラスを追加
  bootstrap: [AppComponent]
})
export class AppModule { }

上記のコードでは、AppModuleのprovidersにMessageServiceを指定しています。これにより、Angularが必要に応じてサービスのインスタンスを作成し、注入できるようになります。

MessageServiceをAppModuleに登録する手順は、以下のとおりです。

サービスクラスをAppModuleモジュールに登録する手順

  1. サービスクラスをインポートする
  2. NgModuleデコレーターのprovidersパラメータにサービスクラスを追加する

それぞれの手順を順番に説明します。

1. サービスクラスをインポートする

AppModuleのファイルに、以下のインポート文を追加します。

import { MessageService } from './message.service';

まず、AppModuleのファイルでMessageServiceをインポートします。

2. NgModuleデコレーターのprovidersパラメータにサービスクラスを追加する

@NgModuleデコレーターの設定に、以下のprovidersを追加します。

providers: [MessageService],

次に、@NgModuleデコレーターのproviders配列にMessageServiceを追加します。

この例では、アプリケーションのルートモジュールであるAppModuleに登録しているため、サービスをアプリケーション全体で利用できます。遅延読み込みされるモジュールのprovidersに登録した場合は、そのモジュール側のインジェクターの範囲で利用する形になります。

コンポーネントの中のみでサービスを利用する場合

特定のコンポーネントと、その配下の子コンポーネントでサービスを利用したい場合は、コンポーネントのprovidersに登録します。MessageServiceサービスクラスとAppComponentコンポーネントクラスを以下のように記述します。

MessageServiceサービスクラス

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

@Injectable() //「providedIn: 'root'」を削除
export class MessageService {
  message(): string {
    return 'Hello Angular!';
  }
}

サービス側では、providedIn: 'root'を削除し、@Injectable()だけを記述します。サービスを利用できるようにするための登録は、以下のprovidersで行います。

AppComponentコンポーネントクラス

import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-root',
  template: `{{receiveMessage}}`,
  providers: [MessageService],  //@ComponentデコレーターのprovidersパラメータにMessageServiceサービスクラスを追加
})
export class AppComponent {
  receiveMessage: string | undefined;

  constructor(private messageService: MessageService) { }

  ngOnInit() {
    this.receiveMessage = this.messageService.message();
  }
}

@Componentデコレーターのproviders配列に、以下のようにMessageServiceを追加します。

providers: [MessageService],

この場合、登録したサービスはAppComponentと、その配下の子コンポーネントから利用できます。また、登録先のコンポーネントのインスタンスごとに、サービスのインスタンスが作成されます。AppComponentはアプリケーションの最上位にあるため、この例では配下の広い範囲から利用できます。

本記事のまとめ

この記事では、Angularの「サービス」について、以下の内容を説明しました。

  • サービスは、データの取得や計算などの処理をまとめ、コンポーネントなどから利用できるクラスである
  • サービスを作成し、コンポーネントに注入すると、そのメソッドを呼び出せる
  • providedIn: 'root'を指定しない場合は、モジュールやコンポーネントなどのprovidersに登録する

お読みいただきありがとうございました。

スポンサーリンク