Dev Study
← 解説「HttpClient — APIとの通信」に戻る

サンプルコードで身につける: HttpClient — APIとの通信

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1GETリクエストを送って応答を見る

HttpClient の最小例で、get() を呼んで subscribe で応答を受け取り、まずは console.log で中身を見ます。「リクエストは subscribe して初めて送信される」「結果はコールバックに届く」という2つの基本がこの数行に詰まっています。新しいAPIをつなぐときの動作確認も、実務ではこの形から始めます。

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-ping',
  template: '<button (click)="load()">取得</button>',
})
export class PingComponent {
  private http = inject(HttpClient);

  load() {
    // 結果は subscribe のコールバックに届く
    this.http.get('/api/ping').subscribe((res) => {
      console.log('サーバーからの応答:', res);
    });
  }
}

2型付きで取得して一覧表示する

get<Article[]>() と型引数を付け、届いたデータをシグナルに set して @for で並べる、データ取得画面の基本形です。型のおかげで a.title のようなプロパティ名の打ち間違いをコンパイル時に発見できます。「通信結果をシグナルに反映し、表示はテンプレートに任せる」流れを掴む例です。

import { Component, OnInit, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

type Article = { id: number; title: string };

@Component({
  selector: 'app-article-titles',
  template: '@for (a of articles(); track a.id) { <p>{{ a.title }}</p> }',
})
export class ArticleTitlesComponent implements OnInit {
  private http = inject(HttpClient);
  articles = signal<Article[]>([]);

  // ngOnInit はコンポーネント初期化時に1回だけ呼ばれるフック
  ngOnInit() {
    // 型引数のおかげでプロパティ名の間違いに気づける
    this.http.get<Article[]>('/api/articles').subscribe((data) => {
      this.articles.set(data);
    });
  }
}

3POSTでデータを送信する

取得だけでなく、登録や更新は post() で行います。第2引数に渡したオブジェクトがJSONとしてサーバーへ送られる、というGETとの違いがポイントです。会員登録、コメント投稿、設定の保存など「サーバーに変更を加える」操作はすべてこの形の延長です。

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-signup-form',
  template: '<button (click)="register()">この名前で登録</button>',
})
export class SignupFormComponent {
  private http = inject(HttpClient);

  register() {
    // 第2引数のオブジェクトがJSONとして送信される
    this.http.post('/api/users', { userName: '田中' }).subscribe(() => {
      console.log('登録が完了しました');
    });
  }
}

4検索条件をクエリパラメータで渡す

params オプションに渡した値は /api/products?q=コーヒー のようにURLへ付加されます。検索キーワードやページ番号など「サーバーへの絞り込み条件」はこの形で渡すのが標準です。URLの文字列を自分で連結しなくても、エスケープ込みでHttpClientが組み立ててくれます。

import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-product-search',
  template:
    '<input (input)="onInput($event)">' +
    '<button (click)="search()">検索</button>',
})
export class ProductSearchComponent {
  private http = inject(HttpClient);
  private keyword = '';

  onInput(e: Event) {
    this.keyword = (e.target as HTMLInputElement).value;
  }

  search() {
    // params の値は /api/products?q=... の形でURLに付く
    this.http
      .get<string[]>('/api/products', { params: { q: this.keyword } })
      .subscribe((found) => console.log('ヒット:', found));
  }
}

5ローディングとエラーまで面倒を見る

subscribe に next と error の2つのコールバックを渡し、読み込み中・失敗・成功の3状態を画面に出し分ける実務の完成形です。通信は失敗することがある、という前提を画面の設計に組み込んでいます。注文履歴やマイページなど、実際のデータ取得画面はほぼこの構造になります。

import { Component, OnInit, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

type Order = { id: number; total: number };

@Component({
  selector: 'app-order-history',
  // 読み込み中・失敗・成功の3状態を出し分ける
  template:
    '@if (isLoading()) { <p>読み込み中...</p> }' +
    ' @else if (loadError()) { <p>取得に失敗しました</p> }' +
    ' @else { @for (o of orders(); track o.id) { <p>{{ o.total }} 円</p> } }',
})
export class OrderHistoryComponent implements OnInit {
  private http = inject(HttpClient);
  orders = signal<Order[]>([]);
  isLoading = signal(true);
  loadError = signal(false);

  // ngOnInit はコンポーネント初期化時に1回だけ呼ばれるフック
  ngOnInit() {
    this.http.get<Order[]>('/api/orders').subscribe({
      next: (data) => {
        this.orders.set(data);
        this.isLoading.set(false);
      },
      error: () => {
        this.loadError.set(true);
        this.isLoading.set(false);
      },
    });
  }
}