-
-
+
Features that actually
Angular v21 is here!
-ð¹ïž Start your adventure
+ +Angular v22 is here!
+â¡ Explore what's new
@@ -36,49 +36,49 @@
- The framework for building scalable web apps with confidence
Features that actually
help you solve problems
-
+
-
Signals
- Control Flow
- Deferrable Views
- Hydration
+ Signals
+ Control Flow
+ Deferrable Views
+ Hydration
-
-
- @defer (on idle) {
+
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer (on idle) {
+
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer {
+
diff --git a/adev-ja/src/app/features/home/home.component.html b/adev-ja/src/app/features/home/home.component.html
index 5f39e07600..8899e45a43 100644
--- a/adev-ja/src/app/features/home/home.component.html
+++ b/adev-ja/src/app/features/home/home.component.html
@@ -3,9 +3,9 @@
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer {
+
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
+
ããªãã®åé¡è§£æ±ºãå©ãã
Angular v21ããªãªãŒã¹ãããŸããïŒ
-ð¹ïž åéºãå§ããã
+ +Angular v22ããªãªãŒã¹ãããŸããïŒ
+â¡ æ°æ©èœãèŠã
@@ -36,49 +36,49 @@
- ã¹ã±ãŒã©ãã«ãªWebã¢ããªã確信ãæã£ãŠæ§ç¯ã§ãããã¬
ããªãã®åé¡è§£æ±ºãå©ãã
å€ãã®æ©èœ
-
+
-
ã·ã°ãã«
- å¶åŸ¡ãããŒ
- é
å»¶å¯èœãã¥ãŒ
- ãã€ãã¬ãŒã·ã§ã³
+ ã·ã°ãã«
+ å¶åŸ¡ãããŒ
+ é
å»¶å¯èœãã¥ãŒ
+ ãã€ãã¬ãŒã·ã§ã³
-
-
- @defer (on idle) {
+
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer (on idle) {
+
-
-npm install -g @angular/cli
-
-
-
-For details about changes between versions, and information about updating from previous releases, see the Releases tab on GitHub: https://github.com/angular/angular-cli/releases
-
-## Basic workflow
-
-Invoke the tool on the command line through the `ng` executable.
-Online help is available on the command line.
-Enter the following to list commands or options for a given command \(such as [new](cli/new)\) with a short description.
-
-
-
-ng --help
-ng new --help
-
-
-
-To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands:
-
-
-
-ng new my-first-project
-cd my-first-project
-ng serve
-
-
-
-In your browser, open http://localhost:4200/ to see the new application run.
-When you use the [ng serve](cli/serve) command to build an application and serve it locally, the server automatically rebuilds the application and reloads the page when you change any of the source files.
-
-_ __ [*optional-arg*] `[options]`
-
-- Most commands, and some options, have aliases.
- Aliases are shown in the syntax statement for each command.
-
-- Option names are prefixed with a double dash \(`--`\) characters.
- Option aliases are prefixed with a single dash \(`-`\) character.
- Arguments are not prefixed.
- For example:
-
-
-
- ng build my-app -c production
-
-
-
-- Typically, the name of a generated artifact can be given as an argument to the command or specified with the `--name` option.
-
-- Arguments and option names must be given in [dash-case](guide/glossary#case-types).
- For example: `--my-option-name`
-
-### Boolean options
-
-Boolean options have two forms: `--this-option` sets the flag to `true`, `--no-this-option` sets it to `false`.
-If neither option is supplied, the flag remains in its default state, as listed in the reference documentation.
-
-### Array options
-
-Array options can be provided in two forms: `--option value1 value2` or `--option value1 --option value2`.
-
-### Relative paths
-
-Options that specify files can be given as absolute paths, or as paths relative to the current working directory, which is generally either the workspace or project root.
-
-### Schematics
-
-The [ng generate](cli/generate) and [ng add](cli/add) commands take, as an argument, the artifact or library to be generated or added to the current project.
-In addition to any general options, each artifact or library defines its own options in a _schematic_.
-Schematic options are supplied to the command in the same format as immediate command options.
-
-
-
-
-
-
-
-@reviewed 2022-02-28
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/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
+
+
+
+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ãæ§ç¯ããããã®ç€ç³ãªåºç€
+
+
+
+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éçºè
åãã€ãã³ãã®åç»ãã芧ããã ããŸãã
+
+
diff --git a/adev-ja/src/content/guide/aria/accordion.en.md b/adev-ja/src/content/guide/aria/accordion.en.md
index 4a27dc5e0b..d99a4a7ab8 100644
--- a/adev-ja/src/content/guide/aria/accordion.en.md
+++ b/adev-ja/src/content/guide/aria/accordion.en.md
@@ -145,8 +145,8 @@ Use the `ngAccordionContent` directive on an `ng-template` to defer rendering co
```angular-html
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer {
+
diff --git a/adev-ja/src/app/features/update/recommendations.en.ts b/adev-ja/src/app/features/update/recommendations.en.ts
index f0c49b2079..df358a32c2 100644
--- a/adev-ja/src/app/features/update/recommendations.en.ts
+++ b/adev-ja/src/app/features/update/recommendations.en.ts
@@ -2933,4 +2933,285 @@ export const RECOMMENDATIONS: Step[] = [
action:
"Starting `@angular/ssr` 21.1.5, if your application uses SSR with `CommonEngine`, set the `allowedHosts` option in your `server.ts` (for example, `new CommonEngine({allowedHosts: ['localhost', '*.yourdomain.com']})`). Without it, SSR silently falls back to client-side rendering. This requirement comes from security advisory [GHSA-x288-3778-4hhx](https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx) (also backported to 20.3.17 and 19.2.21).",
},
+ {
+ action:
+ "In the application's project directory, run `ng update @angular/core@22 @angular/cli@22` to update your application to Angular v22.",
+ level: ApplicationComplexity.Basic,
+ necessaryAsOf: 2200,
+ possibleIn: 2200,
+ step: '22.0.0_ng_update',
+ },
+
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ material: true,
+ step: 'update @angular/material',
+ action: 'Run `ng update @angular/material@22`.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-update-nodejs-version',
+ action:
+ 'Angular v22 requires Node.js v22.22.3 or v24.15.0 and later. Update your Node.js version to meet this minimum requirement. You can check your current version with `node --version`.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-update-typescript-version',
+ action:
+ 'Update your project to use TypeScript 6.0 or later. Versions older than 6.0 are no longer supported. Use `ng update` which will handle this automatically.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-data-attributes-input-output-binding',
+ action:
+ 'Data-prefixed attributes (e.g., `data-*`) no longer bind to inputs or outputs. If you were relying on this behavior, use explicit property bindings instead (e.g., `[attr.data-value]="value"` or `[dataValue]="value"` for a component input).',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-duplicate-input-output-bindings',
+ action:
+ 'The compiler now throws an error when inputs, outputs, or model are binding to the same property/output. Review your component decorators and ensure no duplicate bindings exist.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-safe-navigation-nullability-narrowing',
+ action:
+ 'Safe navigation (`?.`) and nullish coalescing (`??`) now correctly narrow down nullable types in templates. This may trigger `nullishCoalescingNotNullable` and `optionalChainNotNullable` diagnostics on existing projects. Either fix the diagnostics by updating your templates, or temporarily disable them in your `tsconfig.json` under `angularCompilerOptions`.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-safe-navigation-returns-undefined',
+ action:
+ 'Angular expressions with optional chaining (`?.`) now return `undefined` instead of `null`. You can use the `$safeNavigationMigration()` magic function to revert to the previous behavior.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-in-expressions',
+ action:
+ '`in` variables in template expressions now throw an error as it does in native JavaScript. If you have variables named `in` in your component or template, update your template expressions to use `this.in` or rename your variable.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-animation-callback-event-signature-change',
+ action:
+ 'The `AnimationCallbackEvent.animationComplete` signature has changed. Update any code that depends on the old signature of this event. Review your animation event handlers and tests.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-http-xhr-backend-explicit-opt-in',
+ action:
+ 'If your application uses upload progress reporting through `HttpXhrBackend`, explicitly opt-in by using `provideHttpClient(withXhr())`. The default HTTP client no longer includes XHR support by default.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-deprecate-report-progress-option',
+ action:
+ 'The `reportProgress` option in HTTP requests is deprecated. Use `reportUploadProgress` or `reportDownloadProgress` instead for more explicit control over progress reporting.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-provide-routes-removed',
+ action:
+ '`provideRoutes()` has been removed. Use `provideRouter()` instead, or configure routes as a multi token using `ROUTES` if necessary. Update your application bootstrap configuration.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-upgrade-angular-js-global-migration',
+ action:
+ 'If using AngularJS interoperability, replace deprecated `getAngularLib()` and `setAngularLib()` with `getAngularJSGlobal()` and `setAngularJSGlobal()` respectively.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-component-factory-resolver',
+ action:
+ '`ComponentFactoryResolver` and `ComponentFactory` are no longer available. Pass the component class directly to APIs like `ViewContainerRef.createComponent()` or use the standalone `createComponent()` function instead.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-create-ng-module-ref',
+ action:
+ '`createNgModuleRef` has been removed. Use `createNgModule()` instead for dynamic module creation scenarios.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-compile-time-duplicate-selectors',
+ action:
+ 'Elements with multiple matching selectors now throw a compile-time error. Ensure your components use unique selectors and review any directives that might have conflicting selectors.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-component-onpush-default',
+ action:
+ 'Components with no `changeDetection` property defined are now `OnPush` by default. To maintain `Eager` (the previous default) change detection, explicitly set `changeDetection: ChangeDetectionStrategy.Eager` in your component decorator.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-check-no-changes',
+ action:
+ '`ChangeDetectorRef.checkNoChanges()` has been removed. In tests, use `fixture.detectChanges()` instead or verify your component state through other means.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-leave-animations-scope-change',
+ action:
+ 'Leave animations are no longer limited to the element being removed. They now support nested animations scoped to component boundaries. Review your animation configurations if you relied on the previous scoping behavior.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-params-inheritance-strategy-default',
+ action:
+ '`paramsInheritanceStrategy` now defaults to `"always"` instead of `"emptyOnly"`. This means route parameters are inherited from all parent routes. To restore the previous behavior, explicitly set `paramsInheritanceStrategy: "emptyOnly"` in your router configuration.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-can-match-current-snapshot-required',
+ action:
+ 'The `currentSnapshot` parameter in `CanMatchFn` and the `canMatch` method of the `CanMatch` interface is now required. Update any class implementations of `CanMatch` to include this required third argument.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-hammer-js-removed',
+ action:
+ 'Hammer.js integration has been removed from Angular platform-browser. If you need touch gesture support, implement your own gesture detection or use an alternative library.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-app-ref-bootstrap-typing',
+ action:
+ 'The second argument of `appRef.bootstrap()` no longer accepts `any` type. Ensure the element you pass is not nullable and matches the expected type.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-platform-browser-styles-removal',
+ action:
+ 'Unused styles are now automatically removed when their associated `host` is dropped. Be aware that other DOM on the page may be affected if those styles are used by elements outside of Angular or if not using `ViewEncapsulation.Emulated`.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-title-strategy-return-type',
+ action:
+ 'The return type for `TitleStrategy.getResolvedTitleForRoute` has changed from `any` to a stricter type (e.g., `string | undefined`). Update your custom `TitleStrategy` implementations to match the new signature.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-incremental-hydration-default',
+ action:
+ 'Incremental hydration is now the default behavior for applications using Server-Side Rendering (SSR). Review your application if you relied on the previous non-incremental hydration behavior. You can use `withNoIncrementalHydration()` to restore the previous behavior if needed.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-full-template-type-check-removed',
+ action:
+ 'The `fullTemplateTypeCheck` compiler option has been removed. Use `strictTemplates` instead to enable strict template type checking in your `tsconfig.json`.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-strict-templates-default',
+ action:
+ 'The `strictTemplates` compiler option now defaults to `true`. If your project was not using strict template type checking, you may see new compilation errors. Resolve these errors or explicitly set `strictTemplates: false` in your `tsconfig.json` to opt out.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-webpack-builders-deprecated',
+ action:
+ 'Webpack builders (`@angular-devkit/build-angular` and `@angular-devkit/build-webpack`) are now deprecated. Migrate to the `@angular/build` builders (esbuild/application) for your application builds.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-ssr-commonengine-deprecated',
+ action:
+ '`CommonEngine` APIs from `@angular/ssr` are deprecated. Migrate to `AngularNodeAppEngine` or `AngularAppEngine` instead.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-istanbul-lib-instrument-optional',
+ action:
+ '`istanbul-lib-instrument` is now an optional peer dependency. If your project uses Karma with code coverage enabled, ensure `istanbul-lib-instrument` is explicitly installed.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-dev-server-port-env-priority',
+ action:
+ '`ng serve` now gives the highest priority to the `PORT` environment variable. This value overrides any port configured in `angular.json` or provided via the `--port` flag.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-architect-cli-removed',
+ action:
+ 'The `@angular-devkit/architect-cli` package is no longer available. Use the `architect` CLI tool from the `@angular-devkit/architect` package instead.',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-experimental-test-builders-removed',
+ action:
+ 'The experimental `@angular-devkit/build-angular:jest` and `@angular-devkit/build-angular:web-test-runner` test builders have been removed.',
+ },
];
diff --git a/adev-ja/src/app/features/update/recommendations.ts b/adev-ja/src/app/features/update/recommendations.ts
index 911cf26691..66574888d0 100644
--- a/adev-ja/src/app/features/update/recommendations.ts
+++ b/adev-ja/src/app/features/update/recommendations.ts
@@ -2933,4 +2933,285 @@ export const RECOMMENDATIONS: Step[] = [
action:
"`@angular/ssr` 21.1.5以éãã¢ããªã±ãŒã·ã§ã³ãSSRã§`CommonEngine`ã䜿çšããŠããå Žåã¯ã`server.ts`ã§`allowedHosts`ãªãã·ã§ã³ãèšå®ããŠãã ããïŒäŸ: `new CommonEngine({allowedHosts: ['localhost', '*.yourdomain.com']})`ïŒãèšå®ããªãå ŽåãSSRã¯ãµã€ã¬ã³ãã«ã¯ã©ã€ã¢ã³ããµã€ãã¬ã³ããªã³ã°ã«ãã©ãŒã«ããã¯ããŸãããã®èŠä»¶ã¯ã»ãã¥ãªãã£ã¢ããã€ã¶ãª [GHSA-x288-3778-4hhx](https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx) ã«åºã¥ããã®ã§ãïŒ20.3.17ããã³19.2.21ã«ãããã¯ããŒããããŠããŸãïŒã",
},
+ {
+ action:
+ "ã¢ããªã±ãŒã·ã§ã³ã®ãããžã§ã¯ããã£ã¬ã¯ããªã§ã`ng update @angular/core@22 @angular/cli@22` ãå®è¡ããŠãã¢ããªã±ãŒã·ã§ã³ã Angular v22 ã«æŽæ°ããŸãã",
+ level: ApplicationComplexity.Basic,
+ necessaryAsOf: 2200,
+ possibleIn: 2200,
+ step: '22.0.0_ng_update',
+ },
+
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ material: true,
+ step: 'update @angular/material',
+ action: '`ng update @angular/material@22` ãå®è¡ããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-update-nodejs-version',
+ action:
+ 'Angular v22 ã«ã¯ Node.js v22.22.3 ãŸã㯠v24.15.0 以éãå¿
èŠã§ãããã®æå°èŠä»¶ãæºããããã« Node.js ã®ããŒãžã§ã³ãæŽæ°ããŠãã ããã`node --version` ã§çŸåšã®ããŒãžã§ã³ã確èªã§ããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-update-typescript-version',
+ action:
+ 'ãããžã§ã¯ãã TypeScript 6.0 以éã䜿çšããããã«æŽæ°ããŠãã ããã6.0 ããå€ãããŒãžã§ã³ã¯ãµããŒããããªããªããŸããã`ng update` ã䜿çšããã°èªåçã«åŠçãããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-data-attributes-input-output-binding',
+ action:
+ 'data ãã¬ãã£ãã¯ã¹ä»ã屿§ (äŸ: `data-*`) ã¯å
¥åãåºåã«ãã€ã³ããããªããªããŸããããã®åäœã«äŸåããŠããå Žåã¯ãæç€ºçãªããããã£ãã€ã³ãã£ã³ã°ã䜿çšããŠãã ãã (äŸ: `[attr.data-value]="value"` ããã³ã³ããŒãã³ãå
¥åã«å¯Ÿãã `[dataValue]="value"`)ã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-duplicate-input-output-bindings',
+ action:
+ 'å
¥åãåºåããŸã㯠model ãåãããããã£/åºåã«ãã€ã³ããããŠããå Žåãã³ã³ãã€ã©ãŒããšã©ãŒãã¹ããŒããããã«ãªããŸãããã³ã³ããŒãã³ããã³ã¬ãŒã¿ãŒã確èªããéè€ãããã€ã³ãã£ã³ã°ãååšããªãããã«ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-safe-navigation-nullability-narrowing',
+ action:
+ 'ã»ãŒãããã²ãŒã·ã§ã³ (`?.`) ãš nullish åäœ (`??`) ããã³ãã¬ãŒãå
ã§ nullable åãæ£ããçµã蟌ãããã«ãªããŸãããæ¢åãããžã§ã¯ãã§ã¯ `nullishCoalescingNotNullable` ãš `optionalChainNotNullable` ã®èšºæãçºçããå¯èœæ§ããããŸãããã³ãã¬ãŒããæŽæ°ããŠèšºæãä¿®æ£ãããã`tsconfig.json` ã® `angularCompilerOptions` ã§ããããäžæçã«ç¡å¹åããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-safe-navigation-returns-undefined',
+ action:
+ 'ãªãã·ã§ãã«ãã§ã€ãã³ã° (`?.`) ã䜿çšãã Angular ã®åŒã¯ã`null` ã®ä»£ããã« `undefined` ãè¿ãããã«ãªããŸããã以åã®åäœã«æ»ãã«ã¯ `$safeNavigationMigration()` ããžãã¯é¢æ°ã䜿çšã§ããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-in-expressions',
+ action:
+ 'ãã³ãã¬ãŒãåŒäžã® `in` 倿°ã¯ããã€ãã£ã JavaScript ãšåæ§ã«ãšã©ãŒãã¹ããŒããããã«ãªããŸãããã³ã³ããŒãã³ãããã³ãã¬ãŒãã« `in` ãšããååã®å€æ°ãããå Žåã¯ããã³ãã¬ãŒãåŒã `this.in` ã«æŽæ°ãããã倿°ã®ååã倿ŽããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-animation-callback-event-signature-change',
+ action:
+ '`AnimationCallbackEvent.animationComplete` ã®ã·ã°ããã£ã倿ŽãããŸããããã®ã€ãã³ãã®å€ãã·ã°ããã£ã«äŸåããŠããã³ãŒããæŽæ°ããŠãã ãããã¢ãã¡ãŒã·ã§ã³ã€ãã³ããã³ãã©ãŒãšãã¹ãã確èªããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-http-xhr-backend-explicit-opt-in',
+ action:
+ 'ã¢ããªã±ãŒã·ã§ã³ã `HttpXhrBackend` çµç±ã§ã¢ããããŒãã®é²æã¬ããŒãã䜿çšããŠããå Žåã¯ã`provideHttpClient(withXhr())` ã䜿çšããŠæç€ºçã«ãªããã€ã³ããŠãã ãããããã©ã«ãã® HTTP ã¯ã©ã€ã¢ã³ãã¯ãXHR ãµããŒããããã©ã«ãã§å«ãŸãªããªããŸããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-deprecate-report-progress-option',
+ action:
+ 'HTTP ãªã¯ãšã¹ãã® `reportProgress` ãªãã·ã§ã³ã¯éæšå¥šã«ãªããŸããã鲿ã¬ããŒããããæç€ºçã«å¶åŸ¡ããã«ã¯ã代ããã« `reportUploadProgress` ãŸã㯠`reportDownloadProgress` ã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-provide-routes-removed',
+ action:
+ '`provideRoutes()` ã¯åé€ãããŸããã代ããã« `provideRouter()` ã䜿çšããããå¿
èŠã«å¿ã㊠`ROUTES` ã䜿çšããŠãã«ãããŒã¯ã³ãšããŠã«ãŒããèšå®ããŠãã ãããã¢ããªã±ãŒã·ã§ã³ã®ããŒãã¹ãã©ããèšå®ãæŽæ°ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-upgrade-angular-js-global-migration',
+ action:
+ 'AngularJS ãšã®çžäºéçšã䜿çšããŠããå Žåã¯ãéæšå¥šã® `getAngularLib()` ããã³ `setAngularLib()` ããããã `getAngularJSGlobal()` ããã³ `setAngularJSGlobal()` ã«çœ®ãæããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-component-factory-resolver',
+ action:
+ '`ComponentFactoryResolver` ãš `ComponentFactory` ã¯äœ¿çšã§ããªããªããŸããã`ViewContainerRef.createComponent()` ã®ãã㪠API ã«ã³ã³ããŒãã³ãã¯ã©ã¹ãçŽæ¥æž¡ããã代ããã«ã¹ã¿ã³ãã¢ãã³ã® `createComponent()` 颿°ã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-create-ng-module-ref',
+ action:
+ '`createNgModuleRef` ã¯åé€ãããŸãããåçãªã¢ãžã¥ãŒã«äœæã·ããªãªã§ã¯ã代ããã« `createNgModule()` ã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-compile-time-duplicate-selectors',
+ action:
+ 'è€æ°ã®äžèŽããã»ã¬ã¯ã¿ãŒãæã€èŠçŽ ã¯ãã³ã³ãã€ã«æãšã©ãŒãã¹ããŒããããã«ãªããŸãããã³ã³ããŒãã³ããäžæã®ã»ã¬ã¯ã¿ãŒã䜿çšããŠããããšã確èªããç«¶åããã»ã¬ã¯ã¿ãŒãæã€ãã£ã¬ã¯ãã£ãã確èªããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-component-onpush-default',
+ action:
+ '`changeDetection` ããããã£ãå®çŸ©ãããŠããªãã³ã³ããŒãã³ãã¯ãããã©ã«ãã§ `OnPush` ã«ãªããŸããã`Eager` (以åã®ããã©ã«ã) ã®å€æŽæ€ç¥ãç¶æããã«ã¯ãã³ã³ããŒãã³ããã³ã¬ãŒã¿ãŒã§ `changeDetection: ChangeDetectionStrategy.Eager` ãæç€ºçã«èšå®ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-remove-check-no-changes',
+ action:
+ '`ChangeDetectorRef.checkNoChanges()` ã¯åé€ãããŸããããã¹ãã§ã¯ã代ããã« `fixture.detectChanges()` ã䜿çšããããä»ã®ææ®µã§ã³ã³ããŒãã³ãã®ç¶æ
ãæ€èšŒããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-leave-animations-scope-change',
+ action:
+ 'leave ã¢ãã¡ãŒã·ã§ã³ã¯ãåé€ãããèŠçŽ ã®ã¿ã«éå®ãããªããªããŸãããã³ã³ããŒãã³ãå¢çã«ã¹ã³ãŒãããããã¹ããããã¢ãã¡ãŒã·ã§ã³ããµããŒãããããã«ãªããŸããã以åã®ã¹ã³ãŒãåäœã«äŸåããŠããå Žåã¯ãã¢ãã¡ãŒã·ã§ã³èšå®ã確èªããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-params-inheritance-strategy-default',
+ action:
+ '`paramsInheritanceStrategy` ã®ããã©ã«ãã `"emptyOnly"` ãã `"always"` ã«å€æŽãããŸãããããã«ãããã«ãŒããã©ã¡ãŒã¿ãŒããã¹ãŠã®èŠªã«ãŒãããç¶æ¿ãããããã«ãªããŸãã以åã®åäœã«æ»ãã«ã¯ãã«ãŒã¿ãŒèšå®ã§ `paramsInheritanceStrategy: "emptyOnly"` ãæç€ºçã«èšå®ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-can-match-current-snapshot-required',
+ action:
+ '`CanMatchFn` ããã³ `CanMatch` ã€ã³ã¿ãŒãã§ãŒã¹ã® `canMatch` ã¡ãœããã® `currentSnapshot` ãã©ã¡ãŒã¿ãŒãå¿
é ã«ãªããŸããã`CanMatch` ã®ã¯ã©ã¹å®è£
ãæŽæ°ããŠãå¿
é ã®ç¬¬3åŒæ°ãå«ããããã«ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-hammer-js-removed',
+ action:
+ 'Hammer.js çµ±å㯠Angular ã® platform-browser ããåé€ãããŸãããã¿ãããžã§ã¹ãã£ãŒã®ãµããŒããå¿
èŠãªå Žåã¯ãç¬èªã®ãžã§ã¹ãã£ãŒæ€åºãå®è£
ãããã代æ¿ã©ã€ãã©ãªã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-app-ref-bootstrap-typing',
+ action:
+ '`appRef.bootstrap()` ã®ç¬¬2åŒæ°ã¯ `any` åãåãä»ããªããªããŸãããæž¡ãèŠçŽ ã nullable ã§ãªããæåŸ
ãããåãšäžèŽããŠããããšã確èªããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-platform-browser-styles-removal',
+ action:
+ 'é¢é£ä»ãããã `host` ãåé€ããããšãæªäœ¿çšã®ã¹ã¿ã€ã«ãèªåçã«åé€ãããããã«ãªããŸããããããã®ã¹ã¿ã€ã«ã Angular ã®å€éšã®èŠçŽ ã§äœ¿çšãããŠããå Žåã `ViewEncapsulation.Emulated` ã䜿çšããŠããªãå ŽåãããŒãžäžã®ä»ã® DOM ã«åœ±é¿ããå¯èœæ§ãããããšã«æ³šæããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-title-strategy-return-type',
+ action:
+ '`TitleStrategy.getResolvedTitleForRoute` ã®æ»ãåã `any` ããããå³å¯ãªå (äŸ: `string | undefined`) ã«å€æŽãããŸãããæ°ããã·ã°ããã£ã«åãããŠãã«ã¹ã¿ã `TitleStrategy` ã®å®è£
ãæŽæ°ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-incremental-hydration-default',
+ action:
+ 'ãµãŒããŒãµã€ãã¬ã³ããªã³ã° (SSR) ã䜿çšããã¢ããªã±ãŒã·ã§ã³ã®ããã©ã«ãåäœãšããŠãã€ã³ã¯ãªã¡ã³ã¿ã«ãã€ãã¬ãŒã·ã§ã³ãæ¡çšãããŸããã以åã®éã€ã³ã¯ãªã¡ã³ã¿ã«ãã€ãã¬ãŒã·ã§ã³ã®åäœã«äŸåããŠããå Žåã¯ãã¢ããªã±ãŒã·ã§ã³ã確èªããŠãã ãããå¿
èŠã«å¿ã㊠`withNoIncrementalHydration()` ã䜿çšããã°ä»¥åã®åäœã«æ»ããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-full-template-type-check-removed',
+ action:
+ '`fullTemplateTypeCheck` ã³ã³ãã€ã©ãŒãªãã·ã§ã³ã¯åé€ãããŸããã`tsconfig.json` ã§å³å¯ãªãã³ãã¬ãŒãåãã§ãã¯ãæå¹ã«ããã«ã¯ã代ããã« `strictTemplates` ã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-strict-templates-default',
+ action:
+ '`strictTemplates` ã³ã³ãã€ã©ãŒãªãã·ã§ã³ãããã©ã«ãã§ `true` ã«ãªããŸããããããžã§ã¯ãã§å³å¯ãªãã³ãã¬ãŒãåãã§ãã¯ã䜿çšããŠããªãã£ãå Žåãæ°ããã³ã³ãã€ã«ãšã©ãŒã衚瀺ãããå¯èœæ§ããããŸãããããã®ãšã©ãŒã解決ãããã`tsconfig.json` ã§ `strictTemplates: false` ãæç€ºçã«èšå®ããŠãªããã¢ãŠãããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-webpack-builders-deprecated',
+ action:
+ 'Webpack ãã«ã㌠(`@angular-devkit/build-angular` ãš `@angular-devkit/build-webpack`) ã¯éæšå¥šã«ãªããŸãããã¢ããªã±ãŒã·ã§ã³ã®ãã«ãã«ã¯ `@angular/build` ãã«ã㌠(esbuild/application) ã«ç§»è¡ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-ssr-commonengine-deprecated',
+ action:
+ '`@angular/ssr` ã® `CommonEngine` API ã¯éæšå¥šã«ãªããŸããã代ããã« `AngularNodeAppEngine` ãŸã㯠`AngularAppEngine` ã«ç§»è¡ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-istanbul-lib-instrument-optional',
+ action:
+ '`istanbul-lib-instrument` ã¯ãªãã·ã§ã³ã®ãã¢äŸåé¢ä¿ã«ãªããŸããããããžã§ã¯ãã§ã³ãŒãã«ãã¬ããžãæå¹ã«ãã Karma ã䜿çšããŠããå Žåã¯ã`istanbul-lib-instrument` ãæç€ºçã«ã€ã³ã¹ããŒã«ããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Basic,
+ step: '22.0.0-dev-server-port-env-priority',
+ action:
+ '`ng serve` 㯠`PORT` ç°å¢å€æ°ã«æãé«ãåªå
床ãäžããããã«ãªããŸããããã®å€ã¯ `angular.json` ã§èšå®ãããããŒãã `--port` ãã©ã°ã§æž¡ãããããŒããäžæžãããŸãã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Advanced,
+ step: '22.0.0-architect-cli-removed',
+ action:
+ '`@angular-devkit/architect-cli` ããã±ãŒãžã¯äœ¿çšã§ããªããªããŸããã代ããã« `@angular-devkit/architect` ããã±ãŒãžã® `architect` CLI ããŒã«ã䜿çšããŠãã ããã',
+ },
+ {
+ possibleIn: 2200,
+ necessaryAsOf: 2200,
+ level: ApplicationComplexity.Medium,
+ step: '22.0.0-experimental-test-builders-removed',
+ action:
+ 'å®éšç㪠`@angular-devkit/build-angular:jest` ããã³ `@angular-devkit/build-angular:web-test-runner` ãã¹ããã«ããŒã¯åé€ãããŸããã',
+ },
];
diff --git a/adev-ja/src/app/features/update/update.component.en.ts b/adev-ja/src/app/features/update/update.component.en.ts
index 79745cf952..6445d9188c 100644
--- a/adev-ja/src/app/features/update/update.component.en.ts
+++ b/adev-ja/src/app/features/update/update.component.en.ts
@@ -6,16 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';
-import {Step, RECOMMENDATIONS, ApplicationComplexity} from './recommendations';
import {Clipboard} from '@angular/cdk/clipboard';
import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu';
-import {MatCheckbox} from '@angular/material/checkbox';
-import {MatButtonToggleGroup, MatButtonToggle} from '@angular/material/button-toggle';
+import {Component, inject, signal} from '@angular/core';
import {IconComponent} from '@angular/docs';
+import {MatButtonToggle, MatButtonToggleGroup} from '@angular/material/button-toggle';
+import {MatCheckbox} from '@angular/material/checkbox';
+import {MatSnackBar} from '@angular/material/snack-bar';
import {ActivatedRoute, Router} from '@angular/router';
import {marked} from 'marked';
-import {MatSnackBar} from '@angular/material/snack-bar';
+import {ApplicationComplexity, RECOMMENDATIONS, Step} from './recommendations';
/**
* Configure marked with a custom link renderer so external links in the
@@ -52,7 +52,6 @@ const isWindows = typeof window !== 'undefined' && window.navigator.userAgent.in
CdkMenuItem,
IconComponent,
],
- changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'(click)': 'copyCode($event)',
},
@@ -82,6 +81,7 @@ export default class UpdateComponent {
protected afterRecommendations: Step[] = [];
protected readonly versions = [
+ {name: '22.0', number: 2200},
{name: '21.0', number: 2100},
{name: '20.0', number: 2000},
{name: '19.0', number: 1900},
@@ -120,9 +120,9 @@ export default class UpdateComponent {
{name: '2.1', number: 201},
{name: '2.0', number: 200},
];
- protected from = this.versions.find((version) => version.name === '20.0')!;
- protected to = this.versions.find((version) => version.name === '21.0')!;
- protected futureVersion = 2200;
+ protected from = this.versions.find((version) => version.name === '21.0')!;
+ protected to = this.versions.find((version) => version.name === '22.0')!;
+ protected futureVersion = 2300;
protected readonly steps: Step[] = RECOMMENDATIONS;
diff --git a/adev-ja/src/app/features/update/update.component.ts b/adev-ja/src/app/features/update/update.component.ts
index 2e753be1cd..9e8f471bb6 100644
--- a/adev-ja/src/app/features/update/update.component.ts
+++ b/adev-ja/src/app/features/update/update.component.ts
@@ -6,16 +6,16 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';
-import {Step, RECOMMENDATIONS, ApplicationComplexity} from './recommendations';
import {Clipboard} from '@angular/cdk/clipboard';
import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu';
-import {MatCheckbox} from '@angular/material/checkbox';
-import {MatButtonToggleGroup, MatButtonToggle} from '@angular/material/button-toggle';
+import {Component, inject, signal} from '@angular/core';
import {IconComponent} from '@angular/docs';
+import {MatButtonToggle, MatButtonToggleGroup} from '@angular/material/button-toggle';
+import {MatCheckbox} from '@angular/material/checkbox';
+import {MatSnackBar} from '@angular/material/snack-bar';
import {ActivatedRoute, Router} from '@angular/router';
import {marked} from 'marked';
-import {MatSnackBar} from '@angular/material/snack-bar';
+import {ApplicationComplexity, RECOMMENDATIONS, Step} from './recommendations';
/**
* Configure marked with a custom link renderer so external links in the
@@ -52,7 +52,6 @@ const isWindows = typeof window !== 'undefined' && window.navigator.userAgent.in
CdkMenuItem,
IconComponent,
],
- changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'(click)': 'copyCode($event)',
},
@@ -82,6 +81,7 @@ export default class UpdateComponent {
protected afterRecommendations: Step[] = [];
protected readonly versions = [
+ {name: '22.0', number: 2200},
{name: '21.0', number: 2100},
{name: '20.0', number: 2000},
{name: '19.0', number: 1900},
@@ -120,9 +120,9 @@ export default class UpdateComponent {
{name: '2.1', number: 201},
{name: '2.0', number: 200},
];
- protected from = this.versions.find((version) => version.name === '20.0')!;
- protected to = this.versions.find((version) => version.name === '21.0')!;
- protected futureVersion = 2200;
+ protected from = this.versions.find((version) => version.name === '21.0')!;
+ protected to = this.versions.find((version) => version.name === '22.0')!;
+ protected futureVersion = 2300;
protected readonly steps: Step[] = RECOMMENDATIONS;
diff --git a/adev-ja/src/app/routing/navigation-entries/index.en.ts b/adev-ja/src/app/routing/navigation-entries/index.en.ts
index 02adf87b29..c021ea7f27 100644
--- a/adev-ja/src/app/routing/navigation-entries/index.en.ts
+++ b/adev-ja/src/app/routing/navigation-entries/index.en.ts
@@ -116,6 +116,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/signals/resource',
contentPath: 'guide/signals/resource',
},
+ {
+ label: 'Debounced signals',
+ path: 'guide/signals/debounced',
+ contentPath: 'guide/signals/debounced',
+ status: 'new',
+ },
{
label: 'Side effects for non-reactives APIs',
path: 'guide/signals/effect',
@@ -315,6 +321,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/di/creating-and-using-services',
status: 'updated',
},
+ {
+ label: 'Lazy loading services',
+ path: 'guide/di/lazy-loading-services',
+ contentPath: 'guide/di/lazy-loading-services',
+ status: 'new',
+ },
{
label: 'Defining dependency providers',
path: 'guide/di/defining-dependency-providers',
@@ -937,6 +949,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'ai/design-patterns',
contentPath: 'ai/design-patterns',
},
+ {
+ label: 'WebMCP',
+ path: 'ai/webmcp',
+ contentPath: 'ai/webmcp',
+ status: 'new',
+ },
],
},
{
@@ -1137,6 +1155,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/templates/defer',
category: 'Loading Performance',
},
+ {
+ label: 'Lazy loading services',
+ path: 'best-practices/performance/lazy-loading-services',
+ contentPath: 'guide/di/lazy-loading-services',
+ category: 'Loading Performance',
+ },
{
label: 'Image optimization',
path: 'best-practices/performance/image-optimization',
@@ -1200,11 +1224,16 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
{
label: 'Developer Events',
children: [
+ {
+ label: 'Angular v22 Release',
+ path: 'events/v22',
+ contentPath: 'events/v22',
+ status: 'new',
+ },
{
label: 'Angular v21 Release',
path: 'events/v21',
contentPath: 'events/v21',
- status: 'new',
},
],
},
diff --git a/adev-ja/src/app/routing/navigation-entries/index.ts b/adev-ja/src/app/routing/navigation-entries/index.ts
index b4431358fc..c83e0c99ca 100644
--- a/adev-ja/src/app/routing/navigation-entries/index.ts
+++ b/adev-ja/src/app/routing/navigation-entries/index.ts
@@ -116,6 +116,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/signals/resource',
contentPath: 'guide/signals/resource',
},
+ {
+ label: 'ã·ã°ãã«ã®ãããŠã³ã¹',
+ path: 'guide/signals/debounced',
+ contentPath: 'guide/signals/debounced',
+ status: 'new',
+ },
{
label: 'éãªã¢ã¯ãã£ãAPIã®ããã®å¯äœçš',
path: 'guide/signals/effect',
@@ -315,6 +321,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/di/creating-and-using-services',
status: 'updated',
},
+ {
+ label: 'ãµãŒãã¹ã®é
å»¶èªã¿èŸŒã¿',
+ path: 'guide/di/lazy-loading-services',
+ contentPath: 'guide/di/lazy-loading-services',
+ status: 'new',
+ },
{
label: 'äŸåæ§ãããã€ããŒã®å®çŸ©',
path: 'guide/di/defining-dependency-providers',
@@ -937,6 +949,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'ai/design-patterns',
contentPath: 'ai/design-patterns',
},
+ {
+ label: 'WebMCP',
+ path: 'ai/webmcp',
+ contentPath: 'ai/webmcp',
+ status: 'new',
+ },
],
},
{
@@ -1137,6 +1155,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'guide/templates/defer',
category: 'Loading Performance',
},
+ {
+ label: 'ãµãŒãã¹ã®é
å»¶èªã¿èŸŒã¿',
+ path: 'best-practices/performance/lazy-loading-services',
+ contentPath: 'guide/di/lazy-loading-services',
+ category: 'Loading Performance',
+ },
{
label: 'ç»åã®æé©å',
path: 'best-practices/performance/image-optimization',
@@ -1200,11 +1224,16 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
{
label: 'éçºè
åãã€ãã³ã',
children: [
+ {
+ label: 'Angular v22 Release',
+ path: 'events/v22',
+ contentPath: 'events/v22',
+ status: 'new',
+ },
{
label: 'Angular v21 Release',
path: 'events/v21',
contentPath: 'events/v21',
- status: 'new',
},
],
},
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ïŒ** **éä¿¡ãšãªã»ãã**
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å®è£
ã䜿çšããããšãæ€èšããŠãã ããã
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èŠçŽ ã¯ãã¢ã¯ã»ã·ããªãã£ã«ãšã£ãŠéèŠãªãããã€ãã®æšæºçãªçžäºäœçšãã¿ãŒã³ãæããŠããŸãã
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/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`ïŒããã§ãã¯ããŸã:
diff --git a/adev-ja/src/content/cli/index.md b/adev-ja/src/content/cli/index.md
deleted file mode 100644
index d6c5d06146..0000000000
--- a/adev-ja/src/content/cli/index.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# CLI Overview and Command Reference
-
-The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications directly from a command shell.
-
-## Installing Angular CLI
-
-Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately.
-
-Install the CLI using the `npm` package manager:
-
-
+ @defer (on idle) {
+
- }
-
+
+ }
-
-
- @defer {
+
+ @defer (on idle) {
+
- }
-
+
+ }
-
-When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory.
-Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command.
-
-If the current working directory is not the right place for your project, you can change to a more appropriate directory by running `cd `.
-
-
-
-## Workspaces and project files
-
-The [ng new](cli/new) command creates an _Angular workspace_ folder and generates a new application skeleton.
-A workspace can contain multiple applications and libraries.
-The initial application created by the [ng new](cli/new) command is at the top level of the workspace.
-When you generate an additional application or library in a workspace, it goes into a `projects/` subfolder.
-
-A newly generated application contains the source files for a root module, with a root component and template.
-Each application has a `src` folder that contains the logic, data, and assets.
-
-You can edit the generated files directly, or add to and modify them using CLI commands.
-Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on.
-Commands such as [add](cli/add) and [generate](cli/generate), which create or operate on applications and libraries, must be executed from within a workspace or project folder.
-
-- See more about the [Workspace file structure](guide/file-structure).
-
-### Workspace and project configuration
-
-A single workspace configuration file, `angular.json`, is created at the top level of the workspace.
-This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets.
-
-The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
-
-
-
-**NOTE**:
-Option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands must be dash-case. - -
-
-- See more about [Workspace Configuration](guide/workspace-config).
-
-## CLI command-language syntax
-
-Command syntax is shown as follows:
-
-`ng` _-Option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands must be dash-case. - -
-
-
+
+
@@ -159,6 +159,55 @@ Use the `ngAccordionContent` directive on an `ng-template` to defer rendering co
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.
+## Testing
+
+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
+
+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
@@ -187,12 +236,12 @@ 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) |
+| 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
@@ -214,11 +263,10 @@ 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 |
+| 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
@@ -241,7 +289,7 @@ The structural directive applied to an `ng-template` inside an accordion panel t
This directive has no inputs, outputs, or methods. Apply it to an `ng-template` element:
```angular-html
-
@@ -159,6 +159,55 @@ Use the `ngAccordionContent` directive on an `ng-template` to defer rendering co
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.
+## Testing
+
+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
+
+describe('MyAccordionComponent', () => {
+ let fixture: ComponentFixture
+
diff --git a/adev-ja/src/content/guide/aria/accordion.md b/adev-ja/src/content/guide/aria/accordion.md
index df3d224cf9..d5d3d600f5 100644
--- a/adev-ja/src/content/guide/aria/accordion.md
+++ b/adev-ja/src/content/guide/aria/accordion.md
@@ -145,10 +145,10 @@ NOTE: `multiExpandable`å
¥åã¯ããã©ã«ãã§`true`ã§ããåäžå±éã®
```angular-html
-
-
+
+
-
+
@@ -159,6 +159,55 @@ NOTE: `multiExpandable`å
¥åã¯ããã©ã«ãã§`true`ã§ããåäžå±éã®
ããã©ã«ãã§ã¯ãããã«ãæããããŸããåŸãã³ã³ãã³ãã¯DOMã«æ®ããŸããããã«ãéãããšãã«DOMããã³ã³ãã³ããåé€ããã«ã¯ã`[preserveContent]="false"`ãèšå®ããŸãã
+## Testing {#testing}
+
+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
+
+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);
+ });
+});
+```
+
## API
### AccordionGroup {#accordiongroup}
@@ -187,12 +236,12 @@ NOTE: `multiExpandable`å
¥åã¯ããã©ã«ãã§`true`ã§ããåäžå±éã®
#### Inputs {#inputs}
-| ãããã㣠| å | ããã©ã«ã | 説æ |
-| ---------- | --------- | ------- | -------------------------------------------------------------- |
-| `id` | `string` | auto | ããªã¬ãŒã®äžæã®èå¥å |
-| `panelId` | `string` | â | **å¿
é ã**é¢é£ä»ããããããã«ã®`panelId`ãšäžèŽããå¿
èŠããããŸã |
-| `disabled` | `boolean` | `false` | ãã®ããªã¬ãŒãç¡å¹ã«ããŸã |
-| `expanded` | `boolean` | `false` | ããã«ãå±éãããŠãããã©ããïŒåæ¹åãã€ã³ãã£ã³ã°ããµããŒãïŒ |
+| ãããã㣠| å | ããã©ã«ã | 説æ |
+| ---------- | ---------------- | ------- | -------------------------------------------------------------- |
+| `panel` | `AccordionPanel` | â | **å¿
é ã**å¶åŸ¡å¯Ÿè±¡ã®ã¢ã³ãŒãã£ãªã³ããã«ãžã®åç
§ |
+| `id` | `string` | auto | ããªã¬ãŒã®äžæã®èå¥å |
+| `disabled` | `boolean` | `false` | ãã®ããªã¬ãŒãç¡å¹ã«ããŸã |
+| `expanded` | `boolean` | `false` | ããã«ãå±éãããŠãããã©ããïŒåæ¹åãã€ã³ãã£ã³ã°ããµããŒãïŒ |
#### ã·ã°ãã« {#signals}
@@ -214,11 +263,10 @@ NOTE: `multiExpandable`å
¥åã¯ããã©ã«ãã§`true`ã§ããåäžå±éã®
#### Inputs {#inputs}
-| ãããã㣠| å | ããã©ã«ã | 説æ |
-| ----------------- | --------- | ------- | ---------------------------------------------------------------- |
-| `id` | `string` | auto | ããã«ã®äžæã®èå¥å |
-| `panelId` | `string` | â | **å¿
é ã**é¢é£ä»ããããããªã¬ãŒã®`panelId`ãšäžèŽããå¿
èŠããããŸã |
-| `preserveContent` | `boolean` | `true` | ããã«ãæããããŸããåŸãã³ã³ãã³ããDOMã«ä¿æãããã©ãã |
+| ãããã㣠| å | ããã©ã«ã | 説æ |
+| ----------------- | --------- | ------- | ---------------------------------------------------- |
+| `id` | `string` | auto | ããã«ã®äžæã®èå¥å |
+| `preserveContent` | `boolean` | `true` | ããã«ãæããããŸããåŸãã³ã³ãã³ããDOMã«ä¿æãããã©ãã |
#### ã·ã°ãã« {#signals}
@@ -241,7 +289,7 @@ NOTE: `multiExpandable`å
¥åã¯ããã©ã«ãã§`true`ã§ããåäžå±éã®
ãã®ãã£ã¬ã¯ãã£ãã«ã¯ãinputãoutputãã¡ãœããã¯ãããŸããã`ng-template`èŠçŽ ã«é©çšããŠãã ãã:
```angular-html
- {
// Required
@@ -321,7 +322,6 @@ import {StatefulInput} from './stateful-input';
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Login {
loginModel = signal({email: ''});
@@ -360,7 +360,6 @@ import {FormValueControl} from '@angular/forms/signals';
(blur)="updateModel()"
/>
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CurrencyInput implements FormValueControl {
// æ°å€ (1234.56) ãæ ŒçŽããŸã
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/field-state-management.en.md b/adev-ja/src/content/guide/forms/signals/field-state-management.en.md
index 82b74aa000..ddcb60a2df 100644
--- a/adev-ja/src/content/guide/forms/signals/field-state-management.en.md
+++ b/adev-ja/src/content/guide/forms/signals/field-state-management.en.md
@@ -222,30 +222,31 @@ Availability state signals control whether fields are interactive, editable, or
The `disabled()` signal indicates whether a field accepts user input. Disabled fields appear in the UI but users cannot interact with them.
```angular-ts
-import { Component, signal } from '@angular/core'
-import { form, FormField, disabled } from '@angular/forms/signals'
+import {Component, signal} from '@angular/core';
+import {form, FormField, disabled} from '@angular/forms/signals';
@Component({
selector: 'app-order',
imports: [FormField],
+ // TIP: The `[formField]` directive automatically binds the `disabled` attribute based
+ // on the field's `disabled()` state, so you don't need to manually add `[disabled]="field().disabled()"`
template: `
-
@if (orderForm.couponCode().disabled()) {
diff --git a/adev/src/app/features/tutorial/tutorial.component.ts b/adev/src/app/features/tutorial/tutorial.component.ts
-index 5824767d27..87df2f5ed7 100644
+index 507c20dfbe..ad938109b4 100644
--- a/adev/src/app/features/tutorial/tutorial.component.ts
+++ b/adev/src/app/features/tutorial/tutorial.component.ts
-@@ -50,7 +50,7 @@ import {SplitResizerHandler} from './split-resizer-handler.service';
- import {PAGE_PREFIX} from '../../core/constants/pages';
+@@ -47,7 +47,7 @@ import {
+ import {SplitResizerHandler} from './split-resizer-handler.service';
import {TutorialNavigationList} from './tutorial-navigation-list';
-const INTRODUCTION_LABEL = 'Introduction';
+
diff --git a/adev-ja/src/content/guide/aria/autocomplete.en.md b/adev-ja/src/content/guide/aria/autocomplete.en.md
index 73f2ab7a44..b7906c8ab5 100644
--- a/adev-ja/src/content/guide/aria/autocomplete.en.md
+++ b/adev-ja/src/content/guide/aria/autocomplete.en.md
@@ -52,7 +52,7 @@ Angular's autocomplete provides a fully accessible combobox implementation with:
- **Keyboard Navigation** - Navigate options with arrow keys, select with Enter, close with Escape
- **Screen Reader Support** - Built-in ARIA attributes for assistive technologies
-- **Three Filter Modes** - Choose between auto-select, manual selection, or highlighting behavior
+- **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
@@ -149,42 +149,140 @@ Highlight mode allows the user to navigate options with arrow keys without chang
+### 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 provides the container for autocomplete functionality.
+The `ngCombobox` directive is applied directly onto the editable text `` or `
}
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StatefulInput implements FormValueControl {
// Required
@@ -321,7 +322,6 @@ import {StatefulInput} from './stateful-input';
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Login {
loginModel = signal({email: ''});
@@ -360,7 +360,6 @@ import {FormValueControl} from '@angular/forms/signals';
(blur)="updateModel()"
/>
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CurrencyInput implements FormValueControl {
// Stores numeric value (1234.56)
diff --git a/adev-ja/src/content/guide/forms/signals/custom-controls.md b/adev-ja/src/content/guide/forms/signals/custom-controls.md
index 951ae37bb6..c22c25bfaf 100644
--- a/adev-ja/src/content/guide/forms/signals/custom-controls.md
+++ b/adev-ja/src/content/guide/forms/signals/custom-controls.md
@@ -55,7 +55,6 @@ import {FormCheckboxControl} from '@angular/forms/signals';
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BasicToggle implements FormCheckboxControl {
/** Whether the toggle is checked */
@@ -94,7 +93,6 @@ import {BasicToggle} from './basic-toggle';
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Registration {
registrationModel = signal({
@@ -218,7 +216,6 @@ import {CustomToggle} from './custom-toggle';
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyForm {
formModel = signal({
@@ -249,7 +246,12 @@ TIP: ãã©ãŒã ã¢ãã«ã®äœæãšç®¡çã«é¢ããå®å
šãªæ
å ±ã«ã€ã
```angular-ts
import {Component, model, input, ChangeDetectionStrategy} from '@angular/core';
-import {FormValueControl, WithOptionalFieldTree, ValidationError, DisabledReason} from '@angular/forms/signals';
+import {
+ FormValueControl,
+ WithOptionalFieldTree,
+ ValidationError,
+ DisabledReason,
+} from '@angular/forms/signals';
@Component({
selector: 'app-stateful-input',
@@ -285,7 +287,6 @@ import {FormValueControl, WithOptionalFieldTree, ValidationError, DisabledReason
}
`,
- changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StatefulInput implements FormValueControlCoupon code is only available for orders over $50
} - ` + `, }) export class Order { orderModel = signal({ total: 25, - couponCode: '' - }) + couponCode: '', + }); - orderForm = form(this.orderModel, schemaPath => { - disabled(schemaPath.couponCode, ({valueOf}) => valueOf(schemaPath.total) < 50) - }) + orderForm = form(this.orderModel, (schemaPath) => { + disabled(schemaPath.couponCode, {when: ({valueOf}) => valueOf(schemaPath.total) < 50}); + }); } ``` @@ -253,7 +254,7 @@ In this example, we use `valueOf(schemaPath.total)` to check the value of the `t NOTE: The schema callback parameter (`schemaPath` in these examples) is a `SchemaPathTree` object that provides paths to all fields in your form. You can name this parameter anything you like. -When defining rules like `disabled()`, `hidden()`, or `readonly()`, the logic callback receives a `FieldContext` object that is typically destructured (such as `({valueOf})`). Two methods commonly used in validation rules are: +When defining rules like `disabled()`, `hidden()`, or `readonly()`, the `when` function receives a `FieldContext` object that is typically destructured (such as `({valueOf})`). Two methods commonly used in validation rules are: - `valueOf(schemaPath.otherField)` - Read the value of another field in the form - `value()` - A signal containing the value of the field the rule is applied to @@ -292,7 +293,7 @@ export class Profile { }); profileForm = form(this.profileModel, (schemaPath) => { - hidden(schemaPath.publicUrl, ({valueOf}) => !valueOf(schemaPath.isPublic)); + hidden(schemaPath.publicUrl, {when: ({valueOf}) => !valueOf(schemaPath.isPublic)}); }); } ``` @@ -439,7 +440,7 @@ const orderModel = signal({ }); const orderForm = form(orderModel, (schemaPath) => { - hidden(schemaPath.shippingAddress, ({valueOf}) => !valueOf(schemaPath.requiresShipping)); + hidden(schemaPath.shippingAddress, {when: ({valueOf}) => !valueOf(schemaPath.requiresShipping)}); }); ``` @@ -516,7 +517,9 @@ export class Order { }); orderForm = form(this.orderModel, (schemaPath) => { - hidden(schemaPath.shippingAddress, ({valueOf}) => !valueOf(schemaPath.requiresShipping)); + hidden(schemaPath.shippingAddress, { + when: ({valueOf}) => !valueOf(schemaPath.requiresShipping), + }); }); } ``` @@ -621,6 +624,8 @@ While field state typically updates through user interactions (typing, focusing, Signal Forms provides a `FormRoot` directive that simplifies form submission. It automatically prevents the default browser form submission behavior and sets the `novalidate` attribute on the `