Skip to content

Commit 09ea1cc

Browse files
authored
feat(docs): translate guide/routing/data-fetching-with-resources to Japanese (#1204)
1 parent fbbf73a commit 09ea1cc

2 files changed

Lines changed: 297 additions & 47 deletions

File tree

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Data fetching with resources
2+
3+
The Angular Router integrates with Angular signals through the `resources` route configuration. This allows you to fetch data reactively using `Resource` APIs.
4+
5+
## Why use route resources?
6+
7+
Route resources offer several advantages over traditional [data resolvers](/guide/routing/data-resolvers):
8+
9+
- **Parallel execution**: Route resources across all matched routes load concurrently instead of one route at a time.
10+
- **Non-blocking data loading**: Use `nonBlocking()` to activate the route immediately and render loading skeletons or UI states while data loads in the background.
11+
- **Reload without renavigation**: Call `.reload()` on individual resources or update signal parameters to refresh data without rerunning guards or rematching routes.
12+
- **Reactive data fetching**: Resources integrate directly with Angular signals, automatically re-evaluating when signal dependencies change and exposing reactive status signals like `isLoading()` and `error()`.
13+
14+
## Enabling route resources
15+
16+
To enable route resources, provide `withRouterResources()` to your router configuration:
17+
18+
```ts
19+
import {provideRouter, withComponentInputBinding, withRouterResources} from '@angular/router';
20+
21+
bootstrapApplication(App, {
22+
providers: [provideRouter(routes, withComponentInputBinding(), withRouterResources())],
23+
});
24+
```
25+
26+
TIP: Enable `withComponentInputBinding()` so the router can bind resolved resources directly to component inputs.
27+
28+
## Defining route resources
29+
30+
You define resources on a route with the `resources` function. The function runs in an injection context, so you can use `inject()` to access services, API clients, or stores directly inside the route definition.
31+
32+
```angular-ts
33+
import {Component, inject, input, resource} from '@angular/core';
34+
import {Routes} from '@angular/router';
35+
import {UserService} from './user.service';
36+
37+
const routes: Routes = [
38+
{
39+
path: 'user/:id',
40+
component: UserProfile,
41+
resources: (ctx) => {
42+
const userService = inject(UserService);
43+
return {
44+
user: resource({
45+
params: () => ctx.params()['id'],
46+
loader: ({params: id}) => userService.getUser(id),
47+
}),
48+
};
49+
},
50+
},
51+
];
52+
53+
@Component({
54+
template: `<p>User: {{ user().name }}</p>`,
55+
})
56+
export class UserProfile {
57+
// The router binds only the value for blocking resources.
58+
user = input.required<User>();
59+
}
60+
```
61+
62+
### The `ResourceContext` object
63+
64+
The `resources` function receives a `ResourceContext` that provides access to reactive route signals: `params`, `queryParams`, `fragment`, and `data`.
65+
66+
### Supported resource implementations
67+
68+
The `resources` function can return any Angular `Resource` implementation, such as `resource()`, `rxResource()`, or a custom resource.
69+
70+
```ts
71+
import {Routes} from '@angular/router';
72+
import {rxResource} from '@angular/core/rxjs-interop';
73+
74+
const routes: Routes = [
75+
{
76+
path: 'user/:id',
77+
component: UserProfile,
78+
resources: (ctx) => ({
79+
user: rxResource({
80+
params: () => ctx.params()['id'],
81+
stream: ({params: id}) => fetchUserObservable(id),
82+
}),
83+
}),
84+
},
85+
];
86+
```
87+
88+
NOTE: `rxResource` uses the `stream` property instead of `loader` to accept a function that returns an Observable.
89+
90+
The `resources` function can also be `async` and return a `Promise` if you need to perform asynchronous setup or dynamic imports before configuring resources:
91+
92+
```ts
93+
resources: async (ctx) => {
94+
const {fetchUserData} = await import('./user-api');
95+
return {
96+
user: resource({
97+
params: () => ctx.params()['id'],
98+
loader: ({params: id}) => fetchUserData(id),
99+
}),
100+
};
101+
},
102+
```
103+
104+
## Fine-grained change tracking with signals
105+
106+
A resource tracks the signals that its `params` function reads. Read the exact value you need so that the resource refetches only when that value changes:
107+
108+
```ts
109+
resources: (ctx) => ({
110+
products: resource({
111+
// Tracks only the 'category' query parameter
112+
params: () => ctx.queryParams()['category'],
113+
loader: ({params: category}) => fetchProducts(category),
114+
}),
115+
}),
116+
```
117+
118+
A navigation that changes an unrelated query parameter, such as `?sort=desc` or `?page=2`, leaves `category` unchanged, so the resource does not refetch.
119+
120+
TIP: Read specific properties, such as `ctx.params()['id']`, instead of returning an entire parameters object, such as `ctx.params()`. The router creates a new object on every navigation, so returning the whole object refetches the resource even when the individual values are unchanged.
121+
122+
## Parallel execution
123+
124+
Data resolvers execute sequentially from parent route to child route. If a parent route resolver takes 200ms and a child route resolver takes 300ms, the navigation is blocked for 500ms.
125+
126+
Route resources across the matched route hierarchy run concurrently, so the same navigation completes in 300ms, the time of the slowest resource.
127+
128+
## Blocking and non-blocking resources
129+
130+
By default, every resource returned from `resources` is blocking: the router waits until the data is fully loaded before it activates the route and the component.
131+
132+
For a blocking resource, the router binds the resolved value to the component input, so the input type is `T` instead of `Resource<T>`. The component never observes a `loading` state because the router blocks navigation until the resource loads, and it never observes an `error` state because the router cancels the navigation when the resource errors.
133+
134+
To handle loading states in the UI instead, wrap the resource in `nonBlocking()`. The router activates the component immediately and binds the full `Resource<T>` object to the component input, which gives you access to `isLoading()`, `error()`, and the other resource signals.
135+
136+
```angular-ts
137+
import {Component, input, Resource, resource} from '@angular/core';
138+
import {Routes, nonBlocking} from '@angular/router';
139+
140+
const routes: Routes = [
141+
{
142+
path: 'reports',
143+
component: Reports,
144+
resources: () => ({
145+
reportData: nonBlocking(
146+
resource({
147+
loader: () => fetchHeavyReportData(),
148+
}),
149+
),
150+
}),
151+
},
152+
];
153+
154+
@Component({
155+
template: `
156+
@if (reportData().isLoading()) {
157+
<p>Loading...</p>
158+
} @else if (reportData().error()) {
159+
<p>Error loading report.</p>
160+
} @else if (reportData().hasValue()) {
161+
<report-view [data]="reportData().value()" />
162+
}
163+
`,
164+
})
165+
export class Reports {
166+
reportData = input.required<Resource<ReportData>>();
167+
}
168+
```
169+
170+
NOTE: If a blocking resource errors, the router cancels the navigation and emits a `NavigationError` event. A resource wrapped in `nonBlocking()` completes the navigation and exposes the failure through its `error()` signal.
171+
172+
### Redirecting from a resource
173+
174+
If a blocking resource needs to redirect the user (for example, if an item is not found), throw a `RedirectCommand` inside the resource loader. The router cancels the current navigation and redirects to the specified URL:
175+
176+
```ts
177+
import {inject, resource} from '@angular/core';
178+
import {RedirectCommand, Router, Routes} from '@angular/router';
179+
180+
const routes: Routes = [
181+
{
182+
path: 'user/:id',
183+
component: UserProfile,
184+
resources: (ctx) => {
185+
const router = inject(Router);
186+
187+
return {
188+
user: resource({
189+
params: () => ctx.params()['id'],
190+
loader: async ({params: id}) => {
191+
const user = await fetchUser(id);
192+
if (!user) {
193+
throw new RedirectCommand(router.parseUrl('/not-found'));
194+
}
195+
return user;
196+
},
197+
}),
198+
};
199+
},
200+
},
201+
];
202+
```
203+
204+
## Reloading resources without renavigation
205+
206+
With data resolvers, refetching data requires a route navigation (for example, navigating with `onSameUrlNavigation: 'reload'`), which rematches routes and reruns guards and resolvers.
207+
208+
Route resources support two ways to refresh data in place:
209+
210+
1. **Programmatic reload**: Call `.reload()` on the `Resource` instance.
211+
2. **Reactive reload**: Update a signal that the resource's `params` function reads, such as an application filter or state signal, which reruns the loader.
212+
213+
Because the router binds only the value of a blocking resource to a component input, read the `Resource` instance from `ActivatedRoute` or `ActivatedRouteSnapshot` when you need to call `.reload()` or inspect status signals:
214+
215+
```angular-ts
216+
import {Component, inject, input} from '@angular/core';
217+
import {ActivatedRoute} from '@angular/router';
218+
219+
@Component({
220+
template: `
221+
<p>User: {{ user().name }}</p>
222+
<button (click)="refreshUser()">Refresh</button>
223+
`,
224+
})
225+
export class UserProfile {
226+
user = input.required<User>();
227+
private userResource = inject(ActivatedRoute).resources?.['user'];
228+
229+
refreshUser() {
230+
// Reloads only this specific resource without renavigating the route
231+
this.userResource?.reload();
232+
}
233+
}
234+
```
235+
236+
## Transitional states during pending navigations
237+
238+
While a navigation is pending, the router freezes the resources that it exposes on `ActivatedRoute`, which masks intermediate `loading` and `reloading` states.
239+
240+
If you navigate from `/user/1` to `/user/2` and the router reuses the `UserProfile` component, the component keeps rendering the data from `/user/1` until `/user/2` resolves. The router then unfreezes the resources and the UI transitions directly to the new data with no loading flash.
241+
242+
The router exposes these resources as read-only, even when the `resources` function returns a writable resource such as `resource()`. You can read the resource signals and call `reload()`, but not `set()` or `update()`. A `reload()` call during an active navigation or during rollback recovery returns `false` so that it cannot interrupt the router's transition tracking.
243+
244+
### Rollback recovery on cancellation
245+
246+
If a navigation is cancelled (for example, by a guard), the router reverts the state tree to the previous state. This reversion can cause the resource's signal dependencies, such as route parameters, to revert to their previous values.
247+
248+
Because the parameters changed back, the resource might automatically trigger a new load to fetch data for the old parameters. To prevent flashing a loading state for data that was already visible, the router retains the previous resource snapshot in the UI until the resource has settled in the reverted state.
249+
250+
TIP: Forward the `abortSignal` provided by the resource loader to your asynchronous calls (like `fetch`). When the router rolls back parameters or supersedes navigations, the pending request is cleanly aborted: `loader: ({params: id, abortSignal}) => fetchUser(id, {signal: abortSignal})`.

0 commit comments

Comments
 (0)