← 解説「httpResource() — HTTP取得をさらに短く書く」に戻る
サンプルコードで身につける: httpResource() — HTTP取得をさらに短く書く
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1resource() + fetch との比較
同じ「ユーザー取得」をresource()とhttpResource()で書き比べます。loaderの定型がまるごと消え、URLを返す関数だけになります。読み取り側のvalue()やisLoading()は同じです。
// ── Before: resource() + fetch ──
// user = resource({
// params: () => ({ id: this.userId() }),
// loader: ({ params }) =>
// fetch(`/api/users/${params.id}`).then((r) => r.json()),
// });
// ── After: httpResource() ──
// user = httpResource<User>(() => `/api/users/${this.userId()}`);
// 短くなるだけでなく、通信が HttpClient 経由になるので
// インターセプター(認証ヘッダー・共通エラー処理)が効く。
// 読み取りは同じ: user.value() / user.isLoading() / user.error()
2一覧と詳細 — シグナル変更で自動再取得
一覧で選んだIDのシグナルをURLの中で読んでいるので、選択が変わるだけで詳細が自動で取り直されます。subscribeも手動の再取得メソッドも書いていない点に注目してください。
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
type Article = { id: number; title: string; body: string };
@Component({
selector: 'app-article-viewer',
template:
'<button (click)="selectedId.set(1)">記事1</button>' +
'<button (click)="selectedId.set(2)">記事2</button>' +
'@if (article.value(); as a) {' +
' <h2>{{ a.title }}</h2><p>{{ a.body }}</p>' +
'}',
})
export class ArticleViewerComponent {
selectedId = signal(1);
// ボタンで selectedId が変わる → URLが変わる → 自動再取得
article = httpResource<Article>(
() => `/api/articles/${this.selectedId()}`,
);
}
3クエリパラメータ付きのリクエスト
URL文字列の代わりにオブジェクトを返すと、paramsやheadersも指定できます。検索条件のシグナルをparamsに使えば、条件変更のたびに検索し直す画面がこれだけで書けます。
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
type Product = { id: number; name: string };
@Component({
selector: 'app-product-search',
template:
'<input (input)="keyword.set($any($event.target).value)">' +
'@for (p of results.value() ?? []; track p.id) {' +
' <p>{{ p.name }}</p>' +
'}',
})
export class ProductSearchComponent {
keyword = signal('');
// オブジェクト形式なら params や headers も書ける
results = httpResource<Product[]>(() => ({
url: '/api/products',
params: { q: this.keyword() },
}));
}
4読み込み中・エラー・成功の3状態を出し分ける
httpResourceもresource()と同じ状態シグナルを持つので、テンプレートの出し分けはこれまでと同じ形です。error()にはHttpClient由来のエラー情報が入ります。
import { Component } from '@angular/core';
import { httpResource } from '@angular/common/http';
type News = { id: number; headline: string };
@Component({
selector: 'app-news-list',
template:
'@if (news.isLoading()) {' +
' <p>読み込み中...</p>' +
'} @else if (news.error()) {' +
' <p>取得に失敗しました <button (click)="news.reload()">再試行</button></p>' +
'} @else {' +
' @for (n of news.value() ?? []; track n.id) { <p>{{ n.headline }}</p> }' +
'}',
})
export class NewsListComponent {
news = httpResource<News[]>(() => '/api/news');
// reload() で同じ条件のまま取り直せるのも resource() と同じ
}
5取得は httpResource、更新は HttpClient
httpResourceは読み取り(GET)専用なので、保存や削除は従来通りHttpClientで行います。更新が成功したらreload()で一覧を取り直す、という組み合わせが基本の型です。
import { Component, inject } from '@angular/core';
import { HttpClient, httpResource } from '@angular/common/http';
type Todo = { id: number; title: string };
@Component({
selector: 'app-todo-list',
template:
'@for (t of todos.value() ?? []; track t.id) { <p>{{ t.title }}</p> }' +
'<button (click)="add()">追加</button>',
})
export class TodoListComponent {
private http = inject(HttpClient);
// 表示のための取得: httpResource
todos = httpResource<Todo[]>(() => '/api/todos');
// ユーザー操作による変更: HttpClient
add() {
this.http
.post('/api/todos', { title: '新しいタスク' })
.subscribe(() => this.todos.reload()); // 成功したら取り直す
}
}