本文へスキップ
BecomeCoder

Angularコース · 第4章 まとめとこれから · レッスン11

サービスと依存性注入(DI)― ロジックとデータを共有する

ブラウザで完結

導入

@Input / @Output は「親子の間」でデータを受け渡す仕組みでした。でも実務では、親子でもない離れたコンポーネントどうしで、同じデータや処理を共有したくなります(ログイン中のユーザー情報、買い物カゴ、サーバー通信…)。

そこで Angular の心臓部が登場します。それが サービス(共通ロジックの置き場)と、それを部品に配る仕組み 依存性注入(DI: Dependency Injection) です。「Angular の中心的な考え方」と言われるのがこれです。

説明

考え方はシンプルです。共有したいものを「サービス」というクラス 1 つにまとめ、必要なコンポーネントがそれを inject() で受け取るだけ。サービスは(既定で)アプリ全体にたった 1 つだけ作られ、みんなが同じものを見ます。

flowchart TB
  svc["CounterService(アプリに1つだけ)<br/>count / increment()"]
  a["AppComponent<br/>ボタンで +1 する"]
  b["DisplayComponent ①<br/>数を表示"]
  c["DisplayComponent ②<br/>数を表示"]
  svc -->|"inject() で受け取る"| a
  svc -->|"inject() で受け取る"| b
  svc -->|"inject() で受け取る"| c

@Injectable({ providedIn: 'root' }) を付けたクラスがサービスです。providedIn: 'root' は「アプリ全体で共有する 1 つを用意して」という意味です。

// 共有したいデータとロジックをまとめたサービス
@Injectable({ providedIn: 'root' })
class CounterService {
  count = 0;
  increment(): void {
    this.count++;
  }
}

// 子:サービスを inject して、数を表示するだけ
@Component({
  selector: 'app-display',
  standalone: true,
  template: `<p>表示コンポーネント: {{ counter.count }}</p>`,
})
class DisplayComponent {
  counter = inject(CounterService); // 同じ1つを受け取る
}

// 親:ボタンで increment を呼ぶ。子を2つ並べる。
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [DisplayComponent],
  template: `
    <h2>サービスで状態を共有</h2>
    <button (click)="counter.increment()">+1</button>
    <p>ボタン側: {{ counter.count }}</p>
    <app-display></app-display>
    <app-display></app-display>
  `,
})
class AppComponent {
  counter = inject(CounterService);
}

ボタンを押すと、親も、2 つの子も、全部いっしょに数が増えます。3 つのコンポーネントが inject(CounterService) で受け取っているのは、すべて同じ 1 つのサービスだからです。@Input で配線しなくても、データが共有できました。

  • @Injectable({ providedIn: 'root' }) … このクラスをアプリ共通のサービスとして登録する印。
  • inject(CounterService) … 「その共有の 1 つをください」と Angular にお願いする書き方。Angular が自動で渡してくれる(=依存性の”注入”)。
  • サービスに count のようなデータや、increment() のような処理、将来は HttpClient によるサーバー通信もまとめられます。

やってみよう

  • CounterServicereset(): void { this.count = 0; } を足し、AppComponent に「リセット」ボタンを付けて、全員が 0 に戻ることを確かめましょう。
  • <app-display> を 3 つ 4 つと増やしても、全部が同期することを見てみましょう。

実際に動かしてみよう

下のエディタにAngularのコンポーネントを書くと、右側のプレビューにその場で描画されます。本文の例を打ち込んだり書き換えたりして、テンプレート構文やバインディングの動きを確かめながら進めましょう(初回だけ読み込みに少し時間がかかります)。

Angular — ブラウザ内で実行

ブラウザ内でAngular(JIT)を動かす学習用環境(同梱)を読み込みます(初回のみ少し時間がかかります)。
スクロールして表示された時点でも自動で読み込まれます。