サービスとは、アプリケーションが必要とする値、関数、または機能を包括的に表すカテゴリーです。 サービスは、通常、狭く明確に定義された目的を持つクラスです。 コンポーネントは、DIを使用できるクラスの一種です。
Angularは、モジュール性と再利用性を高めるために、コンポーネントとサービスを区別しています。 コンポーネントのビュー関連の機能を他の処理から分離することで、コンポーネントクラスをスリムで効率的にできます。
理想的には、コンポーネントの役割は、ユーザー体験を実現することだけです。 コンポーネントは、データバインディング用のプロパティとメソッドを提供し、ビュー(テンプレートによってレンダリングされる)とアプリケーションロジック(多くの場合、モデルの概念が含まれる)の仲介役を果たすべきです。
コンポーネントは、サーバーからのデータの取得、ユーザー入力の検証、またはコンソールへの直接ログ記録など、特定のタスクをサービスに委譲できます。 このような処理タスクを注入可能サービスクラスで定義することで、これらのタスクをどのコンポーネントからも利用できるようになります。 また、状況に応じて、同じ種類のサービスの異なるプロバイダーを構成することで、アプリケーションをより適応性高くできます。
Angularは、これらの原則を強制しません。 Angularは、アプリケーションロジックをサービスに分解し、それらのサービスをDIを介してコンポーネントに提供することを容易にすることで、これらの原則に従うように支援します。
サービスの例
ブラウザコンソールにログ記録するサービスクラスの例を以下に示します。
src/app/logger.service.ts (class)
export class Logger { log(msg: unknown) { console.log(msg); } error(msg: unknown) { console.error(msg); } warn(msg: unknown) { console.warn(msg); }}
サービスは、他のサービスに依存できます。
たとえば、次のHeroService
はLogger
サービスに依存し、BackendService
を使用してヒーローを取得します。
そのサービスは、さらにHttpClient
サービスに依存して、サーバーからヒーローを非同期に取得する場合があります。
src/app/hero.service.ts (class)
export class HeroService { private heroes: Hero[] = []; constructor( private backend: BackendService, private logger: Logger) {} async getHeroes() { // Fetch this.heroes = await this.backend.getAll(Hero); // Log this.logger.log(`Fetched ${this.heroes.length} heroes.`); return this.heroes; }}
注入可能なサービスの作成
Angular CLIは、新しいサービスを作成するためのコマンドを提供します。 次の例では、既存のアプリケーションに新しいサービスを追加します。
src/app/heroes
フォルダーに新しいHeroService
クラスを生成するには、次の手順に従います。
- 次のAngular CLIコマンドを実行します。
ng generate service heroes/hero
このコマンドは、次のデフォルトのHeroService
を作成します。
src/app/heroes/hero.service.ts (CLI-generated)
import { Injectable } from '@angular/core';@Injectable({ providedIn: 'root',})export class HeroService {}
@Injectable()
デコレーターは、AngularがDIシステムでこのクラスを使用できることを指定します。
メタデータprovidedIn: 'root'
は、HeroService
がアプリケーション全体で提供されることを意味します。
ヒーローのモックデータを取得するために、mock.heroes.ts
からヒーローを返すgetHeroes()
メソッドを追加します。
src/app/heroes/hero.service.ts
import { Injectable } from '@angular/core';import { HEROES } from './mock-heroes';@Injectable({ // このサービスがルートアプリケーションインジェクターによって作成されることを宣言します。 providedIn: 'root',})export class HeroService { getHeroes() { return HEROES; }}
明確さと保守性の観点から、コンポーネントとサービスは別々のファイルに定義することをお勧めします。
サービスの注入
コンポーネントにサービスを依存性として注入するには、コンポーネントのconstructor()
を使用し、依存性の型を持つコンストラクター引数を指定します。
次の例では、HeroListComponent
コンストラクターにHeroService
を指定しています。
heroService
の型はHeroService
です。
HeroService
クラスはすでに@Injectable
デコレーターで修飾されているため、AngularはHeroService
型を依存性として認識します。
src/app/heroes/hero-list.component (constructor signature)
constructor(heroService: HeroService)
他のサービスでのサービスの注入
サービスが別のサービスに依存する場合、コンポーネントへの注入と同じパターンに従います。
次の例では、HeroService
はLogger
サービスに依存して、そのアクティビティを報告します。
src/app/heroes/hero.service.ts
import { Injectable } from '@angular/core';import { HEROES } from './mock-heroes';import { Logger } from '../logger.service';@Injectable({ providedIn: 'root',})export class HeroService { constructor(private logger: Logger) {} getHeroes() { this.logger.log('Getting heroes.'); return HEROES; }}
この例では、getHeroes()
メソッドは、ヒーローを取得する際にメッセージをログ記録することで、Logger
サービスを使用しています。