From 298fa250b65658c0d75f37fa3d43085ed10b1c46 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:27:57 +0900 Subject: [PATCH 01/66] chore: bump toolchain to match upstream v22 (Node 22.22.2 / pnpm 10.34.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit angular/angular v22.0.0 が利用する Node.js / pnpm に揃える。 - .node-version: 22.12.0 → 22.22.2 - packageManager: pnpm@10.15.0 → pnpm@10.34.1 --- .node-version | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.node-version b/.node-version index d135defb28..19464bf8db 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.12.0 \ No newline at end of file +22.22.2 \ No newline at end of file diff --git a/package.json b/package.json index 375938f99e..1c03170f02 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "list-untranslated": "tsx tools/list-untranslated.ts", "translate": "tsx --env-file=.env tools/translator/main.ts" }, - "packageManager": "pnpm@10.15.0", + "packageManager": "pnpm@10.34.1", "devDependencies": { "@langchain/core": "0.3.58", "@langchain/google-genai": "0.2.12", From 0312f05dc049482b197100ca1684cd168ee78e5c Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:30:08 +0900 Subject: [PATCH 02/66] chore: update origin to fa546f382d --- origin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/origin b/origin index cea6588bb3..fa546f382d 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit cea6588bb301c9a71fe703fd1cb3a2165fd768b4 +Subproject commit fa546f382de10af46d0508733c6630ffe4bef328 From 8ecded994f03a0024849f2488aace399521f2123 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:30:50 +0900 Subject: [PATCH 03/66] fix: migrate untranslated files --- .../best-practices/performance/overview.md | 1 + .../ecosystem/rxjs-interop/signals-interop.md | 2 - .../guide/di/creating-injectable-service.md | 23 ++--- .../di/debugging-and-troubleshooting-di.md | 57 ++++++------- .../guide/di/defining-dependency-providers.md | 6 +- .../guide/forms/signals/async-operations.md | 28 +++--- .../signals/designing-your-form-model.md | 6 +- .../content/guide/forms/signals/form-logic.md | 60 ++++++------- .../content/guide/forms/signals/migration.md | 2 +- .../content/guide/forms/signals/schemas.md | 8 +- .../src/content/guide/http/http-resource.md | 2 - adev-ja/src/content/guide/i18n/merge.md | 11 ++- adev-ja/src/content/guide/i18n/prepare.md | 20 +++++ .../src/content/guide/signals/debounced.md | 85 +++++++++++++++++++ adev-ja/src/content/guide/testing/services.md | 12 +-- .../configs/angular-compiler-options.md | 9 -- .../reference/configs/workspace-config.md | 48 +++++++++-- .../src/content/reference/errors/NG0204.md | 20 ++--- .../src/content/reference/errors/NG0919.md | 6 +- .../src/content/reference/errors/NG8023.md | 47 ++++++++++ .../src/content/reference/errors/NG8024.md | 51 +++++++++++ .../reference/extended-diagnostics/NG8021.md | 66 ++++++++------ .../migrations/route-lazy-loading.md | 1 - adev-ja/src/content/tools/cli/aot-compiler.md | 2 +- .../tools/cli/schematics-for-libraries.md | 6 +- .../playground/5-aria-accordion/config.json | 5 ++ 26 files changed, 406 insertions(+), 178 deletions(-) create mode 100644 adev-ja/src/content/guide/signals/debounced.md create mode 100644 adev-ja/src/content/reference/errors/NG8023.md create mode 100644 adev-ja/src/content/reference/errors/NG8024.md create mode 100644 adev-ja/src/content/tutorials/playground/5-aria-accordion/config.json diff --git a/adev-ja/src/content/best-practices/performance/overview.md b/adev-ja/src/content/best-practices/performance/overview.md index 0280057f3f..0754c74c34 100644 --- a/adev-ja/src/content/best-practices/performance/overview.md +++ b/adev-ja/src/content/best-practices/performance/overview.md @@ -10,6 +10,7 @@ Loading performance determines how quickly your application becomes visible and | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | | [Lazy-loaded routes](best-practices/performance/lazy-loaded-routes#lazily-loaded-components-and-routes) | Defers loading route components until navigation, reducing the initial bundle size | Applications with multiple routes where not all are needed on initial load | | [Deferred loading with `@defer`](best-practices/performance/defer) | Splits components into separate bundles that load on demand | Components not visible on initial render, heavy third-party libraries, below-the-fold content | +| [Lazy loading services with `injectAsync`](guide/di/lazy-loading-services) | Splits rarely used services into separate chunks and loads them on demand | Services backed by large libraries or infrequently used features | | [Image optimization](best-practices/performance/image-optimization) | Prioritizes LCP images, lazy loads others, generates responsive `srcset` attributes | Any application that displays images | | [Server-side rendering](best-practices/performance/ssr) | Renders pages on the server for faster first paint and better SEO, with [hydration](guide/hydration) to restore interactivity and [incremental hydration](guide/incremental-hydration) to defer hydrating sections until needed | Content-heavy applications, pages that need search engine indexing | 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 9b28a809a4..3e8fd13c9c 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -128,8 +128,6 @@ Here, only the last value (3) will be logged. ## Using `rxResource` for async data -IMPORTANT: `rxResource` is [experimental](reference/releases#experimental). It's ready for you to try, but it might change before it is stable. - 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`. ```typescript diff --git a/adev-ja/src/content/guide/di/creating-injectable-service.md b/adev-ja/src/content/guide/di/creating-injectable-service.md index 7776a6091e..bbc4094b68 100644 --- a/adev-ja/src/content/guide/di/creating-injectable-service.md +++ b/adev-ja/src/content/guide/di/creating-injectable-service.md @@ -73,28 +73,21 @@ ng generate service heroes/hero This command creates the following default `HeroService`: ```ts {header: 'heroes/hero.service.ts (CLI-generated)'} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({ - providedIn: 'root', -}) +@Service() export class HeroService {} ``` -The `@Injectable()` decorator specifies that Angular can use this class in the DI system. -The `providedIn: 'root'` metadata specifies that the `HeroService` is available throughout your application. +The `@Service()` decorator specifies that Angular can use this class in the DI system and that the `HeroService` is available throughout your application. Add a `getHeroes()` method that returns the heroes from `mock.heroes.ts` to get the hero mock data: ```ts {header: 'hero.service.ts'} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; import {HEROES} from './mock-heroes'; -@Injectable({ - // declares that this service should be created - // by the root application injector. - providedIn: 'root', -}) +@Service() export class HeroService { getHeroes() { return HEROES; @@ -133,13 +126,11 @@ When a service depends on another service, follow the same pattern as injecting In the following example, `HeroService` depends on a `Logger` service to report its activities: ```ts {header: 'hero.service.ts, highlight: [[3],[9],[12]]} -import {inject, Injectable} from '@angular/core'; +import {inject, Service} from '@angular/core'; import {HEROES} from './mock-heroes'; import {Logger} from '../logger.service'; -@Injectable({ - providedIn: 'root', -}) +@Service() export class HeroService { private logger = inject(Logger); 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 e5f9ae9644..e41812c9c9 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 @@ -42,15 +42,15 @@ Angular only searches up the hierarchy, never down. Parent components cannot acc **Solution:** Provide the service at a higher level (application or parent component). ```ts {prefer} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class DataStore { // Available everywhere } ``` -TIP: Use `providedIn: 'root'` by default for services that don't need component-specific state. This makes services available everywhere and enables tree-shaking. +TIP: `@Service` makes services available everywhere and enables tree-shaking. If you don't want to scope it to the entire app, specify `autoProvided: false`. #### Services and lazy-loaded routes @@ -86,12 +86,12 @@ Lazy-loaded routes create child injectors that are only available after the rout 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). -**Solution:** Use `providedIn: 'root'` for services that need to be shared across lazy boundaries. +**Solution:** Use `@Service` for services that need to be shared across lazy boundaries. ```ts {prefer, header: 'Provide at root for shared services'} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class FeatureClient { // Available everywhere, including before lazy load } @@ -132,12 +132,12 @@ export class UserSettings { Each component gets its own `UserClient` instance. Changes in one component don't affect the other. -**Solution:** Use `providedIn: 'root'` for singletons. +**Solution:** Use `@Service` for singletons. ```ts {prefer, header: 'Root-level singleton'} import {Injectable} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class UserClient { // Single instance shared across all components } @@ -517,6 +517,7 @@ Angular searches in this order: When you see a `NullInjectorError`, the service isn't provided at any level the component can access. Check that: +- The service has `@Service()` or - The service has `@Injectable({providedIn: 'root'})`, or - The service is in a `providers` array the component can reach @@ -530,9 +531,9 @@ When debugging DI issues, use DevTools to answer these questions: - **Is the service provided?** Select the component that fails to inject and check if the service appears in the Injector section. - **At what level?** Walk up the component tree to find where the service is actually provided (component, route, or application level). -- **Multiple instances?** If a singleton service appears in multiple component injectors, it's likely provided in component `providers` arrays instead of using `providedIn: 'root'`. +- **Multiple instances?** If a singleton service appears in multiple component injectors, it's likely provided in component `providers` arrays instead of using `@Service` or `providedIn: 'root'`. -If a service never appears in any injector, verify it has the `@Injectable()` decorator with `providedIn: 'root'` or is listed in a `providers` array. +If a service never appears in any injector, verify it has the `@Service` decorator or is listed in a `providers` array. ### Logging and tracing injection @@ -543,9 +544,9 @@ When DevTools isn't enough, use logging to trace injection behavior. Add console logs to service constructors to see when services are created. ```ts -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class UserClient { constructor() { console.log('UserClient created'); @@ -639,8 +640,8 @@ When DI fails, follow this systematic approach: **Step 2: Check the basics** -- Does the service have `@Injectable()`? -- Is `providedIn` set correctly? +- Does the service have `@Service` or `@Injectable()`? +- If you use `@Injectable`, is `providedIn` set correctly? - Are imports correct? - Is the file included in compilation? @@ -681,9 +682,9 @@ NullInjectorError: No provider for UserClient! The dependency path shows that `App` injected `AuthClient`, which tried to inject `UserClient`, but no provider was found. -#### Missing @Injectable decorator +#### Missing the `@Service ` or `@Injectable` decorator -The most common cause is forgetting the `@Injectable()` decorator on a service class. +The most common cause is forgetting the `@Service` or `@Injectable()` decorator on a service class. ```ts {avoid, header: 'Missing decorator'} export class UserClient { @@ -693,14 +694,12 @@ export class UserClient { } ``` -Angular requires the `@Injectable()` decorator to generate the metadata needed for dependency injection. +Angular requires the `@Service()` decorator to generate the metadata needed for dependency injection. -```ts {prefer, header: 'Include @Injectable'} -import {Injectable} from '@angular/core'; +```ts {prefer, header: 'Include @Service'} +import {Service} from '@angular/core'; -@Injectable({ - providedIn: 'root', -}) +@Service() export class UserClient { getUser() { return {name: 'Alice'}; @@ -708,7 +707,7 @@ export class UserClient { } ``` -NOTE: Classes with zero-argument constructors can work without `@Injectable()`, but this is not recommended. Always include the decorator for consistency and to avoid issues when adding dependencies later. +NOTE: Classes with zero-argument constructors can work without `@Service()`, but this is not recommended. Always include the decorator for consistency and to avoid issues when adding dependencies later. #### Missing providedIn configuration @@ -725,14 +724,12 @@ export class UserClient { } ``` -Specify `providedIn: 'root'` to make the service available throughout your application. +Use the `@Service` decorator to make the service available throughout your application. ```ts {prefer, header: 'Specify providedIn'} -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({ - providedIn: 'root', -}) +@Service() export class UserClient { getUser() { return {name: 'Alice'}; @@ -740,7 +737,7 @@ export class UserClient { } ``` -The `providedIn: 'root'` configuration makes the service available application-wide and enables tree-shaking (the service is removed from the bundle if never injected). +The `@Service` decorator makes the service available application-wide and enables tree-shaking (the service is removed from the bundle if never injected). #### Standalone component missing imports @@ -760,7 +757,7 @@ export class UserProfile { } ``` -Ensure the service uses `providedIn: 'root'` or add it to the component's `providers` array. +Ensure the service uses `@Service` or add it to the component's `providers` array. ```angular-ts {prefer, header: 'Service uses providedIn: root'} import {Component, inject} from '@angular/core'; 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 87217b8f44..0de69a6f02 100644 --- a/adev-ja/src/content/guide/di/defining-dependency-providers.md +++ b/adev-ja/src/content/guide/di/defining-dependency-providers.md @@ -2,7 +2,7 @@ Angular provides two ways to make services available for injection: -1. **Automatic provision** - Using `providedIn` in the `@Injectable` decorator or by providing a factory in the `InjectionToken` configuration +1. **Automatic provision** - Using `providedIn` in the `@Injectable` decorator, the [`@Service`](guide/di/creating-and-using-services#using-the-service-decorator) decorator, or by providing a factory in the `InjectionToken` configuration 2. **Manual provision** - Using the `providers` array in components, directives, routes, or application config In the [previous guide](/guide/di/creating-and-using-services), you learned how to create services using `providedIn: 'root'`, which handles most common use cases. This guide explores additional patterns for both automatic and manual provider configuration. @@ -36,7 +36,7 @@ NOTE: The string parameter (e.g., `'api.url'`) is a description purely for debug ### InjectionToken with `providedIn: 'root'` -An `InjectionToken` that has a `factory` results in `providedIn: 'root'` by default (but can be overidden via the `providedIn` prop). +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 @@ -279,7 +279,7 @@ Provider identifiers allow Angular's dependency injection (DI) system to retriev #### Class names -Class name use the imported class directly as the identifier: +Class names use the imported class directly as the identifier: ```angular-ts import {Component} from '@angular/core'; 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 fab01f32a5..a0542c9489 100644 --- a/adev-ja/src/content/guide/forms/signals/async-operations.md +++ b/adev-ja/src/content/guide/forms/signals/async-operations.md @@ -53,7 +53,7 @@ export class Registration { const username = value(); return username ? `/api/users/check?username=${username}` : undefined; }, - onSuccess: (response) => { + onSuccess: (response: {available: boolean}) => { return response.available ? null : { @@ -114,7 +114,7 @@ export class Registration { return `/api/users/check?username=${username}`; }, - onSuccess: (response, {value}) => { + onSuccess: (response: {available: boolean}, {value}) => { if (response.available) { // Cache successful validations this.validatedUsernames.add(value()); @@ -149,7 +149,7 @@ request: ({value}) => ({ The `onSuccess` function receives the HTTP response and returns validation errors or `undefined` for valid values: ```ts -onSuccess: (response) => { +onSuccess: (response: { valid: boolean; message?: string }) => { if (response.valid) return undefined; return { @@ -162,7 +162,7 @@ onSuccess: (response) => { Return multiple errors when needed: ```ts -onSuccess: (response) => { +onSuccess: (response: { usernameTaken: boolean; profanity: boolean }) => { const errors = []; if (response.usernameTaken) { errors.push({ @@ -207,7 +207,7 @@ validateHttp(schemaPath.field, { }), timeout: 5000, }, - onSuccess: (response) => + onSuccess: (response: {valid: boolean}) => response.valid ? null : { @@ -281,7 +281,7 @@ export class Registration { validateAsync(schemaPath.username, { params: ({value}) => { const username = value(); - return username.length >= 3 ? username : undefined; + return username.length >= 3 ? username : undefined!; }, factory: this.createUsernameResource, onSuccess: (result) => { @@ -390,7 +390,7 @@ export class Registration { // Skip the request for blank values return username ? `/api/users/check?username=${username}` : undefined; }, - onSuccess: (response) => + onSuccess: (response: {available: boolean}) => response.available ? null : {kind: 'usernameTaken', message: 'Username is already taken'}, onError: () => ({ kind: 'serverError', @@ -447,7 +447,7 @@ const shorterWhenLonger: Debouncer = ({value}, abortSignal) => { }); }; -form(this.registrationModel, (schemaPath) => { +const registrationForm = form(registrationModel, (schemaPath) => { debounce(schemaPath.username, shorterWhenLonger); }); ``` @@ -468,7 +468,7 @@ form(this.registrationModel, (schemaPath) => { // Skip the request for blank values return username ? `/api/users/check?username=${username}` : undefined; }, - onSuccess: (response) => + onSuccess: (response: {available: boolean}) => response.available ? null : {kind: 'usernameTaken', message: 'Username is already taken'}, onError: () => ({ kind: 'serverError', @@ -512,7 +512,7 @@ export class Registration { params: ({value}) => { const username = value(); // Skip validation for short usernames - return username.length >= 3 ? username : undefined; + return username.length >= 3 ? username : undefined!; }, debounce: 300, // Reference to the factory defined above @@ -627,7 +627,7 @@ form(model, (schemaPath) => { // 2. This async validation rule only runs if synchronous validation passes validateHttp(schemaPath.username, { request: ({value}) => `/api/check?username=${value()}`, - onSuccess: (result) => + onSuccess: (result: {valid: boolean}) => result.valid ? null : { @@ -665,7 +665,7 @@ form(model, (schemaPath) => { // Then check availability validateHttp(schemaPath.email, { request: ({value}) => `/api/emails/check?email=${value()}`, - onSuccess: (result) => + onSuccess: (result: {available: boolean}) => result.available ? null : { @@ -695,7 +695,7 @@ validateHttp(schemaPath.username, { return `/api/users/check?username=${username}`; }, - onSuccess: (result) => + onSuccess: (result: {valid: boolean}) => result.valid ? null : { @@ -718,7 +718,7 @@ import {validateHttp} from '@angular/forms/signals'; validateHttp(schemaPath.field, { request: ({value}) => `/api/validate?field=${value()}`, - onSuccess: (result) => { + onSuccess: (result: {valid: boolean; message?: string}) => { if (result.valid) return null; // Use server message when available return { diff --git a/adev-ja/src/content/guide/forms/signals/designing-your-form-model.md b/adev-ja/src/content/guide/forms/signals/designing-your-form-model.md index 750d8ac170..f851af4d4a 100644 --- a/adev-ja/src/content/guide/forms/signals/designing-your-form-model.md +++ b/adev-ja/src/content/guide/forms/signals/designing-your-form-model.md @@ -211,9 +211,9 @@ interface BillPayFormModel { const billPaySchema = schema((billPay) => { // Hide credit card details when user has selected a method other than credit card. - hidden(billPay.method.card, ({valueOf}) => valueOf(billPay.method.type) !== 'card'); + hidden(billPay.method.card, {when: ({valueOf}) => valueOf(billPay.method.type) !== 'card'}); // Hide bank account details when user has selected a method other than bank account. - hidden(billPay.method.bank, ({valueOf}) => valueOf(billPay.method.type) !== 'bank'); + hidden(billPay.method.bank, {when: ({valueOf}) => valueOf(billPay.method.type) !== 'bank'}); }); ``` @@ -358,7 +358,7 @@ class MyForm { protected readonly myForm = form(this.formModel, (root) => { // Disable the entire form when the resource is loading. - disabled(root, () => this.domainModelResource.isLoading()); + disabled(root, {when: () => this.domainModelResource.isLoading()}); }); } ``` 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 181c927015..8ff94d7512 100644 --- a/adev-ja/src/content/guide/forms/signals/form-logic.md +++ b/adev-ja/src/content/guide/forms/signals/form-logic.md @@ -14,12 +14,12 @@ Use rules when field behavior depends on other field values or needs to update r ## How rules work -Rules bind reactive logic to specific fields in your form. Most rules accept a reactive logic function as an optional argument. The reactive logic function automatically recomputes whenever the signals it references change, just like a `computed`. +Rules bind reactive logic to specific fields in your form. Most conditional rules accept an options object with a `when` function. The `when` function automatically recomputes whenever the signals it references change, just like a `computed`. ```ts const orderForm = form(this.orderModel, (schemaPath) => { - disabled(schemaPath.couponCode, ({valueOf}) => valueOf(schemaPath.total) < 50); - //~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + disabled(schemaPath.couponCode, {when: ({valueOf}) => valueOf(schemaPath.total) < 50}); + //~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //rule path reactive logic function }); ``` @@ -70,7 +70,7 @@ export class Settings { ### Conditional disabling -To disable a field based on conditions, provide a reactive logic function that returns `true` (disabled) or `false` (enabled): +To disable a field based on conditions, provide a `when` function that returns `true` (disabled) or `false` (enabled): ```angular-ts import {Component, signal} from '@angular/core'; @@ -98,7 +98,7 @@ export class Order { }); orderForm = form(this.orderModel, (schemaPath) => { - disabled(schemaPath.couponCode, ({valueOf}) => valueOf(schemaPath.total) < 50); + disabled(schemaPath.couponCode, {when: ({valueOf}) => valueOf(schemaPath.total) < 50}); }); } ``` @@ -143,14 +143,15 @@ export class Order { }); orderForm = form(this.orderModel, (schemaPath) => { - disabled(schemaPath.couponCode, ({valueOf}) => - valueOf(schemaPath.total) < 50 ? 'Order must be $50 or more to use a coupon' : false, - ); + disabled(schemaPath.couponCode, { + when: ({valueOf}) => + valueOf(schemaPath.total) < 50 ? 'Order must be $50 or more to use a coupon' : false, + }); }); } ``` -The reactive logic function returns: +The `when` function returns: - A **string** to disable the field with a reason - `false` to enable the field (not just any falsy value - use `false` explicitly) @@ -163,12 +164,13 @@ You can also call `disabled()` multiple times on the same field, and all of the ```angular-ts orderForm = form(this.orderModel, (schemaPath) => { - disabled(schemaPath.promoCode, ({valueOf}) => - !valueOf(schemaPath.hasAccount) ? 'You must have an account to use promo codes' : false, - ); - disabled(schemaPath.promoCode, ({valueOf}) => - valueOf(schemaPath.total) < 25 ? 'Order must be at least $25' : false, - ); + disabled(schemaPath.promoCode, { + when: ({valueOf}) => + !valueOf(schemaPath.hasAccount) ? 'You must have an account to use promo codes' : false, + }); + disabled(schemaPath.promoCode, { + when: ({valueOf}) => (valueOf(schemaPath.total) < 25 ? 'Order must be at least $25' : false), + }); }); ``` @@ -184,7 +186,7 @@ NOTE: Like disabled fields, hidden fields also skip validation. See the [Validat ### Basic field hiding -Use `hidden()` with a reactive logic function that returns `true` (hidden) or `false` (visible): +Use `hidden()` with a `when` function that returns `true` (hidden) or `false` (visible): ```angular-ts import {Component, signal} from '@angular/core'; @@ -214,7 +216,7 @@ export class Profile { }); profileForm = form(this.profileModel, (schemaPath) => { - hidden(schemaPath.publicUrl, ({valueOf}) => !valueOf(schemaPath.isPublic)); + hidden(schemaPath.publicUrl, {when: ({valueOf}) => !valueOf(schemaPath.isPublic)}); }); } ``` @@ -264,7 +266,7 @@ The `[FormField]` directive automatically binds the `readonly` attribute based o ### Conditional readonly -To make a field readonly based on conditions, provide a reactive logic function: +To make a field readonly based on conditions, provide a `when` function: ```angular-ts import {Component, signal} from '@angular/core'; @@ -292,7 +294,7 @@ export class Document { }); documentForm = form(this.documentModel, (schemaPath) => { - readonly(schemaPath.title, ({valueOf}) => valueOf(schemaPath.isLocked)); + readonly(schemaPath.title, {when: ({valueOf}) => valueOf(schemaPath.isLocked)}); }); } ``` @@ -478,7 +480,7 @@ import {form, FormField, required, min, max} from '@angular/forms/signals'; imports: [FormField], template: ` @@ -519,12 +521,8 @@ import {form, FormField, max} from '@angular/forms/signals'; `, }) @@ -580,10 +578,12 @@ export class Promo { }); promoForm = form(this.promoModel, (schemaPath) => { - disabled(schemaPath.promoCode, ({valueOf}) => - !valueOf(schemaPath.hasAccount) ? 'You must have an account' : false, - ); - hidden(schemaPath.promoCode, ({valueOf}) => valueOf(schemaPath.subscriptionType) === 'free'); + disabled(schemaPath.promoCode, { + when: ({valueOf}) => (!valueOf(schemaPath.hasAccount) ? 'You must have an account' : false), + }); + hidden(schemaPath.promoCode, { + when: ({valueOf}) => valueOf(schemaPath.subscriptionType) === 'free', + }); debounce(schemaPath.promoCode, 300); metadata(schemaPath.promoCode, PLACEHOLDER, () => 'Enter promo code'); }); diff --git a/adev-ja/src/content/guide/forms/signals/migration.md b/adev-ja/src/content/guide/forms/signals/migration.md index e093cc1121..0dcea029ee 100644 --- a/adev-ja/src/content/guide/forms/signals/migration.md +++ b/adev-ja/src/content/guide/forms/signals/migration.md @@ -347,7 +347,7 @@ export class UserProfile { readonly emailControl = new SignalFormControl('', (p) => { // The control becomes disabled whenever isLoading is true - disabled(p, () => this.isLoading()); + disabled(p, {when: () => this.isLoading()}); }); async saveData() { diff --git a/adev-ja/src/content/guide/forms/signals/schemas.md b/adev-ja/src/content/guide/forms/signals/schemas.md index a3006f5815..1a21ffee5c 100644 --- a/adev-ja/src/content/guide/forms/signals/schemas.md +++ b/adev-ja/src/content/guide/forms/signals/schemas.md @@ -4,13 +4,13 @@ Signal Forms uses a two-layer architecture to separate _how your form is structu When you pass a schema function to `form()`, that function _runs once_ during form creation. Its job is to set up the form's logic tree by declaring which fields have validation, which fields are disabled, and which fields depend on other fields. This is the **structural layer** of your form. -Inside a schema function, you call rule functions such as `disabled()` and `validate()`. These rule functions accept reactive logic that recomputes whenever the signals they reference change. Other rules like `required()` accept optional configuration, including a `when` function that conditionally activates the rule. Together, these form the **behavioral layer** of your form during runtime. +Inside a schema function, you call rule functions such as `disabled()` and `validate()`. These rule functions accept reactive logic that recomputes whenever the signals they reference change. Conditional rules like `disabled()` and `required()` accept optional configuration, including a `when` function that activates the rule. Together, these form the **behavioral layer** of your form during runtime. ```ts contactForm = form(this.contactModel, (schemaPath) => { // Schema function: runs ONCE during form creation required(schemaPath.name); - disabled(schemaPath.couponCode, ({valueOf}) => valueOf(schemaPath.total) < 50); + disabled(schemaPath.couponCode, {when: ({valueOf}) => valueOf(schemaPath.total) < 50}); // ^^^ Reactive logic: recomputes when total changes }); ``` @@ -173,14 +173,14 @@ function isBankTransfer(value: PaymentMethod): value is BankTransfer { } paymentForm = form(this.paymentModel, (schemaPath) => { - applyWhenValue(schemaPath.payment, isCreditCard, (payment) => { + applyWhenValue(schemaPath, isCreditCard, (payment) => { // TypeScript knows payment is scoped to CreditCard required(payment.cardNumber); required(payment.expiry); required(payment.cvv); }); - applyWhenValue(schemaPath.payment, isBankTransfer, (payment) => { + applyWhenValue(schemaPath, isBankTransfer, (payment) => { // TypeScript knows payment is scoped to BankTransfer required(payment.accountNumber); required(payment.routingNumber); diff --git a/adev-ja/src/content/guide/http/http-resource.md b/adev-ja/src/content/guide/http/http-resource.md index 1c6623fc48..a20d691991 100644 --- a/adev-ja/src/content/guide/http/http-resource.md +++ b/adev-ja/src/content/guide/http/http-resource.md @@ -1,7 +1,5 @@ # Reactive data fetching with `httpResource` -IMPORTANT: `httpResource` is [experimental](reference/releases#experimental). It's ready for you to try, but it might change before it is stable. - `httpResource` is a reactive wrapper around `HttpClient` that gives you the request status and response as signals. You can thus use these signals with `computed`, `effect`, `linkedSignal`, or any other reactive API. Because it's built on top of `HttpClient`, `httpResource` supports all the same features, such as interceptors. For more about Angular's `resource` pattern, see [Async reactivity with `resource`](/guide/signals/resource). diff --git a/adev-ja/src/content/guide/i18n/merge.md b/adev-ja/src/content/guide/i18n/merge.md index a78d663260..2a819e5ce1 100644 --- a/adev-ja/src/content/guide/i18n/merge.md +++ b/adev-ja/src/content/guide/i18n/merge.md @@ -31,10 +31,12 @@ Use the `i18n` project option in the [`angular.json`][GuideWorkspaceConfig] work The following sub-options identify the source language and tell the compiler where to find supported translations for the project. -| Suboption | Details | -| :------------- | :--------------------------------------------------------------------------- | -| `sourceLocale` | The locale you use within the application source code \(`en-US` by default\) | -| `locales` | A map of locale identifiers to translation files | +| Suboption | Details | +| :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sourceLocale` | The locale you use within the application source code \(`en-US` by default\). Can also be an object with `code`, `baseHref`, and `subPath` properties. | +| `locales` | A map of locale identifiers to translation files. Each entry can also be an object with `translation`, `baseHref`, and `subPath` properties. | + +For the full list of `i18n` properties and their types, see [i18n options][GuideWorkspaceConfigI18n]. ### `angular.json` for `en-US` and `fr` example @@ -149,3 +151,4 @@ TLDR: Compile once, then translate for each locale. [GuideI18nCommonMergeGenerateApplicationVariantsForEachLocale]: guide/i18n/merge#generate-application-variants-for-each-locale 'Generate application variants for each locale - Merge translations into the application | Angular' [GuideI18nCommonTranslationFilesChangeTheSourceLanguageFileFormat]: guide/i18n/translation-files#change-the-source-language-file-format 'Change the source language file format - Work with translation files | Angular' [GuideWorkspaceConfig]: reference/configs/workspace-config 'Angular workspace configuration | Angular' +[GuideWorkspaceConfigI18n]: reference/configs/workspace-config#i18n-options 'i18n options - Angular workspace configuration | Angular' diff --git a/adev-ja/src/content/guide/i18n/prepare.md b/adev-ja/src/content/guide/i18n/prepare.md index 071fdfbc55..b2fc6175ba 100644 --- a/adev-ja/src/content/guide/i18n/prepare.md +++ b/adev-ja/src/content/guide/i18n/prepare.md @@ -48,6 +48,26 @@ The following example shows the `` element transformed into a non- +### Name the interpolation placeholder + +By default, Angular generates a placeholder name for each interpolation in a translated message. To give it a meaningful name that helps translators understand the context, add an `//i18n(ph="name")` comment inside the interpolation. + +```html +{{ expression //i18n(ph="placeholder_name") }} +``` + +For example: + +```html +

Hello, {{ username //i18n(ph="name") }}!

+``` + +This is the template equivalent of naming a placeholder in component code with [`$localize`][ApiLocalizeInitLocalize]: + +```ts +$localize`Hello, ${username}:name:!`; +``` + ## Mark element attributes for translations In a component template, the i18n metadata is the value of the `i18n-{attribute_name}` attribute. diff --git a/adev-ja/src/content/guide/signals/debounced.md b/adev-ja/src/content/guide/signals/debounced.md new file mode 100644 index 0000000000..146b798e02 --- /dev/null +++ b/adev-ja/src/content/guide/signals/debounced.md @@ -0,0 +1,85 @@ +# Debouncing signals with `debounced` + +IMPORTANT: `debounced` is [experimental](reference/releases#experimental). It's ready for you to try, but it might change before it is stable. + +Use `debounced` to delay reacting to a signal's value until it stops changing. It returns a `Resource` whose value reflects the debounced value of the source signal. + +```angular-ts +import {debounced, resource, signal} from '@angular/core'; + +@Component({ + template: ` + + + @if (results.isLoading()) { +

Searching…

+ } + @for (item of results.value(); track item.id) { +
  • {{ item.name }}
  • + } + `, +}) +export class Search { + query = signal(''); + + debouncedQuery = debounced(this.query, 300); + + results = resource({ + params: () => this.debouncedQuery.value(), + loader: ({params}) => fetchResults(params), + }); +} +``` + +`debounced` takes the source signal and a wait duration in milliseconds. The returned resource's `value()` always contains the last settled value, and `status()` tells you whether a new value is still pending. + +## Status during debounce + +While the debounce timer is counting down, `status()` is `'loading'` and `value()` returns the previously resolved value. When the timer expires, the resource settles to `'resolved'`. If the source signal throws, the resource enters `'error'` immediately no timer runs. + +See [Resource status](/guide/signals/resource#resource-status) for the full list of statuses and their `value()` behavior. + +## Custom wait function + +Instead of a millisecond duration, you can pass a function that returns a `Promise`. The resource resolves when the promise resolves. If the source signal changes before the promise settles, Angular discards the previous promise and starts a new one. + +```ts +debouncedQuery = debounced(query, (value, lastSnapshot) => { + // Retry immediately after an error rather than making the user wait again. + if (lastSnapshot.status === 'error') return; + // Short queries get a longer delay—the user is likely still typing. + const ms = value.length < 3 ? 500 : 200; + return new Promise((resolve) => setTimeout(resolve, ms)); +}); +``` + +See the `DebounceTimer` type in the API reference for details. + +## Equality + +By default, `debounced` uses `Object.is` to compare values. + +Provide a custom equality function with the `equal` option when the default identity check is too strict: + +```ts +debouncedFilter = debounced(filter, 200, { + equal: (a, b) => a.category === b.category && a.minPrice === b.minPrice, +}); +``` + +## Injection context + +`debounced` must be called inside an [injection context](guide/di/dependency-injection-context). Angular automatically destroys the debounced resource and cancels any pending timer when the injector is destroyed. + +To use `debounced` outside of an injection context, pass an explicit `Injector` via the options: + +```ts +@Service() +export class SearchService { + private injector = inject(Injector); + + createDebouncedQuery(query: Signal): Resource { + return debounced(query, 300, {injector: this.injector}); + } +} +``` diff --git a/adev-ja/src/content/guide/testing/services.md b/adev-ja/src/content/guide/testing/services.md index d1273f340e..1da612b18f 100644 --- a/adev-ja/src/content/guide/testing/services.md +++ b/adev-ja/src/content/guide/testing/services.md @@ -9,9 +9,9 @@ This guide uses [Vitest](https://vitest.dev/), which Angular CLI projects includ Consider a `Calculator` service that performs basic arithmetic: ```ts { header: 'calculator.ts' } -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class Calculator { add(a: number, b: number): number { return a + b; @@ -58,9 +58,9 @@ Most services depend on other services to run properly. By default, `TestBed` pr Consider an `OrderTotal` service that relies on a `TaxCalculator` to compute the final price of an order: ```ts { header: 'tax-calculator.ts' } -import {Injectable} from '@angular/core'; +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class TaxCalculator { calculate(subtotal: number): number { return subtotal * 0.05; @@ -69,10 +69,10 @@ export class TaxCalculator { ``` ```ts { header: 'order-total.ts' } -import {inject, Injectable} from '@angular/core'; +import {inject, Service} from '@angular/core'; import {TaxCalculator} from './tax-calculator'; -@Injectable({providedIn: 'root'}) +@Service() export class OrderTotal { private taxCalculator = inject(TaxCalculator); 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 3a5e57ca78..88ab09179d 100644 --- a/adev-ja/src/content/reference/configs/angular-compiler-options.md +++ b/adev-ja/src/content/reference/configs/angular-compiler-options.md @@ -130,15 +130,6 @@ For example, if a library uses the `public_api.ts` file as the library index of The `flatModuleOutFile` option could then be set, for example, to `"index.js"`, which produces `index.d.ts` and `index.metadata.json` files. The `module` field of the library's `package.json` would be `"index.js"` and the `typings` field would be `"index.d.ts"`. -### `fullTemplateTypeCheck` - -When `true`, the recommended value, enables the binding expression validation phase of the template compiler. This phase uses TypeScript to verify binding expressions. -For more information, see [Template type checking](tools/cli/template-typecheck). - -Default is `false`, but when you use the Angular CLI command `ng new --strict`, it is set to `true` in the new project's configuration. - -IMPORTANT: The `fullTemplateTypeCheck` option has been deprecated in Angular 13 in favor of the `strictTemplates` family of compiler options. - ### `generateCodeForLibraries` When `true`, creates factory files \(`.ngfactory.js` and `.ngstyle.js`\) for `.d.ts` files with a corresponding `.metadata.json` file. The default value is `true`. diff --git a/adev-ja/src/content/reference/configs/workspace-config.md b/adev-ja/src/content/reference/configs/workspace-config.md index 151c4b8b79..322310abd8 100644 --- a/adev-ja/src/content/reference/configs/workspace-config.md +++ b/adev-ja/src/content/reference/configs/workspace-config.md @@ -62,14 +62,46 @@ The following properties are a set of options that customize the Angular CLI. The following top-level configuration properties are available for each project, under `projects['project-name']`. -| Property | Details | Value type | Default value | -| :------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :-------------- | -| `root` | The root directory for this project's files, relative to the workspace directory. Empty for the initial application, which resides at the top level of the workspace. | `string` | None (required) | -| `projectType` | One of "application" or "library" An application can run independently in a browser, while a library cannot. | `application` \| `library` | None (required) | -| `sourceRoot` | The root directory for this project's source files. | `string` | `''` | -| `prefix` | A string that Angular prepends to selectors when generating new components, directives, and pipes using `ng generate`. Can be customized to identify an application or feature area. | `string` | `'app'` | -| `schematics` | A set of schematics that customize the `ng generate` sub-command option defaults for this project. See the [Generation schematics](#schematics) section. | See [schematics](#schematics) | `{}` | -| `architect` | Configuration defaults for Architect builder targets for this project. | See [Configuring builder targets](#configuring-builder-targets) | `{}` | +| Property | Details | Value type | Default value | +| :------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------- | :-------------- | +| `root` | The root directory for this project's files, relative to the workspace directory. Empty for the initial application, which resides at the top level of the workspace. | `string` | None (required) | +| `projectType` | One of "application" or "library" An application can run independently in a browser, while a library cannot. | `application` \| `library` | None (required) | +| `sourceRoot` | The root directory for this project's source files. | `string` | `''` | +| `prefix` | A string that Angular prepends to selectors when generating new components, directives, and pipes using `ng generate`. Can be customized to identify an application or feature area. | `string` | `'app'` | +| `i18n` | Internationalization options for the project. Defines the source locale and additional locales to build. See [Define locales in the build configuration](guide/i18n/merge#define-locales-in-the-build-configuration). | See [i18n options](#i18n-options) | `{}` | +| `schematics` | A set of schematics that customize the `ng generate` sub-command option defaults for this project. See the [Generation schematics](#schematics) section. | See [schematics](#schematics) | `{}` | +| `architect` | Configuration defaults for Architect builder targets for this project. | See [Configuring builder targets](#configuring-builder-targets) | `{}` | + +## i18n options + +Use the `i18n` project option to define the application's source locale and any additional locales to build. + +| Property | Details | Value type | Default value | +| :------------- | :----------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :------------ | +| `sourceLocale` | The locale used in the application source code. Can be a locale identifier string or a [configuration object](#sourcelocale-object). | `string` \| [sourceLocale object](#sourcelocale-object) | `"en-US"` | +| `locales` | A map of locale identifiers to translation files or [locale configuration objects](#locale-object). | `object` | `{}` | + +### `sourceLocale` object + +Pass an object instead of a string to customize the output directory or base HREF for the source locale: + +| Property | Details | Value type | Default value | +| :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :--------- | :------------ | +| `code` | The source locale identifier. | `string` | `"en-US"` | +| `baseHref` | Overrides the HTML `` for this locale. The output directory name stays as the locale code. Cannot be used together with `subPath`. | `string` | Locale code | +| `subPath` | Sets both the output directory name and the HTML `` for this locale. Cannot be used together with `baseHref`. | `string` | Locale code | + +### Locale object + +Each `locales` entry can be a path string, an array of paths, or an object: + +| Property | Details | Value type | Default value | +| :------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------- | :------------ | +| `translation` | Path or paths to the translation file(s) for this locale. | `string` \| `string[]` | | +| `baseHref` | Overrides the HTML `` for this locale. The output directory name stays as the locale identifier. Cannot be used together with `subPath`. | `string` | Locale code | +| `subPath` | Sets both the output directory name and the HTML `` for this locale. Cannot be used together with `baseHref`. | `string` | Locale code | + +HELPFUL: Use `subPath` rather than `baseHref` when you also need to rename the output directory — for example, to output to `de-DE/` instead of `de/`. ## Schematics diff --git a/adev-ja/src/content/reference/errors/NG0204.md b/adev-ja/src/content/reference/errors/NG0204.md index 55077d2dda..4bcd7a4c1d 100644 --- a/adev-ja/src/content/reference/errors/NG0204.md +++ b/adev-ja/src/content/reference/errors/NG0204.md @@ -4,28 +4,28 @@ This error occurs when Angular cannot resolve a dependency for a class during de The most common causes are: -1. A service class is missing the `@Injectable()` decorator +1. A service class is missing the `@Service()` or the `@Injectable` decorator 2. An `InjectionToken` lacks a proper provider definition 3. A constructor parameter cannot be resolved -NOTE: The `inject()` function takes an explicit token, so the "unresolvable parameter" scenario does not apply to it directly. However, if the injected class itself is missing `@Injectable()` and has its own constructor dependencies, the error can still occur. +NOTE: The `inject()` function takes an explicit token, so the "unresolvable parameter" scenario does not apply to it directly. However, if the injected class itself is missing `@Service()` and has its own constructor dependencies, the error can still occur. ## Common scenarios -### Missing `@Injectable()` decorator +### Missing `@Service()` decorator -When a class has constructor dependencies but lacks the `@Injectable()` decorator, Angular cannot resolve its parameters: +When a class has constructor dependencies but lacks the `@Service()` decorator, Angular cannot resolve its parameters: -```ts {header: 'Missing @Injectable() decorator'} +```ts {header: 'Missing @Service() decorator'} export class UserClient { constructor(private http: HttpClient) {} // Angular can't resolve this } ``` -Add the `@Injectable()` decorator to fix this: +Add the `@Service()` decorator to fix this: ```ts -@Injectable({providedIn: 'root'}) +@Service() export class UserClient { constructor(private http: HttpClient) {} } @@ -36,7 +36,7 @@ export class UserClient { This error also appears when Angular cannot determine the type of a constructor parameter: ```ts -@Injectable({providedIn: 'root'}) +@Service() export class DataStore { // Angular can't resolve 'config' without a provider constructor(private config: AppConfig) {} @@ -49,11 +49,11 @@ Ensure all constructor parameters either have providers configured or use `@Opti The error message includes details about which token could not be resolved: -- `Can't resolve all parameters for X: (?, ?, ?)` — The `?` marks indicate unresolvable parameters. Check that the class has `@Injectable()` and all dependencies have providers. +- `Can't resolve all parameters for X: (?, ?, ?)` — The `?` marks indicate unresolvable parameters. Check that the class has `@Service` or `@Injectable()` and all dependencies have providers. - `Token X is missing a ɵprov definition` — An `InjectionToken` was used without configuring a provider. Register the token with a value using `{provide: TOKEN, useValue: ...}` or add a default factory to the token definition. Work backwards from the error's stack trace to identify where the problematic injection occurs, then verify that: -1. The class has `@Injectable()` decorator +1. The class has a `@Service` or `@Injectable()` decorator 2. All constructor parameters have registered providers 3. Any `InjectionToken` has a configured provider or default value diff --git a/adev-ja/src/content/reference/errors/NG0919.md b/adev-ja/src/content/reference/errors/NG0919.md index 65a18558be..a45d6dd86c 100644 --- a/adev-ja/src/content/reference/errors/NG0919.md +++ b/adev-ja/src/content/reference/errors/NG0919.md @@ -58,10 +58,10 @@ ComponentA -> ComponentB -> ComponentC -> ComponentA Move shared functionality to a separate file that doesn't import either component: -```angular-ts {header:"shared.service.ts"} -import {Injectable} from '@angular/core'; +```ts {header:"shared.service.ts"} +import {Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class SharedService { // Shared logic here } diff --git a/adev-ja/src/content/reference/errors/NG8023.md b/adev-ja/src/content/reference/errors/NG8023.md new file mode 100644 index 0000000000..1cbdb86b6d --- /dev/null +++ b/adev-ja/src/content/reference/errors/NG8023.md @@ -0,0 +1,47 @@ +# Multiple Components Match Same Element + +Two or more [components](guide/components) in the compilation scope match the same element in a template. +Because Angular can associate only one component with a given element, selectors must be unique enough to prevent ambiguity. + +NOTE: This is the build-time equivalent of the runtime error [NG0300: Selector Collision](errors/NG0300). Detecting this at compile time means the error surfaces immediately. + +```angular-ts +import {Component} from '@angular/core'; + +@Component({ + selector: '[stroked-button]', + template: '', +}) +export class StrokedBtn {} + +@Component({ + selector: '[raised-button]', + template: '', +}) +export class RaisedBtn {} + +@Component({ + selector: 'app-root', + // NG8023: StrokedBtn and RaisedBtn both match this element. + template: ``, + imports: [StrokedBtn, RaisedBtn], +}) +export class App {} +``` + +## Debugging the error + +Use the element name from the error message to search for places where you're using the same selector declaration in your codebase: + +```ts + +@Component({ + selector: 'YOUR_STRING', +… +}) + +``` + +Ensure that each component has a unique CSS selector. This will guarantee that Angular renders the component you expect. + +If you're having trouble finding multiple components with this selector tag name, check for components from imported component libraries, such as Angular Material. Make sure you're following the [best practices](style-guide#choosing-component-selectors) for your selectors to prevent collisions. diff --git a/adev-ja/src/content/reference/errors/NG8024.md b/adev-ja/src/content/reference/errors/NG8024.md new file mode 100644 index 0000000000..bb73480d64 --- /dev/null +++ b/adev-ja/src/content/reference/errors/NG8024.md @@ -0,0 +1,51 @@ +# Conflicting Host Directive Binding + +Two or more host directive declarations expose the **same input or output under different aliases**, +making it impossible for Angular to merge the duplicate host directive instances. + +This error arises from the [diamond composition pattern](guide/directives/directive-composition-api#host-directive-de-duplication): +two directives both declare the same shared directive in their `hostDirectives`, but each exposes +one of its bindings under a different alias name. + +```angular-ts +@Directive() +export class Shared { + readonly value = input(''); +} + +@Directive({ + selector: '[dirA]', + hostDirectives: [{directive: Shared, inputs: ['value: aliasA']}], +}) +export class DirA {} + +@Directive({ + selector: '[dirB]', + hostDirectives: [{directive: Shared, inputs: ['value: aliasB']}], // different alias! +}) +export class DirB {} + +@Component({ + selector: 'app-root', + // NG8024: Shared.value is exposed as both "aliasA" (via DirA) and "aliasB" (via DirB). + template: `
    `, + imports: [DirA, DirB], +}) +export class AppComponent {} +``` + +## Debugging the error + +The error message identifies the directive and the input or output that is conflicting. Find every +`hostDirectives` declaration that includes that shared directive and inspect the `inputs` or +`outputs` arrays for the conflicting binding. + +To resolve the error, pick one of the following approaches: + +- **Use the same alias in both places.** Both host directive declarations must expose the binding + under the same public name. +- **Remove the alias from one or both declarations.** If neither declaration needs to expose the + binding to consumers, omit it from the `inputs` or `outputs` arrays entirely. + +See [Host directive de-duplication](guide/directives/directive-composition-api#host-directive-de-duplication) +for more information on how Angular merges duplicate host directives and what constitutes a conflict. diff --git a/adev-ja/src/content/reference/extended-diagnostics/NG8021.md b/adev-ja/src/content/reference/extended-diagnostics/NG8021.md index 2052b20c96..e720b29918 100644 --- a/adev-ja/src/content/reference/extended-diagnostics/NG8021.md +++ b/adev-ja/src/content/reference/extended-diagnostics/NG8021.md @@ -29,9 +29,7 @@ This diagnostic flags the following problematic patterns: ### `immediate` with prefetch triggers -**Bad — prefetch never runs** - -```angular-ts +```angular-ts {avoid, header: "prefetch never runs"} @Component({ template: ` @defer (on immediate; prefetch on idle) { @@ -42,9 +40,7 @@ This diagnostic flags the following problematic patterns: class MyComponent {} ``` -**Good — remove redundant prefetch** - -```angular-ts +```angular-ts {prefer, header: "remove redundant prefetch"} @Component({ template: ` @defer (on immediate) { @@ -57,9 +53,7 @@ class MyComponent {} ### Prefetch timer not earlier than main timer -**Bad — prefetch is later than main** - -```angular-ts +```angular-ts {avoid, header: "prefetch is later than main"} @Component({ template: ` @defer (on timer(100ms); prefetch on timer(3000ms)) { @@ -70,9 +64,7 @@ class MyComponent {} class MyComponent {} ``` -**Bad — equal timing provides no benefit** - -```angular-ts +```angular-ts {avoid, header: "equal timing provides no benefit"} @Component({ template: ` @defer (on timer(500ms); prefetch on timer(500ms)) { @@ -83,9 +75,7 @@ class MyComponent {} class MyComponent {} ``` -**Good — prefetch fires earlier** - -```angular-ts +```angular-ts {prefer, header: "prefetch fires earlier"} @Component({ template: ` @defer (on timer(1000ms); prefetch on timer(500ms)) { @@ -96,11 +86,39 @@ class MyComponent {} class MyComponent {} ``` -### Identical prefetch and main triggers +### Prefetch without main triggers + +```angular-ts {avoid, header: "unexpected early fetch"} +@Component({ + template: ` + @defer (prefetch when someFlag()) { + + } + `, +}) +class MyComponent { + someFlag = signal(false); +} +``` -**Bad — identical viewport trigger** +This configuration may suggest that the prefetch will only run when `someFlag` becomes true. However, since the main trigger still defaults to `on idle`, the deferred content can be fetched earlier during the browser’s idle period, effectively bypassing the intended condition. -```angular-ts +```angular-ts {prefer, header: "explicitly define main trigger"} +@Component({ + template: ` + @defer (on interaction; prefetch when someFlag()) { + + } + `, +}) +class MyComponent { + someFlag = signal(false); +} +``` + +### Identical prefetch and main triggers + +```angular-ts {avoid, header: "identical viewport trigger"} @Component({ template: ` @defer (on viewport; prefetch on viewport) { @@ -111,9 +129,7 @@ class MyComponent {} class MyComponent {} ``` -**Bad — identical interaction target** - -```angular-ts +```angular-ts {avoid, header: "identical interaction target"} @Component({ template: ` @@ -125,9 +141,7 @@ class MyComponent {} class MyComponent {} ``` -**Good — remove redundant prefetch** - -```angular-ts +```angular-ts {prefer, header: "remove redundant prefetch"} @Component({ template: ` @@ -139,9 +153,7 @@ class MyComponent {} class MyComponent {} ``` -**Good — use different targets for prefetch and main** - -```angular-ts +```angular-ts {prefer, header: "use different targets for prefetch and main"} @Component({ template: `
    Hover to prefetch
    diff --git a/adev-ja/src/content/reference/migrations/route-lazy-loading.md b/adev-ja/src/content/reference/migrations/route-lazy-loading.md index 5be512dac6..bd2b3e58da 100644 --- a/adev-ja/src/content/reference/migrations/route-lazy-loading.md +++ b/adev-ja/src/content/reference/migrations/route-lazy-loading.md @@ -25,7 +25,6 @@ The schematic will attempt to find all the places where the application routes a - `RouterModule.forRoot` and `RouterModule.forChild` - `Router.resetConfig` - `provideRouter` -- `provideRoutes` - variables of type `Routes` or `Route[]` (e.g. `const routes: Routes = [{...}]`) The migration will check all the components in the routes, check if they are standalone and eagerly loaded, and if so, it will convert them to lazy loaded routes. diff --git a/adev-ja/src/content/tools/cli/aot-compiler.md b/adev-ja/src/content/tools/cli/aot-compiler.md index 88a5ccf732..3249387d58 100644 --- a/adev-ja/src/content/tools/cli/aot-compiler.md +++ b/adev-ja/src/content/tools/cli/aot-compiler.md @@ -369,7 +369,7 @@ And it does not interfere with the ES module's exported API. One of the Angular compiler's most helpful features is the ability to type-check expressions within templates, and catch any errors before they cause crashes at runtime. In the template type-checking phase, the Angular template compiler uses the TypeScript compiler to validate the binding expressions in templates. -Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's TypeScript configuration file +Enable this phase explicitly by adding the compiler option `"strictTemplates"` in the `"angularCompilerOptions"` of the project's TypeScript configuration file (see [Angular Compiler Options](reference/configs/angular-compiler-options)). Template validation produces error messages when a type error is detected in a template binding diff --git a/adev-ja/src/content/tools/cli/schematics-for-libraries.md b/adev-ja/src/content/tools/cli/schematics-for-libraries.md index eb59c775c4..39f9a8ab84 100644 --- a/adev-ja/src/content/tools/cli/schematics-for-libraries.md +++ b/adev-ja/src/content/tools/cli/schematics-for-libraries.md @@ -144,12 +144,10 @@ Schematic templates support special syntax to execute code and variable substitu ```ts {header:projects/my-lib/schematics/my-service/files/__name@dasherize__.service.ts.template (Schematic Template)} - import { Injectable } from '@angular/core'; + import { Service } from '@angular/core'; import { HttpClient } from '@angular/common/http'; - @Injectable({ - providedIn: 'root' - }) + @Service() export class <%= classify(name) %>Service { private http = inject(HttpClient); } diff --git a/adev-ja/src/content/tutorials/playground/5-aria-accordion/config.json b/adev-ja/src/content/tutorials/playground/5-aria-accordion/config.json new file mode 100644 index 0000000000..153951a267 --- /dev/null +++ b/adev-ja/src/content/tutorials/playground/5-aria-accordion/config.json @@ -0,0 +1,5 @@ +{ + "type": "editor-only", + "title": "Angular Aria Template", + "openFiles": ["src/main.ts", "src/main.css"] +} From 6a3d4ef61a70a0b0dd820c0cfba411a9154b660a Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:35:30 +0900 Subject: [PATCH 04/66] feat(docs): translate events/v22 to Japanese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Angular v22 リリースイベントページの日本語翻訳と英語スナップショットを追加。 --- adev-ja/src/content/events/v22.en.md | 32 ++++++++++++++++++++++++++++ adev-ja/src/content/events/v22.md | 32 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 adev-ja/src/content/events/v22.en.md create mode 100644 adev-ja/src/content/events/v22.md diff --git a/adev-ja/src/content/events/v22.en.md b/adev-ja/src/content/events/v22.en.md new file mode 100644 index 0000000000..18ec2ae2aa --- /dev/null +++ b/adev-ja/src/content/events/v22.en.md @@ -0,0 +1,32 @@ +# Angular v22: Rock solid foundation for building what's next for the web + +![A digital illustration styled like a vintage postcard with a thin white border. The top left features the words "Welcome to" written in a cursive, yellow script font. The main focus is a large, brown rocky mountain in the center. Imposed on the mountain face is a 3D white text block reading "Angular V22" along with the Angular logo icon. Dense pine trees in shades of green, yellow, and grey line the foreground at the base of the mountain. A winding path trails up the right side of the mountain, and a few birds fly in the sunset-colored purple and orange sky.](assets/images/v22-event/angular-v22-hero.png {loading: 'eager', fetchpriority: 'high'} 'Angular v22 Hero Image') + +Angular v22 is ready for you to build modern, high-performance web applications. This release introduces key stabilization updates, template enhancements, and API improvements: + +- **Stabilized APIs**: Signal Forms, Asynchronous Signals, and Angular Aria are now stable, offering a production-ready reactive foundation. +- **Template enhancements**: New template features streamline development, improve ergonomics, and enhance code clarity. +- **API improvements**: Core APIs have been updated for better performance, simpler syntax, and more robust typing. +- **Angular AI integration**: Streamlined support and updates for AI-driven development workflows. + +## Key resources + + + + Discover all the major features, community updates, and design decisions behind Angular v22. + + + Review the detailed, commit-by-commit list of new features, bug fixes, and breaking changes. + + + Get instructions on how to update your project to Angular v22. + + + +## Developer event + +The v22 release video will be launching soon, [so tune in to the Angular YouTube Channel Friday, June 5 @ 9 AM Pacific](https://goo.gle/angular-v22-yt). + +In the meantime, you can watch the Angular v21 Developer Event: + + diff --git a/adev-ja/src/content/events/v22.md b/adev-ja/src/content/events/v22.md new file mode 100644 index 0000000000..d72b32722c --- /dev/null +++ b/adev-ja/src/content/events/v22.md @@ -0,0 +1,32 @@ +# Angular v22: 次世代Webを構築するための盤石な基盤 + +![白いふち取りのあるビンテージ風絵葉書のようなデジタルイラスト。左上には筆記体の黄色いスクリプトフォントで「Welcome to」と書かれている。中央には大きな茶色い岩山が描かれ、山肌にはAngularのロゴアイコンとともに「Angular V22」と読める3D白文字のブロックが重ねられている。手前には緑・黄・灰色の松の木が山の麓に密集して並び、右側には山頂へと続く曲がりくねった道がある。夕暮れの紫と橙の空には数羽の鳥が飛んでいる。](assets/images/v22-event/angular-v22-hero.png {loading: 'eager', fetchpriority: 'high'} 'Angular v22 Hero Image') + +Angular v22は、モダンで高性能なWebアプリケーションを構築するための準備を整えました。このリリースでは、主要な安定化のアップデート、テンプレートの拡張、APIの改善が導入されています。 + +- **安定化されたAPI**: シグナルフォーム、非同期シグナル、Angular Ariaが安定版となり、プロダクションレディなリアクティブ基盤を提供します。 +- **テンプレートの拡張**: 新しいテンプレート機能が、開発の効率化、エルゴノミクスの向上、コードの明確性の強化を実現します。 +- **APIの改善**: コアAPIが、より良いパフォーマンス、よりシンプルな構文、より堅牢な型付けを目指して更新されました。 +- **Angular AI統合**: AI駆動の開発ワークフローのための合理化されたサポートとアップデート。 + +## 主要リソース {#key-resources} + + + + Angular v22のすべての主要機能、コミュニティのアップデート、設計判断の背景を知ることができます。 + + + 新機能、バグ修正、破壊的変更の詳細なコミット単位のリストを確認できます。 + + + プロジェクトをAngular v22へアップデートする手順を確認できます。 + + + +## 開発者向けイベント {#developer-event} + +v22リリースビデオがまもなく公開されます。[6月5日(金)太平洋時間午前9時、Angular YouTubeチャンネルでご視聴ください](https://goo.gle/angular-v22-yt)。 + +それまでの間、Angular v21開発者向けイベントの動画をご覧いただけます。 + + From c509fcff2b1d138fa9465be7e07cdd74aad3a893 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:41:44 +0900 Subject: [PATCH 05/66] feat(docs): translate ai/webmcp to Japanese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebMCP (Web Model Context Protocol) の日本語翻訳と英語スナップショットを追加。 --- adev-ja/src/content/ai/webmcp.en.md | 259 ++++++++++++++++++++++++++++ adev-ja/src/content/ai/webmcp.md | 259 ++++++++++++++++++++++++++++ 2 files changed, 518 insertions(+) create mode 100644 adev-ja/src/content/ai/webmcp.en.md create mode 100644 adev-ja/src/content/ai/webmcp.md diff --git a/adev-ja/src/content/ai/webmcp.en.md b/adev-ja/src/content/ai/webmcp.en.md new file mode 100644 index 0000000000..b175a4dc85 --- /dev/null +++ b/adev-ja/src/content/ai/webmcp.en.md @@ -0,0 +1,259 @@ +# WebMCP + +Web Model Context Protocol (WebMCP) is an [emerging web standard](https://github.com/webmachinelearning/webmcp/) that allows web applications to expose structured tools directly to AI agents running natively in the browser. Tools defined by an application allow AI assistants to interact with it directly, providing additional capabilities to the agent and reducing the need for DOM interactions. + +For example, an application to register a new user might provide a WebMCP tool for a browser's AI agent to create the user directly rather than requiring the agent to go through a complex wizard UI via DOM interactions. + +Angular provides experimental support for WebMCP, allowing you to easily register tools tied to your application's dependency injection lifecycle and automatically turn your Signal Forms into AI-ready tools. + +IMPORTANT: The WebMCP spec is very early in its lifecycle and is undergoing frequent changes. As such, WebMCP support in Angular is currently [**experimental**](reference/releases#experimental). APIs are subject to change even outside of major versions. + +## Provide tools for the application + +Use [`provideExperimentalWebMcpTools`](api/core/provideExperimentalWebMcpTools) in your application config to register tools for the entire lifecycle of the application. Tools provided this way are automatically registered when the application initializes and unregistered when the application is destroyed. + +The `execute` callback is invoked in the injection context of the associated `Injector`, meaning you can [`inject`](api/core/inject) services directly. + +```ts {header:"main.ts"} +import {Service, inject, provideExperimentalWebMcpTools} from '@angular/core'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {AppRoot} from './app-root'; + +@Service() +class Greeter { + sayHello(): string { + return 'Hello agent!'; + } +} + +bootstrapApplication(AppRoot, { + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'greet', + description: 'Greets the agent.', + inputSchema: {type: 'object', properties: {}}, + execute: () => { + const greeter = inject(Greeter); + + return {content: [{type: 'text', text: greeter.sayHello()}]}; + }, + }, + ]), + ], +}); +``` + +### Define tool parameters + +When a tool requires input from the AI assistant, define the expected arguments inside `inputSchema` using [JSON Schema](https://json-schema.org/) syntax. Angular automatically infers the parameter types passed into your `execute` callback based on the schema definition. + +```ts {header:"main.ts"} +import {provideExperimentalWebMcpTools} from '@angular/core'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {AppRoot} from './app-root'; + +bootstrapApplication(AppRoot, { + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'searchCatalog', + description: 'Searches the store catalog for products matching a query.', + inputSchema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The search keywords.', + }, + maxResults: { + type: 'number', + description: 'Maximum number of results to return.', + }, + }, + required: ['query'], + additionalProperties: false, + }, + execute: ({query, maxResults}) => { + // Type of `query` is inferred as `string`. + // Type of `maxResults` is inferred as `number | undefined`. + + // Consider validating this at runtime, since inputs may not be validated to match the schema. + if (typeof query !== 'string') throw new Error(`Bad query: ${query}`); + if (typeof maxResults !== 'number' && maxResults !== undefined) + throw new Error(`Bad maxResults: ${maxResults}`); + + const limit = maxResults ?? 5; + return { + content: [{type: 'text', text: `Returning up to ${limit} results for "${query}".`}], + }; + }, + }, + ]), + ], +}); +``` + +TIP: Use `required: ['param1', 'param2', ...]` to remove `undefined` from the types of those parameters and use `additionalProperties: false` to restrict the argument object's type to only these parameters. + +## Provide tools for a route + +When building complex applications, you may only want certain tools available when the user is viewing specific routes. You can achieve this by providing tools directly in route definitions. + +```ts {header:"routes.ts"} +import {provideExperimentalWebMcpTools} from '@angular/core'; +import {Routes} from '@angular/router'; + +export const routes: Routes = [ + { + path: 'dashboard', + loadComponent: () => import('./dashboard').then((m) => m.Dashboard), + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'exportDashboardReports', + description: 'Exports the current dashboard analytics.', + inputSchema: {type: 'object', properties: {}}, + execute: () => ({ + content: [{type: 'text', text: 'Dashboard export successfully triggered.'}], + }), + }, + ]), + ], + }, +]; +``` + +NOTE: When registering tools to a particular route, consider configuring the router to use [`withExperimentalAutoCleanupInjectors`](api/router/withExperimentalAutoCleanupInjectors) to ensure tools are automatically _unregistered_ when the user navigates away from the route. Without this option, WebMCP tools declared on routes will remain accessible to AI agents even after the user has navigated to a different route. + +```ts {header:"app.config.ts"} +import {ApplicationConfig} from '@angular/core'; +import {provideRouter, withExperimentalAutoCleanupInjectors} from '@angular/router'; +import {routes} from './routes'; + +export const appConfig: ApplicationConfig = { + providers: [provideRouter(routes, withExperimentalAutoCleanupInjectors())], +}; +``` + +## Provide tools within services + +For dynamic use cases, the [`declareExperimentalWebMcpTool`](api/core/declareExperimentalWebMcpTool) function registers a tool directly within an injection context and automatically unregisters it when that context is destroyed. + +```ts {header:"counter.ts"} +import {Service, declareExperimentalWebMcpTool, signal, inject} from '@angular/core'; + +@Service() +export class Counter { + readonly count = signal(0); + + constructor() { + declareExperimentalWebMcpTool({ + name: 'getCounter', + description: 'Reads the global counter.', + inputSchema: {type: 'object', properties: {}}, + execute: () => ({ + content: [{type: 'text', text: `The count is: ${this.count()}.`}], + }), + }); + } +} +``` + +While `declareExperimentalWebMcpTool` works in any injection context, watch out for [name collisions](#name-collisions) and prefer using it in root services. + +## Implicit tools in Signal Forms + +You can create a WebMCP tool implicitly from an existing Angular [Signal Form](essentials/signal-forms) with minimal configuration. Angular converts your form models into rich WebMCP tools, effectively supporting highly dynamic forms without requiring you to manually write JSON schemas or event handlers. + +### Enable the WebMCP forms feature + +First, add [`provideExperimentalWebMcpForms`](api/forms/signals/provideExperimentalWebMcpForms) to your root application providers: + +```ts {header:"main.ts"} +import {bootstrapApplication} from '@angular/platform-browser'; +import {provideExperimentalWebMcpForms} from '@angular/forms/signals'; +import {AppRoot} from './app-root'; + +bootstrapApplication(AppRoot, { + providers: [provideExperimentalWebMcpForms()], +}); +``` + +### Opt in a Signal Form + +Second, when defining a Signal Form using [`form`](api/forms/signals/form), pass the `experimentalWebMcpTool` configuration option to opt-in to an implicit WebMCP tool. Angular will inspect your form's data model and automatically generate a JSON schema for connected AI agents. + +```ts {header:"user-registration.ts"} +import {Component, signal} from '@angular/core'; +import {form, required, minLength} from '@angular/forms/signals'; + +@Component({ + selector: 'app-user-registration', + templateUrl: './user-registration.html', +}) +export class UserRegistration { + private readonly model = signal({ + firstName: '', + lastName: '', + age: 0, + hobbies: ['Web Development'], + }); + + readonly userForm = form( + this.model, + (f) => { + required(f.firstName, {message: 'First name is mandatory.'}); + required(f.lastName, {message: 'Last name is mandatory.'}); + }, + { + // Implicitly registers a WebMCP tool named `registerUser` with parameters derived from `model`. + experimentalWebMcpTool: { + name: 'registerUser', + description: 'Registers a new user.', + }, + submission: { + action: async (formValue) => { + console.log('Submitting user:', formValue); + // ... + }, + }, + }, + ); +} +``` + +In this example, Angular generates a WebMCP tool with a JSON schema which: + +1. includes `firstName`, `lastName`, `age`, and `hobbies` as parameters inferred from the initial value of the `model` signal. +2. defines `firstName` and `lastName` as _required_ fields as inferred from the [`required`](api/forms/signals/required) validator. +3. defines `hobbies` as an array of strings, allowing the agent to provide an arbitrary amount of hobbies. + +Beyond inferring the input schema, Angular also connects the WebMCP tool to the form's validation logic and submission handler. This means the agent will observe any validation errors triggered by its inputs or any failures which happen during submission, allowing it to self-correct and potentially retry. + +NOTE: Async validators are _not_ triggered and should be handled by the submission action. + +#### Constraints + +Angular infers the WebMCP schema from the initial value of your form model. This requires: + +- Concrete initial values (`''`, `0`, `false`): Angular cannot infer data types from `null` or `undefined`. +- Non-empty arrays (`['Hello!']`): Angular cannot infer data types from an empty array and requires at least one initial value. + +## Best practices + +Keep the following best practices in mind: + +### Name collisions + +WebMCP requires each tool to have a unique name and will throw an error if the same tool name is registered multiple times. This means calling `declareExperimentalWebMcpTool` or `provideExperimentalWebMcpTools` in a context where they might be registered multiple times (such as a component constructor) may lead to errors at runtime. + +Prefer placing tools on application providers, route providers, or root services where possible. When putting tools on a component, including [implicit tools in Signal Forms](#implicit-tools-in-signal-forms), ensure that component is only ever rendered on the page at most _once_ at any given time. + +### Validate tool inputs + +Angular does not provide any implicit validation that the inputs provided by an agent actually match the defined JSON schema. Consider explicitly validating arguments to the `execute` function before using them to ensure reliability. + +### Testing + +Consider using a mock WebMCP implementation like [`@mcp-b/webmcp-polyfill`](https://www.npmjs.com/package/@mcp-b/webmcp-polyfill) to effectively unit test your tools. diff --git a/adev-ja/src/content/ai/webmcp.md b/adev-ja/src/content/ai/webmcp.md new file mode 100644 index 0000000000..a0dfe26049 --- /dev/null +++ b/adev-ja/src/content/ai/webmcp.md @@ -0,0 +1,259 @@ +# WebMCP + +Web Model Context Protocol (WebMCP)は、Webアプリケーションがブラウザ内でネイティブに実行されるAIエージェントに構造化されたツールを直接公開できるようにする[新興のWeb標準](https://github.com/webmachinelearning/webmcp/)です。アプリケーションによって定義されたツールにより、AIアシスタントはアプリケーションと直接対話できるようになり、エージェントに追加の機能を提供し、DOM操作の必要性を減らします。 + +例えば、新規ユーザーを登録するアプリケーションは、エージェントがDOM操作を通じて複雑なウィザードUIを操作することを要求する代わりに、ブラウザのAIエージェントがユーザーを直接作成するためのWebMCPツールを提供する場合があります。 + +AngularはWebMCPの実験的サポートを提供しており、アプリケーションの依存性の注入ライフサイクルに結びついたツールを簡単に登録し、シグナルフォームを自動的にAI対応ツールに変換できます。 + +IMPORTANT: WebMCP仕様はライフサイクルの非常に初期の段階にあり、頻繁に変更されています。そのため、AngularにおけるWebMCPサポートは現在[**実験的**](reference/releases#experimental)です。APIはメジャーバージョン以外でも変更される可能性があります。 + +## アプリケーションにツールを提供する {#provide-tools-for-the-application} + +アプリケーション設定で[`provideExperimentalWebMcpTools`](api/core/provideExperimentalWebMcpTools)を使用して、アプリケーションのライフサイクル全体にわたってツールを登録します。この方法で提供されたツールは、アプリケーションの初期化時に自動的に登録され、アプリケーションの破棄時に登録解除されます。 + +`execute`コールバックは関連付けられた`Injector`の注入コンテキストで呼び出されるため、サービスを直接[`inject`](api/core/inject)できます。 + +```ts {header:"main.ts"} +import {Service, inject, provideExperimentalWebMcpTools} from '@angular/core'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {AppRoot} from './app-root'; + +@Service() +class Greeter { + sayHello(): string { + return 'Hello agent!'; + } +} + +bootstrapApplication(AppRoot, { + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'greet', + description: 'Greets the agent.', + inputSchema: {type: 'object', properties: {}}, + execute: () => { + const greeter = inject(Greeter); + + return {content: [{type: 'text', text: greeter.sayHello()}]}; + }, + }, + ]), + ], +}); +``` + +### ツールパラメータを定義する {#define-tool-parameters} + +ツールがAIアシスタントからの入力を必要とする場合、[JSON Schema](https://json-schema.org/)構文を使用して`inputSchema`内に期待される引数を定義します。Angularはスキーマ定義に基づいて、`execute`コールバックに渡されるパラメータの型を自動的に推論します。 + +```ts {header:"main.ts"} +import {provideExperimentalWebMcpTools} from '@angular/core'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {AppRoot} from './app-root'; + +bootstrapApplication(AppRoot, { + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'searchCatalog', + description: 'Searches the store catalog for products matching a query.', + inputSchema: { + type: 'object', + properties: { + query: { + type: 'string', + description: 'The search keywords.', + }, + maxResults: { + type: 'number', + description: 'Maximum number of results to return.', + }, + }, + required: ['query'], + additionalProperties: false, + }, + execute: ({query, maxResults}) => { + // Type of `query` is inferred as `string`. + // Type of `maxResults` is inferred as `number | undefined`. + + // Consider validating this at runtime, since inputs may not be validated to match the schema. + if (typeof query !== 'string') throw new Error(`Bad query: ${query}`); + if (typeof maxResults !== 'number' && maxResults !== undefined) + throw new Error(`Bad maxResults: ${maxResults}`); + + const limit = maxResults ?? 5; + return { + content: [{type: 'text', text: `Returning up to ${limit} results for "${query}".`}], + }; + }, + }, + ]), + ], +}); +``` + +TIP: `required: ['param1', 'param2', ...]`を使用してそれらのパラメータの型から`undefined`を削除し、`additionalProperties: false`を使用して引数オブジェクトの型をこれらのパラメータのみに制限します。 + +## ルートにツールを提供する {#provide-tools-for-a-route} + +複雑なアプリケーションを構築する場合、ユーザーが特定のルートを表示しているときにのみ特定のツールを利用できるようにしたい場合があります。これは、ルート定義で直接ツールを提供することで実現できます。 + +```ts {header:"routes.ts"} +import {provideExperimentalWebMcpTools} from '@angular/core'; +import {Routes} from '@angular/router'; + +export const routes: Routes = [ + { + path: 'dashboard', + loadComponent: () => import('./dashboard').then((m) => m.Dashboard), + providers: [ + provideExperimentalWebMcpTools([ + { + name: 'exportDashboardReports', + description: 'Exports the current dashboard analytics.', + inputSchema: {type: 'object', properties: {}}, + execute: () => ({ + content: [{type: 'text', text: 'Dashboard export successfully triggered.'}], + }), + }, + ]), + ], + }, +]; +``` + +NOTE: 特定のルートにツールを登録する場合、ユーザーがルートから移動したときにツールが自動的に_登録解除_されるように、ルーターを構成して[`withExperimentalAutoCleanupInjectors`](api/router/withExperimentalAutoCleanupInjectors)を使用することを検討してください。このオプションがない場合、ルートで宣言されたWebMCPツールは、ユーザーが別のルートに移動した後でもAIエージェントからアクセス可能なままになります。 + +```ts {header:"app.config.ts"} +import {ApplicationConfig} from '@angular/core'; +import {provideRouter, withExperimentalAutoCleanupInjectors} from '@angular/router'; +import {routes} from './routes'; + +export const appConfig: ApplicationConfig = { + providers: [provideRouter(routes, withExperimentalAutoCleanupInjectors())], +}; +``` + +## サービス内でのツールの提供 {#provide-tools-within-services} + +動的なユースケースの場合、[`declareExperimentalWebMcpTool`](api/core/declareExperimentalWebMcpTool)関数は注入コンテキスト内にツールを直接登録し、そのコンテキストが破棄されたときに自動的に登録を解除します。 + +```ts {header:"counter.ts"} +import {Service, declareExperimentalWebMcpTool, signal, inject} from '@angular/core'; + +@Service() +export class Counter { + readonly count = signal(0); + + constructor() { + declareExperimentalWebMcpTool({ + name: 'getCounter', + description: 'Reads the global counter.', + inputSchema: {type: 'object', properties: {}}, + execute: () => ({ + content: [{type: 'text', text: `The count is: ${this.count()}.`}], + }), + }); + } +} +``` + +`declareExperimentalWebMcpTool`は任意の注入コンテキストで機能しますが、[名前の衝突](#name-collisions)に注意し、ルートサービスで使用することを推奨します。 + +## シグナルフォームの暗黙的ツール {#implicit-tools-in-signal-forms} + +最小限の設定で、既存のAngularの[Signal Form](essentials/signal-forms)から暗黙的にWebMCPツールを作成できます。AngularはフォームモデルをリッチなWebMCPツールに変換し、JSONスキーマやイベントハンドラーを手動で記述することなく、高度に動的なフォームを効果的にサポートします。 + +### WebMCPフォーム機能の有効化 {#enable-the-webmcp-forms-feature} + +まず、ルートアプリケーションのプロバイダーに[`provideExperimentalWebMcpForms`](api/forms/signals/provideExperimentalWebMcpForms)を追加します: + +```ts {header:"main.ts"} +import {bootstrapApplication} from '@angular/platform-browser'; +import {provideExperimentalWebMcpForms} from '@angular/forms/signals'; +import {AppRoot} from './app-root'; + +bootstrapApplication(AppRoot, { + providers: [provideExperimentalWebMcpForms()], +}); +``` + +### Signal Formのオプトイン {#opt-in-a-signal-form} + +次に、[`form`](api/forms/signals/form)を使用してSignal Formを定義する際、`experimentalWebMcpTool`設定オプションを渡して暗黙的なWebMCPツールにオプトインします。Angularはフォームのデータモデルを検査し、接続されたAIエージェント用のJSONスキーマを自動的に生成します。 + +```ts {header:"user-registration.ts"} +import {Component, signal} from '@angular/core'; +import {form, required, minLength} from '@angular/forms/signals'; + +@Component({ + selector: 'app-user-registration', + templateUrl: './user-registration.html', +}) +export class UserRegistration { + private readonly model = signal({ + firstName: '', + lastName: '', + age: 0, + hobbies: ['Web Development'], + }); + + readonly userForm = form( + this.model, + (f) => { + required(f.firstName, {message: 'First name is mandatory.'}); + required(f.lastName, {message: 'Last name is mandatory.'}); + }, + { + // Implicitly registers a WebMCP tool named `registerUser` with parameters derived from `model`. + experimentalWebMcpTool: { + name: 'registerUser', + description: 'Registers a new user.', + }, + submission: { + action: async (formValue) => { + console.log('Submitting user:', formValue); + // ... + }, + }, + }, + ); +} +``` + +この例では、Angularは以下のJSONスキーマを持つWebMCPツールを生成します: + +1. `model`シグナルの初期値から推論されたパラメーターとして、`firstName`、`lastName`、`age`、および`hobbies`を含みます。 +2. [`required`](api/forms/signals/required)バリデーターから推論された_必須_フィールドとして、`firstName`と`lastName`を定義します。 +3. `hobbies`を文字列の配列として定義し、エージェントが任意の数の趣味を提供できるようにします。 + +入力スキーマの推論にとどまらず、AngularはWebMCPツールをフォームの検証ロジックと送信ハンドラーにも接続します。これにより、エージェントは自身の入力によってトリガーされた検証エラーや送信中に発生した失敗を監視し、自己修正して再試行できるようになります。 + +NOTE: 非同期バリデーターはトリガーされ_ない_ため、送信アクションで処理する必要があります。 + +#### 制約事項 {#constraints} + +Angularはフォームモデルの初期値からWebMCPスキーマを推論します。これには以下が必要です: + +- 具体的な初期値(`''`、`0`、`false`): Angularは`null`や`undefined`からデータ型を推論できません。 +- 空ではない配列(`['Hello!']`): Angularは空の配列からデータ型を推論できず、少なくとも1つの初期値を必要とします。 + +## ベストプラクティス {#best-practices} + +以下のベストプラクティスを念頭に置いてください: + +### 名前の衝突 {#name-collisions} + +WebMCPは各ツールが一意の名前を持つことを要求し、同じツール名が複数回登録された場合はエラーをスローします。これは、複数回登録される可能性のあるコンテキスト(コンポーネントのコンストラクターなど)で`declareExperimentalWebMcpTool`や`provideExperimentalWebMcpTools`を呼び出すと、実行時にエラーが発生する可能性があることを意味します。 + +可能な限り、ツールはアプリケーションプロバイダー、ルートプロバイダー、またはルートサービスに配置することを推奨します。[Signal Formsの暗黙的ツール](#implicit-tools-in-signal-forms)を含め、コンポーネントにツールを配置する場合は、そのコンポーネントが常にページ上で最大_1回_しかレンダリングされないことを確認してください。 + +### ツール入力の検証 {#validate-tool-inputs} + +Angularは、エージェントによって提供された入力が定義されたJSONスキーマと実際に一致するかどうかの暗黙的な検証を提供しません。信頼性を確保するために、使用する前に`execute`関数への引数を明示的に検証することを検討してください。 + +### テスト {#testing} + +ツールを効果的にユニットテストするために、[`@mcp-b/webmcp-polyfill`](https://www.npmjs.com/package/@mcp-b/webmcp-polyfill)のようなモックWebMCP実装を使用することを検討してください。 From 4edf36f7478bb381e3fc8ae00a34caa65d382d60 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 09:43:21 +0900 Subject: [PATCH 06/66] feat(docs): translate guide/di/lazy-loading-services to Japanese MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit injectAsync を使ったサービスの遅延読み込みガイドの日本語翻訳と英語スナップショットを追加。 --- .../guide/di/lazy-loading-services.en.md | 85 +++++++++++++++++++ .../content/guide/di/lazy-loading-services.md | 85 +++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 adev-ja/src/content/guide/di/lazy-loading-services.en.md create mode 100644 adev-ja/src/content/guide/di/lazy-loading-services.md diff --git a/adev-ja/src/content/guide/di/lazy-loading-services.en.md b/adev-ja/src/content/guide/di/lazy-loading-services.en.md new file mode 100644 index 0000000000..3e5dbd4128 --- /dev/null +++ b/adev-ja/src/content/guide/di/lazy-loading-services.en.md @@ -0,0 +1,85 @@ +# Lazy loading services + +IMPORTANT: For lazy loading to work, the service you load must be auto-provided. Decorate it with either `@Injectable({providedIn: 'root'})` or [`@Service()`](guide/di/creating-and-using-services#using-the-service-decorator). Without auto-provisioning, Angular has no way to construct the service after it loads. + +Angular's `injectAsync` function lets you load a service on demand, only when it's actually needed. This is useful when a service depends on a large library or rarely used feature, and you don't want to pay for it on the initial page load. + +When you use `injectAsync`, the service's code is split out by your bundler into a separate JavaScript chunk and downloaded the first time you ask for the instance. Once loaded, Angular resolves the service through the regular DI system, so it can still depend on other injectables and behaves like any other singleton. + +## Lazily injecting a service + +Imagine a `ReportExporter` that depends on a heavy spreadsheet library. Most users open the report; only a few click **Export**. Load the exporter on demand: + +```angular-ts +import {Component, injectAsync} from '@angular/core'; + +@Component({ + selector: 'app-report', + template: ``, +}) +export class Report { + private exporter = injectAsync(() => import('./report-exporter').then((m) => m.ReportExporter)); + + async export() { + const exporter = await this.exporter(); + exporter.export(); + } +} +``` + +The first call to `this.exporter()` triggers the dynamic import and resolves the service from DI. Subsequent calls reuse the same promise, so the chunk is only fetched once. + +If the lazy-loaded service is the [default export](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export), pass the dynamic import directly, Angular unwraps the `default` for you: + +```ts {header: report-exporter.ts} +@Service() +export default class ReportExporter { + /* … */ +} +``` + +```ts {header: report.ts} +private exporter = injectAsync(() => import('./report-exporter')); +``` + +## Prefetching the dependency + +By default, the lazy chunk is only fetched when you invoke the returned function. You can start the download earlier by passing a `prefetch` trigger in the options. A trigger is any function that returns a `Promise`, when it resolves, Angular kicks off the loader. + +Angular ships with `onIdle`, a built-in trigger that waits until the browser becomes idle: + +```ts +import {Component, injectAsync, onIdle} from '@angular/core'; + +@Component({ + /* … */ +}) +export class Report { + private exporter = injectAsync(() => import('./report-exporter').then((m) => m.ReportExporter), { + prefetch: onIdle, + }); +} +``` + +You can also configure `onIdle` with a maximum wait time so the prefetch always happens within a known window, even on busy pages: + +```ts +injectAsync(loader, {prefetch: () => onIdle({timeout: 1_000})}); +``` + +NOTE: Prefetching is opportunistic. If the user invokes the feature before the prefetch fires, Angular still loads the dependency immediately and resolves your `await` as soon as it's ready. + +## Provide a custom prefetch trigger + +A `PrefetchTrigger` is just a function that returns a promise, the loader runs as soon as the promise resolves. Use this to align prefetching with your own signals, such as a hover or a scheduler tick: + +```ts +import {PrefetchTrigger} from '@angular/core'; + +export function onHover(target: HTMLElement): PrefetchTrigger { + return () => + new Promise((resolve) => { + target.addEventListener('pointerenter', () => resolve(), {once: true}); + }); +} +``` diff --git a/adev-ja/src/content/guide/di/lazy-loading-services.md b/adev-ja/src/content/guide/di/lazy-loading-services.md new file mode 100644 index 0000000000..e8c8f31c12 --- /dev/null +++ b/adev-ja/src/content/guide/di/lazy-loading-services.md @@ -0,0 +1,85 @@ +# サービスの遅延読み込み + +IMPORTANT: 遅延読み込みを機能させるには、読み込むサービスが自動プロバイドされている必要があります。`@Injectable({providedIn: 'root'})`または[`@Service()`](guide/di/creating-and-using-services#using-the-service-decorator)のいずれかで装飾してください。自動プロビジョニングがない場合、Angularは読み込み後にサービスを構築する方法がありません。 + +Angularの`injectAsync`関数を使用すると、実際に必要な場合にのみ、オンデマンドでサービスを読み込むことができます。これは、サービスが大規模なライブラリやめったに使用されない機能に依存しており、初期ページ読み込み時にそのコストを払いたくない場合に便利です。 + +`injectAsync`を使用すると、サービスのコードはバンドラーによって個別のJavaScriptチャンクに分割され、インスタンスを初めて要求したときにダウンロードされます。読み込みが完了すると、Angularは通常のDIシステムを通じてサービスを解決するため、他の注入可能に依存し続けることができ、他のシングルトンと同様に動作します。 + +## サービスの遅延インジェクト {#lazily-injecting-a-service} + +重いスプレッドシートライブラリに依存する`ReportExporter`を想像してください。ほとんどのユーザーはレポートを開きますが、**Export**をクリックするのはごく一部です。エクスポーターをオンデマンドでロードします: + +```angular-ts +import {Component, injectAsync} from '@angular/core'; + +@Component({ + selector: 'app-report', + template: ``, +}) +export class Report { + private exporter = injectAsync(() => import('./report-exporter').then((m) => m.ReportExporter)); + + async export() { + const exporter = await this.exporter(); + exporter.export(); + } +} +``` + +`this.exporter()`への最初の呼び出しは動的インポートをトリガーし、DIからサービスを解決します。後続の呼び出しは同じPromiseを再利用するため、チャンクは一度だけフェッチされます。 + +遅延読み込みされるサービスが[デフォルトエクスポート](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export)である場合、動的インポートを直接渡すと、Angularが自動的に`default`をアンラップします: + +```ts {header: report-exporter.ts} +@Service() +export default class ReportExporter { + /* … */ +} +``` + +```ts {header: report.ts} +private exporter = injectAsync(() => import('./report-exporter')); +``` + +## 依存関係のプリフェッチ {#prefetching-the-dependency} + +デフォルトでは、遅延チャンクは返された関数を呼び出したときにのみフェッチされます。オプションで`prefetch`トリガーを渡すことで、ダウンロードをより早く開始できます。トリガーは`Promise`を返す任意の関数であり、それが解決されると、Angularはローダーを起動します。 + +Angularには、ブラウザがアイドル状態になるまで待機する組み込みトリガーである`onIdle`が同梱されています: + +```ts +import {Component, injectAsync, onIdle} from '@angular/core'; + +@Component({ + /* … */ +}) +export class Report { + private exporter = injectAsync(() => import('./report-exporter').then((m) => m.ReportExporter), { + prefetch: onIdle, + }); +} +``` + +また、最大の待機時間を指定して`onIdle`を設定できるため、ビジーなページであっても、プリフェッチは常に一定の期間内に実行されます: + +```ts +injectAsync(loader, {prefetch: () => onIdle({timeout: 1_000})}); +``` + +NOTE: プリフェッチはオポチュニスティックです。プリフェッチが発火する前にユーザーが機能を呼び出した場合、Angularは依然として依存関係を即座にロードし、準備ができ次第`await`を解決します。 + +## カスタムプリフェッチトリガーの提供 {#provide-a-custom-prefetch-trigger} + +`PrefetchTrigger`はpromiseを返す単なる関数であり、promiseが解決されるとすぐにローダーが実行されます。ホバーやスケジューラのティックなど、独自のシグナルとプリフェッチを同期させるためにこれを使用します: + +```ts +import {PrefetchTrigger} from '@angular/core'; + +export function onHover(target: HTMLElement): PrefetchTrigger { + return () => + new Promise((resolve) => { + target.addEventListener('pointerenter', () => resolve(), {once: true}); + }); +} +``` From 3a6c37a67ddb872935f6efac20221d973367fc3e Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:18 +0900 Subject: [PATCH 07/66] fix(migrate): md-class-b src/content/ai --- adev-ja/src/content/ai/ai-tutor.en.md | 4 +--- adev-ja/src/content/ai/ai-tutor.md | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/adev-ja/src/content/ai/ai-tutor.en.md b/adev-ja/src/content/ai/ai-tutor.en.md index 7c922be054..e5a6144966 100644 --- a/adev-ja/src/content/ai/ai-tutor.en.md +++ b/adev-ja/src/content/ai/ai-tutor.en.md @@ -141,9 +141,7 @@ You will build your application over a five-phase journey. You can follow this p - **Module 16:** Introduction to Forms - **Module 17:** Intro to Angular Material -### **Phase 5: Experimental Signal Forms (⚠️ WARNING: Subject to Change)** - -**CRITICAL NOTE FOR THIS PHASE:** Signal Forms are currently an [**EXPERIMENTAL** feature](/reference/releases#experimental). The API may change significantly in future Angular releases. Please proceed with the understanding that this section demonstrates a cutting-edge feature. +### **Phase 5: Signal Forms** - **Module 18**: **Introduction to Signal Forms** - **Module 19**: **Submitting & Resetting** diff --git a/adev-ja/src/content/ai/ai-tutor.md b/adev-ja/src/content/ai/ai-tutor.md index 7ff8f7d375..1e35df924a 100644 --- a/adev-ja/src/content/ai/ai-tutor.md +++ b/adev-ja/src/content/ai/ai-tutor.md @@ -141,9 +141,7 @@ Angular AIチューターは、完全でモダンなAngularアプリケーショ - **モジュール16:** フォーム入門 - **モジュール17:** Angular Material入門 -### **フェーズ5:実験的なシグナルフォーム(⚠️ 警告:変更の可能性あり)** {#phase-5-experimental-signal-forms-warning-subject-to-change} - -**このフェーズに関する重要な注意:** シグナルフォームは現在[**実験的な**機能](/reference/releases#experimental)です。APIは将来のAngularリリースで大幅に変更される可能性があります。このセクションは最先端の機能を示すものであることをご理解の上、お進みください。 +### **フェーズ5:シグナルフォーム** {#phase-5-signal-forms} - **モジュール18:** **シグナルフォーム入門** - **モジュール19:** **送信とリセット** From 0981ef5e27f448c5760393aee495ef5ffebd2023 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:18 +0900 Subject: [PATCH 08/66] fix(migrate): md-class-b src/content/best-practices --- adev-ja/src/content/best-practices/a11y.en.md | 3 +++ adev-ja/src/content/best-practices/a11y.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/adev-ja/src/content/best-practices/a11y.en.md b/adev-ja/src/content/best-practices/a11y.en.md index 54994a5c19..2531454d88 100644 --- a/adev-ja/src/content/best-practices/a11y.en.md +++ b/adev-ja/src/content/best-practices/a11y.en.md @@ -63,6 +63,9 @@ For example: For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.dev/cdk/a11y/overview). +For custom-styled components that need reusable WAI-ARIA interaction patterns, [Angular Aria](guide/aria/overview) provides headless directives for patterns such as accordion, combobox, listbox, menu, tabs, and toolbar. +These directives handle keyboard interaction, ARIA attributes, focus management, and screen reader support while letting you provide the HTML structure and styling for your application. + ### Augmenting native elements Native HTML elements capture several standard interaction patterns that are important to accessibility. diff --git a/adev-ja/src/content/best-practices/a11y.md b/adev-ja/src/content/best-practices/a11y.md index 7c94114817..643bccea2c 100644 --- a/adev-ja/src/content/best-practices/a11y.md +++ b/adev-ja/src/content/best-practices/a11y.md @@ -63,6 +63,9 @@ Angularチームが保守している[Angular Material](https://material.angular これらのツールやその他のツールの詳細については、[Angular CDKアクセシビリティの概要](https://material.angular.dev/cdk/a11y/overview)を参照してください。 +再利用可能なWAI-ARIAインタラクションパターンを必要とするカスタムスタイルのコンポーネントには、[Angular Aria](guide/aria/overview)がアコーディオン、コンボボックス、リストボックス、メニュー、タブ、ツールバーなどのパターン向けのヘッドレスディレクティブを提供します。 +これらのディレクティブは、キーボード操作、ARIA属性、フォーカス管理、スクリーンリーダーサポートを処理する一方で、アプリケーションのHTML構造とスタイリングは開発者が提供できるようにします。 + ### ネイティブ要素の拡張 {#augmenting-native-elements} ネイティブHTML要素は、アクセシビリティにとって重要な、いくつかの標準的な相互作用パターンを捉えています。 From cf21fb755555540dc4e4e87784cb5f3c3a7e7ae6 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:18 +0900 Subject: [PATCH 09/66] fix(migrate): md-class-c src/content/best-practices/runtime-performance --- .../skipping-subtrees.en.md | 19 +++---------------- .../runtime-performance/skipping-subtrees.md | 19 +++---------------- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.en.md b/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.en.md index 02304d483b..405836da6e 100644 --- a/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.en.md +++ b/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.en.md @@ -4,33 +4,20 @@ JavaScript, by default, uses mutable data structures that you can reference from Change detection is sufficiently fast for most applications. However, when an application has an especially large component tree, running change detection across the whole application can cause performance issues. You can address this by configuring change detection to only run on a subset of the component tree. -If you are confident that a part of the application is not affected by a state change, you can use [OnPush](/api/core/ChangeDetectionStrategy) to skip change detection in an entire component subtree. - ## Using `OnPush` -OnPush change detection instructs Angular to run change detection for a component subtree **only** when: +OnPush is the default change detection strategy in Angular (since v22). It instructs Angular to run change detection for a component subtree **only** when: - The root component of the subtree receives new inputs as the result of a template binding. Angular compares the current and past value of the input with `==`. - Angular handles an event _(for example using event binding, output binding, or `@HostListener` )_ in the subtree's root component or any of its children whether they are using OnPush change detection or not. -You can set the change detection strategy of a component to `OnPush` in the `@Component` decorator: - -```ts -import {ChangeDetectionStrategy, Component} from '@angular/core'; - -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class MyComponent {} -``` - ## Common change detection scenarios This section examines several common change detection scenarios to illustrate Angular's behavior. -### An event is handled by a component with default change detection +### An event is handled by a component with `Eager` change detection -If Angular handles an event within a component without `OnPush` strategy, the framework executes change detection on the entire component tree. Angular will skip descendant component subtrees with roots using `OnPush`, which have not received new inputs. +If Angular handles an event within a component with the `Eager` strategy, the framework executes change detection on the entire component tree. Angular will skip descendant component subtrees with roots using `OnPush`, which have not received new inputs. As an example, if we set the change detection strategy of `MainComponent` to `OnPush` and the user interacts with a component outside the subtree with root `MainComponent`, Angular will check all the pink components from the diagram below (`AppComponent`, `HeaderComponent`, `SearchComponent`, `ButtonComponent`) unless `MainComponent` receives new inputs: diff --git a/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.md b/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.md index 2f549e4185..bbd639bdf4 100644 --- a/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.md +++ b/adev-ja/src/content/best-practices/runtime-performance/skipping-subtrees.md @@ -4,33 +4,20 @@ JavaScriptは、デフォルトでは、複数の異なるコンポーネント 変更検知は、ほとんどのアプリケーションにとって十分に高速です。ただし、アプリケーションが特に大きなコンポーネントツリーを持っている場合、アプリケーション全体で変更検知を実行すると、パフォーマンスの問題が発生する可能性があります。これは、コンポーネントツリーのサブセットでのみ変更検知が実行されるように構成することで対処できます。 -アプリケーションの一部が状態変化の影響を受けないと確信できる場合は、[OnPush](/api/core/ChangeDetectionStrategy)を使用して、コンポーネントのサブツリー全体の変更検知をスキップできます。 - ## `OnPush`の使用 {#using-onpush} -OnPush変更検知は、Angularにコンポーネントのサブツリーの変更検知を次の場合**のみ**実行するように指示します。 +OnPushはAngularのデフォルトの変更検知戦略です(v22以降)。Angularにコンポーネントのサブツリーの変更検知を次の場合**のみ**実行するように指示します。 - サブツリーのルートコンポーネントが、テンプレートバインディングの結果として新しいインプットを受け取った場合。Angularは、インプットの現在と過去の値を`==`で比較します。 - Angularが、OnPush変更検知を使用しているかどうかに関係なく、サブツリーのルートコンポーネント、または、その子でイベント *(例えば、イベントバインディング、アウトプットバインディング、または`@HostListener`を使用)* を処理する場合。 -コンポーネントの変更検知戦略を`@Component`デコレーターで`OnPush`に設定できます。 - -```ts -import {ChangeDetectionStrategy, Component} from '@angular/core'; - -@Component({ - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class MyComponent {} -``` - ## 一般的な変更検知のシナリオ このセクションでは、Angularの動作を説明するために、いくつかの一般的な変更検知のシナリオを検証します。 -### デフォルトの変更検知を持つコンポーネントによってイベントが処理される場合 +### `Eager`な変更検知を持つコンポーネントによってイベントが処理される場合 -Angularが`OnPush`戦略なしでコンポーネント内でイベントを処理する場合、フレームワークはコンポーネントツリー全体で変更検知を実行します。Angularは、新しいインプットを受け取っていない、`OnPush`を使用しているルートを持つ子孫コンポーネントのサブツリーをスキップします。 +Angularが`Eager`戦略を持つコンポーネント内でイベントを処理する場合、フレームワークはコンポーネントツリー全体で変更検知を実行します。Angularは、新しいインプットを受け取っていない、`OnPush`を使用しているルートを持つ子孫コンポーネントのサブツリーをスキップします。 例として、`MainComponent`の変更検知戦略を`OnPush`に設定し、ユーザーが`MainComponent`をルートとするサブツリーの外部のコンポーネントとやり取りする場合、`MainComponent`が新しいインプットを受け取らない限り、Angularは下の図のすべてのピンク色のコンポーネント(`AppComponent`、`HeaderComponent`、`SearchComponent`、`ButtonComponent`)をチェックします: From 4c855fc93e037f3c0ed8ee8aae2c1a359ef46521 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:18 +0900 Subject: [PATCH 10/66] fix(migrate): md-class-a src/content/guide --- adev-ja/src/content/guide/ssr.en.md | 4 ++-- adev-ja/src/content/guide/ssr.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adev-ja/src/content/guide/ssr.en.md b/adev-ja/src/content/guide/ssr.en.md index 7fa99205ad..a0cc7686cd 100644 --- a/adev-ja/src/content/guide/ssr.en.md +++ b/adev-ja/src/content/guide/ssr.en.md @@ -345,9 +345,9 @@ export class Checkout { When working with server-side rendering, you should avoid directly referencing browser-specific globals like `document`. Instead, use the [`DOCUMENT`](api/core/DOCUMENT) token to access the document object in a platform-agnostic way. ```ts -import {Injectable, inject, DOCUMENT} from '@angular/core'; +import {inject, DOCUMENT, Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class CanonicalLinkService { private readonly document = inject(DOCUMENT); diff --git a/adev-ja/src/content/guide/ssr.md b/adev-ja/src/content/guide/ssr.md index 3ebc573444..d4c6c4255d 100644 --- a/adev-ja/src/content/guide/ssr.md +++ b/adev-ja/src/content/guide/ssr.md @@ -345,9 +345,9 @@ export class Checkout { サーバーサイドレンダリングを使用する場合、`document` のようなブラウザ固有のグローバルを直接参照することは避けるべきです。代わりに、[`DOCUMENT`](api/core/DOCUMENT) トークンを使用して、プラットフォームに依存しない方法でdocumentオブジェクトにアクセスします。 ```ts -import {Injectable, inject, DOCUMENT} from '@angular/core'; +import {inject, DOCUMENT, Service} from '@angular/core'; -@Injectable({providedIn: 'root'}) +@Service() export class CanonicalLinkService { private readonly document = inject(DOCUMENT); From 4513a85fec2b78a41522c8ef0b6f0740d4a85cd9 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:19 +0900 Subject: [PATCH 11/66] fix(migrate): md-class-c src/content/guide --- adev-ja/src/content/guide/security.en.md | 6 ++++++ adev-ja/src/content/guide/security.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/adev-ja/src/content/guide/security.en.md b/adev-ja/src/content/guide/security.en.md index 388ea1bd12..d4854d6d89 100644 --- a/adev-ja/src/content/guide/security.en.md +++ b/adev-ja/src/content/guide/security.en.md @@ -450,6 +450,12 @@ const nodeAppEngine = new AngularNodeAppEngine({ }); ``` +For the Node.js variant `AngularNodeAppEngine`, you can also provide the `NG_TRUST_PROXY_HEADERS` environment variable (with comma-separated list of headers as a value) to allow the usage of these headers. + +```bash {hideDollar} +export NG_TRUST_PROXY_HEADERS="X-FORWARDED-HOST,X-FORWARDED-PREFIX" +``` + IMPORTANT: Only enable `trustProxyHeaders` if your application is behind a trusted proxy that strictly validates or overrides these headers. Otherwise, attackers can spoof these headers to cause [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. ## Auditing Angular applications diff --git a/adev-ja/src/content/guide/security.md b/adev-ja/src/content/guide/security.md index ae2c131471..396bf078e4 100644 --- a/adev-ja/src/content/guide/security.md +++ b/adev-ja/src/content/guide/security.md @@ -450,6 +450,12 @@ const nodeAppEngine = new AngularNodeAppEngine({ }); ``` +For the Node.js variant `AngularNodeAppEngine`, you can also provide the `NG_TRUST_PROXY_HEADERS` environment variable (with comma-separated list of headers as a value) to allow the usage of these headers. + +```bash {hideDollar} +export NG_TRUST_PROXY_HEADERS="X-FORWARDED-HOST,X-FORWARDED-PREFIX" +``` + IMPORTANT: Only enable `trustProxyHeaders` if your application is behind a trusted proxy that strictly validates or overrides these headers. Otherwise, attackers can spoof these headers to cause [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. ## Angularアプリケーションの監査 {#auditing-angular-applications} From aee926512e3272dc7e63e0d9a69a18f4c1f50174 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:19 +0900 Subject: [PATCH 12/66] chore(migrate): defer md-class-d src/content/guide --- .../content/guide/incremental-hydration.en.md | 210 ------------------ .../content/guide/incremental-hydration.md | 136 +++++++----- .../guide/incremental-hydration.md.bak | 210 ++++++++++++++++++ 3 files changed, 286 insertions(+), 270 deletions(-) delete mode 100644 adev-ja/src/content/guide/incremental-hydration.en.md create mode 100644 adev-ja/src/content/guide/incremental-hydration.md.bak diff --git a/adev-ja/src/content/guide/incremental-hydration.en.md b/adev-ja/src/content/guide/incremental-hydration.en.md deleted file mode 100644 index a4f5606bd2..0000000000 --- a/adev-ja/src/content/guide/incremental-hydration.en.md +++ /dev/null @@ -1,210 +0,0 @@ -# Incremental Hydration - -**Incremental hydration** is an advanced type of [hydration](guide/hydration) that can leave sections of your application dehydrated and _incrementally_ trigger hydration of those sections as they are needed. - -## Why use incremental hydration? - -Incremental hydration is a performance improvement that builds on top of full application hydration. It can produce smaller initial bundles while still providing an end-user experience that is comparable to a full application hydration experience. Smaller bundles improve initial load times, reducing [First Input Delay (FID)](https://web.dev/fid) and [Cumulative Layout Shift (CLS)](https://web.dev/cls). - -Incremental hydration also lets you use deferrable views (`@defer`) for content that may not have been deferrable before. Specifically, you can now use deferrable views for content that is above the fold. Prior to incremental hydration, putting a `@defer` block above the fold would result in placeholder content rendering and then being replaced by the `@defer` block's main template content. This would result in a layout shift. Incremental hydration means the main template of the `@defer` block will render with no layout shift on hydration. - -## How do you enable incremental hydration in Angular? - -You can enable incremental hydration for applications that already use server-side rendering (SSR) with hydration. Follow the [Angular SSR Guide](guide/ssr) to enable server-side rendering and the [Angular Hydration Guide](guide/hydration) to enable hydration first. - -Enable incremental hydration by adding the `withIncrementalHydration()` function to the `provideClientHydration` provider. - -```typescript -import { - bootstrapApplication, - provideClientHydration, - withIncrementalHydration, -} from '@angular/platform-browser'; -... - -bootstrapApplication(App, { - providers: [provideClientHydration(withIncrementalHydration())] -}); -``` - -Incremental Hydration depends on and enables [event replay](guide/hydration#capturing-and-replaying-events) automatically. If you already have `withEventReplay()` in your list, you can safely remove it after enabling incremental hydration. - -## How does incremental hydration work? - -Incremental hydration builds on top of full-application [hydration](guide/hydration), [deferrable views](/guide/templates/defer), and [event replay](guide/hydration#capturing-and-replaying-events). With incremental hydration, you can add additional triggers to `@defer` blocks that define incremental hydration boundaries. Adding a `hydrate` trigger to a defer block tells Angular that it should load that defer block's dependencies during server-side rendering and render the main template rather than the `@placeholder`. When client-side rendering, the dependencies are still deferred, and the defer block content stays dehydrated until its `hydrate` trigger fires. That trigger tells the defer block to fetch its dependencies and hydrate the content. Any browser events, specifically those that match listeners registered in your component, that are triggered by the user prior to hydration are queued up and replayed once the hydration process is complete. - -## Controlling hydration of content with triggers - -You can specify **hydrate triggers** that control when Angular loads and hydrates deferred content. These are additional triggers that can be used alongside regular `@defer` triggers. - -Each `@defer` block may have multiple hydrate event triggers, separated with a semicolon (`;`). Angular triggers hydration when _any_ of the triggers fire. - -There are three types of hydrate triggers: `hydrate on`, `hydrate when`, and `hydrate never`. - -### `hydrate on` - -`hydrate on` specifies a condition for when hydration is triggered for the `@defer` block. - -The available triggers are as follows: - -| Trigger | Description | -| --------------------------------------------------- | ---------------------------------------------------------------------- | -| [`hydrate on idle`](#hydrate-on-idle) | Triggers when the browser is idle. | -| [`hydrate on viewport`](#hydrate-on-viewport) | Triggers when specified content enters the viewport | -| [`hydrate on interaction`](#hydrate-on-interaction) | Triggers when the user interacts with specified element | -| [`hydrate on hover`](#hydrate-on-hover) | Triggers when the mouse hovers over specified area | -| [`hydrate on immediate`](#hydrate-on-immediate) | Triggers immediately after non-deferred content has finished rendering | -| [`hydrate on timer`](#hydrate-on-timer) | Triggers after a specific duration | - -#### `hydrate on idle` - -The `hydrate on idle` trigger loads the deferrable view's dependencies and hydrates the content once the browser has reached an idle state, based on `requestIdleCallback`. - -```angular-html -@defer (hydrate on idle) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -#### `hydrate on viewport` - -The `hydrate on viewport` trigger loads the deferrable view's dependencies and hydrates the corresponding page of the app when the specified content enters the viewport using the -[Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). - -```angular-html -@defer (hydrate on viewport) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -#### `hydrate on interaction` - -The `hydrate on interaction` trigger loads the deferrable view's dependencies and hydrates the content when the user interacts with the specified element through -`click` or `keydown` events. - -```angular-html -@defer (hydrate on interaction) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -#### `hydrate on hover` - -The `hydrate on hover` trigger loads the deferrable view's dependencies and hydrates the content when the mouse has hovered over the triggered area through the -`mouseover` and `focusin` events. - -```angular-html -@defer (hydrate on hover) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -#### `hydrate on immediate` - -The `hydrate on immediate` trigger loads the deferrable view's dependencies and hydrates the content immediately. This means that the deferred block loads as soon -as all other non-deferred content has finished rendering. - -```angular-html -@defer (hydrate on immediate) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -#### `hydrate on timer` - -The `hydrate on timer` trigger loads the deferrable view's dependencies and hydrates the content after a specified duration. - -```angular-html -@defer (hydrate on timer(500ms)) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -The duration parameter must be specified in milliseconds (`ms`) or seconds (`s`). - -### `hydrate when` - -The `hydrate when` trigger accepts a custom conditional expression and loads the deferrable view's dependencies and hydrates the content when the -condition becomes truthy. - -```angular-html -@defer (hydrate when condition) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -NOTE: `hydrate when` conditions only trigger when they are the top-most dehydrated `@defer` block. The condition provided for the trigger is -specified in the parent component, which needs to exist before it can be triggered. If the parent block is dehydrated, that expression will not yet -be resolvable by Angular. - -### `hydrate never` - -The `hydrate never` allows users to specify that the content in the defer block should remain dehydrated indefinitely, effectively becoming static -content. Note that this applies to the initial render only. During a subsequent client-side render, a `@defer` block with `hydrate never` would -still fetch dependencies, as hydration only applies to initial load of server-side rendered content. In the example below, subsequent client-side -renders would load the `@defer` block dependencies on viewport. - -```angular-html -@defer (on viewport; hydrate never) { - -} @placeholder { -
    Large component placeholder
    -} -``` - -NOTE: Using `hydrate never` prevents hydration of the entire nested subtree of a given `@defer` block. No other `hydrate` triggers fire for content nested underneath that block. - -## Hydrate triggers alongside regular triggers - -Hydrate triggers are additional triggers that are used alongside regular triggers on a `@defer` block. Hydration is an initial load optimization, and that means hydrate triggers only apply to that initial load. Any subsequent client side render will still use the regular trigger. - -```angular-html -@defer (on idle; hydrate on interaction) { - -} @placeholder { -
    Example Placeholder
    -} -``` - -In this example, on the initial load, the `hydrate on interaction` applies. Hydration will be triggered on interaction with the `` component. On any subsequent page load that is client-side rendered, for example when a user clicks a routerLink that loads a page with this component, the `on idle` will apply. - -## How does incremental hydration work with nested `@defer` blocks? - -Angular's component and dependency system is hierarchical, which means hydrating any component requires all of its parents also be hydrated. So if hydration is triggered for a child `@defer` block of a nested set of dehydrated `@defer` blocks, hydration is triggered from the top-most dehydrated `@defer` block down to the triggered child and fire in that order. - -```angular-html -@defer (hydrate on interaction) { - - @defer (hydrate on hover) { - - } @placeholder { -
    Child placeholder
    - } -} @placeholder { -
    Parent Placeholder
    -} -``` - -In the above example, hovering over the nested `@defer` block triggers hydration. The parent `@defer` block with the `` hydrates first, then the child `@defer` block with `` hydrates after. - -## Constraints - -Incremental hydration has the same constraints as full-application hydration, including limits on direct DOM manipulation and requiring valid HTML structure. Visit the [Hydration guide constraints](guide/hydration#constraints) section for more details. - -## Do I still need to specify `@placeholder` blocks? - -Yes. `@placeholder` block content is not used for incremental hydration, but a `@placeholder` is still necessary for subsequent client-side rendering cases. If your content was not on the route that was part of the initial load, then any navigation to the route that has your `@defer` block content renders like a regular `@defer` block. So the `@placeholder` is rendered in those client-side rendering cases. diff --git a/adev-ja/src/content/guide/incremental-hydration.md b/adev-ja/src/content/guide/incremental-hydration.md index dc83c22026..e4ac873f9e 100644 --- a/adev-ja/src/content/guide/incremental-hydration.md +++ b/adev-ja/src/content/guide/incremental-hydration.md @@ -1,64 +1,75 @@ -# インクリメンタルハイドレーション +# Incremental Hydration -**インクリメンタルハイドレーション**は、アプリケーションの一部を非ハイドレーション状態のままにし、必要に応じてそれらのセクションのハイドレーションを*段階的に*トリガーできる、高度なタイプの[ハイドレーション](guide/hydration)です。 +**Incremental hydration** is an advanced type of [hydration](guide/hydration) that can leave sections of your application dehydrated and _incrementally_ trigger hydration of those sections as they are needed. -## インクリメンタルハイドレーションを使用する理由 +## Why use incremental hydration? -インクリメンタルハイドレーションは、完全なアプリケーションハイドレーションを基盤としたパフォーマンス向上策です。完全なアプリケーションハイドレーションと同等のエンドユーザー体験を提供しながら、より小さな初期バンドルを作成できます。バンドルサイズが小さくなると、初期ロード時間が短縮され、[First Input Delay (FID)](https://web.dev/fid)と[Cumulative Layout Shift (CLS)](https://web.dev/cls)が改善されます。 +Incremental hydration is a performance improvement that builds on top of full application hydration. It can produce smaller initial bundles while still providing an end-user experience that is comparable to a full application hydration experience. Smaller bundles improve initial load times, reducing [First Input Delay (FID)](https://web.dev/fid) and [Cumulative Layout Shift (CLS)](https://web.dev/cls). -インクリメンタルハイドレーションを使用すると、以前は遅延できなかった可能性のあるコンテンツに遅延可能ビュー(`@defer`)を使用できるようになります。具体的には、画面の上部に表示されるコンテンツに遅延可能ビューを使用できるようになりました。インクリメンタルハイドレーション以前は、画面の上部に`@defer`ブロックを配置すると、プレースホルダーコンテンツがレンダリングされ、その後`@defer`ブロックのメインテンプレートコンテンツに置き換えられます。これにより、レイアウトシフトが発生します。インクリメンタルハイドレーションを使用すると、`@defer`ブロックのメインテンプレートは、ハイドレーション時にレイアウトシフトなしでレンダリングされます。 +Incremental hydration also lets you use deferrable views (`@defer`) for content that may not have been deferrable before. Specifically, you can now use deferrable views for content that is above the fold. Prior to incremental hydration, putting a `@defer` block above the fold would result in placeholder content rendering and then being replaced by the `@defer` block's main template content. This would result in a layout shift. Incremental hydration means the main template of the `@defer` block will render with no layout shift on hydration. -## Angularでインクリメンタルハイドレーションを有効にする方法 +## How do you enable incremental hydration in Angular? -ハイドレーションによるサーバーサイドレンダリング(SSR)を既に使用しているアプリケーションでインクリメンタルハイドレーションを有効にできます。サーバーサイドレンダリングを有効にするには[Angular SSRガイド](guide/ssr)を、ハイドレーションを有効にするには[Angularハイドレーションガイド](guide/hydration)を参照してください。 +You can enable incremental hydration for applications that already use server-side rendering (SSR) with hydration. Follow the [Angular SSR Guide](guide/ssr) to enable server-side rendering and the [Angular Hydration Guide](guide/hydration) to enable hydration first. -`provideClientHydration`プロバイダーに`withIncrementalHydration()`関数を追加することで、インクリメンタルハイドレーションを有効にします。 +Incremental hydration is enabled by default when you use `provideClientHydration()`. -```typescript +```ts +import {bootstrapApplication, provideClientHydration} from '@angular/platform-browser'; + +bootstrapApplication(App, { + providers: [provideClientHydration()], +}); +``` + +NOTE: Incremental Hydration depends on and enables [event replay](guide/hydration#capturing-and-replaying-events) automatically. If you already have `withEventReplay()` in your list, you can safely remove it. + +To opt out of incremental hydration, use `withNoIncrementalHydration()`: + +```ts import { bootstrapApplication, provideClientHydration, - withIncrementalHydration, + withNoIncrementalHydration, } from '@angular/platform-browser'; -... bootstrapApplication(App, { - providers: [provideClientHydration(withIncrementalHydration())] + providers: [provideClientHydration(withNoIncrementalHydration())], }); ``` -インクリメンタルハイドレーションは[イベントリプレイ](guide/hydration#capturing-and-replaying-events)に依存し、自動的に有効にします。既にリストに`withEventReplay()`がある場合は、インクリメンタルハイドレーションを有効にした後、安全に削除できます。 - -## インクリメンタルハイドレーションの動作方法 +## How does incremental hydration work? -インクリメンタルハイドレーションは、完全なアプリケーション[ハイドレーション](guide/hydration)、[遅延可能ビュー](/guide/templates/defer)、および[イベントリプレイ](guide/hydration#capturing-and-replaying-events)を基盤として構築されています。インクリメンタルハイドレーションを使用すると、インクリメンタルハイドレーションの境界を定義する`@defer`ブロックに追加のトリガーを追加できます。`defer`ブロックに`hydrate`トリガーを追加すると、Angularはサーバーサイドレンダリング中にその`defer`ブロックの依存関係をロードし、`@placeholder`ではなくメインテンプレートをレンダリングする必要があることを認識します。クライアントサイドレンダリングの場合、依存関係はまだ遅延され、`hydrate`トリガーが起動するまで`defer`ブロックのコンテンツは非ハイドレーション状態のままです。そのトリガーは、`defer`ブロックに依存関係を取得してコンテンツをハイドレーションするよう指示します。ハイドレーションの前にユーザーによってトリガーされたブラウザイベント、特にコンポーネントに登録されたリスナーと一致するイベントは、キューに入れられ、ハイドレーションプロセスが完了すると再生されます。 +Incremental hydration builds on top of full-application [hydration](guide/hydration), [deferrable views](/guide/templates/defer), and [event replay](guide/hydration#capturing-and-replaying-events). With incremental hydration, you can add additional triggers to `@defer` blocks that define incremental hydration boundaries. Adding a `hydrate` trigger to a defer block tells Angular that it should load that defer block's dependencies during server-side rendering and render the main template rather than the `@placeholder`. When client-side rendering, the dependencies are still deferred, and the defer block content stays dehydrated until its `hydrate` trigger fires. That trigger tells the defer block to fetch its dependencies and hydrate the content. Any browser events, specifically those that match listeners registered in your component, that are triggered by the user prior to hydration are queued up and replayed once the hydration process is complete. -## トリガーによるコンテンツのハイドレーション制御 +## Controlling hydration of content with triggers -Angularが遅延コンテンツをロードしてハイドレーションするタイミングを制御する**ハイドレーショントリガー**を指定できます。これらは、通常の`@defer`トリガーとともに使用できる追加のトリガーです。 +You can specify **hydrate triggers** that control when Angular loads and hydrates deferred content. These are additional triggers that can be used alongside regular `@defer` triggers. -各`@defer`ブロックには、セミコロン(`;`)で区切られた複数のハイドレーションイベントトリガーを含めることができます。Angularは、いずれかのトリガーが起動するとハイドレーションを開始します。 +Each `@defer` block may have multiple hydrate event triggers, separated with a semicolon (`;`). Angular triggers hydration when _any_ of the triggers fire. -ハイドレーショントリガーには、`hydrate on`、`hydrate when`、`hydrate never`の3種類があります。 +There are three types of hydrate triggers: `hydrate on`, `hydrate when`, and `hydrate never`. ### `hydrate on` -`hydrate on`は、`@defer`ブロックのハイドレーションがトリガーされる条件を指定します。 +`hydrate on` specifies a condition for when hydration is triggered for the `@defer` block. -使用可能なトリガーは次のとおりです。 +The available triggers are as follows: -| トリガー | 説明 | +| Trigger | Description | | --------------------------------------------------- | ---------------------------------------------------------------------- | -| [`hydrate on idle`](#hydrate-on-idle) | ブラウザがアイドル状態になったときにトリガーされます。 | -| [`hydrate on viewport`](#hydrate-on-viewport) | 指定されたコンテンツがビューポートに入ったときにトリガーされます。 | -| [`hydrate on interaction`](#hydrate-on-interaction) | ユーザーが指定された要素と対話したときにトリガーされます。 | -| [`hydrate on hover`](#hydrate-on-hover) | マウスが指定された領域にホバーしたときにトリガーされます。 | -| [`hydrate on immediate`](#hydrate-on-immediate) | 非遅延コンテンツのレンダリングが完了した直後にトリガーされます。 | -| [`hydrate on timer`](#hydrate-on-timer) | 特定の期間後にトリガーされます。 | +| [`hydrate on idle`](#hydrate-on-idle) | Triggers when the browser is idle. Supports an optional timeout. | +| [`hydrate on viewport`](#hydrate-on-viewport) | Triggers when specified content enters the viewport | +| [`hydrate on interaction`](#hydrate-on-interaction) | Triggers when the user interacts with specified element | +| [`hydrate on hover`](#hydrate-on-hover) | Triggers when the mouse hovers over specified area | +| [`hydrate on immediate`](#hydrate-on-immediate) | Triggers immediately after non-deferred content has finished rendering | +| [`hydrate on timer`](#hydrate-on-timer) | Triggers after a specific duration | #### `hydrate on idle` -`hydrate on idle`トリガーは、`requestIdleCallback`に基づいてブラウザがアイドル状態に達すると、遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 +The `hydrate on idle` trigger loads the deferrable view's dependencies and hydrates the content once the browser has reached an idle state, based on `requestIdleCallback`. + +You can optionally specify a timeout in milliseconds that is passed to [`requestIdleCallback`](https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback). If the browser doesn't schedule the callback soon enough, the work will run no later than the specified timeout. ```angular-html @defer (hydrate on idle) { @@ -66,12 +77,17 @@ Angularが遅延コンテンツをロードしてハイドレーションする } @placeholder {
    Large component placeholder
    } + + +@defer (hydrate on idle(500)) { + +} ``` #### `hydrate on viewport` -`hydrate on viewport`トリガーは、[Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API)を使用して指定されたコンテンツがビューポートに入ったときに、 -対応するアプリケーションのページの遅延可能ビューの依存関係をロードし、ハイドレーションします。 +The `hydrate on viewport` trigger loads the deferrable view's dependencies and hydrates the corresponding page of the app when the specified content enters the viewport using the +[Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). ```angular-html @defer (hydrate on viewport) { @@ -83,8 +99,8 @@ Angularが遅延コンテンツをロードしてハイドレーションする #### `hydrate on interaction` -`hydrate on interaction`トリガーは、ユーザーが`click`または`keydown`イベントを通じて指定された要素と対話したときに、 -遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 +The `hydrate on interaction` trigger loads the deferrable view's dependencies and hydrates the content when the user interacts with the specified element through +`click` or `keydown` events. ```angular-html @defer (hydrate on interaction) { @@ -96,8 +112,8 @@ Angularが遅延コンテンツをロードしてハイドレーションする #### `hydrate on hover` -`hydrate on hover`トリガーは、`mouseover`および`focusin`イベントを通じてマウスがトリガー領域にホバーしたときに、 -遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 +The `hydrate on hover` trigger loads the deferrable view's dependencies and hydrates the content when the mouse has hovered over the triggered area through the +`mouseover` and `focusin` events. ```angular-html @defer (hydrate on hover) { @@ -109,8 +125,8 @@ Angularが遅延コンテンツをロードしてハイドレーションする #### `hydrate on immediate` -`hydrate on immediate`トリガーは、遅延可能ビューの依存関係をロードし、すぐにコンテンツをハイドレーションします。 -これは、他のすべての非遅延コンテンツのレンダリングが完了するとすぐに、遅延ブロックがロードされることを意味します。 +The `hydrate on immediate` trigger loads the deferrable view's dependencies and hydrates the content immediately. This means that the deferred block loads as soon +as all other non-deferred content has finished rendering. ```angular-html @defer (hydrate on immediate) { @@ -122,7 +138,7 @@ Angularが遅延コンテンツをロードしてハイドレーションする #### `hydrate on timer` -`hydrate on timer`トリガーは、指定された期間後に遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 +The `hydrate on timer` trigger loads the deferrable view's dependencies and hydrates the content after a specified duration. ```angular-html @defer (hydrate on timer(500ms)) { @@ -132,12 +148,12 @@ Angularが遅延コンテンツをロードしてハイドレーションする } ``` -期間パラメーターは、ミリ秒(`ms`)または秒(`s`)で指定する必要があります。 +The duration parameter must be specified in milliseconds (`ms`) or seconds (`s`). ### `hydrate when` -`hydrate when`トリガーはカスタムの条件式を受け入れ、条件が真になったときに -遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 +The `hydrate when` trigger accepts a custom conditional expression and loads the deferrable view's dependencies and hydrates the content when the +condition becomes truthy. ```angular-html @defer (hydrate when condition) { @@ -147,16 +163,16 @@ Angularが遅延コンテンツをロードしてハイドレーションする } ``` -NOTE: `hydrate when`条件は、最上位の非ハイドレーション`@defer`ブロックである場合にのみトリガーされます。 -トリガーに提供される条件は親コンポーネントで指定され、トリガーされる前に存在する必要があります。 -親ブロックが非ハイドレーション状態の場合、その式はまだAngularによって解決できません。 +NOTE: `hydrate when` conditions only trigger when they are the top-most dehydrated `@defer` block. The condition provided for the trigger is +specified in the parent component, which needs to exist before it can be triggered. If the parent block is dehydrated, that expression will not yet +be resolvable by Angular. ### `hydrate never` -`hydrate never`を使用すると、`defer`ブロック内のコンテンツを無期限に非ハイドレーション状態のままにでき、事実上静的コンテンツになります。 -これは最初のレンダリングにのみ適用されることに注意してください。 -後続のクライアントサイドレンダリングでは、`hydrate never`を含む`@defer`ブロックは依存関係をロードします。ハイドレーションはサーバーサイドレンダリングされたコンテンツの最初のロードにのみ適用されるためです。 -次の例では、後続のクライアントサイドレンダリングでは、ビューポートで`@defer`ブロックの依存関係がロードされます。 +The `hydrate never` allows users to specify that the content in the defer block should remain dehydrated indefinitely, effectively becoming static +content. Note that this applies to the initial render only. During a subsequent client-side render, a `@defer` block with `hydrate never` would +still fetch dependencies, as hydration only applies to initial load of server-side rendered content. In the example below, subsequent client-side +renders would load the `@defer` block dependencies on viewport. ```angular-html @defer (on viewport; hydrate never) { @@ -166,11 +182,11 @@ NOTE: `hydrate when`条件は、最上位の非ハイドレーション`@defer` } ``` -NOTE: `hydrate never`を使用すると、指定された`@defer`ブロックのネストされたサブツリー全体のハイドレーションが防止されます。そのブロックの下にネストされたコンテンツに対しては、他の`hydrate`トリガーは起動しません。 +NOTE: Using `hydrate never` prevents hydration of the entire nested subtree of a given `@defer` block. No other `hydrate` triggers fire for content nested underneath that block. -## 通常のトリガーとハイドレーショントリガーの併用 +## Hydrate triggers alongside regular triggers -ハイドレーショントリガーは、`@defer`ブロックの通常のトリガーと併用される追加のトリガーです。ハイドレーションは最初のロードの最適化であるため、ハイドレーショントリガーはその最初のロードにのみ適用されます。後続のクライアントサイドレンダリングでは、通常のトリガーが引き続き使用されます。 +Hydrate triggers are additional triggers that are used alongside regular triggers on a `@defer` block. Hydration is an initial load optimization, and that means hydrate triggers only apply to that initial load. Any subsequent client side render will still use the regular trigger. ```angular-html @defer (on idle; hydrate on interaction) { @@ -180,11 +196,11 @@ NOTE: `hydrate never`を使用すると、指定された`@defer`ブロックの } ``` -この例では、最初のロード時に`hydrate on interaction`が適用されます。``コンポーネントとの対話でハイドレーションがトリガーされます。クライアントサイドでレンダリングされる後続のページロード(たとえば、このコンポーネントを含むページをロードするrouterLinkをクリックした場合)では、`on idle`が適用されます。 +In this example, on the initial load, the `hydrate on interaction` applies. Hydration will be triggered on interaction with the `` component. On any subsequent page load that is client-side rendered, for example when a user clicks a routerLink that loads a page with this component, the `on idle` will apply. -## ネストされた`@defer`ブロックとインクリメンタルハイドレーションの連携方法 +## How does incremental hydration work with nested `@defer` blocks? -Angularのコンポーネントと依存関係のシステムは階層的であるため、コンポーネントをハイドレーションするには、そのすべての親もハイドレーションされている必要があります。したがって、ネストされた非ハイドレーション`@defer`ブロックのセットの子`@defer`ブロックでハイドレーションがトリガーされた場合、ハイドレーションは最上位の非ハイドレーション`@defer`ブロックからトリガーされた子まで順にトリガーされ、その順序で実行されます。 +Angular's component and dependency system is hierarchical, which means hydrating any component requires all of its parents also be hydrated. So if hydration is triggered for a child `@defer` block of a nested set of dehydrated `@defer` blocks, hydration is triggered from the top-most dehydrated `@defer` block down to the triggered child and fire in that order. ```angular-html @defer (hydrate on interaction) { @@ -199,12 +215,12 @@ Angularのコンポーネントと依存関係のシステムは階層的であ } ``` -上記の例では、ネストされた`@defer`ブロックにホバーするとハイドレーションがトリガーされます。``を含む親`@defer`ブロックが最初にハイドレーションされ、その後``を含む子`@defer`ブロックがハイドレーションされます。 +In the above example, hovering over the nested `@defer` block triggers hydration. The parent `@defer` block with the `` hydrates first, then the child `@defer` block with `` hydrates after. -## 制約事項 +## Constraints -インクリメンタルハイドレーションには、完全なアプリケーションハイドレーションと同じ制約事項があり、直接的なDOM操作の制限や有効なHTML構造の必要性などが含まれます。詳細については、[ハイドレーションガイドの制約事項](guide/hydration#constraints)セクションを参照してください。 +Incremental hydration has the same constraints as full-application hydration, including limits on direct DOM manipulation and requiring valid HTML structure. Visit the [Hydration guide constraints](guide/hydration#constraints) section for more details. -## `@placeholder`ブロックはまだ指定する必要がありますか? +## Do I still need to specify `@placeholder` blocks? -はい。`@placeholder`ブロックのコンテンツはインクリメンタルハイドレーションには使用されませんが、後続のクライアントサイドレンダリングの場合には`@placeholder`が必要です。コンテンツが最初のロードの一部であったルートにない場合、`@defer`ブロックコンテンツを持つルートへのナビゲーションは、通常の`@defer`ブロックのようにレンダリングされます。そのため、`@placeholder`はこれらのクライアントサイドレンダリングの場合にレンダリングされます。 +Yes. `@placeholder` block content is not used for incremental hydration, but a `@placeholder` is still necessary for subsequent client-side rendering cases. If your content was not on the route that was part of the initial load, then any navigation to the route that has your `@defer` block content renders like a regular `@defer` block. So the `@placeholder` is rendered in those client-side rendering cases. diff --git a/adev-ja/src/content/guide/incremental-hydration.md.bak b/adev-ja/src/content/guide/incremental-hydration.md.bak new file mode 100644 index 0000000000..dc83c22026 --- /dev/null +++ b/adev-ja/src/content/guide/incremental-hydration.md.bak @@ -0,0 +1,210 @@ +# インクリメンタルハイドレーション + +**インクリメンタルハイドレーション**は、アプリケーションの一部を非ハイドレーション状態のままにし、必要に応じてそれらのセクションのハイドレーションを*段階的に*トリガーできる、高度なタイプの[ハイドレーション](guide/hydration)です。 + +## インクリメンタルハイドレーションを使用する理由 + +インクリメンタルハイドレーションは、完全なアプリケーションハイドレーションを基盤としたパフォーマンス向上策です。完全なアプリケーションハイドレーションと同等のエンドユーザー体験を提供しながら、より小さな初期バンドルを作成できます。バンドルサイズが小さくなると、初期ロード時間が短縮され、[First Input Delay (FID)](https://web.dev/fid)と[Cumulative Layout Shift (CLS)](https://web.dev/cls)が改善されます。 + +インクリメンタルハイドレーションを使用すると、以前は遅延できなかった可能性のあるコンテンツに遅延可能ビュー(`@defer`)を使用できるようになります。具体的には、画面の上部に表示されるコンテンツに遅延可能ビューを使用できるようになりました。インクリメンタルハイドレーション以前は、画面の上部に`@defer`ブロックを配置すると、プレースホルダーコンテンツがレンダリングされ、その後`@defer`ブロックのメインテンプレートコンテンツに置き換えられます。これにより、レイアウトシフトが発生します。インクリメンタルハイドレーションを使用すると、`@defer`ブロックのメインテンプレートは、ハイドレーション時にレイアウトシフトなしでレンダリングされます。 + +## Angularでインクリメンタルハイドレーションを有効にする方法 + +ハイドレーションによるサーバーサイドレンダリング(SSR)を既に使用しているアプリケーションでインクリメンタルハイドレーションを有効にできます。サーバーサイドレンダリングを有効にするには[Angular SSRガイド](guide/ssr)を、ハイドレーションを有効にするには[Angularハイドレーションガイド](guide/hydration)を参照してください。 + +`provideClientHydration`プロバイダーに`withIncrementalHydration()`関数を追加することで、インクリメンタルハイドレーションを有効にします。 + +```typescript +import { + bootstrapApplication, + provideClientHydration, + withIncrementalHydration, +} from '@angular/platform-browser'; +... + +bootstrapApplication(App, { + providers: [provideClientHydration(withIncrementalHydration())] +}); +``` + +インクリメンタルハイドレーションは[イベントリプレイ](guide/hydration#capturing-and-replaying-events)に依存し、自動的に有効にします。既にリストに`withEventReplay()`がある場合は、インクリメンタルハイドレーションを有効にした後、安全に削除できます。 + +## インクリメンタルハイドレーションの動作方法 + +インクリメンタルハイドレーションは、完全なアプリケーション[ハイドレーション](guide/hydration)、[遅延可能ビュー](/guide/templates/defer)、および[イベントリプレイ](guide/hydration#capturing-and-replaying-events)を基盤として構築されています。インクリメンタルハイドレーションを使用すると、インクリメンタルハイドレーションの境界を定義する`@defer`ブロックに追加のトリガーを追加できます。`defer`ブロックに`hydrate`トリガーを追加すると、Angularはサーバーサイドレンダリング中にその`defer`ブロックの依存関係をロードし、`@placeholder`ではなくメインテンプレートをレンダリングする必要があることを認識します。クライアントサイドレンダリングの場合、依存関係はまだ遅延され、`hydrate`トリガーが起動するまで`defer`ブロックのコンテンツは非ハイドレーション状態のままです。そのトリガーは、`defer`ブロックに依存関係を取得してコンテンツをハイドレーションするよう指示します。ハイドレーションの前にユーザーによってトリガーされたブラウザイベント、特にコンポーネントに登録されたリスナーと一致するイベントは、キューに入れられ、ハイドレーションプロセスが完了すると再生されます。 + +## トリガーによるコンテンツのハイドレーション制御 + +Angularが遅延コンテンツをロードしてハイドレーションするタイミングを制御する**ハイドレーショントリガー**を指定できます。これらは、通常の`@defer`トリガーとともに使用できる追加のトリガーです。 + +各`@defer`ブロックには、セミコロン(`;`)で区切られた複数のハイドレーションイベントトリガーを含めることができます。Angularは、いずれかのトリガーが起動するとハイドレーションを開始します。 + +ハイドレーショントリガーには、`hydrate on`、`hydrate when`、`hydrate never`の3種類があります。 + +### `hydrate on` + +`hydrate on`は、`@defer`ブロックのハイドレーションがトリガーされる条件を指定します。 + +使用可能なトリガーは次のとおりです。 + +| トリガー | 説明 | +| --------------------------------------------------- | ---------------------------------------------------------------------- | +| [`hydrate on idle`](#hydrate-on-idle) | ブラウザがアイドル状態になったときにトリガーされます。 | +| [`hydrate on viewport`](#hydrate-on-viewport) | 指定されたコンテンツがビューポートに入ったときにトリガーされます。 | +| [`hydrate on interaction`](#hydrate-on-interaction) | ユーザーが指定された要素と対話したときにトリガーされます。 | +| [`hydrate on hover`](#hydrate-on-hover) | マウスが指定された領域にホバーしたときにトリガーされます。 | +| [`hydrate on immediate`](#hydrate-on-immediate) | 非遅延コンテンツのレンダリングが完了した直後にトリガーされます。 | +| [`hydrate on timer`](#hydrate-on-timer) | 特定の期間後にトリガーされます。 | + +#### `hydrate on idle` + +`hydrate on idle`トリガーは、`requestIdleCallback`に基づいてブラウザがアイドル状態に達すると、遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 + +```angular-html +@defer (hydrate on idle) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +#### `hydrate on viewport` + +`hydrate on viewport`トリガーは、[Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API)を使用して指定されたコンテンツがビューポートに入ったときに、 +対応するアプリケーションのページの遅延可能ビューの依存関係をロードし、ハイドレーションします。 + +```angular-html +@defer (hydrate on viewport) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +#### `hydrate on interaction` + +`hydrate on interaction`トリガーは、ユーザーが`click`または`keydown`イベントを通じて指定された要素と対話したときに、 +遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 + +```angular-html +@defer (hydrate on interaction) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +#### `hydrate on hover` + +`hydrate on hover`トリガーは、`mouseover`および`focusin`イベントを通じてマウスがトリガー領域にホバーしたときに、 +遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 + +```angular-html +@defer (hydrate on hover) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +#### `hydrate on immediate` + +`hydrate on immediate`トリガーは、遅延可能ビューの依存関係をロードし、すぐにコンテンツをハイドレーションします。 +これは、他のすべての非遅延コンテンツのレンダリングが完了するとすぐに、遅延ブロックがロードされることを意味します。 + +```angular-html +@defer (hydrate on immediate) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +#### `hydrate on timer` + +`hydrate on timer`トリガーは、指定された期間後に遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 + +```angular-html +@defer (hydrate on timer(500ms)) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +期間パラメーターは、ミリ秒(`ms`)または秒(`s`)で指定する必要があります。 + +### `hydrate when` + +`hydrate when`トリガーはカスタムの条件式を受け入れ、条件が真になったときに +遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。 + +```angular-html +@defer (hydrate when condition) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +NOTE: `hydrate when`条件は、最上位の非ハイドレーション`@defer`ブロックである場合にのみトリガーされます。 +トリガーに提供される条件は親コンポーネントで指定され、トリガーされる前に存在する必要があります。 +親ブロックが非ハイドレーション状態の場合、その式はまだAngularによって解決できません。 + +### `hydrate never` + +`hydrate never`を使用すると、`defer`ブロック内のコンテンツを無期限に非ハイドレーション状態のままにでき、事実上静的コンテンツになります。 +これは最初のレンダリングにのみ適用されることに注意してください。 +後続のクライアントサイドレンダリングでは、`hydrate never`を含む`@defer`ブロックは依存関係をロードします。ハイドレーションはサーバーサイドレンダリングされたコンテンツの最初のロードにのみ適用されるためです。 +次の例では、後続のクライアントサイドレンダリングでは、ビューポートで`@defer`ブロックの依存関係がロードされます。 + +```angular-html +@defer (on viewport; hydrate never) { + +} @placeholder { +
    Large component placeholder
    +} +``` + +NOTE: `hydrate never`を使用すると、指定された`@defer`ブロックのネストされたサブツリー全体のハイドレーションが防止されます。そのブロックの下にネストされたコンテンツに対しては、他の`hydrate`トリガーは起動しません。 + +## 通常のトリガーとハイドレーショントリガーの併用 + +ハイドレーショントリガーは、`@defer`ブロックの通常のトリガーと併用される追加のトリガーです。ハイドレーションは最初のロードの最適化であるため、ハイドレーショントリガーはその最初のロードにのみ適用されます。後続のクライアントサイドレンダリングでは、通常のトリガーが引き続き使用されます。 + +```angular-html +@defer (on idle; hydrate on interaction) { + +} @placeholder { +
    Example Placeholder
    +} +``` + +この例では、最初のロード時に`hydrate on interaction`が適用されます。``コンポーネントとの対話でハイドレーションがトリガーされます。クライアントサイドでレンダリングされる後続のページロード(たとえば、このコンポーネントを含むページをロードするrouterLinkをクリックした場合)では、`on idle`が適用されます。 + +## ネストされた`@defer`ブロックとインクリメンタルハイドレーションの連携方法 + +Angularのコンポーネントと依存関係のシステムは階層的であるため、コンポーネントをハイドレーションするには、そのすべての親もハイドレーションされている必要があります。したがって、ネストされた非ハイドレーション`@defer`ブロックのセットの子`@defer`ブロックでハイドレーションがトリガーされた場合、ハイドレーションは最上位の非ハイドレーション`@defer`ブロックからトリガーされた子まで順にトリガーされ、その順序で実行されます。 + +```angular-html +@defer (hydrate on interaction) { + + @defer (hydrate on hover) { + + } @placeholder { +
    Child placeholder
    + } +} @placeholder { +
    Parent Placeholder
    +} +``` + +上記の例では、ネストされた`@defer`ブロックにホバーするとハイドレーションがトリガーされます。``を含む親`@defer`ブロックが最初にハイドレーションされ、その後``を含む子`@defer`ブロックがハイドレーションされます。 + +## 制約事項 + +インクリメンタルハイドレーションには、完全なアプリケーションハイドレーションと同じ制約事項があり、直接的なDOM操作の制限や有効なHTML構造の必要性などが含まれます。詳細については、[ハイドレーションガイドの制約事項](guide/hydration#constraints)セクションを参照してください。 + +## `@placeholder`ブロックはまだ指定する必要がありますか? + +はい。`@placeholder`ブロックのコンテンツはインクリメンタルハイドレーションには使用されませんが、後続のクライアントサイドレンダリングの場合には`@placeholder`が必要です。コンテンツが最初のロードの一部であったルートにない場合、`@defer`ブロックコンテンツを持つルートへのナビゲーションは、通常の`@defer`ブロックのようにレンダリングされます。そのため、`@placeholder`はこれらのクライアントサイドレンダリングの場合にレンダリングされます。 From 78201e3b6707bd0fd39a57590dfe79fb26db9405 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:19 +0900 Subject: [PATCH 13/66] fix(migrate): md-class-c src/content/guide/aria --- adev-ja/src/content/guide/aria/overview.en.md | 17 ++++++++++++++--- adev-ja/src/content/guide/aria/overview.md | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/adev-ja/src/content/guide/aria/overview.en.md b/adev-ja/src/content/guide/aria/overview.en.md index 380ca0d4f9..159425cde6 100644 --- a/adev-ja/src/content/guide/aria/overview.en.md +++ b/adev-ja/src/content/guide/aria/overview.en.md @@ -9,9 +9,20 @@ Angular Aria is a collection of headless, accessible directives that implement c ## Installation -```shell -npm install @angular/aria -``` + + + npm install @angular/aria + + + yarn add @angular/aria + + + pnpm add @angular/aria + + + bun add @angular/aria + + ## Showcase diff --git a/adev-ja/src/content/guide/aria/overview.md b/adev-ja/src/content/guide/aria/overview.md index 554654e25f..7b4690f4b3 100644 --- a/adev-ja/src/content/guide/aria/overview.md +++ b/adev-ja/src/content/guide/aria/overview.md @@ -9,9 +9,20 @@ Angular Ariaは、一般的な[WAI-ARIAパターン](https://www.w3.org/WAI/ARIA ## インストール {#installation} -```shell -npm install @angular/aria -``` + + + npm install @angular/aria + + + yarn add @angular/aria + + + pnpm add @angular/aria + + + bun add @angular/aria + + ## ショーケース {#showcase} From 2e581cdeac5b2cb095040aa94ba42ee1ed0cfa78 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Thu, 4 Jun 2026 10:21:19 +0900 Subject: [PATCH 14/66] chore(migrate): defer md-class-d src/content/guide/aria --- .../src/content/guide/aria/accordion.en.md | 249 ----------------- adev-ja/src/content/guide/aria/accordion.md | 232 +++++++++------- .../src/content/guide/aria/accordion.md.bak | 249 +++++++++++++++++ .../src/content/guide/aria/autocomplete.md | 206 ++++++++++---- ...autocomplete.en.md => autocomplete.md.bak} | 106 ++++---- adev-ja/src/content/guide/aria/combobox.en.md | 255 ----------------- adev-ja/src/content/guide/aria/combobox.md | 257 ++++++++++-------- .../src/content/guide/aria/combobox.md.bak | 255 +++++++++++++++++ adev-ja/src/content/guide/aria/grid.en.md | 80 ++++++ adev-ja/src/content/guide/aria/grid.md | 80 ++++++ adev-ja/src/content/guide/aria/listbox.en.md | 55 +++- adev-ja/src/content/guide/aria/listbox.md | 49 ++++ adev-ja/src/content/guide/aria/menu.en.md | 83 +++++- adev-ja/src/content/guide/aria/menu.md | 71 +++++ adev-ja/src/content/guide/aria/menubar.en.md | 62 ++++- adev-ja/src/content/guide/aria/menubar.md | 52 ++++ .../src/content/guide/aria/multiselect.en.md | 201 -------------- adev-ja/src/content/guide/aria/multiselect.md | 190 +++++++++---- .../src/content/guide/aria/multiselect.md.bak | 201 ++++++++++++++ adev-ja/src/content/guide/aria/select.en.md | 193 ------------- adev-ja/src/content/guide/aria/select.md | 187 +++++++++---- adev-ja/src/content/guide/aria/select.md.bak | 193 +++++++++++++ adev-ja/src/content/guide/aria/tabs.en.md | 75 ++++- adev-ja/src/content/guide/aria/tabs.md | 65 ++++- adev-ja/src/content/guide/aria/toolbar.en.md | 44 +++ adev-ja/src/content/guide/aria/toolbar.md | 44 +++ adev-ja/src/content/guide/aria/tree.en.md | 230 ---------------- adev-ja/src/content/guide/aria/tree.md | 255 ++++++++++------- adev-ja/src/content/guide/aria/tree.md.bak | 230 ++++++++++++++++ 29 files changed, 2784 insertions(+), 1665 deletions(-) delete mode 100644 adev-ja/src/content/guide/aria/accordion.en.md create mode 100644 adev-ja/src/content/guide/aria/accordion.md.bak rename adev-ja/src/content/guide/aria/{autocomplete.en.md => autocomplete.md.bak} (54%) delete mode 100644 adev-ja/src/content/guide/aria/combobox.en.md create mode 100644 adev-ja/src/content/guide/aria/combobox.md.bak delete mode 100644 adev-ja/src/content/guide/aria/multiselect.en.md create mode 100644 adev-ja/src/content/guide/aria/multiselect.md.bak delete mode 100644 adev-ja/src/content/guide/aria/select.en.md create mode 100644 adev-ja/src/content/guide/aria/select.md.bak delete mode 100644 adev-ja/src/content/guide/aria/tree.en.md create mode 100644 adev-ja/src/content/guide/aria/tree.md.bak diff --git a/adev-ja/src/content/guide/aria/accordion.en.md b/adev-ja/src/content/guide/aria/accordion.en.md deleted file mode 100644 index 4a27dc5e0b..0000000000 --- a/adev-ja/src/content/guide/aria/accordion.en.md +++ /dev/null @@ -1,249 +0,0 @@ - - - - - - - - -## Overview - -An accordion organizes related content into expandable and collapsible sections, reducing page scrolling and helping users focus on relevant information. Each section has a trigger button and a content panel. Clicking a trigger toggles the visibility of its associated panel. - - - - - - - -## Usage - -Accordions work well for organizing content into logical groups where users typically need to view one section at a time. - -**Use accordions when:** - -- Displaying FAQs with multiple questions and answers -- Organizing long forms into manageable sections -- Reducing scrolling on content-heavy pages -- Progressively disclosing related information - -**Avoid accordions when:** - -- Building navigation menus (use the [Menu](guide/aria/menu) component instead) -- Creating tabbed interfaces (use the [Tabs](guide/aria/tabs) component instead) -- Showing a single collapsible section (use a disclosure pattern instead) -- Users need to see multiple sections simultaneously (consider a different layout) - -## Features - -- **Expansion modes** - Control whether one or multiple panels can be open at the same time -- **Keyboard navigation** - Navigate between triggers using arrow keys, Home, and End -- **Lazy rendering** - Content is only created when a panel first expands, improving initial load performance -- **Disabled states** - Disable the entire group or individual triggers -- **Focus management** - Control whether disabled items can receive keyboard focus -- **Programmatic control** - Expand, collapse, or toggle panels from your component code -- **RTL support** - Automatic support for right-to-left languages - -## Examples - -### Single expansion mode - -Set `[multiExpandable]="false"` to allow only one panel to be open at a time. Opening a new panel automatically closes any previously open panel. - - - - - - - - - - - - - - - - - - - - - - - - - -This mode works well for FAQs or situations where you want users to focus on one answer at a time. - -### Multiple expansion mode - -Set `[multiExpandable]="true"` to allow multiple panels to be open simultaneously. Users can expand as many panels as needed without closing others. - - - - - - - - - - - - - - - - - - - - - - - - - -This mode is useful for form sections or when users need to compare content across multiple panels. - -NOTE: The `multiExpandable` input defaults to `true`. Set it to `false` explicitly if you want single expansion behavior. - -### Disabled accordion items - -Disable specific triggers using the `disabled` input. Control how disabled items behave during keyboard navigation using the `softDisabled` input on the accordion group. - - - - - - - - - - - - - - - - - - - - - - - - - -When `[softDisabled]="true"` (the default), disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped entirely during keyboard navigation. - -### Lazy content rendering - -Use the `ngAccordionContent` directive on an `ng-template` to defer rendering content until the panel first expands. This improves performance for accordions with heavy content like images, charts, or complex components. - -```angular-html -
    -
    - -
    - - - Description - - -
    -
    -
    -``` - -By default, content remains in the DOM after the panel collapses. Set `[preserveContent]="false"` to remove the content from the DOM when the panel closes. - -## APIs - -### AccordionGroup - -The container directive that manages keyboard navigation and expansion behavior for a group of accordion items. - -#### Inputs - -| Property | Type | Default | Description | -| ----------------- | --------- | ------- | ------------------------------------------------------------------------- | -| `disabled` | `boolean` | `false` | Disables all triggers in the group | -| `multiExpandable` | `boolean` | `true` | Whether multiple panels can be expanded simultaneously | -| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable. When `false`, they are skipped | -| `wrap` | `boolean` | `false` | Whether keyboard navigation wraps from last to first item and vice versa | - -#### Methods - -| Method | Parameters | Description | -| ------------- | ---------- | ---------------------------------------------------------------- | -| `expandAll` | none | Expands all panels (only works when `multiExpandable` is `true`) | -| `collapseAll` | none | Collapses all panels | - -### AccordionTrigger - -The directive applied to the button element that toggles panel visibility. - -#### Inputs - -| Property | Type | Default | Description | -| ---------- | --------- | ------- | -------------------------------------------------------------- | -| `id` | `string` | auto | Unique identifier for the trigger | -| `panelId` | `string` | — | **Required.** Must match the `panelId` of the associated panel | -| `disabled` | `boolean` | `false` | Disables this trigger | -| `expanded` | `boolean` | `false` | Whether the panel is expanded (supports two-way binding) | - -#### Signals - -| Property | Type | Description | -| -------- | ----------------- | --------------------------------------- | -| `active` | `Signal` | Whether the trigger currently has focus | - -#### Methods - -| Method | Parameters | Description | -| ---------- | ---------- | --------------------------------- | -| `expand` | none | Expands the associated panel | -| `collapse` | none | Collapses the associated panel | -| `toggle` | none | Toggles the panel expansion state | - -### AccordionPanel - -The directive applied to the element containing the collapsible content. - -#### Inputs - -| Property | Type | Default | Description | -| ----------------- | --------- | ------- | ---------------------------------------------------------------- | -| `id` | `string` | auto | Unique identifier for the panel | -| `panelId` | `string` | — | **Required.** Must match the `panelId` of the associated trigger | -| `preserveContent` | `boolean` | `true` | Whether to keep content in DOM after panel collapses | - -#### Signals - -| Property | Type | Description | -| --------- | ----------------- | --------------------------------------- | -| `visible` | `Signal` | Whether the panel is currently expanded | - -#### Methods - -| Method | Parameters | Description | -| ---------- | ---------- | --------------------------- | -| `expand` | none | Expands this panel | -| `collapse` | none | Collapses this panel | -| `toggle` | none | Toggles the expansion state | - -### AccordionContent - -The structural directive applied to an `ng-template` inside an accordion panel to enable lazy rendering. - -This directive has no inputs, outputs, or methods. Apply it to an `ng-template` element: - -```angular-html -
    - - - -
    -``` diff --git a/adev-ja/src/content/guide/aria/accordion.md b/adev-ja/src/content/guide/aria/accordion.md index df3d224cf9..d99a4a7ab8 100644 --- a/adev-ja/src/content/guide/aria/accordion.md +++ b/adev-ja/src/content/guide/aria/accordion.md @@ -1,14 +1,14 @@ - + - - + + -## 概要 {#overview} +## Overview -アコーディオンは、関連するコンテンツを展開・折りたたみ可能なセクションに整理し、ページのスクロールを減らし、ユーザーが関連情報に集中するのを助けます。各セクションには、トリガーボタンとコンテンツパネルがあります。トリガーをクリックすると、関連するパネルの表示/非表示が切り替わります。 +An accordion organizes related content into expandable and collapsible sections, reducing page scrolling and helping users focus on relevant information. Each section has a trigger button and a content panel. Clicking a trigger toggles the visibility of its associated panel. @@ -16,39 +16,39 @@ -## 使い方 {#usage} +## Usage -アコーディオンは、ユーザーが通常一度に1つのセクションを表示する必要がある場合に、コンテンツを論理的なグループに整理するのに適しています。 +Accordions work well for organizing content into logical groups where users typically need to view one section at a time. -**アコーディオンを使用する場合:** +**Use accordions when:** -- 複数の質問と回答を持つFAQを表示する -- 長いフォームを管理しやすいセクションに整理する -- コンテンツの多いページでのスクロールを減らす -- 関連情報を段階的に開示する +- Displaying FAQs with multiple questions and answers +- Organizing long forms into manageable sections +- Reducing scrolling on content-heavy pages +- Progressively disclosing related information -**アコーディオンを避けるべき場合:** +**Avoid accordions when:** -- ナビゲーションメニューを構築する(代わりに[Menu](guide/aria/menu)コンポーネントを使用してください) -- タブ付きインターフェースを作成する(代わりに[Tabs](guide/aria/tabs)コンポーネントを使用してください) -- 単一の折りたたみ可能なセクションを表示する(代わりにdisclosureパターンを使用してください) -- ユーザーが複数のセクションを同時に見る必要がある(異なるレイアウトを検討してください) +- Building navigation menus (use the [Menu](guide/aria/menu) component instead) +- Creating tabbed interfaces (use the [Tabs](guide/aria/tabs) component instead) +- Showing a single collapsible section (use a disclosure pattern instead) +- Users need to see multiple sections simultaneously (consider a different layout) -## 機能 {#features} +## Features -- **展開モード** - 一度に1つまたは複数のパネルを開けるかどうかを制御します -- **キーボードナビゲーション** - 矢印キー、Home、Endを使用してトリガー間を移動します -- **遅延レンダリング** - コンテンツはパネルが最初に展開されたときにのみ作成され、初期読み込みのパフォーマンスを向上させます -- **無効状態** - グループ全体または個々のトリガーを無効にします -- **フォーカス管理** - 無効化されたアイテムがキーボードフォーカスを受け取れるかどうかを制御します -- **プログラムによる制御** - コンポーネントのコードからパネルを展開、折りたたみ、または切り替えます -- **RTLサポート** - 右から左へ記述する言語を自動的にサポートします +- **Expansion modes** - Control whether one or multiple panels can be open at the same time +- **Keyboard navigation** - Navigate between triggers using arrow keys, Home, and End +- **Lazy rendering** - Content is only created when a panel first expands, improving initial load performance +- **Disabled states** - Disable the entire group or individual triggers +- **Focus management** - Control whether disabled items can receive keyboard focus +- **Programmatic control** - Expand, collapse, or toggle panels from your component code +- **RTL support** - Automatic support for right-to-left languages -## 例 {#examples} +## Examples -### 単一展開モード {#single-expansion-mode} +### Single expansion mode -`[multiExpandable]="false"`を設定すると、一度に開けるパネルが1つだけになります。新しいパネルを開くと、以前に開いていたパネルは自動的に閉じます。 +Set `[multiExpandable]="false"` to allow only one panel to be open at a time. Opening a new panel automatically closes any previously open panel. @@ -74,11 +74,11 @@ -このモードは、FAQや、ユーザーに一度に1つの回答に集中してもらいたい場合に適しています。 +This mode works well for FAQs or situations where you want users to focus on one answer at a time. -### 複数展開モード {#multiple-expansion-mode} +### Multiple expansion mode -`[multiExpandable]="true"`を設定すると、複数のパネルを同時に開くことができます。ユーザーは他のパネルを閉じることなく、必要なだけパネルを展開できます。 +Set `[multiExpandable]="true"` to allow multiple panels to be open simultaneously. Users can expand as many panels as needed without closing others. @@ -104,13 +104,13 @@ -このモードは、フォームのセクションや、ユーザーが複数のパネルにわたるコンテンツを比較する必要がある場合に便利です。 +This mode is useful for form sections or when users need to compare content across multiple panels. -NOTE: `multiExpandable`入力はデフォルトで`true`です。単一展開の動作が必要な場合は、明示的に`false`に設定してください。 +NOTE: The `multiExpandable` input defaults to `true`. Set it to `false` explicitly if you want single expansion behavior. -### 無効化されたアコーディオンアイテム {#disabled-accordion-items} +### Disabled accordion items -`disabled`入力を使用して特定のトリガーを無効にします。アコーディオンのグループで`softDisabled`入力を使用し、キーボードナビゲーション中に無効化されたアイテムの動作を制御します。 +Disable specific triggers using the `disabled` input. Control how disabled items behave during keyboard navigation using the `softDisabled` input on the accordion group. @@ -136,19 +136,19 @@ NOTE: `multiExpandable`入力はデフォルトで`true`です。単一展開の -`[softDisabled]="true"`(デフォルト)の場合、無効化されたアイテムはフォーカスを受け取れますが、アクティブにはできません。`[softDisabled]="false"`の場合、無効化されたアイテムはキーボードナビゲーション中に完全にスキップされます。 +When `[softDisabled]="true"` (the default), disabled items can receive focus but cannot be activated. When `[softDisabled]="false"`, disabled items are skipped entirely during keyboard navigation. -### コンテンツの遅延レンダリング {#lazy-content-rendering} +### Lazy content rendering -`ngAccordionContent`ディレクティブを`ng-template`で使用すると、パネルが最初に展開されるまでコンテンツのレンダリングを遅延させることができます。これにより、画像、チャート、または複雑なコンポーネントなどの重いコンテンツを持つアコーディオンのパフォーマンスが向上します。 +Use the `ngAccordionContent` directive on an `ng-template` to defer rendering content until the panel first expands. This improves performance for accordions with heavy content like images, charts, or complex components. ```angular-html
    - -
    + +
    - + Description @@ -157,91 +157,139 @@ NOTE: `multiExpandable`入力はデフォルトで`true`です。単一展開の
    ``` -デフォルトでは、パネルが折りたたまれた後もコンテンツはDOMに残ります。パネルが閉じたときにDOMからコンテンツを削除するには、`[preserveContent]="false"`を設定します。 +By default, content remains in the DOM after the panel collapses. Set `[preserveContent]="false"` to remove the content from the DOM when the panel closes. -## API +## Testing -### AccordionGroup {#accordiongroup} +Angular Aria provides component harnesses for testing accordion components. +Here is an example of how to use the harnesses in a component test: -アコーディオンアイテムのグループのキーボードナビゲーションと展開動作を管理するコンテナディレクティブです。 +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {AccordionGroupHarness} from '@angular/aria/accordion/testing'; +import {MyAccordionComponent} from './my-accordion'; // Your component -#### Inputs {#inputs} +describe('MyAccordionComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; -| プロパティ | 型 | デフォルト | 説明 | + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyAccordionComponent], + }); + + fixture = TestBed.createComponent(MyAccordionComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should allow expanding panels', async () => { + // Load the accordion group harness + const group = await loader.getHarness(AccordionGroupHarness); + + // Get all individual accordions (items) in the group + const accordions = await group.getAccordions(); + expect(accordions.length).toBe(3); + + // Verify initial state (first expanded, others collapsed) + expect(await accordions[0].isExpanded()).toBe(true); + expect(await accordions[1].isExpanded()).toBe(false); + + // Expand the second panel + await accordions[1].expand(); + + // Verify updated state + expect(await accordions[1].isExpanded()).toBe(true); + // If multiExpandable is false, the first one should now be collapsed + expect(await accordions[0].isExpanded()).toBe(false); + }); +}); +``` + +## APIs + +### AccordionGroup + +The container directive that manages keyboard navigation and expansion behavior for a group of accordion items. + +#### Inputs + +| Property | Type | Default | Description | | ----------------- | --------- | ------- | ------------------------------------------------------------------------- | -| `disabled` | `boolean` | `false` | グループ内のすべてのトリガーを無効にします | -| `multiExpandable` | `boolean` | `true` | 複数のパネルを同時に展開できるかどうか | -| `softDisabled` | `boolean` | `true` | `true`の場合、無効化されたアイテムはフォーカス可能です。`false`の場合、スキップされます | -| `wrap` | `boolean` | `false` | キーボードナビゲーションが最後のアイテムから最初のアイテムへ、またはその逆にラップするかどうか | +| `disabled` | `boolean` | `false` | Disables all triggers in the group | +| `multiExpandable` | `boolean` | `true` | Whether multiple panels can be expanded simultaneously | +| `softDisabled` | `boolean` | `true` | When `true`, disabled items are focusable. When `false`, they are skipped | +| `wrap` | `boolean` | `false` | Whether keyboard navigation wraps from last to first item and vice versa | -#### Methods {#methods} +#### Methods -| メソッド | パラメータ | 説明 | +| Method | Parameters | Description | | ------------- | ---------- | ---------------------------------------------------------------- | -| `expandAll` | none | すべてのパネルを展開します(`multiExpandable`が`true`の場合のみ機能します) | -| `collapseAll` | none | すべてのパネルを折りたたみます | +| `expandAll` | none | Expands all panels (only works when `multiExpandable` is `true`) | +| `collapseAll` | none | Collapses all panels | -### AccordionTrigger {#accordiontrigger} +### AccordionTrigger -パネルの表示/非表示を切り替えるボタン要素に適用されるディレクティブです。 +The directive applied to the button element that toggles panel visibility. -#### Inputs {#inputs} +#### Inputs -| プロパティ | 型 | デフォルト | 説明 | -| ---------- | --------- | ------- | -------------------------------------------------------------- | -| `id` | `string` | auto | トリガーの一意の識別子 | -| `panelId` | `string` | — | **必須。**関連付けられたパネルの`panelId`と一致する必要があります | -| `disabled` | `boolean` | `false` | このトリガーを無効にします | -| `expanded` | `boolean` | `false` | パネルが展開されているかどうか(双方向バインディングをサポート) | +| Property | Type | Default | Description | +| ---------- | ---------------- | ------- | -------------------------------------------------------------- | +| `panel` | `AccordionPanel` | — | **Required.** The reference of the controlled accordion panel. | +| `id` | `string` | auto | Unique identifier for the trigger | +| `disabled` | `boolean` | `false` | Disables this trigger | +| `expanded` | `boolean` | `false` | Whether the panel is expanded (supports two-way binding) | -#### シグナル {#signals} +#### Signals -| プロパティ | 型 | 説明 | +| Property | Type | Description | | -------- | ----------------- | --------------------------------------- | -| `active` | `Signal` | トリガーが現在フォーカスを持っているかどうか | +| `active` | `Signal` | Whether the trigger currently has focus | -#### Methods {#methods} +#### Methods -| メソッド | パラメータ | 説明 | +| Method | Parameters | Description | | ---------- | ---------- | --------------------------------- | -| `expand` | none | 関連付けられたパネルを展開します | -| `collapse` | none | 関連付けられたパネルを折りたたみます | -| `toggle` | none | パネルの展開状態を切り替えます | +| `expand` | none | Expands the associated panel | +| `collapse` | none | Collapses the associated panel | +| `toggle` | none | Toggles the panel expansion state | -### AccordionPanel {#accordionpanel} +### AccordionPanel -折りたたみ可能なコンテンツを含む要素に適用されるディレクティブです。 +The directive applied to the element containing the collapsible content. -#### Inputs {#inputs} +#### Inputs -| プロパティ | 型 | デフォルト | 説明 | -| ----------------- | --------- | ------- | ---------------------------------------------------------------- | -| `id` | `string` | auto | パネルの一意の識別子 | -| `panelId` | `string` | — | **必須。**関連付けられたトリガーの`panelId`と一致する必要があります | -| `preserveContent` | `boolean` | `true` | パネルが折りたたまれた後もコンテンツをDOMに保持するかどうか | +| Property | Type | Default | Description | +| ----------------- | --------- | ------- | ---------------------------------------------------- | +| `id` | `string` | auto | Unique identifier for the panel | +| `preserveContent` | `boolean` | `true` | Whether to keep content in DOM after panel collapses | -#### シグナル {#signals} +#### Signals -| プロパティ | 型 | 説明 | +| Property | Type | Description | | --------- | ----------------- | --------------------------------------- | -| `visible` | `Signal` | パネルが現在展開されているかどうか | +| `visible` | `Signal` | Whether the panel is currently expanded | -#### Methods {#methods} +#### Methods -| メソッド | パラメータ | 説明 | +| Method | Parameters | Description | | ---------- | ---------- | --------------------------- | -| `expand` | none | このパネルを展開します | -| `collapse` | none | このパネルを折りたたみます | -| `toggle` | none | 展開状態を切り替えます | +| `expand` | none | Expands this panel | +| `collapse` | none | Collapses this panel | +| `toggle` | none | Toggles the expansion state | -### AccordionContent {#accordioncontent} +### AccordionContent -遅延レンダリングを有効にするために、アコーディオンパネル内の`ng-template`に適用される構造ディレクティブです。 +The structural directive applied to an `ng-template` inside an accordion panel to enable lazy rendering. -このディレクティブには、input、output、メソッドはありません。`ng-template`要素に適用してください: +This directive has no inputs, outputs, or methods. Apply it to an `ng-template` element: ```angular-html -
    +
    diff --git a/adev-ja/src/content/guide/aria/accordion.md.bak b/adev-ja/src/content/guide/aria/accordion.md.bak new file mode 100644 index 0000000000..df3d224cf9 --- /dev/null +++ b/adev-ja/src/content/guide/aria/accordion.md.bak @@ -0,0 +1,249 @@ + + + + + + + + +## 概要 {#overview} + +アコーディオンは、関連するコンテンツを展開・折りたたみ可能なセクションに整理し、ページのスクロールを減らし、ユーザーが関連情報に集中するのを助けます。各セクションには、トリガーボタンとコンテンツパネルがあります。トリガーをクリックすると、関連するパネルの表示/非表示が切り替わります。 + + + + + + + +## 使い方 {#usage} + +アコーディオンは、ユーザーが通常一度に1つのセクションを表示する必要がある場合に、コンテンツを論理的なグループに整理するのに適しています。 + +**アコーディオンを使用する場合:** + +- 複数の質問と回答を持つFAQを表示する +- 長いフォームを管理しやすいセクションに整理する +- コンテンツの多いページでのスクロールを減らす +- 関連情報を段階的に開示する + +**アコーディオンを避けるべき場合:** + +- ナビゲーションメニューを構築する(代わりに[Menu](guide/aria/menu)コンポーネントを使用してください) +- タブ付きインターフェースを作成する(代わりに[Tabs](guide/aria/tabs)コンポーネントを使用してください) +- 単一の折りたたみ可能なセクションを表示する(代わりにdisclosureパターンを使用してください) +- ユーザーが複数のセクションを同時に見る必要がある(異なるレイアウトを検討してください) + +## 機能 {#features} + +- **展開モード** - 一度に1つまたは複数のパネルを開けるかどうかを制御します +- **キーボードナビゲーション** - 矢印キー、Home、Endを使用してトリガー間を移動します +- **遅延レンダリング** - コンテンツはパネルが最初に展開されたときにのみ作成され、初期読み込みのパフォーマンスを向上させます +- **無効状態** - グループ全体または個々のトリガーを無効にします +- **フォーカス管理** - 無効化されたアイテムがキーボードフォーカスを受け取れるかどうかを制御します +- **プログラムによる制御** - コンポーネントのコードからパネルを展開、折りたたみ、または切り替えます +- **RTLサポート** - 右から左へ記述する言語を自動的にサポートします + +## 例 {#examples} + +### 単一展開モード {#single-expansion-mode} + +`[multiExpandable]="false"`を設定すると、一度に開けるパネルが1つだけになります。新しいパネルを開くと、以前に開いていたパネルは自動的に閉じます。 + + + + + + + + + + + + + + + + + + + + + + + + + +このモードは、FAQや、ユーザーに一度に1つの回答に集中してもらいたい場合に適しています。 + +### 複数展開モード {#multiple-expansion-mode} + +`[multiExpandable]="true"`を設定すると、複数のパネルを同時に開くことができます。ユーザーは他のパネルを閉じることなく、必要なだけパネルを展開できます。 + + + + + + + + + + + + + + + + + + + + + + + + + +このモードは、フォームのセクションや、ユーザーが複数のパネルにわたるコンテンツを比較する必要がある場合に便利です。 + +NOTE: `multiExpandable`入力はデフォルトで`true`です。単一展開の動作が必要な場合は、明示的に`false`に設定してください。 + +### 無効化されたアコーディオンアイテム {#disabled-accordion-items} + +`disabled`入力を使用して特定のトリガーを無効にします。アコーディオンのグループで`softDisabled`入力を使用し、キーボードナビゲーション中に無効化されたアイテムの動作を制御します。 + + + + + + + + + + + + + + + + + + + + + + + + + +`[softDisabled]="true"`(デフォルト)の場合、無効化されたアイテムはフォーカスを受け取れますが、アクティブにはできません。`[softDisabled]="false"`の場合、無効化されたアイテムはキーボードナビゲーション中に完全にスキップされます。 + +### コンテンツの遅延レンダリング {#lazy-content-rendering} + +`ngAccordionContent`ディレクティブを`ng-template`で使用すると、パネルが最初に展開されるまでコンテンツのレンダリングを遅延させることができます。これにより、画像、チャート、または複雑なコンポーネントなどの重いコンテンツを持つアコーディオンのパフォーマンスが向上します。 + +```angular-html +
    +
    + +
    + + + Description + + +
    +
    +
    +``` + +デフォルトでは、パネルが折りたたまれた後もコンテンツはDOMに残ります。パネルが閉じたときにDOMからコンテンツを削除するには、`[preserveContent]="false"`を設定します。 + +## API + +### AccordionGroup {#accordiongroup} + +アコーディオンアイテムのグループのキーボードナビゲーションと展開動作を管理するコンテナディレクティブです。 + +#### Inputs {#inputs} + +| プロパティ | 型 | デフォルト | 説明 | +| ----------------- | --------- | ------- | ------------------------------------------------------------------------- | +| `disabled` | `boolean` | `false` | グループ内のすべてのトリガーを無効にします | +| `multiExpandable` | `boolean` | `true` | 複数のパネルを同時に展開できるかどうか | +| `softDisabled` | `boolean` | `true` | `true`の場合、無効化されたアイテムはフォーカス可能です。`false`の場合、スキップされます | +| `wrap` | `boolean` | `false` | キーボードナビゲーションが最後のアイテムから最初のアイテムへ、またはその逆にラップするかどうか | + +#### Methods {#methods} + +| メソッド | パラメータ | 説明 | +| ------------- | ---------- | ---------------------------------------------------------------- | +| `expandAll` | none | すべてのパネルを展開します(`multiExpandable`が`true`の場合のみ機能します) | +| `collapseAll` | none | すべてのパネルを折りたたみます | + +### AccordionTrigger {#accordiontrigger} + +パネルの表示/非表示を切り替えるボタン要素に適用されるディレクティブです。 + +#### Inputs {#inputs} + +| プロパティ | 型 | デフォルト | 説明 | +| ---------- | --------- | ------- | -------------------------------------------------------------- | +| `id` | `string` | auto | トリガーの一意の識別子 | +| `panelId` | `string` | — | **必須。**関連付けられたパネルの`panelId`と一致する必要があります | +| `disabled` | `boolean` | `false` | このトリガーを無効にします | +| `expanded` | `boolean` | `false` | パネルが展開されているかどうか(双方向バインディングをサポート) | + +#### シグナル {#signals} + +| プロパティ | 型 | 説明 | +| -------- | ----------------- | --------------------------------------- | +| `active` | `Signal` | トリガーが現在フォーカスを持っているかどうか | + +#### Methods {#methods} + +| メソッド | パラメータ | 説明 | +| ---------- | ---------- | --------------------------------- | +| `expand` | none | 関連付けられたパネルを展開します | +| `collapse` | none | 関連付けられたパネルを折りたたみます | +| `toggle` | none | パネルの展開状態を切り替えます | + +### AccordionPanel {#accordionpanel} + +折りたたみ可能なコンテンツを含む要素に適用されるディレクティブです。 + +#### Inputs {#inputs} + +| プロパティ | 型 | デフォルト | 説明 | +| ----------------- | --------- | ------- | ---------------------------------------------------------------- | +| `id` | `string` | auto | パネルの一意の識別子 | +| `panelId` | `string` | — | **必須。**関連付けられたトリガーの`panelId`と一致する必要があります | +| `preserveContent` | `boolean` | `true` | パネルが折りたたまれた後もコンテンツをDOMに保持するかどうか | + +#### シグナル {#signals} + +| プロパティ | 型 | 説明 | +| --------- | ----------------- | --------------------------------------- | +| `visible` | `Signal` | パネルが現在展開されているかどうか | + +#### Methods {#methods} + +| メソッド | パラメータ | 説明 | +| ---------- | ---------- | --------------------------- | +| `expand` | none | このパネルを展開します | +| `collapse` | none | このパネルを折りたたみます | +| `toggle` | none | 展開状態を切り替えます | + +### AccordionContent {#accordioncontent} + +遅延レンダリングを有効にするために、アコーディオンパネル内の`ng-template`に適用される構造ディレクティブです。 + +このディレクティブには、input、output、メソッドはありません。`ng-template`要素に適用してください: + +```angular-html +
    + + + +
    +``` diff --git a/adev-ja/src/content/guide/aria/autocomplete.md b/adev-ja/src/content/guide/aria/autocomplete.md index 79d1b8d688..b7906c8ab5 100644 --- a/adev-ja/src/content/guide/aria/autocomplete.md +++ b/adev-ja/src/content/guide/aria/autocomplete.md @@ -1,12 +1,12 @@ - + -## 概要 {#overview} +## Overview -ユーザーが入力するにつれてオプションをフィルタリングして提案し、リストから値を見つけて選択するのに役立つ、アクセシブルな入力フィールドです。 +An accessible input field that filters and suggests options as users type, helping them find and select values from a list. - + @@ -22,7 +22,7 @@ - + @@ -31,37 +31,37 @@ -## 使用法 {#usage} +## Usage -オートコンプリートは、タイピングがスクロールよりも速い大規模なオプションのセットからユーザーが選択する必要がある場合に最適です。次のような場合にオートコンプリートの使用を検討してください: +Autocomplete works best when users need to select from a large set of options where typing is faster than scrolling. Consider using autocomplete when: -- **オプションリストが長い** (20項目以上) - タイピングすることで、ドロップダウンをスクロールするよりも速く選択肢を絞り込めます -- **ユーザーが探しているものを知っている** - 期待される値の一部を入力できます (州名、製品名、ユーザー名など) -- **オプションが予測可能なパターンに従っている** - ユーザーが部分一致を推測できます (国コード、メールドメイン、カテゴリーなど) -- **スピードが重要である** - フォームは、広範なナビゲーションなしで迅速に選択できるというメリットがあります +- **The option list is long** (more than 20 items) - Typing narrows down choices faster than scrolling through a dropdown +- **Users know what they're looking for** - They can type part of the expected value (like a state name, product, or username) +- **Options follow predictable patterns** - Users can guess partial matches (like country codes, email domains, or categories) +- **Speed matters** - Forms benefit from quick selection without extensive navigation -次のような場合はオートコンプリートを避けてください: +Avoid autocomplete when: -- リストのオプションが10個未満の場合 - 通常のドロップダウンやラジオグループの方が視認性が高いためです -- ユーザーがオプションを閲覧する必要がある場合 - 発見が重要である場合は、すべてのオプションを最初から表示します -- オプションが馴染みのないものである場合 - ユーザーはリストに存在することを知らないものを入力できないためです +- The list has fewer than 10 options - A regular dropdown or radio group provides better visibility +- Users need to browse options - If discovery is important, show all options upfront +- Options are unfamiliar - Users can't type what they don't know exists in the list -## 機能 {#features} +## Features -Angularのオートコンプリートは、以下の機能を備えた、完全にアクセシブルなコンボボックスの実装を提供します: +Angular's autocomplete provides a fully accessible combobox implementation with: -- **キーボードナビゲーション** - 矢印キーでオプションを移動し、Enterで選択、Escapeで閉じます -- **スクリーンリーダーのサポート** - 支援技術のための組み込みARIA属性 -- **3つのフィルターモード** - 自動選択、手動選択、またはハイライト表示の動作から選択できます -- **シグナルベースのリアクティビティ** - Angularシグナルを使用したリアクティブな状態管理 -- **Popover APIとの統合** - ネイティブのHTML Popover APIを活用し、最適な配置を実現します -- **双方向テキストのサポート** - 右から左へ記述する言語(RTL)に自動的に対応します +- **Keyboard Navigation** - Navigate options with arrow keys, select with Enter, close with Escape +- **Screen Reader Support** - Built-in ARIA attributes for assistive technologies +- **Dynamic Highlight Behavior** - Built-in support for inline selection suggestions +- **Signal-Based Reactivity** - Reactive state management using Angular signals +- **Popover API Integration** - Leverages the native HTML Popover API for optimal positioning +- **Bidirectional Text Support** - Automatically handles right-to-left (RTL) languages -## 例 +## Examples -### 自動選択モード {#auto-select-mode} +### Auto-select mode -ユーザーがテキストの一部を入力すると、その入力が利用可能なオプションと一致することの即時確認が期待されます。自動選択モードは、ユーザーが入力するにつれて、フィルタリングされた最初のオプションに一致するように入力値を更新し、必要なキーストロークの数を減らし、検索が正しい方向に向かっていることを即座にフィードバックします。 +Users typing partial text expect immediate confirmation that their input matches an available option. Auto-select mode updates the input value to match the first filtered option as users type, reducing the number of keystrokes needed and providing instant feedback that their search is on the right track. @@ -89,9 +89,9 @@ Angularのオートコンプリートは、以下の機能を備えた、完全 -### 手動選択モード {#manual-selection-mode} +### Manual selection mode -手動選択モードでは、ユーザーが候補リストをナビゲートしている間、入力されたテキストは変更されず、自動更新による混乱を防ぎます。入力は、ユーザーがEnterキーまたはクリックで明示的に選択を確定した場合にのみ変更されます。 +Manual selection mode keeps the typed text unchanged while users navigate the suggestion list, preventing confusion from automatic updates. The input only changes when users explicitly confirm their choice with Enter or a click. @@ -119,9 +119,9 @@ Angularのオートコンプリートは、以下の機能を備えた、完全 -### ハイライトモード {#highlight-mode} +### Highlight mode -ハイライトモードでは、ユーザーは矢印キーでオプションをナビゲートできますが、Enterキーまたはクリックで明示的に新しいオプションを選択するまで、閲覧中に入力値は変更されません。 +Highlight mode allows the user to navigate options with arrow keys without changing the input value as they browse until they explicitly select a new option with Enter or click. @@ -149,42 +149,140 @@ Angularのオートコンプリートは、以下の機能を備えた、完全 -## API +### Signal Forms Integration + +Angular Aria integrates seamlessly with the signal-based [Signal Forms](guide/forms/signals/overview) API. You can encapsulate complex inputs into reusable custom control components implementing `FormValueControl`. + +The following example demonstrates a country selector component implementing `FormValueControl`, bound to the parent form using `[formField]` and protected by schema validation rules. + + + + + + + + + + +## Testing + +The autocomplete pattern can be tested using a combination of `ComboboxHarness` and `ListboxHarness` from `@angular/aria/combobox/testing` and `@angular/aria/listbox/testing`. +Here is an example of how to use the harnesses to test an autocomplete component: + +```typescript +import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {HarnessLoader} from '@angular/cdk/testing'; +import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; +import {ComboboxHarness} from '@angular/aria/combobox/testing'; +import {ListboxHarness} from '@angular/aria/listbox/testing'; +import {MyAutocompleteComponent} from './my-autocomplete'; // Your component + +describe('MyAutocompleteComponent', () => { + let fixture: ComponentFixture; + let loader: HarnessLoader; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [MyAutocompleteComponent], + }); + + fixture = TestBed.createComponent(MyAutocompleteComponent); + await fixture.whenStable(); + loader = TestbedHarnessEnvironment.loader(fixture); + }); + + it('should filter options based on input', async () => { + const combobox = await loader.getHarness(ComboboxHarness); + + // Type in the input to trigger filtering + await combobox.setValue('ap'); + expect(await combobox.isOpen()).toBe(true); + + // Get the listbox harness from the popup + const listbox = await combobox.getPopupWidget(ListboxHarness); + const options = await listbox.getOptions(); + + // Verify options are filtered (e.g., 'Apple', 'Apricot') + expect(options.length).toBe(2); + expect(await options[0].getText()).toBe('Apple'); + + // Select the first option + await options[0].click(); + + // Verify the input value is updated and popup is closed + expect(await combobox.isOpen()).toBe(false); + expect(await combobox.getValue()).toBe('Apple'); + }); +}); +``` + +## APIs + +### Combobox Directive + +The `ngCombobox` directive is applied directly onto the editable text `` or `