diff --git a/adev/src/content/guide/signals/resource.md b/adev/src/content/guide/signals/resource.md index 5ff6b7ca589f..95f9721465c1 100644 --- a/adev/src/content/guide/signals/resource.md +++ b/adev/src/content/guide/signals/resource.md @@ -140,6 +140,54 @@ The `id` value must be unique within your application and identical on the serve IMPORTANT: Because the cached value is serialized into the page's HTML, avoid setting `id` on resources that load data specific to the user who triggered the server-side render, especially if the rendered HTML can be cached or shared between users. +## Chaining resources + +Sometimes one resource depends on the result of another. You can express this dependency using the `chain` function available in the `params` context object. + +```typescript +import {resource} from '@angular/core'; + +const userResource = resource({ + params: () => ({id: getUserId()}), + loader: ({params}) => fetchUser(params), +}); + +const userPostsResource = resource({ + params: ({chain}) => chain(userResource)?.id, + loader: ({params: userId}) => fetchPostsForUser(userId), +}); +``` + +`chain(userResource)` reads the value of `userResource` and automatically propagates its status to `userPostsResource`: + +- If `userResource` is **loading**, `userPostsResource` also enters the `loading` state and its loader does not run. +- If `userResource` is **idle**, `userPostsResource` also becomes `idle`. +- If `userResource` is in an **error** state, `userPostsResource` also enters the `error` state. +- If `userResource` is **resolved**, `chain` returns its value, which `userPostsResource` then uses as its params. + +When `userResource` is not yet resolved, `chain` throws to propagate its status, so the params function does not continue. When `userResource` is resolved, `chain` returns its value, which can itself be `undefined` if that's the resolved value. The example handles this with `chain(userResource)?.id`, so an `undefined` value results in `undefined` params and `userPostsResource` becomes `idle`. + +NOTE: Pass the chained value directly as the params value rather than wrapping it in an object. A params value like `{userId: undefined}` is still a defined value, so the loader would run with an `undefined` id instead of the resource becoming `idle`. + +### Chaining vs. reading resource values directly + +You might be tempted to read a resource's value directly inside `params`: + +```typescript +// Avoid: reads value() directly without status propagation +const userPostsResource = resource({ + params: () => { + const user = userResource.value(); // may be undefined + return user ? {userId: user.id} : undefined; + }, + loader: ({params}) => fetchPostsForUser(params.userId), +}); +``` + +While this works, returning `undefined` from `params` makes the resource go `idle` rather than reflecting the actual state of the upstream resource. Using `chain` is preferred because it correctly mirrors `loading` and `error` states. + +Reach for `chain` only when the downstream resource performs its own async work that depends on the upstream value. If you only need to derive a value synchronously from a resource, use `computed` instead. + ## Reactive data fetching with `httpResource` [`httpResource`](/guide/http/http-resource) is a wrapper around `HttpClient` that gives you the request status and response as signals. It makes HTTP requests through the Angular HTTP stack, including interceptors.