From 6eb598d46328010f80dae12fb117a3b7ababcbfd Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sun, 4 Oct 2026 16:39:17 +0900 Subject: [PATCH 01/66] chore: update origin to ef0359676c --- origin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/origin b/origin index 4d985a179..ef0359676 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit 4d985a179e66428d46e60b4622435f88c39e1d2b +Subproject commit ef0359676c8e25e363ee39e8060cf13933cfe1d9 From a191b3535a05616e2857026df284b8f0f6d87aaa Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sun, 4 Oct 2026 16:39:24 +0900 Subject: [PATCH 02/66] fix: migrate untranslated files --- .../runtime-performance/zone-pollution.md | 8 +- .../ecosystem/rxjs-interop/output-interop.md | 4 +- .../ecosystem/rxjs-interop/signals-interop.md | 6 +- .../ecosystem/service-workers/app-shell.md | 1 + .../service-workers/communications.md | 17 +- .../ecosystem/service-workers/config.md | 6 +- .../ecosystem/service-workers/devops.md | 18 ++ .../service-workers/getting-started.md | 1 + .../ecosystem/service-workers/overview.md | 2 +- .../di/debugging-and-troubleshooting-di.md | 23 +- .../guide/di/defining-dependency-providers.md | 30 +-- .../guide/forms/signals/async-operations.md | 10 +- .../guide/forms/signals/cross-field-logic.md | 22 +- .../guide/forms/signals/field-metadata.md | 2 + .../content/guide/forms/signals/form-logic.md | 39 +++ .../guide/forms/signals/form-submission.md | 2 +- .../content/guide/forms/signals/migration.md | 2 +- .../src/content/guide/http/http-resource.md | 15 ++ adev-ja/src/content/guide/i18n/locale-id.md | 2 +- .../content/guide/i18n/manage-marked-text.md | 6 +- adev-ja/src/content/guide/i18n/prepare.md | 8 +- .../content/guide/i18n/translation-files.md | 20 +- .../routing/data-fetching-with-resources.md | 250 ++++++++++++++++++ adev-ja/src/content/guide/signals/effect.md | 18 +- .../guide/templates/error-boundaries.md | 112 ++++++++ adev-ja/src/content/guide/testing/services.md | 2 +- .../configs/angular-compiler-options.md | 16 +- .../content/reference/configs/npm-packages.md | 29 +- .../reference/configs/workspace-config.md | 30 ++- .../src/content/reference/errors/NG01354.md | 64 +++++ .../src/content/reference/errors/NG02802.md | 2 +- .../src/content/reference/errors/NG05106.md | 41 +++ .../src/content/reference/errors/NG0600.md | 59 +++++ .../src/content/reference/errors/NG0991.md | 80 ++++++ .../src/content/reference/errors/NG8011.md | 85 ++++++ .../reference/extended-diagnostics/NG8104.md | 4 +- .../reference/extended-diagnostics/NG8107.md | 2 +- .../reference/extended-diagnostics/NG8111.md | 3 +- .../reference/extended-diagnostics/NG8112.md | 63 +++++ .../reference/extended-diagnostics/NG8114.md | 7 +- .../extended-diagnostics/overview.md | 1 + .../reference/migrations/control-flow.md | 16 +- .../reference/migrations/inject-function.md | 18 +- .../migrations/injectable-to-service.md | 79 ++++++ .../reference/migrations/ngstyle-to-style.md | 18 +- .../content/reference/migrations/outputs.md | 6 +- .../content/reference/migrations/overview.md | 3 + .../migrations/route-lazy-loading.md | 6 +- .../router-testing-module-migration.md | 3 +- .../reference/migrations/signal-inputs.md | 2 +- .../reference/migrations/standalone.md | 38 +-- adev-ja/src/content/tools/cli/aot-compiler.md | 101 +------ .../content/tools/cli/aot-metadata-errors.md | 31 --- .../tools/cli/build-system-migration.md | 1 - adev-ja/src/content/tools/cli/cli-builder.md | 5 +- .../content/tools/cli/schematics-authoring.md | 2 +- .../tools/cli/schematics-for-libraries.md | 2 +- adev-ja/src/content/tools/cli/schematics.md | 8 - .../content/tools/cli/template-typecheck.md | 79 +++--- adev-ja/src/content/tools/devtools/router.md | 2 +- .../tools/libraries/angular-package-format.md | 39 +-- .../tools/libraries/creating-libraries.md | 87 ++++++ .../tools/libraries/using-libraries.md | 2 +- .../first-app/steps/05-inputs/config.json | 6 +- .../first-app/steps/08-ngFor/README.md | 2 +- .../first-app/steps/09-services/README.md | 2 +- .../first-app/steps/10-routing/README.md | 4 +- .../first-app/steps/11-details-page/README.md | 2 +- .../first-app/steps/12-forms/README.md | 2 +- .../first-app/steps/13-search/README.md | 4 +- .../first-app/steps/14-http/README.md | 10 +- 71 files changed, 1285 insertions(+), 407 deletions(-) create mode 100644 adev-ja/src/content/guide/routing/data-fetching-with-resources.md create mode 100644 adev-ja/src/content/guide/templates/error-boundaries.md create mode 100644 adev-ja/src/content/reference/errors/NG01354.md create mode 100644 adev-ja/src/content/reference/errors/NG05106.md create mode 100644 adev-ja/src/content/reference/errors/NG0600.md create mode 100644 adev-ja/src/content/reference/errors/NG0991.md create mode 100644 adev-ja/src/content/reference/errors/NG8011.md create mode 100644 adev-ja/src/content/reference/extended-diagnostics/NG8112.md create mode 100644 adev-ja/src/content/reference/migrations/injectable-to-service.md diff --git a/adev-ja/src/content/best-practices/runtime-performance/zone-pollution.md b/adev-ja/src/content/best-practices/runtime-performance/zone-pollution.md index 38dfda071..1519a9914 100644 --- a/adev-ja/src/content/best-practices/runtime-performance/zone-pollution.md +++ b/adev-ja/src/content/best-practices/runtime-performance/zone-pollution.md @@ -25,7 +25,7 @@ In such cases, you can instruct Angular to avoid calling change detection for ta import { Component, NgZone, OnInit, inject } from '@angular/core'; @Component(...) -class AppComponent implements OnInit { +class App implements OnInit { private ngZone = inject(NgZone); ngOnInit() { @@ -43,7 +43,7 @@ import { Component, NgZone, OnInit, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) -class AppComponent implements OnInit { +class App implements OnInit { private ngZone = inject(NgZone); ngOnInit() { @@ -65,7 +65,7 @@ import { Component, NgZone, OnInit, output, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) -class AppComponent implements OnInit { +class App implements OnInit { private ngZone = inject(NgZone); plotlyClick = output(); @@ -97,7 +97,7 @@ import { Component, NgZone, OnInit, output, inject } from '@angular/core'; import * as Plotly from 'plotly.js-dist-min'; @Component(...) -class AppComponent implements OnInit { +class App implements OnInit { private ngZone = inject(NgZone); plotlyClick = output(); diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md index 3a5fef2cd..d2fe7e59e 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md @@ -2,13 +2,13 @@ TIP: This guide assumes you're familiar with [component and directive outputs](guide/components/outputs). -The `@angular/rxjs-interop` package offers two APIs related to component and directive outputs. +The `@angular/core/rxjs-interop` package offers two APIs related to component and directive outputs. ## Creating an output based on an RxJs Observable The `outputFromObservable` lets you create a component or directive output that emits based on an RxJS observable: -```ts {highlight:[11]} +```ts {highlight:[9]} import {Directive} from '@angular/core'; import {outputFromObservable} from '@angular/core/rxjs-interop'; diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md index 3e8fd13c9..db1ff3699 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -105,7 +105,7 @@ export class SearchResults { As the `query` signal changes, the `query$` Observable emits the latest query and triggers a new HTTP request. -### Injection context +### Injection context {#injection-context-to-observable} `toObservable` by default needs to run in an [injection context](guide/di/dependency-injection-context), such as during construction of a component or service. If an injection context is not available, you can manually specify the `Injector` to use instead. @@ -128,7 +128,7 @@ Here, only the last value (3) will be logged. ## Using `rxResource` for async data -Angular's [`resource` function](/guide/signals/resource) gives you a way to incorporate async data into your application's signal-based code. Building on top of this pattern, `rxResource` lets you define a resource where the source of your data is defined in terms of an RxJS `Observable`. Instead of accepting a `loader` function, `rxResource` accepts a `stream` function that accepts an RxJS `Observable`. +Angular's [`resource` function](/guide/signals/resource) gives you a way to incorporate async data into your application's signal-based code. Building on top of this pattern, `rxResource` lets you define a resource where the source of your data is defined in terms of an RxJS `Observable`. Instead of accepting a `loader` function, `rxResource` accepts a `stream` function that returns an RxJS `Observable`. ```typescript import {Component, inject} from '@angular/core'; @@ -154,3 +154,5 @@ export class UserProfile { The `stream` property accepts a factory function for an RxJS `Observable`. This factory function is passed the resource's `params` value and returns an `Observable`. The resource calls this factory function every time the `params` computation produces a new value. See [Resource loaders](/guide/signals/resource#resource-loaders) for more details on the parameters passed to the factory function. In all other ways, `rxResource` behaves like and provides the same APIs as `resource` for specifying parameters, reading values, checking loading state, and examining errors. + +The `Observable` returned from `stream` must always emit a value or an error before it completes; see [`NG0991`](/errors/NG0991) for what happens otherwise and how to avoid it. diff --git a/adev-ja/src/content/ecosystem/service-workers/app-shell.md b/adev-ja/src/content/ecosystem/service-workers/app-shell.md index 7a7d9afc4..a68e1739a 100644 --- a/adev-ja/src/content/ecosystem/service-workers/app-shell.md +++ b/adev-ja/src/content/ecosystem/service-workers/app-shell.md @@ -38,6 +38,7 @@ src └── main.server.ts # main server application bootstrapping ``` + ```shell diff --git a/adev-ja/src/content/ecosystem/service-workers/communications.md b/adev-ja/src/content/ecosystem/service-workers/communications.md index 22822c68f..f10efab14 100644 --- a/adev-ja/src/content/ecosystem/service-workers/communications.md +++ b/adev-ja/src/content/ecosystem/service-workers/communications.md @@ -14,15 +14,14 @@ The `SwUpdate` service supports three separate operations: ### Version updates -The `versionUpdates` is an `Observable` property of `SwUpdate` and emits five event types: - -| Event types | Details | -| :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `VersionDetectedEvent` | Emitted when the service worker has detected a new version of the app on the server and is about to start downloading it. | -| `NoNewVersionDetectedEvent` | Emitted when the service worker has checked the version of the app on the server and did not find a new version. | -| `VersionReadyEvent` | Emitted when a new version of the app is available to be activated by clients. It may be used to notify the user of an available update or prompt them to refresh the page. | -| `VersionInstallationFailedEvent` | Emitted when the installation of a new version failed. It may be used for logging/monitoring purposes. | -| `VersionFailedEvent` | Emitted when a version encounters a critical failure (such as broken hash errors) that affects all clients using that version. Provides error details for debugging and transparency. | +The `versionUpdates` is an `Observable` property of `SwUpdate` and emits four event types: + +| Event types | Details | +| :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `VersionDetectedEvent` | Emitted when the service worker has detected a new version of the app on the server and is about to start downloading it. | +| `NoNewVersionDetectedEvent` | Emitted when the service worker has checked the version of the app on the server and did not find a new version. | +| `VersionReadyEvent` | Emitted when a new version of the app is available to be activated by clients. It may be used to notify the user of an available update or prompt them to refresh the page. | +| `VersionInstallationFailedEvent` | Emitted when the installation of a new version failed. It may be used for logging/monitoring purposes. | diff --git a/adev-ja/src/content/ecosystem/service-workers/config.md b/adev-ja/src/content/ecosystem/service-workers/config.md index d0ea43baa..a65c9628a 100644 --- a/adev-ja/src/content/ecosystem/service-workers/config.md +++ b/adev-ja/src/content/ecosystem/service-workers/config.md @@ -201,7 +201,7 @@ export interface DataGroup { Each `DataGroup` is defined by the following data group properties. -#### `name` +#### `name` {#datagroups-name} Similar to `assetGroups`, every data group has a `name` which uniquely identifies it. @@ -318,7 +318,7 @@ If you are not able to implement CORS — for example, if you don't control the -#### `cacheQueryOptions` +#### `cacheQueryOptions` {#datagroups-cachequeryoptions} See [assetGroups](#assetgroups) for details. @@ -332,7 +332,7 @@ The ServiceWorker redirects navigation requests that don't match any `asset` or A request is considered to be a navigation request if: - Its [method](https://developer.mozilla.org/docs/Web/API/Request/method) is `GET` -- Its [mode](https://developer.mozilla.org/docs/Web/API/Request/mode) is `navigation` +- Its [mode](https://developer.mozilla.org/docs/Web/API/Request/mode) is `navigate` - It accepts a `text/html` response as determined by the value of the `Accept` header - Its URL matches the following criteria: - The URL must not contain a file extension (that is, a `.`) in the last path segment diff --git a/adev-ja/src/content/ecosystem/service-workers/devops.md b/adev-ja/src/content/ecosystem/service-workers/devops.md index 498cceabd..38830ddd5 100644 --- a/adev-ja/src/content/ecosystem/service-workers/devops.md +++ b/adev-ja/src/content/ecosystem/service-workers/devops.md @@ -110,6 +110,24 @@ Most updates to the Angular service worker are transparent to the application. T Occasionally, a bug fix or feature in the Angular service worker might require the invalidation of old caches. In this case, the service worker transparently refreshes the application from the network. +#### Updating the service worker when only its response headers change + +Browsers only install a new service worker when the service worker script is byte-different from the installed one. +Because `ngsw-worker.js` is usually identical across builds, changing only the response headers that the server sends with it, such as a `Content-Security-Policy`, does not update the installed service worker. +The service worker keeps running with the headers it was installed with. + +To make browsers install the service worker again, register it with a versioned script URL, and change the version whenever those headers change: + +```ts +provideServiceWorker('ngsw-worker.js?v=2', { + enabled: !isDevMode(), + registrationStrategy: 'registerWhenStable:30000', +}); +``` + +Registering a different script URL makes the browser fetch and install the service worker again, even if its content is unchanged. +The Angular service worker resolves its caches and `ngsw.json` relative to its registration scope, not its script URL, so the query parameter doesn't affect cached content. + ### Bypassing the service worker In some cases, you might want to bypass the service worker entirely and let the browser handle the request. diff --git a/adev-ja/src/content/ecosystem/service-workers/getting-started.md b/adev-ja/src/content/ecosystem/service-workers/getting-started.md index a99bb75ca..26c2bfc8d 100644 --- a/adev-ja/src/content/ecosystem/service-workers/getting-started.md +++ b/adev-ja/src/content/ecosystem/service-workers/getting-started.md @@ -256,6 +256,7 @@ export const appConfig: ApplicationConfig = { Available registration strategies: - **`'registerWhenStable:timeout'`** (default: `'registerWhenStable:30000'`) - Register as soon as the application stabilizes (no pending micro-/macro-tasks) but no later than the specified timeout in milliseconds + The timeout is required. Without it, `'registerWhenStable'` registers the service worker immediately. - **`'registerImmediately'`** - Register the service worker immediately - **`'registerWithDelay:timeout'`** - Register with a delay of the specified timeout in milliseconds diff --git a/adev-ja/src/content/ecosystem/service-workers/overview.md b/adev-ja/src/content/ecosystem/service-workers/overview.md index dc80edeb7..6834f37e6 100644 --- a/adev-ja/src/content/ecosystem/service-workers/overview.md +++ b/adev-ja/src/content/ecosystem/service-workers/overview.md @@ -73,7 +73,7 @@ More specifically: - The browser does not download the service worker script and the `ngsw.json` manifest file - Active attempts to interact with the service worker, such as calling `SwUpdate.checkForUpdate()`, return rejected promises -- The observable events of related services, such as `SwUpdate.available`, are not triggered +- The observable events of related services, such as `SwUpdate.versionUpdates`, are not triggered It is highly recommended that you ensure that your application works even without service worker support in the browser. Although an unsupported browser ignores service worker caching, it still reports errors if the application attempts to interact with the service worker. diff --git a/adev-ja/src/content/guide/di/debugging-and-troubleshooting-di.md b/adev-ja/src/content/guide/di/debugging-and-troubleshooting-di.md index 6d629f282..581f3b726 100644 --- a/adev-ja/src/content/guide/di/debugging-and-troubleshooting-di.md +++ b/adev-ja/src/content/guide/di/debugging-and-troubleshooting-di.md @@ -84,7 +84,7 @@ export class EagerView { Lazy-loaded routes create child injectors that are only available after the route loads. -NOTE: By default, route injectors and their services persist even after navigating away from the route. They are not destroyed until the application is closed. For automatic cleanup of unused route injectors, see [customizing route behavior](guide/routing/customizing-route-behavior#experimental-automatic-cleanup-of-unused-route-injectors). +NOTE: By default, route injectors and their services persist even after navigating away from the route. They are not destroyed until the application is closed. For automatic cleanup of unused route injectors, see [customizing route behavior](guide/routing/customizing-route-behavior#automatic-cleanup-of-unused-route-injectors). **Solution:** Use `@Service` for services that need to be shared across lazy boundaries. @@ -135,7 +135,7 @@ Each component gets its own `UserClient` instance. Changes in one component don' **Solution:** Use `@Service` for singletons. ```ts {prefer, header: 'Root-level singleton'} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; @Service() export class UserClient { @@ -250,7 +250,7 @@ export class UserProfile { Use `runInInjectionContext()` when you need to enable **other code** to call `inject()`. This is useful when accepting callbacks that might use dependency injection: ```angular-ts -import {Component, inject, Injector, input} from '@angular/core'; +import {Component, inject, Injector, input, runInInjectionContext} from '@angular/core'; @Component({ selector: 'app-data-loader', @@ -264,13 +264,13 @@ export class DataLoader { const callback = this.onLoad(); if (callback) { // Enable the callback to use inject() - this.injector.runInInjectionContext(callback); + runInInjectionContext(this.injector, callback); } } } ``` -The `runInInjectionContext()` method creates a temporary injection context, allowing code inside the callback to call `inject()`. +The `runInInjectionContext()` function creates a temporary injection context, allowing code inside the callback to call `inject()`. IMPORTANT: Always capture dependencies at the class level when possible. Use `injector.get()` for simple deferred retrieval, and `runInInjectionContext()` only when external code needs to call `inject()`. @@ -671,7 +671,7 @@ This section provides detailed information about specific Angular DI error codes ### NullInjectorError: No provider for [Service] -**Error code:** None (displayed as `NullInjectorError`) +**Error code:** [NG0201](errors/NG0201) This error occurs when Angular cannot find a provider for a token in the injector hierarchy. The error message includes a dependency path showing where the injection was attempted. @@ -855,12 +855,12 @@ Angular allows `inject()` in these locations: }) export class UserProfile { private userService: UserClient; + user: ReturnType; constructor() { this.userService = inject(UserClient); // Valid + this.user = this.userService.getUser(); } - - user = this.userService.getUser(); } ``` @@ -882,7 +882,7 @@ Angular allows `inject()` in these locations: 4. **Inside runInInjectionContext()** ```angular-ts - import {Component, inject, Injector} from '@angular/core'; + import {Component, inject, Injector, runInInjectionContext} from '@angular/core'; import {UserClient} from './user-client'; @Component({ @@ -893,7 +893,7 @@ Angular allows `inject()` in these locations: private injector = inject(Injector); loadUser() { - this.injector.runInInjectionContext(() => { + runInInjectionContext(this.injector, () => { const userService = inject(UserClient); // Valid console.log(userService.getUser()); }); @@ -907,6 +907,7 @@ Other injection contexts that `inject()` also works in include: - [provideEnvironmentInitializer](api/core/provideEnvironmentInitializer) - Functional [route guards](guide/routing/route-guards) - Functional [data resolvers](guide/routing/data-resolvers) +- Route [resources](guide/routing/data-fetching-with-resources) #### When this error occurs @@ -933,7 +934,7 @@ private userService = inject(UserClient) // Capture at class level private injector = inject(Injector) someCallback() { - this.injector.runInInjectionContext(() => { + runInInjectionContext(this.injector, () => { const service = inject(MyClient) }) } diff --git a/adev-ja/src/content/guide/di/defining-dependency-providers.md b/adev-ja/src/content/guide/di/defining-dependency-providers.md index 580ba7b2a..5784af27f 100644 --- a/adev-ja/src/content/guide/di/defining-dependency-providers.md +++ b/adev-ja/src/content/guide/di/defining-dependency-providers.md @@ -38,8 +38,7 @@ NOTE: The string parameter (e.g., `'api.url'`) is a description purely for debug An `InjectionToken` that has a `factory` results in `providedIn: 'root'` by default (but can be overridden via the `providedIn` prop). -```ts -// 📁 /app/config.token.ts +```ts {header: "/app/config.token.ts"} import {InjectionToken} from '@angular/core'; export interface AppConfig { @@ -75,8 +74,7 @@ export class Header { InjectionToken with factory functions is ideal when you can't use a class but need to provide dependencies globally: -```ts -// 📁 /app/logger.token.ts +```ts {header: "/app/logger.token.ts"} import {InjectionToken, inject} from '@angular/core'; import {APP_CONFIG} from './config.token'; @@ -96,8 +94,9 @@ export const LOGGER_FN = new InjectionToken('logger.function', { }; }, }); +``` -// 📁 /app/storage.token.ts +```ts {header: "/app/storage.token.ts"} // Providing browser APIs as tokens export const LOCAL_STORAGE = new InjectionToken('localStorage', { // providedIn: 'root' is configured as the default @@ -108,8 +107,9 @@ export const SESSION_STORAGE = new InjectionToken('sessionStorage', { providedIn: 'root', factory: () => window.sessionStorage, }); +``` -// 📁 /app/feature-flags.token.ts +```ts {header: "/app/feature-flags.token.ts"} // Complex configuration with runtime logic export const FEATURE_FLAGS = new InjectionToken>('feature.flags', { providedIn: 'root', @@ -300,8 +300,7 @@ The class serves as both the identifier and the implementation, which is why Ang Angular provides a built-in [`InjectionToken`](api/core/InjectionToken) class that creates a unique object reference for injectable values or when you want to provide multiple implementations of the same interface. -```ts -// 📁 /app/tokens.ts +```ts {header: "/app/tokens.ts"} import {InjectionToken} from '@angular/core'; import {DataService} from './data-service.interface'; @@ -624,8 +623,7 @@ Use application-level providers in `bootstrapApplication` when: - **The service has no component-specific configuration** - General-purpose utilities that work the same everywhere - **You're providing global configuration** - API endpoints, feature flags, or environment settings -```ts -// main.ts +```ts {header: "main.ts"} bootstrapApplication(App, { providers: [ {provide: API_BASE_URL, useValue: 'https://api.example.com'}, @@ -709,8 +707,7 @@ Use route-level providers for: - **Lazy-loaded module dependencies** - Services that should only load with specific features - **Route-specific configuration** - Settings that vary by application area -```ts -// routes.ts +```ts {header: "routes.ts"} export const routes: Routes = [ { path: 'admin', @@ -743,8 +740,7 @@ When creating Angular libraries, you often need to provide flexible configuratio Instead of requiring users to manually configure complex providers, library authors can export functions that return provider configurations: -```ts -// 📁 /libs/analytics/src/providers.ts +```ts {header: "/libs/analytics/src/providers.ts"} import {InjectionToken, Provider, inject} from '@angular/core'; // Configuration interface @@ -770,9 +766,10 @@ export class AnalyticsService { export function provideAnalytics(config: AnalyticsConfig): Provider[] { return [{provide: ANALYTICS_CONFIG, useValue: config}, AnalyticsService]; } +``` +```ts {header: "main.ts"} // Usage in consumer app -// main.ts bootstrapApplication(App, { providers: [ provideAnalytics({ @@ -787,8 +784,7 @@ bootstrapApplication(App, { For more complex scenarios, you can combine multiple configuration approaches: -```ts -// 📁 /libs/http-client/src/provider.ts +```ts {header: "/libs/http-client/src/provider.ts"} import {Provider, InjectionToken, inject} from '@angular/core'; // Feature flags for optional functionality diff --git a/adev-ja/src/content/guide/forms/signals/async-operations.md b/adev-ja/src/content/guide/forms/signals/async-operations.md index 95ce8f7a9..9c318793a 100644 --- a/adev-ja/src/content/guide/forms/signals/async-operations.md +++ b/adev-ja/src/content/guide/forms/signals/async-operations.md @@ -211,19 +211,19 @@ onError: (error) => { ### HTTP options -Customize the HTTP request with the `options` parameter: +Customize the HTTP request by returning an `HttpResourceRequest` object from the `request` function: ```ts import {HttpHeaders} from '@angular/common/http'; validateHttp(schemaPath.field, { - request: ({value}) => `/api/validate?value=${value()}`, - options: { + request: ({value}) => ({ + url: `/api/validate?value=${value()}`, headers: new HttpHeaders({ Authorization: 'Bearer token', }), timeout: 5000, - }, + }), onSuccess: (response: {valid: boolean}) => response.valid ? null @@ -597,7 +597,7 @@ When async validation runs, the field's `pending()` signal returns `true`. Durin - `valid()` returns `false` - `invalid()` returns `false` - `errors()` returns an empty array -- `submit()` waits for validation to complete +- `submit()` does not wait for it; by default the `action` runs anyway (see [`ignoreValidators`](guide/forms/signals/form-submission#controlling-validation-gating-with-ignorevalidators)) Show the pending state in your template to provide feedback: diff --git a/adev-ja/src/content/guide/forms/signals/cross-field-logic.md b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md index f244f3720..cdd92b173 100644 --- a/adev-ja/src/content/guide/forms/signals/cross-field-logic.md +++ b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md @@ -32,9 +32,7 @@ Here is an example of using `value` and `valueOf()` to validate that the current import {Component, signal} from '@angular/core'; import {form, validate} from '@angular/forms/signals'; -@Component({ - /* ... */ -}) +@Component({/* ... */}) export class EventForm { eventModel = signal({ startDate: new Date('2026-06-01'), @@ -68,9 +66,7 @@ However, that single validator only places the error on the end date field. If y import {Component, signal} from '@angular/core'; import {form, validate} from '@angular/forms/signals'; -@Component({ - /* ... */ -}) +@Component({/* ... */}) export class EventForm { eventModel = signal({ startDate: new Date('2026-06-01'), @@ -113,9 +109,7 @@ In some forms, certain fields are only required under certain conditions. For ex import {Component, signal} from '@angular/core'; import {form, required} from '@angular/forms/signals'; -@Component({ - /* ... */ -}) +@Component({/* ... */}) export class RegistrationForm { registrationModel = signal({ accountType: 'personal' as 'personal' | 'business', @@ -145,9 +139,7 @@ For example, a confirm-password field should only check for a match once the use import {Component, signal} from '@angular/core'; import {form, validate} from '@angular/forms/signals'; -@Component({ - /* ... */ -}) +@Component({/* ... */}) export class PasswordForm { passwordModel = signal({ password: '', @@ -173,7 +165,7 @@ export class PasswordForm { The `stateOf()` call returns the other field's [field state](api/forms/signals/FieldState), giving you access to signals like `invalid()`, `touched()`, and `dirty()`. Because these are signals, the rule re-evaluates whenever the password field's validity changes. -WARNING: Be careful not to read state which depends on your field's validation, as that creates a circular loop. For example, a validator which checks whether the parent field is valid will create an infinite loop because the parent's validity depends on its children's validity (which includes your validator). +CRITICAL: Be careful not to read state which depends on your field's validation, as that creates a circular loop. For example, a validator which checks whether the parent field is valid will create an infinite loop because the parent's validity depends on its children's validity (which includes your validator). ## Using validateTree @@ -185,9 +177,7 @@ For example, in a Sudoku puzzle, each row must contain unique numbers. This is a import {Component, signal} from '@angular/core'; import {form, validateTree} from '@angular/forms/signals'; -@Component({ - /* ... */ -}) +@Component({/* ... */}) export class SudokuRow { rowModel = signal({ cell1: 1, diff --git a/adev-ja/src/content/guide/forms/signals/field-metadata.md b/adev-ja/src/content/guide/forms/signals/field-metadata.md index a35a117a2..fee677e44 100644 --- a/adev-ja/src/content/guide/forms/signals/field-metadata.md +++ b/adev-ja/src/content/guide/forms/signals/field-metadata.md @@ -355,6 +355,8 @@ import {URL_PREVIEW} from './url-preview'; `, }) export class LinkEditor { + URL_PREVIEW = URL_PREVIEW; + linksModel = signal({links: [{url: ''}]}); linksForm = form(this.linksModel, (path) => { diff --git a/adev-ja/src/content/guide/forms/signals/form-logic.md b/adev-ja/src/content/guide/forms/signals/form-logic.md index c5a5a87c8..11e6c5d38 100644 --- a/adev-ja/src/content/guide/forms/signals/form-logic.md +++ b/adev-ja/src/content/guide/forms/signals/form-logic.md @@ -221,6 +221,45 @@ export class Profile { } ``` +### Always hidden + +To make a field permanently hidden, call `hidden()` with just the field path: + +```angular-ts +import {Component, signal} from '@angular/core'; +import {form, FormField, hidden} from '@angular/forms/signals'; + +@Component({ + selector: 'app-profile', + imports: [FormField], + template: ` + + + + @if (!profileForm.publicUrl().hidden()) { + + } + `, +}) +export class Profile { + profileModel = signal({ + isPublic: false, + publicUrl: '', + }); + + profileForm = form(this.profileModel, (schemaPath) => { + // This field is now permanently hidden and excluded from active validation + hidden(schemaPath.publicUrl); + }); +} +``` + ## Display uneditable fields with `readonly()` The `readonly()` rule prevents users from updating a field. The `[FormField]` directive automatically binds this state to the HTML `readonly` attribute, which prevents editing while still allowing users to focus and select text. diff --git a/adev-ja/src/content/guide/forms/signals/form-submission.md b/adev-ja/src/content/guide/forms/signals/form-submission.md index b67608446..633374844 100644 --- a/adev-ja/src/content/guide/forms/signals/form-submission.md +++ b/adev-ja/src/content/guide/forms/signals/form-submission.md @@ -314,7 +314,7 @@ submission: { ## Concurrent submissions -When a submission is in progress, subsequent calls to `submit()` for the same form or any of its parents return `false` immediately without running the action. This prevents duplicate submissions and side effects if a user triggers the submit action multiple times quickly. +When a submission is in progress, subsequent calls to `submit()` for the same form or any of its descendants return `false` immediately without running the action. This prevents duplicate submissions and side effects if a user triggers the submit action multiple times quickly. ## Next steps diff --git a/adev-ja/src/content/guide/forms/signals/migration.md b/adev-ja/src/content/guide/forms/signals/migration.md index 8e592be1d..1b21e489a 100644 --- a/adev-ja/src/content/guide/forms/signals/migration.md +++ b/adev-ja/src/content/guide/forms/signals/migration.md @@ -503,7 +503,7 @@ import {BasicInput} from './basic-input';

Text: {{ reactiveFormGroup.value.reactiveControlName }}

`, - imports: [ReactiveFormsModule], + imports: [ReactiveFormsModule, BasicInput], }) export class ExampleComponent { readonly reactiveFormGroup = new FormGroup({ diff --git a/adev-ja/src/content/guide/http/http-resource.md b/adev-ja/src/content/guide/http/http-resource.md index bb045cc23..ffe1949cc 100644 --- a/adev-ja/src/content/guide/http/http-resource.md +++ b/adev-ja/src/content/guide/http/http-resource.md @@ -76,6 +76,21 @@ httpResource.blob(() => ({ … })); // returns a Blob object in value() httpResource.arrayBuffer(() => ({ … })); // returns an ArrayBuffer in value() ``` +### Tracking download progress + +Set `reportProgress: true` in the request object to track the download progress of the response. The latest `HttpProgressEvent` is available through the `progress` signal of the resource: + +```ts +file = httpResource.blob(() => ({ + url: `/api/files/${fileId()}`, + reportProgress: true, +})); + +downloaded = computed(() => this.file.progress()?.loaded ?? 0); +``` + +NOTE: Unlike `HttpClient`, where `reportProgress` is deprecated in favor of `reportUploadProgress` and `reportDownloadProgress`, `httpResource` keeps the single `reportProgress` option. It only enables download progress events. + ## Response parsing and validation When fetching data, you may want to validate responses against a predefined schema, often using popular open-source libraries like [Zod](https://zod.dev) or [Valibot](https://valibot.dev). You can integrate validation libraries like this with `httpResource` by specifying a `parse` option. The return type of the `parse` function determines the type of the resource's `value`. diff --git a/adev-ja/src/content/guide/i18n/locale-id.md b/adev-ja/src/content/guide/i18n/locale-id.md index 36336bb56..d5759321d 100644 --- a/adev-ja/src/content/guide/i18n/locale-id.md +++ b/adev-ja/src/content/guide/i18n/locale-id.md @@ -5,7 +5,7 @@ Angular uses the Unicode _locale identifier_ \(Unicode locale ID\) to find the c - A locale ID conforms to the [Unicode Common Locale Data Repository (CLDR) core specification][UnicodeCldrDevelopmentCoreSpecification]. - For more information about locale IDs, see [Unicode Language and Locale Identifiers][UnicodeCldrDevelopmentCoreSpecificationLocaleIDs]. + For more information about locale IDs, see [Unicode Language and Locale Identifiers][UnicodeCldrDevelopmentCoreSpecificationLocaleID]. - CLDR and Angular use [BCP 47 tags][RfcEditorInfoBcp47] as the base for the locale ID diff --git a/adev-ja/src/content/guide/i18n/manage-marked-text.md b/adev-ja/src/content/guide/i18n/manage-marked-text.md index e2de6098c..453f583a3 100644 --- a/adev-ja/src/content/guide/i18n/manage-marked-text.md +++ b/adev-ja/src/content/guide/i18n/manage-marked-text.md @@ -9,7 +9,7 @@ As described in [How meanings control text extraction and merges][GuideI18nCommo The following example displays translation units with unique IDs. - + When you change the translatable text, the extractor generates a new ID for that translation unit. In most cases, changes in the source text also require a change to the translation. @@ -34,7 +34,7 @@ variableText1 = $localize`:@@introductionHeader:Hello i18n!`; When you specify a custom ID, the extractor generates a translation unit with the custom ID. - + If you change the text, the extractor does not change the ID. As a result, you don't have to take the extra step to update the translation. @@ -75,7 +75,7 @@ For example, in the following code snippet the same `myId` custom ID is defined The following displays the translation in French. - + Both elements now use the same translation \(`Bonjour`\), because both were defined with the same custom ID. diff --git a/adev-ja/src/content/guide/i18n/prepare.md b/adev-ja/src/content/guide/i18n/prepare.md index 225e130d4..970c1b68a 100644 --- a/adev-ja/src/content/guide/i18n/prepare.md +++ b/adev-ja/src/content/guide/i18n/prepare.md @@ -35,7 +35,7 @@ The following `
` tag will display translated text as part of `div` and `ari - + ### Translate inline text without HTML element @@ -124,7 +124,7 @@ Include [interpolations](guide/templates/binding#render-dynamic-text-with-text-i $localize`string_to_translate ${variable_name}`; ``` -### Name the interpolation placeholder +### Name the interpolation placeholder {#name-the-interpolation-placeholder-in-code} ```ts $localize`string_to_translate ${variable_name}:placeholder_name:`; @@ -190,7 +190,7 @@ $localize`:site header|An introduction header for this sample:Hello i18n!`; The Angular extraction tool generates a translation unit entry for each `i18n` attribute in a template. -The Angular extraction tool assigns each translation unit a unique ID based on the _meaning_ and _description_. +The Angular extraction tool assigns each translation unit a unique ID based on its source text and _meaning_. The _description_ does not affect the ID. HELPFUL: For more information about the Angular extraction tool, see [Work with translation files](guide/i18n/translation-files). @@ -265,7 +265,7 @@ other { default_quantity } HELPFUL: For more information about pluralization categories, see [Choosing plural category names][UnicodeCldrIndexCldrSpecPluralRulesTocChoosingPluralCategoryNames] in the [CLDR - Unicode Common Locale Data Repository][UnicodeCldrMain]. - + Many locales don't support some of the pluralization categories. The default locale \(`en-US`\) uses a very simple `plural()` function that doesn't support the `few` pluralization category. diff --git a/adev-ja/src/content/guide/i18n/translation-files.md b/adev-ja/src/content/guide/i18n/translation-files.md index e684539b0..d082942d7 100644 --- a/adev-ja/src/content/guide/i18n/translation-files.md +++ b/adev-ja/src/content/guide/i18n/translation-files.md @@ -132,20 +132,20 @@ The following actions describe the translation process for French. 1. Open `messages.fr.xlf` and find the first `` element. This is a _translation unit_, also known as a _text node_, that represents the translation of the `

` greeting tag that was previously marked with the `i18n` attribute. - + The `id="introductionHeader"` is a [custom ID][GuideI18nOptionalManageMarkedText], but without the `@@` prefix required in the source HTML. 1. Duplicate the `... ` element in the text node, rename it to `target`, and then replace the content with the French text. - + In a more complex translation, the information and context in the [description and meaning elements][GuideI18nCommonPrepareAddHelpfulDescriptionsAndMeanings] help you choose the right words for translation. 1. Translate the other text nodes. The following example displays the way to translate. - + IMPORTANT: Don't change the IDs for translation units. Each `id` attribute is generated by Angular and depends on the content of the component text and the assigned meaning. @@ -169,7 +169,7 @@ To translate a `plural`, translate the ICU format match values. The following example displays the way to translate. - + ## Translate alternate expressions @@ -184,18 +184,18 @@ The following example displays a `select` ICU expression in the component templa In this example, Angular extracts the expression into two translation units. The first contains the text outside of the `select` clause, and uses a placeholder for `select` \(``\): - + IMPORTANT: When you translate the text, move the placeholder if necessary, but don't remove it. If you remove the placeholder, the ICU expression is removed from your translated application. The following example displays the second translation unit that contains the `select` clause. - + The following example displays both translation units after translation is complete. - + ## Translate nested expressions @@ -206,15 +206,15 @@ Angular extracts the expression into two translation units. The following example displays the first translation unit that contains the text outside of the nested expression. - + The following example displays the second translation unit that contains the complete nested expression. - + The following example displays both translation units after translating. - + ## What's next diff --git a/adev-ja/src/content/guide/routing/data-fetching-with-resources.md b/adev-ja/src/content/guide/routing/data-fetching-with-resources.md new file mode 100644 index 000000000..208290d15 --- /dev/null +++ b/adev-ja/src/content/guide/routing/data-fetching-with-resources.md @@ -0,0 +1,250 @@ +# Data fetching with resources + +The Angular Router integrates with Angular signals through the `resources` route configuration. This allows you to fetch data reactively using `Resource` APIs. + +## Why use route resources? + +Route resources offer several advantages over traditional [data resolvers](/guide/routing/data-resolvers): + +- **Parallel execution**: Route resources across all matched routes load concurrently instead of one route at a time. +- **Non-blocking data loading**: Use `nonBlocking()` to activate the route immediately and render loading skeletons or UI states while data loads in the background. +- **Reload without renavigation**: Call `.reload()` on individual resources or update signal parameters to refresh data without rerunning guards or rematching routes. +- **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()`. + +## Enabling route resources + +To enable route resources, provide `withRouterResources()` to your router configuration: + +```ts +import {provideRouter, withComponentInputBinding, withRouterResources} from '@angular/router'; + +bootstrapApplication(App, { + providers: [provideRouter(routes, withComponentInputBinding(), withRouterResources())], +}); +``` + +TIP: Enable `withComponentInputBinding()` so the router can bind resolved resources directly to component inputs. + +## Defining route resources + +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. + +```angular-ts +import {Component, inject, input, resource} from '@angular/core'; +import {Routes} from '@angular/router'; +import {UserService} from './user.service'; + +const routes: Routes = [ + { + path: 'user/:id', + component: UserProfile, + resources: (ctx) => { + const userService = inject(UserService); + return { + user: resource({ + params: () => ctx.params()['id'], + loader: ({params: id}) => userService.getUser(id), + }), + }; + }, + }, +]; + +@Component({ + template: `

User: {{ user().name }}

`, +}) +export class UserProfile { + // The router binds only the value for blocking resources. + user = input.required(); +} +``` + +### The `ResourceContext` object + +The `resources` function receives a `ResourceContext` that provides access to reactive route signals: `params`, `queryParams`, `fragment`, and `data`. + +### Supported resource implementations + +The `resources` function can return any Angular `Resource` implementation, such as `resource()`, `rxResource()`, or a custom resource. + +```ts +import {Routes} from '@angular/router'; +import {rxResource} from '@angular/core/rxjs-interop'; + +const routes: Routes = [ + { + path: 'user/:id', + component: UserProfile, + resources: (ctx) => ({ + user: rxResource({ + params: () => ctx.params()['id'], + stream: ({params: id}) => fetchUserObservable(id), + }), + }), + }, +]; +``` + +NOTE: `rxResource` uses the `stream` property instead of `loader` to accept a function that returns an Observable. + +The `resources` function can also be `async` and return a `Promise` if you need to perform asynchronous setup or dynamic imports before configuring resources: + +```ts +resources: async (ctx) => { + const {fetchUserData} = await import('./user-api'); + return { + user: resource({ + params: () => ctx.params()['id'], + loader: ({params: id}) => fetchUserData(id), + }), + }; +}, +``` + +## Fine-grained change tracking with signals + +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: + +```ts +resources: (ctx) => ({ + products: resource({ + // Tracks only the 'category' query parameter + params: () => ctx.queryParams()['category'], + loader: ({params: category}) => fetchProducts(category), + }), +}), +``` + +A navigation that changes an unrelated query parameter, such as `?sort=desc` or `?page=2`, leaves `category` unchanged, so the resource does not refetch. + +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. + +## Parallel execution + +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. + +Route resources across the matched route hierarchy run concurrently, so the same navigation completes in 300ms, the time of the slowest resource. + +## Blocking and non-blocking resources + +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. + +For a blocking resource, the router binds the resolved value to the component input, so the input type is `T` instead of `Resource`. 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. + +To handle loading states in the UI instead, wrap the resource in `nonBlocking()`. The router activates the component immediately and binds the full `Resource` object to the component input, which gives you access to `isLoading()`, `error()`, and the other resource signals. + +```angular-ts +import {Component, input, Resource, resource} from '@angular/core'; +import {Routes, nonBlocking} from '@angular/router'; + +const routes: Routes = [ + { + path: 'reports', + component: Reports, + resources: () => ({ + reportData: nonBlocking( + resource({ + loader: () => fetchHeavyReportData(), + }), + ), + }), + }, +]; + +@Component({ + template: ` + @if (reportData().isLoading()) { +

Loading...

+ } @else if (reportData().error()) { +

Error loading report.

+ } @else if (reportData().hasValue()) { + + } + `, +}) +export class Reports { + reportData = input.required>(); +} +``` + +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. + +### Redirecting from a resource + +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: + +```ts +import {inject, resource} from '@angular/core'; +import {RedirectCommand, Router, Routes} from '@angular/router'; + +const routes: Routes = [ + { + path: 'user/:id', + component: UserProfile, + resources: (ctx) => { + const router = inject(Router); + + return { + user: resource({ + params: () => ctx.params()['id'], + loader: async ({params: id}) => { + const user = await fetchUser(id); + if (!user) { + throw new RedirectCommand(router.parseUrl('/not-found')); + } + return user; + }, + }), + }; + }, + }, +]; +``` + +## Reloading resources without renavigation + +With data resolvers, refetching data requires a route navigation (for example, navigating with `onSameUrlNavigation: 'reload'`), which rematches routes and reruns guards and resolvers. + +Route resources support two ways to refresh data in place: + +1. **Programmatic reload**: Call `.reload()` on the `Resource` instance. +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. + +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: + +```angular-ts +import {Component, inject, input} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; + +@Component({ + template: ` +

User: {{ user().name }}

+ + `, +}) +export class UserProfile { + user = input.required(); + private userResource = inject(ActivatedRoute).resources?.['user']; + + refreshUser() { + // Reloads only this specific resource without renavigating the route + this.userResource?.reload(); + } +} +``` + +## Transitional states during pending navigations + +While a navigation is pending, the router freezes the resources that it exposes on `ActivatedRoute`, which masks intermediate `loading` and `reloading` states. + +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. + +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. + +### Rollback recovery on cancellation + +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. + +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. + +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})`. diff --git a/adev-ja/src/content/guide/signals/effect.md b/adev-ja/src/content/guide/signals/effect.md index 0acefd12e..9754515c0 100644 --- a/adev-ja/src/content/guide/signals/effect.md +++ b/adev-ja/src/content/guide/signals/effect.md @@ -1,4 +1,4 @@ -## Effects +# Effects Signals are useful because they notify interested consumers when they change. An **effect** is an operation that runs whenever one or more signal values change. You can create an effect with the `effect` function: @@ -14,7 +14,7 @@ Effects always run **at least once.** When an effect runs, it tracks any signal Effects always execute **asynchronously**, during the change detection process. -### Use cases for effects +## Use cases for effects Effects should be the last API you reach for. Always prefer `computed()` for derived values and `linkedSignal()` for values that can be both derived and manually set. If you find yourself copying data from one signal to another with an effect, it's a sign you should move your source-of-truth higher up and use `computed()` or `linkedSignal()` instead. Effects are best for syncing signal state to imperative, non-signal APIs. @@ -31,7 +31,7 @@ Avoid using effects for propagation of state changes. This can result in `Expres Instead, use `computed` signals to model state that depends on other state. -### Injection context +## Injection context By default, you can only create an `effect()` within an [injection context](guide/di/dependency-injection-context) (where you have access to the `inject` function). The easiest way to satisfy this requirement is to call `effect` within a component, directive, or service `constructor`: @@ -68,7 +68,7 @@ export class EffectiveCounter { } ``` -### Execution of effects +## Execution of effects Angular implicitly defines two implicit behaviors for its effects depending on the context they were created in. @@ -82,7 +82,7 @@ The execution of both kinds of `effect` are tied to the change detection process In both cases, if at least one of the effect dependencies changed during the effect execution, the effect will re-run before moving ahead on the change detection process. -### Destroying effects +## Destroying effects When a component or directive is destroyed, Angular automatically cleans up any associated effects. @@ -93,7 +93,7 @@ An `effect` can be created in two different contexts that will affect when it's Effects return an `EffectRef`. You can use the ref's `destroy` method to manually dispose of an effect. You can combine this with the `manualCleanup` option when creating an effect to disable automatic cleanup. Be careful to actually destroy such effects when they're no longer required. -### Effect cleanup functions +## Effect cleanup functions When a component or directive is destroyed, Angular automatically cleans up any associated effects. Effects might start long-running operations, which you should cancel if the effect is destroyed or runs again before the first operation finished. When you create an effect, your function can optionally accept an `onCleanup` function as its first parameter. This `onCleanup` function lets you register a callback that is invoked before the next run of the effect begins, or when the effect is destroyed. @@ -129,7 +129,7 @@ export class MyFancyChart { // Run a single time to create the chart instance afterNextRender({ write: () => { - this.chart = initializeChart(this.canvas().nativeElement(), this.chartData()); + this.chart = initializeChart(this.canvas().nativeElement, this.chartData()); }, }); @@ -162,7 +162,7 @@ The phases are: Using these phases helps prevent layout thrashing and ensures that your DOM operations are performed in a safe and efficient manner. -You can specify the phase by passing an object with a `phase` property to `afterRender` or `afterNextRender`: +You can specify the phases by passing an object with a callback for each phase to `afterRenderEffect`: ```ts afterRenderEffect({ @@ -185,7 +185,7 @@ CRITICAL: If you don't specify the phase, `afterRenderEffect` runs callbacks dur #### Phase executions -The `earlyRead` phase callback receives no parameters. Each subsequent phase receives the return value of the previous phase's callback as a Signal. You can use this to coordinate work across phases. +The `earlyRead` phase callback receives only the cleanup function. Each subsequent phase receives the return value of the previous phase's callback as a Signal. You can use this to coordinate work across phases. Effects run in the following phase order: diff --git a/adev-ja/src/content/guide/templates/error-boundaries.md b/adev-ja/src/content/guide/templates/error-boundaries.md new file mode 100644 index 000000000..3a3bac481 --- /dev/null +++ b/adev-ja/src/content/guide/templates/error-boundaries.md @@ -0,0 +1,112 @@ +# Error boundaries with `@boundary` + +IMPORTANT: `@boundary` is in [developer preview](reference/releases#developer-preview). + +Angular templates support error boundaries to gracefully handle runtime errors that occur during rendering and change detection. + +Error boundaries prevent a single component's failure from crashing the entire application and provide a way to display fallback UI to the user. + +## Catching errors with `@boundary` and `@error` + +The `@boundary` block wraps a section of your template. If any component or directive inside this boundary throws an error during initialization or change detection, the framework catches the error and renders the `@error` block instead. + +```angular-html +@boundary { + +} @error { +

Something went wrong!

+} +``` + +## Accessing the error object + +You can access the caught error by accessing the implicit `$error` variable: + +```angular-html +@boundary { + +} @error { +

Error occurred: {{ $error.message }}

+} +``` + +## Resetting the boundary + +You can attempt to re-render the content of the `@boundary` by calling the implicit `$reset` function in the `@error` block. When called, it resets the boundary state and tries to render the original content again. + +```angular-html +@boundary { + +} @error { +

Loading failed.

+ +} +``` + +## Conditional error handling with `when` + +You can use `when` clauses to conditionally handle specific types of errors, allowing you to provide different fallback UIs. Angular evaluates this condition when it catches an error. + +```angular-html +@boundary { + +} @error (let err; reset = $reset; when isRenderError(err)) { +

Network issue. Check your connection.

+ +} @error { +

An unexpected error occurred: {{ $error.message }}

+} +``` + +Order your `@error` blocks from most specific to least specific, as Angular evaluates the `when` clauses in order and uses the first one that evaluates to true. A final `@error` block without a `when` clause acts as a catch-all fallback. + +## Global error handler integration + +When a boundary catches an error, Angular can still notify the global `ErrorHandler`. You can implement the optional `onViewError` hook in your custom `ErrorHandler` to log these caught errors to your error tracking service. + +```ts +@Injectable() +export class MyErrorHandler implements ErrorHandler { + handleError(error: any): void { + // Handle uncaught errors + } + + onViewError(error: Error, details: ErrorDetails): void { + // Handle errors caught by a @boundary + console.warn('Caught by boundary:', details.boundary); + myErrorTrackingService.log(error); + } +} +``` + +IMPORTANT: If an `@error` block itself throws an error, the error propagates to the next outer `@boundary` or Angular treats it as an unhandled application error. + +## Content projection + +If your component uses [content projection](guide/components/content-projection), wrapping `` in a `@boundary` does not catch errors from projected content. That content belongs to the view that declares it, not the receiving component's view. + +For example, a wrapper component with the following template does not catch errors from components projected into it: + +```angular-html {avoid} +@boundary { + +} @error { +

Something went wrong!

+} +``` + +To catch those errors, wrap the wrapper component and its projected content in a `@boundary` in the parent template: + +```angular-html {prefer} +@boundary { + + + +} @error { +

Something went wrong!

+} +``` + +## Dynamic views and programmatic error handling + +Error handling isn't limited to template syntax. If you are creating components or embedded views dynamically, you can use the `onError` option to handle errors. See the [Handling rendering errors](guide/components/programmatic-rendering#handling-rendering-errors) section in the programmatic rendering guide for more information. diff --git a/adev-ja/src/content/guide/testing/services.md b/adev-ja/src/content/guide/testing/services.md index 99ea12829..d05bd0258 100644 --- a/adev-ja/src/content/guide/testing/services.md +++ b/adev-ja/src/content/guide/testing/services.md @@ -35,7 +35,7 @@ describe('Calculator', () => { beforeEach(() => { // Injects the Calculator service which is available to Angular - // because the service uses `providedIn: 'root'` + // because the service uses `@Service` service = TestBed.inject(Calculator); }); diff --git a/adev-ja/src/content/reference/configs/angular-compiler-options.md b/adev-ja/src/content/reference/configs/angular-compiler-options.md index b141b3547..52db021a0 100644 --- a/adev-ja/src/content/reference/configs/angular-compiler-options.md +++ b/adev-ja/src/content/reference/configs/angular-compiler-options.md @@ -64,13 +64,6 @@ Use `'partial'` for independently published libraries, such as npm packages. `'partial'` compilations output a stable, intermediate format which better supports usage by applications built at different Angular versions from the library. Libraries built at "HEAD" alongside their applications and using the same version of Angular such as in a mono-repository can use `'full'` since there is no risk of version skew. -### `disableExpressionLowering` - -When `true`, the default, transforms code that is or could be used in an annotation, to allow it to be imported from template factory modules. -See [metadata rewriting](tools/cli/aot-compiler#metadata-rewriting) for more information. - -When `false`, disables this rewriting, requiring the rewriting to be done manually. - ### `disableTypeScriptVersionCheck` When `true`, the compiler does not look at the TypeScript version and does not report an error when an unsupported version of TypeScript is used. @@ -91,6 +84,9 @@ These message formats have some issues, such as whitespace handling and reliance The new message format is more resilient to whitespace changes, is the same across all translation file formats, and can be created directly from calls to `$localize`. This allows `$localize` messages in application code to use the same ID as identical `i18n` messages in component templates. +IMPORTANT: This option is only supported by the `@angular-devkit/build-angular:browser` builder. +When using the `@angular/build:application` builder (esbuild), this option has no effect and the new decimal message ID format is always used regardless of this setting. + ### `enableResourceInlining` When `true`, replaces the `templateUrl` and `styleUrls` properties in all `@Component` decorators with inline content in the `template` and `styles` properties. @@ -99,12 +95,6 @@ When enabled, the `.js` output of `ngc` does not include any lazy-loaded templat For library projects created with the Angular CLI, the development configuration default is `true`. -### `enableLegacyTemplate` - -When `true`, enables the deprecated `