From a19d08823022e6df121267a2a9a5e1d23b6d76e3 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 29 Apr 2026 12:41:21 +0900 Subject: [PATCH 01/66] chore: update origin to 42d57c3 --- origin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/origin b/origin index 31d3d5649..42d57c357 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit 31d3d564961b701bda96d94731fbed72c01975fa +Subproject commit 42d57c35781fb65fc4d44df59b6a85287664216a From 4997e959388e9938bb5c4cde4dd5fbc53583de70 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 29 Apr 2026 12:41:55 +0900 Subject: [PATCH 02/66] fix: migrate untranslated files --- adev-ja/src/content/ai/agent-skills.md | 24 ++ .../best-practices/performance/overview.md | 12 +- .../ecosystem/rxjs-interop/output-interop.md | 2 +- .../guide/di/defining-dependency-providers.md | 4 + adev-ja/src/content/guide/drag-drop.md | 2 +- .../guide/forms/signals/async-operations.md | 6 +- .../guide/forms/signals/cross-field-logic.md | 251 ++++++++++++++ .../content/guide/forms/signals/form-logic.md | 26 +- .../guide/forms/signals/form-submission.md | 323 ++++++++++++++++++ .../content/guide/forms/signals/migration.md | 210 +++++++++++- .../content/guide/forms/signals/schemas.md | 243 +++++++++++++ .../guide/routing/loading-strategies.md | 96 ++++++ .../src/content/reference/errors/NG01002.md | 46 +++ .../src/content/reference/errors/NG01902.md | 26 ++ .../reference/extended-diagnostics/NG8021.md | 20 +- .../reference/extended-diagnostics/NG8108.md | 23 +- .../content/reference/migrations/outputs.md | 2 +- .../migrations/route-lazy-loading.md | 2 +- .../tools/cli/build-system-migration.md | 2 +- adev-ja/src/content/tools/cli/cli-builder.md | 12 +- adev-ja/src/content/tools/cli/end-to-end.md | 2 +- adev-ja/src/content/tools/cli/environments.md | 8 +- adev-ja/src/content/tools/devtools/router.md | 30 ++ .../tools/libraries/creating-libraries.md | 6 +- .../first-app/steps/13-search/README.md | 4 +- 25 files changed, 1316 insertions(+), 66 deletions(-) create mode 100644 adev-ja/src/content/ai/agent-skills.md create mode 100644 adev-ja/src/content/guide/forms/signals/cross-field-logic.md create mode 100644 adev-ja/src/content/guide/forms/signals/form-submission.md create mode 100644 adev-ja/src/content/guide/forms/signals/schemas.md create mode 100644 adev-ja/src/content/guide/routing/loading-strategies.md create mode 100644 adev-ja/src/content/reference/errors/NG01002.md create mode 100644 adev-ja/src/content/reference/errors/NG01902.md create mode 100644 adev-ja/src/content/tools/devtools/router.md diff --git a/adev-ja/src/content/ai/agent-skills.md b/adev-ja/src/content/ai/agent-skills.md new file mode 100644 index 000000000..0ee75ffad --- /dev/null +++ b/adev-ja/src/content/ai/agent-skills.md @@ -0,0 +1,24 @@ +# Agent Skills + +Agent Skills are specialized, domain-specific instructions and capabilities designed for AI agents like Gemini CLI. These skills provide architectural guidance, generate idiomatic Angular code, and help scaffold new projects using modern best practices. + +By using Agent Skills, you can ensure that the AI agent you are working with has the most up-to-date information about Angular's conventions, reactivity models (like Signals), and project structure. + +## Available Skills + +The Angular team maintains a collection of official skills that are regularly updated to stay in sync with the latest framework improvements. + +| Skill | Description | +| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **`angular-developer`** | Generates Angular code and provides architectural guidance. Useful for creating components, services, or obtaining best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling, testing, or CLI tooling. | +| **`angular-new-app`** | Creates a new Angular app using the Angular CLI. Provides important guidelines for effectively setting up and structuring a modern Angular application. | + +## Using Agent Skills + +Agent Skills are designed to be used with agentic coding tools like [Gemini CLI](https://geminicli.com/docs/cli/skills/), [Antigravity](https://antigravity.google/docs/skills) and more. Activating a skill loads the specific instructions and resources needed for that task. + +To use these skills in your own environment you may follow the instructions for your specific tool or use a community tool like [skills.sh](https://skills.sh/). + +```bash +npx skills add https://github.com/angular/skills +``` diff --git a/adev-ja/src/content/best-practices/performance/overview.md b/adev-ja/src/content/best-practices/performance/overview.md index 3ff3ac804..0280057f3 100644 --- a/adev-ja/src/content/best-practices/performance/overview.md +++ b/adev-ja/src/content/best-practices/performance/overview.md @@ -6,12 +6,12 @@ Angular includes many optimizations out of the box, but as applications grow, yo Loading performance determines how quickly your application becomes visible and interactive. Slow loading directly impacts [Core Web Vitals](https://web.dev/vitals/) like Largest Contentful Paint (LCP) and Time to First Byte (TTFB). -| Technique | What it does | When to use it | -| :------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | -| [Lazy-loaded routes](guide/routing/define-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 | -| [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 | +| Technique | What it does | When to use it | +| :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | +| [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 | +| [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 | ## Runtime performance diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md index 5990164ab..ae5abe871 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md @@ -37,7 +37,7 @@ The `outputToObservable` function lets you create an RxJS observable from a comp import {outputToObservable} from '@angular/core/rxjs-interop'; @Component(/*...*/) - class CustomSlider { +class CustomSlider { valueChange = output(); } 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 84d4684f4..87217b8f4 100644 --- a/adev-ja/src/content/guide/di/defining-dependency-providers.md +++ b/adev-ja/src/content/guide/di/defining-dependency-providers.md @@ -731,6 +731,10 @@ export const routes: Routes = [ ]; ``` +Services provided at the route level are available to all components and directives within that route, as well as to its guards and resolvers. + +Since these services are instantiated independently of the route’s components, they do not have direct access to route-specific information. + ## Library author patterns When creating Angular libraries, you often need to provide flexible configuration options for consumers while maintaining clean APIs. Angular's own libraries demonstrate powerful patterns for achieving this. diff --git a/adev-ja/src/content/guide/drag-drop.md b/adev-ja/src/content/guide/drag-drop.md index 2e2aebbe3..91769cd4b 100644 --- a/adev-ja/src/content/guide/drag-drop.md +++ b/adev-ja/src/content/guide/drag-drop.md @@ -329,7 +329,7 @@ Combining `cdkDropListHasAnchor` with `cdkDropListSortingDisabled` makes it poss Drag and drop supports animations for both: -- Sorting an draggable element inside a list +- Sorting a draggable element inside a list - Moving the draggable element from the position that the user dropped it to the final position inside the list To set up your animations, define a CSS transition that targets the transform property. The following classes can be used for animations: 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 ec4a837c2..47352590f 100644 --- a/adev-ja/src/content/guide/forms/signals/async-operations.md +++ b/adev-ja/src/content/guide/forms/signals/async-operations.md @@ -328,14 +328,14 @@ export class Registration { private createUsernameResource = (usernameSignal: Signal) => { return rxResource({ - request: () => usernameSignal(), - stream: ({request: username}) => this.usernameService.checkUsername(username), + params: () => usernameSignal(), + stream: ({params: username}) => this.usernameService.checkUsername(username), }); }; registrationForm = form(this.registrationModel, (schemaPath) => { validateAsync(schemaPath.username, { - params: ({value}) => value() || undefined, + params: ({value}) => value(), factory: this.createUsernameResource, onSuccess: (result) => result?.available ? null : {kind: 'usernameTaken', message: 'Username taken'}, diff --git a/adev-ja/src/content/guide/forms/signals/cross-field-logic.md b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md new file mode 100644 index 000000000..f244f3720 --- /dev/null +++ b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md @@ -0,0 +1,251 @@ +# Cross-field logic + +**Cross-field logic** is necessary when any rule, validation, or behavior of one field depends on another field's value or state. + +Signal forms provide a **field context** to every rule function. The field context provides access to the current field's value and state, and lets you read other fields in the form using `valueOf()`, `stateOf()`, and `fieldTreeOf()`. + +This guide covers the field context API in depth and shows common cross-field patterns. For single-field validation, see the [Validation guide](/guide/forms/signals/validation). + +## Understanding the field context + +Every rule function in signal forms receives a **field context** parameter, which is an object that describes the current field and provides access to the rest of the form. + +There are three properties you can access for the current field: + +| Property | Type | Description | +| ----------- | -------------------- | -------------------------------------------------------------------- | +| `value` | `Signal` | The current field's value as a signal | +| `state` | `FieldState` | The current field's state (such as validity, errors, touched, dirty) | +| `fieldTree` | `FieldTree` | The current field's tree, for programmatic access to child fields | + +For cross-field logic, the following three properties allow you to access other parts of the form: + +| Property | Type | Description | +| --------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| `valueOf()` | `(path) => PValue` | Most common. Use when you need another field's raw value for comparisons or calculations. | +| `stateOf()` | `(path) => FieldState` | Use when your logic depends on another field's state, such as whether it's valid, touched, or dirty. | +| `fieldTreeOf()` | `(path) => FieldTree` | Use when you need programmatic access to another field's tree, such as pushing errors to a specific child field with validateTree. | + +Here is an example of using `value` and `valueOf()` to validate that the current field (end date) comes after the start date in the form: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class EventForm { + eventModel = signal({ + startDate: new Date('2026-06-01'), + endDate: new Date('2026-06-05'), + }); + + eventForm = form(this.eventModel, (schemaPath) => { + validate(schemaPath.endDate, ({value, valueOf}) => { + if (value() <= valueOf(schemaPath.startDate)) { + return { + kind: 'invalidDateRange', + message: 'End date must be after start date', + }; + } + + return null; + }); + }); +} +``` + +NOTE: The `fieldContext` parameter is typically destructured to pull out only what the rule needs. The remaining examples in this guide use this pattern. + +## Cross-field validation patterns + +The date range example from the previous section validates the end date against the start date. Because the rule reads `valueOf(schemaPath.startDate)`, it re-evaluates automatically whenever either date changes. In other words, a single validator is enough to keep the error state correct. + +However, that single validator only places the error on the end date field. If you want both fields to show an error when the range is invalid, add a matching validation rule to each field: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class EventForm { + eventModel = signal({ + startDate: new Date('2026-06-01'), + endDate: new Date('2026-06-05'), + }); + + eventForm = form(this.eventModel, (schemaPath) => { + validate(schemaPath.startDate, ({value, valueOf}) => { + if (value() >= valueOf(schemaPath.endDate)) { + return { + kind: 'invalidDateRange', + message: 'Start date must be before end date', + }; + } + return null; + }); + + validate(schemaPath.endDate, ({value, valueOf}) => { + if (value() <= valueOf(schemaPath.startDate)) { + return { + kind: 'invalidDateRange', + message: 'End date must be after start date', + }; + } + return null; + }); + }); +} +``` + +Both rules make use of `valueOf()` to read the other field. Because each rule is reactive, changing either date re-evaluates both validations automatically. + +NOTE: When a rule involves multiple fields, you need to decide where the error belongs: on a specific field, on multiple fields, or on the parent. In general, place the error where the user would most likely go to fix the problem. + +### Conditional requirements + +In some forms, certain fields are only required under certain conditions. For example, a registration form might require a company name only when the user selects a business account type: + +```ts +import {Component, signal} from '@angular/core'; +import {form, required} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class RegistrationForm { + registrationModel = signal({ + accountType: 'personal' as 'personal' | 'business', + companyName: '', + }); + + registrationForm = form(this.registrationModel, (schemaPath) => { + required(schemaPath.companyName, { + when: ({valueOf}) => valueOf(schemaPath.accountType) === 'business', + message: 'Company name is required for business accounts', + }); + }); +} +``` + +The `when` option receives the same field context as any other rule function, so `valueOf` works the same way. When the user switches back to `'personal'`, the condition re-evaluates and the requirement — along with its error — clears automatically. + +Using `required()` with `when` instead of a manual `validate()` check also adds proper required metadata to the field, which enables accessibility features like marking the field as required for screen readers. + +### Validating based on another field's state + +The examples so far use `valueOf()` to read another field's value. Sometimes your logic depends on another field's _state_ instead — whether it's valid, touched, or dirty. Use `stateOf()` for this. + +For example, a confirm-password field should only check for a match once the user has interacted with the password field. If the user hasn't touched the password yet, flagging a mismatch on the confirmation is premature: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class PasswordForm { + passwordModel = signal({ + password: '', + confirmPassword: '', + }); + + passwordForm = form(this.passwordModel, (schemaPath) => { + validate(schemaPath.confirmPassword, ({value, valueOf, stateOf}) => { + if (!stateOf(schemaPath.password).touched()) { + return null; + } + if (value() !== valueOf(schemaPath.password)) { + return { + kind: 'passwordMismatch', + message: 'Passwords do not match', + }; + } + return null; + }); + }); +} +``` + +The `stateOf()` call returns the other field's [field state](api/forms/signals/FieldState), giving you access to signals like `invalid()`, `touched()`, and `dirty()`. Because these are signals, the rule re-evaluates whenever the password field's validity changes. + +WARNING: Be careful not to read state which depends on your field's validation, as that creates a circular loop. For example, a validator which checks whether the parent field is valid will create an infinite loop because the parent's validity depends on its children's validity (which includes your validator). + +## Using validateTree + +The examples so far use `validate()` to check individual fields. Sometimes you need to validate a group of fields where the logic is inherently about multiple fields in a group, and direct errors to specific children within it. `validateTree` handles is ideal for these kinds of scenarios. + +For example, in a Sudoku puzzle, each row must contain unique numbers. This is a group-level rule: you check the entire row, then flag the specific cells that violate it. This kind of validation can't be expressed cleanly with `validate` on individual fields, because each cell would need to know about every other cell. + +```ts +import {Component, signal} from '@angular/core'; +import {form, validateTree} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class SudokuRow { + rowModel = signal({ + cell1: 1, + cell2: 3, + cell3: 1, + cell4: 4, + }); + + rowForm = form(this.rowModel, (schemaPath) => { + validateTree(schemaPath, ({value, fieldTreeOf}) => { + const row = value(); + const entries = [ + {val: row.cell1, fieldTree: fieldTreeOf(schemaPath.cell1)}, + {val: row.cell2, fieldTree: fieldTreeOf(schemaPath.cell2)}, + {val: row.cell3, fieldTree: fieldTreeOf(schemaPath.cell3)}, + {val: row.cell4, fieldTree: fieldTreeOf(schemaPath.cell4)}, + ]; + + const counts = new Map(); + for (const {val} of entries) { + if (val !== 0) { + counts.set(val, (counts.get(val) ?? 0) + 1); + } + } + + const errors = entries + .filter(({val}) => val !== 0 && (counts.get(val) ?? 0) > 1) + .map(({val, fieldTree}) => ({ + kind: 'duplicateInRow', + message: `${val} already appears in this row`, + fieldTree, + })); + + return errors.length > 0 ? errors : null; + }); + }); +} +``` + +The validator runs on the parent field (the row), reads all cell values, counts duplicates, and returns an error for each cell that contains a repeated number. The `fieldTree` property on each error tells Angular exactly which cell should show the error. Without `fieldTree`, the errors would apply to the row itself — not where the user needs to see them. + +Because `validateTree` can return an array of errors, a single validator can flag multiple cells at once. Each error includes a `fieldTree` pointing to its target, so Angular routes the errors to the correct fields. + +### When to use validateTree vs validate + +Prefer `validate()` with `valueOf()` when the error belongs on the field being validated — even if the rule reads from other fields. Reach for `validateTree` when: + +- The validation logic is inherently about a group of fields, not any single field +- The validator needs to return errors targeting different child fields + +TIP: For an introduction to `validateTree` and its return type, see the [Validation guide](/guide/forms/signals/validation). + +## Next steps + +This guide covered the field context API and common cross-field patterns. To learn more about related Signal Forms guide, check out: + + + + + + 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 5aa44718a..fe49c6e34 100644 --- a/adev-ja/src/content/guide/forms/signals/form-logic.md +++ b/adev-ja/src/content/guide/forms/signals/form-logic.md @@ -195,14 +195,14 @@ import {form, FormField, hidden} from '@angular/forms/signals'; imports: [FormField], template: ` @if (!profileForm.publicUrl().hidden()) { } `, @@ -239,12 +239,12 @@ import {form, FormField, readonly} from '@angular/forms/signals'; template: ` `, }) @@ -275,13 +275,13 @@ import {form, FormField, readonly} from '@angular/forms/signals'; imports: [FormField], template: ` `, }) @@ -359,7 +359,7 @@ import {form, FormField, debounce} from '@angular/forms/signals'; template: `

Searching for: {{ searchForm.query().value() }}

@@ -406,7 +406,7 @@ import {form, FormField, debounce} from '@angular/forms/signals'; template: ` `, }) @@ -483,7 +483,7 @@ import {form, FormField, required, min, max} from '@angular/forms/signals'; template: ` @if (ageForm.age().required()) { @@ -516,7 +516,7 @@ import {form, FormField, min, max, validate} from '@angular/forms/signals'; @Component({ selector: 'app-custom', - imports: [formField], + imports: [FormField], template: ` `, }) export class Custom { @@ -643,7 +643,7 @@ import {form, FormField, max} from '@angular/forms/signals'; @Component({ selector: 'app-inventory', - imports: [formField], + imports: [FormField], template: `