サービスとは、アプリケーションが必要とする値、関数、または機能を包括的に表すカテゴリーです。 サービスは、通常、狭く明確に定義された目的を持つクラスです。 コンポーネントは、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
import { inject } from "@angular/core";export class HeroService { private heroes: Hero[] = []; private backend = inject(BackendService); private logger = inject(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; }}
明確さと保守性の観点から、コンポーネントとサービスは別々のファイルに定義することをお勧めします。
サービスの注入
コンポーネントに依存性としてサービスを注入するには、依存性を表すクラスフィールドを宣言し、Angularのinject
関数を使用して初期化できます。
次の例では、HeroListComponent
内でHeroService
を指定しています。
heroService
の型はHeroService
です。
HeroService
クラスはすでに@Injectable
デコレーターで修飾されているため、AngularはHeroService
型を依存性として認識します。
src/app/heroes/hero-list.component.ts
import { inject } from "@angular/core";export class HeroListComponent { private heroService = inject(HeroService);}
コンポーネントのコンストラクターを使用しても同様に、サービスをコンポーネントに注入できます:
src/app/heroes/hero-list.component.ts (constructor signature)
constructor(private heroService: HeroService)
inject
メソッドはクラスと関数の両方で使用できますが、コンストラクターメソッドは当然ながらクラスコンストラクターでのみ使用できます。ただし、いずれの場合も、依存性は通常コンポーネントの構築または初期化において、有効な注入コンテキストでのみ注入できます。
他のサービスでのサービスの注入
サービスが別のサービスに依存する場合、コンポーネントへの注入と同じパターンに従います。
次の例では、HeroService
はLogger
サービスに依存して、そのアクティビティを報告します。
src/app/heroes/hero.service.ts
import { inject, Injectable } from '@angular/core';import { HEROES } from './mock-heroes';import { Logger } from '../logger.service';@Injectable({ providedIn: 'root',})export class HeroService { private logger = inject(Logger); getHeroes() { this.logger.log('Getting heroes.'); return HEROES; }}
この例では、getHeroes()
メソッドは、ヒーローを取得する際にメッセージをログ記録することで、Logger
サービスを使用しています。