すべてのシグナルAPIは同期的です(signal、computed、inputなど)。しかし、アプリケーションは多くの場合、非同期的に利用可能なデータを処理する必要があります。Resourceを使用すると、非同期データをアプリケーションのシグナルベースのコードに組み込み、そのデータに同期的にアクセスできるようになります。
Resourceを使用してあらゆる種類の非同期処理を実行できますが、Resourceの最も一般的なユースケースはサーバーからデータを取得することです。以下は、ユーザーデータを取得するためのリソースを作成する例です。
Resourceを作成する最も簡単な方法は、resource関数を使用することです。
import {resource, Signal} from '@angular/core';
const userId: Signal<string> = getUserId();
const userResource = resource({
// Define a reactive computation.
// The params value recomputes whenever any read signals change.
params: () => ({id: userId()}),
// Define an async loader that retrieves data.
// The resource calls this function every time the `params` value changes.
loader: ({params}) => fetchUser(params),
});
// Create a computed signal based on the result of the resource's loader function.
const firstName = computed(() => {
if (userResource.hasValue()) {
// `hasValue` serves 2 purposes:
// - It acts as type guard to strip `undefined` from the type
// - If protects against reading a throwing `value` when the resource is in error state
return userResource.value().firstName;
}
// fallback in case the resource value is `undefined` or if the resource is in error state
return undefined;
});
resource関数は、2つの主なプロパティであるparamsとloaderを持つResourceOptionsオブジェクトを受け入れます。
paramsプロパティは、パラメータ値を生成するリアクティブな計算を定義します。この計算で読み取られるシグナルが変更されるたびに、リソースは新しいパラメータ値を生成します。これはcomputedと同様です。
loaderプロパティはResourceLoaderを定義します。これは、状態を取得する非同期関数です。リソースは、params計算が新しい値を生成するたびにローダーを呼び出し、その値をローダーに渡します。詳細は下記のResourceローダーを参照してください。
Resourceには、ローダーの結果を含むvalueシグナルがあります。
リソースローダー
リソースを作成する際には、ResourceLoaderを指定します。このローダーは、単一のパラメーター(ResourceLoaderParamsオブジェクト)を受け入れ、値を返す非同期関数です。
ResourceLoaderParamsオブジェクトには、params、previous、abortSignalの3つのプロパティが含まれています。
| プロパティ | 説明 |
|---|---|
params |
リソースのparams計算の値。 |
previous |
statusプロパティを含む、前のResourceStatusを持つオブジェクト。 |
abortSignal |
AbortSignal。詳細は下記のリクエストの中断を参照してください。 |
paramsの計算がundefinedを返す場合、ローダー関数は実行されず、リソースの状態はIdleになります。
リクエストの中断
リソースが読み込み中の場合にparams計算が変更されると、リソースは未処理の読み込み処理を中断します。
ResourceLoaderParams内のabortSignalを使用して、中断されたリクエストに応答できます。例えば、ネイティブのfetch関数はAbortSignalを受け入れます。
const userId: Signal<string> = getUserId();
const userResource = resource({
params: () => ({id: userId()}),
loader: ({params, abortSignal}): Promise<User> => {
// 与えられた`AbortSignal`がリクエストの中断を示している場合、
// fetchは未処理のHTTPリクエストをキャンセルします。
return fetch(`users/${params.id}`, {signal: abortSignal});
},
});
AbortSignalによるリクエストのキャンセルについては、AbortSignal on MDNを参照してください。
再読み込み
reloadメソッドを呼び出すことで、プログラム的にリソースのloaderをトリガーできます。
const userId: Signal<string> = getUserId();
const userResource = resource({
params: () => ({id: userId()}),
loader: ({params}) => fetchUser(params),
});
// ...
userResource.reload();
リソースの状態
リソースオブジェクトには、非同期ローダーの状態を読み取るためのいくつかのシグナルプロパティがあります。
| プロパティ | 説明 |
|---|---|
value |
リソースの最新の値、または値が受信されていない場合はundefined。 |
hasValue |
リソースが値を持っているかどうか。 |
error |
リソースのローダーの実行中に発生した最新のエラー、またはエラーが発生していない場合はundefined。 |
isLoading |
リソースローダーが現在実行中かどうか。 |
status |
後述のリソースの特定のResourceStatus。 |
status シグナルは、文字列定数を使用してリソースの状態を説明する特定の ResourceStatus を提供します。
| ステータス | value() |
説明 |
|---|---|---|
'idle' |
undefined |
リソースに有効なリクエストがなく、ローダーが実行されていません。 |
'error' |
undefined |
ローダーの読み込みがエラーになりました。 |
'loading' |
undefined |
params 値の変更の結果としてローダーが実行中です。 |
'reloading' |
前の値 | リソースの reload メソッドの呼び出しの結果としてローダーが実行中です。 |
'resolved' |
解決された値 | ローダーが完了しました。 |
'local' |
ローカルに設定された値 | リソースの値が .set() または .update() を介してローカルに設定されています。 |
この状態情報を使用して、ローディングインジケーターやエラーメッセージなどのユーザーインターフェース要素を条件付きで表示できます。
httpResource を使用したリアクティブデータ取得
httpResource は HttpClient のラッパーで、リクエストの状態とレスポンスをシグナルとして提供します。これはインターセプターを含むAngular HTTPスタックを通してHTTPリクエストを行います。
Resource composition with snapshots
A ResourceSnapshot is a structured representation of a resource's current state. Every resource has a snapshot property that provides a signal of its current state.
const userId: Signal<string> = getUserId();
const userResource = resource({
params: () => ({id: userId()}),
loader: ({params}) => fetchUser(params),
});
const userSnapshot = userResource.snapshot;
Each snapshot contains a status and either a value or an error.
Composing resources with snapshots
You can create new resources from snapshots using resourceFromSnapshots. This enables composition with signal APIs like computed and linkedSignal to transform resource behavior.
import {linkedSignal, resourceFromSnapshots, Resource, ResourceSnapshot} from '@angular/core';
function withPreviousValue<T>(input: Resource<T>): Resource<T> {
const derived = linkedSignal<ResourceSnapshot<T>, ResourceSnapshot<T>>({
source: input.snapshot,
computation: (snap, previous) => {
if (snap.status === 'loading' && previous && previous.value.status !== 'error') {
// When the input resource enters loading state, we keep the value
// from its previous state, if any.
return {status: 'loading' as const, value: previous.value.value};
}
// Otherwise we simply forward the state of the input resource.
return snap;
},
});
return resourceFromSnapshots(derived);
}
@Component({
/*... */
})
export class AwesomeProfile {
userId = input.required<number>();
user = withPreviousValue(httpResource(() => `/user/${this.userId()}`));
// When userId changes, user.value() keeps the old user data until the new one loads
}