Асинхронная реактивность с ресурсами
Все 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
// - It 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 принимает объект ResourceOptions с двумя основными свойствами: params и loader.
Свойство params задаёт реактивное вычисление, которое возвращает значение параметра. Когда сигналы, прочитанные в этом вычислении, меняются, ресурс возвращает новое значение параметра, подобно computed.
Свойство loader задаёт ResourceLoader — асинхронную функцию, которая получает некоторое состояние. Ресурс вызывает загрузчик каждый раз, когда вычисление params возвращает новое значение, и передаёт это значение загрузчику. Подробнее см. ниже в разделе Загрузчики ресурсов.
У Resource есть сигнал value, содержащий результаты работы загрузчика.
Загрузчики ресурсов
При создании ресурса необходимо указать ResourceLoader. Этот загрузчик представляет собой асинхронную функцию, которая принимает один параметр — объект ResourceLoaderParams — и возвращает значение.
Объект ResourceLoaderParams содержит три свойства: params, previous и abortSignal.
| Свойство | Описание |
|---|---|
params | Значение вычисления params ресурса. |
previous | Объект со свойством status, содержащим предыдущий ResourceStatus. |
abortSignal | AbortSignal. Подробнее см. ниже в разделе Отмена запросов. |
Если вычисление params возвращает undefined, функция загрузчика не запускается, а статус ресурса становится 'idle'.
Отмена запросов
Ресурс прерывает выполняющуюся операцию загрузки, если вычисление params меняется во время загрузки ресурса.
Чтобы обрабатывать отменённые запросы, используйте abortSignal из ResourceLoaderParams. Например, встроенная функция fetch принимает AbortSignal:
const userId: Signal<string> = getUserId();
const userResource = resource({
params: () => ({id: userId()}),
loader: ({params, abortSignal}): Promise<User> => {
// fetch cancels any outstanding HTTP requests when the given `AbortSignal`
// indicates that the request has been aborted.
return fetch(`users/${params.id}`, {signal: abortSignal});
},
});
Подробнее об отмене запросов с помощью AbortSignal см. в документации AbortSignal на MDN.
Повторная загрузка
Вы можете программно запустить loader ресурса, вызвав метод reload.
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, которая предоставляет статус запроса и ответ в виде сигналов. Она выполняет HTTP-запросы через стек HTTP Angular, включая интерсепторы.
Композиция ресурсов с помощью снимков
ResourceSnapshot — это структурированное представление текущего состояния ресурса. У каждого ресурса есть свойство snapshot, предоставляющее сигнал его текущего состояния.
const userId: Signal<string> = getUserId();
const userResource = resource({
params: () => ({id: userId()}),
loader: ({params}) => fetchUser(params),
});
const userSnapshot = userResource.snapshot;
Каждый снимок содержит status и либо value, либо error.
Композиция ресурсов с помощью снимков
Вы можете создавать новые ресурсы на основе снимков с помощью resourceFromSnapshots. Это позволяет комбинировать их с API сигналов, например computed и linkedSignal, чтобы преобразовывать поведение ресурсов.
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
}
Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/signals/resource