diff --git a/adev-ja/src/app/features/home/home.component.en.html b/adev-ja/src/app/features/home/home.component.en.html index 7510cd9f8..8a7a65c1d 100644 --- a/adev-ja/src/app/features/home/home.component.en.html +++ b/adev-ja/src/app/features/home/home.component.en.html @@ -50,35 +50,35 @@

Features that actually
help you solve problems

- @defer (on idle) { - + + @defer (on idle) { - - } + } +
- @defer (on idle) { - + + @defer (on idle) { - - } + } +
- @defer (on idle) { - + + @defer { - - } + } +
- @defer (on idle) { - + + @defer { - - } + } +
@@ -160,7 +160,7 @@

-

What to learn more about Angular?

+

Want to learn more about Angular?

diff --git a/adev-ja/src/app/features/home/home.component.html b/adev-ja/src/app/features/home/home.component.html index d99688f59..5f39e0760 100644 --- a/adev-ja/src/app/features/home/home.component.html +++ b/adev-ja/src/app/features/home/home.component.html @@ -50,35 +50,35 @@

あなたの問題解決を助ける
多くの機能

- @defer (on idle) { - + + @defer (on idle) { - - } + } +
- @defer (on idle) { - + + @defer (on idle) { - - } + } +
- @defer (on idle) { - + + @defer { - - } + } +
- @defer (on idle) { - + + @defer { - - } + } +
@@ -160,7 +160,7 @@

-

Angularの何を学ぶ?

+

Angularについてもっと学びたいですか?

diff --git a/adev-ja/src/app/features/update/recommendations.en.ts b/adev-ja/src/app/features/update/recommendations.en.ts index 36286a3c4..55b054da1 100644 --- a/adev-ja/src/app/features/update/recommendations.en.ts +++ b/adev-ja/src/app/features/update/recommendations.en.ts @@ -2703,6 +2703,14 @@ export const RECOMMENDATIONS: Step[] = [ action: "Upgrade your project's TypeScript version to at least 5.8 before upgrading to Angular v20 to ensure compatibility.", }, + { + possibleIn: 2000, + necessaryAsOf: 2000, + level: ApplicationComplexity.Medium, + step: '20.0.0_set_moduleResolution_to_bundler', + action: + "Set `moduleResolution` to `'bundler'` in your `tsconfig.json`. Angular CLI's `ng update` migration applies this change automatically; if you upgrade manually or override the option in a base tsconfig, set it explicitly so imports of secondary entry-points such as `@angular/core/rxjs-interop` continue to resolve correctly.", + }, { possibleIn: 2000, necessaryAsOf: 2000, diff --git a/adev-ja/src/app/features/update/recommendations.ts b/adev-ja/src/app/features/update/recommendations.ts index c35e86ca4..d98d4017a 100644 --- a/adev-ja/src/app/features/update/recommendations.ts +++ b/adev-ja/src/app/features/update/recommendations.ts @@ -2703,6 +2703,14 @@ export const RECOMMENDATIONS: Step[] = [ action: '互換性を確保するために、Angular v20に更新する前にプロジェクトのTypeScriptバージョンを少なくとも5.8に更新してください。', }, + { + possibleIn: 2000, + necessaryAsOf: 2000, + level: ApplicationComplexity.Medium, + step: '20.0.0_set_moduleResolution_to_bundler', + action: + '`tsconfig.json`の`moduleResolution`を`\'bundler\'`に設定してください。Angular CLIの`ng update`マイグレーションはこの変更を自動的に適用します。手動でアップグレードする場合やベースtsconfig内でオプションをオーバーライドする場合は、`@angular/core/rxjs-interop`のようなセカンダリエントリーポイントのインポートが引き続き正しく解決されるよう、明示的に設定してください。', + }, { possibleIn: 2000, necessaryAsOf: 2000, diff --git a/adev-ja/src/app/features/update/update.component.en.html b/adev-ja/src/app/features/update/update.component.en.html index 5ebb3dcbb..c143eedff 100644 --- a/adev-ja/src/app/features/update/update.component.en.html +++ b/adev-ja/src/app/features/update/update.component.en.html @@ -1,4 +1,4 @@ -
+

Update Guide

@@ -205,4 +205,4 @@

After you update

}
} -
+
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 29ddd1423..4eea44e25 100644 --- a/adev-ja/src/app/features/update/update.component.en.ts +++ b/adev-ja/src/app/features/update/update.component.en.ts @@ -108,7 +108,7 @@ export default class UpdateComponent { ]; protected from = this.versions.find((version) => version.name === '20.0')!; protected to = this.versions.find((version) => version.name === '21.0')!; - protected futureVersion = 2100; + protected futureVersion = 2200; protected readonly steps: Step[] = RECOMMENDATIONS; diff --git a/adev-ja/src/app/features/update/update.component.html b/adev-ja/src/app/features/update/update.component.html index 1aa79160a..bba218f14 100644 --- a/adev-ja/src/app/features/update/update.component.html +++ b/adev-ja/src/app/features/update/update.component.html @@ -1,4 +1,4 @@ -
+

アップデートガイド

@@ -205,4 +205,4 @@

アップデート後

}
} -
+
diff --git a/adev-ja/src/app/features/update/update.component.ts b/adev-ja/src/app/features/update/update.component.ts index f6094276b..76d12660e 100644 --- a/adev-ja/src/app/features/update/update.component.ts +++ b/adev-ja/src/app/features/update/update.component.ts @@ -108,7 +108,7 @@ export default class UpdateComponent { ]; protected from = this.versions.find((version) => version.name === '20.0')!; protected to = this.versions.find((version) => version.name === '21.0')!; - protected futureVersion = 2100; + protected futureVersion = 2200; 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 35be26c71..32990b1af 100644 --- a/adev-ja/src/app/routing/navigation-entries/index.en.ts +++ b/adev-ja/src/app/routing/navigation-entries/index.en.ts @@ -363,6 +363,11 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/routing/define-routes', contentPath: 'guide/routing/define-routes', }, + { + label: 'Route Loading Strategies', + path: 'guide/routing/loading-strategies', + contentPath: 'guide/routing/loading-strategies', + }, { label: 'Show routes with Outlets', path: 'guide/routing/show-routes-with-outlets', @@ -491,6 +496,27 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ category: 'Signal Forms', status: 'new', }, + { + label: 'Cross-field logic', + path: 'guide/forms/signals/cross-field-logic', + contentPath: 'guide/forms/signals/cross-field-logic', + category: 'Signal Forms', + status: 'new', + }, + { + label: 'Form submission', + path: 'guide/forms/signals/form-submission', + contentPath: 'guide/forms/signals/form-submission', + category: 'Signal Forms', + status: 'new', + }, + { + label: 'Schemas', + path: 'guide/forms/signals/schemas', + contentPath: 'guide/forms/signals/schemas', + category: 'Signal Forms', + status: 'new', + }, { label: 'Async operations', path: 'guide/forms/signals/async-operations', @@ -655,6 +681,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/routing/testing', contentPath: 'guide/routing/testing', status: 'new', + isCrossReferenced: true, }, { label: 'Debugging tests', @@ -858,6 +885,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ label: 'Route transition animations', path: 'guide/routing/route-transition-animations', contentPath: 'guide/routing/route-transition-animations', + isCrossReferenced: true, }, ], }, @@ -870,7 +898,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, { label: 'Build with AI', - status: 'new', children: [ { label: 'Get Started', @@ -883,9 +910,10 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'ai/develop-with-ai', }, { - label: 'Design Patterns', - path: 'ai/design-patterns', - contentPath: 'ai/design-patterns', + label: 'Agent Skills', + path: 'ai/agent-skills', + contentPath: 'ai/agent-skills', + status: 'new', }, { label: 'Angular CLI MCP Server setup', @@ -897,6 +925,11 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'ai/ai-tutor', contentPath: 'ai/ai-tutor', }, + { + label: 'Design Patterns', + path: 'ai/design-patterns', + contentPath: 'ai/design-patterns', + }, ], }, { @@ -1032,16 +1065,15 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, // TODO: create those guides // The signal debugging docs should also be added to the signal section - // { // label: 'Signals', // path: 'tools/devtools/signals', // contentPath: 'tools/devtools/signals', // }, - // { - // label: 'Router', - // path: 'tools/devtools/router', - // contentPath: 'tools/devtools/router', - // } + { + label: 'Router Tree', + path: 'tools/devtools/router', + contentPath: 'tools/devtools/router', + }, ], }, { @@ -1086,6 +1118,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, // Loading Performance + { + label: 'Lazy-loaded routes', + path: 'best-practices/performance/lazy-loaded-routes', + contentPath: 'guide/routing/loading-strategies', + category: 'Loading Performance', + }, { label: 'Deferred loading with @defer', path: 'best-practices/performance/defer', diff --git a/adev-ja/src/app/routing/navigation-entries/index.ts b/adev-ja/src/app/routing/navigation-entries/index.ts index 76d484fad..d6f85f826 100644 --- a/adev-ja/src/app/routing/navigation-entries/index.ts +++ b/adev-ja/src/app/routing/navigation-entries/index.ts @@ -363,6 +363,11 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/routing/define-routes', contentPath: 'guide/routing/define-routes', }, + { + label: 'ルートの読み込み戦略', + path: 'guide/routing/loading-strategies', + contentPath: 'guide/routing/loading-strategies', + }, { label: 'アウトレットにルートを表示する', path: 'guide/routing/show-routes-with-outlets', @@ -485,14 +490,35 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ status: 'new', }, { - label: 'Form logic', + label: 'フォームロジック', path: 'guide/forms/signals/form-logic', contentPath: 'guide/forms/signals/form-logic', category: 'Signal Forms', status: 'new', }, { - label: 'Async operations', + label: 'フィールド間ロジック', + path: 'guide/forms/signals/cross-field-logic', + contentPath: 'guide/forms/signals/cross-field-logic', + category: 'Signal Forms', + status: 'new', + }, + { + label: 'フォームの送信', + path: 'guide/forms/signals/form-submission', + contentPath: 'guide/forms/signals/form-submission', + category: 'Signal Forms', + status: 'new', + }, + { + label: 'スキーマ', + path: 'guide/forms/signals/schemas', + contentPath: 'guide/forms/signals/schemas', + category: 'Signal Forms', + status: 'new', + }, + { + label: '非同期処理', path: 'guide/forms/signals/async-operations', contentPath: 'guide/forms/signals/async-operations', category: 'Signal Forms', @@ -655,6 +681,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/routing/testing', contentPath: 'guide/routing/testing', status: 'new', + isCrossReferenced: true, }, { label: 'テストのデバッグ', @@ -858,6 +885,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ label: 'ルート遷移アニメーション', path: 'guide/routing/route-transition-animations', contentPath: 'guide/routing/route-transition-animations', + isCrossReferenced: true, }, ], }, @@ -870,7 +898,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, { label: 'Build with AI', - status: 'new', children: [ { label: 'はじめよう', @@ -883,9 +910,10 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'ai/develop-with-ai', }, { - label: '設計パターン', - path: 'ai/design-patterns', - contentPath: 'ai/design-patterns', + label: 'エージェントスキル', + path: 'ai/agent-skills', + contentPath: 'ai/agent-skills', + status: 'new', }, { label: 'Angular CLI MCPサーバーセットアップ', @@ -897,6 +925,11 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'ai/ai-tutor', contentPath: 'ai/ai-tutor', }, + { + label: '設計パターン', + path: 'ai/design-patterns', + contentPath: 'ai/design-patterns', + }, ], }, { @@ -1032,16 +1065,15 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, // TODO: create those guides // The signal debugging docs should also be added to the signal section - // { // label: 'Signals', // path: 'tools/devtools/signals', // contentPath: 'tools/devtools/signals', // }, - // { - // label: 'Router', - // path: 'tools/devtools/router', - // contentPath: 'tools/devtools/router', - // } + { + label: 'ルーターツリー', + path: 'tools/devtools/router', + contentPath: 'tools/devtools/router', + }, ], }, { @@ -1086,6 +1118,12 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, // Loading Performance + { + label: '遅延読み込みルート', + path: 'best-practices/performance/lazy-loaded-routes', + contentPath: 'guide/routing/loading-strategies', + category: 'Loading Performance', + }, { label: '@deferによる遅延読み込み', path: 'best-practices/performance/defer', diff --git a/adev-ja/src/content/ai/agent-skills.md b/adev-ja/src/content/ai/agent-skills.md new file mode 100644 index 000000000..0ee75ffad --- /dev/null +++ b/adev-ja/src/content/ai/agent-skills.md @@ -0,0 +1,24 @@ +# Agent Skills + +Agent Skills are specialized, domain-specific instructions and capabilities designed for AI agents like Gemini CLI. These skills provide architectural guidance, generate idiomatic Angular code, and help scaffold new projects using modern best practices. + +By using Agent Skills, you can ensure that the AI agent you are working with has the most up-to-date information about Angular's conventions, reactivity models (like Signals), and project structure. + +## Available Skills + +The Angular team maintains a collection of official skills that are regularly updated to stay in sync with the latest framework improvements. + +| Skill | Description | +| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **`angular-developer`** | Generates Angular code and provides architectural guidance. Useful for creating components, services, or obtaining best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling, testing, or CLI tooling. | +| **`angular-new-app`** | Creates a new Angular app using the Angular CLI. Provides important guidelines for effectively setting up and structuring a modern Angular application. | + +## Using Agent Skills + +Agent Skills are designed to be used with agentic coding tools like [Gemini CLI](https://geminicli.com/docs/cli/skills/), [Antigravity](https://antigravity.google/docs/skills) and more. Activating a skill loads the specific instructions and resources needed for that task. + +To use these skills in your own environment you may follow the instructions for your specific tool or use a community tool like [skills.sh](https://skills.sh/). + +```bash +npx skills add https://github.com/angular/skills +``` diff --git a/adev-ja/src/content/ai/overview.en.md b/adev-ja/src/content/ai/overview.en.md index b11f3e662..8fae0a4e5 100644 --- a/adev-ja/src/content/ai/overview.en.md +++ b/adev-ja/src/content/ai/overview.en.md @@ -59,7 +59,7 @@ Here are examples of how to build with Genkit and Angular: Here is an example of how to build with Firebase AI Logic and Angular: -- [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular. +- [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular. This example includes an [in-depth video walkthrough explaining the functionality and demonstrates how to add new features](https://youtube.com/live/4vfDz2al_BI). @@ -79,7 +79,7 @@ When connecting to model providers, it is important to keep your API secrets saf Your application's architecture determines which AI APIs and tools to choose. Specifically, choose based on whether or not your application is client-side or server-side. Tools such as Firebase AI Logic provide a secure connection to the model APIs for client-side code. If you want to use a different API than Firebase AI Logic or prefer to use a different model provider, consider creating a proxy-server or even [Cloud Functions for Firebase](https://firebase.google.com/docs/functions) to serve as a proxy and not expose your API keys. -For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example). +For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example). For server-side connections to model APIs that require API keys, prefer using a secrets manager or environment variable, not `environments.ts`. You should follow standard best practices for securing API keys and credentials. Firebase now provides a new secrets manager with the latest updates from Firebase App Hosting. To learn more, [check out the official documentation](https://firebase.google.com/docs/app-hosting/configure). @@ -91,7 +91,7 @@ If you want to build agentic workflows, where agents are able to act and use too Tool calling further enhances your web apps by expanding your AI integration further than a question and answer style chat bot. In fact, you can empower your model to request function calls using the function calling API of your model provider. The available tools can be used to perform more complex actions within the context of your application. -In the [e-commerce example](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service. +In the [e-commerce example](https://github.com/angular/examples/blob/main/firebase-ai-logic-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service. ### Handling non-deterministic responses @@ -115,4 +115,5 @@ To learn about LLM prompts and AI IDE setup, see the following guides: + diff --git a/adev-ja/src/content/ai/overview.md b/adev-ja/src/content/ai/overview.md index a9d0604c7..0b2cd0408 100644 --- a/adev-ja/src/content/ai/overview.md +++ b/adev-ja/src/content/ai/overview.md @@ -59,7 +59,7 @@ GenkitとAngularで構築する方法の例を次に示します。 Firebase AI LogicとAngularで構築する方法の例を次に示します。 -- [Firebase AI Logic x Angularスターターキット](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - このスターターキットを使用して、タスクを実行できるチャットエージェントを備えたeコマースアプリケーションを構築します。Firebase AI LogicとAngularでの構築経験がない場合は、ここから始めましょう。 +- [Firebase AI Logic x Angularスターターキット](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example) - このスターターキットを使用して、タスクを実行できるチャットエージェントを備えたeコマースアプリケーションを構築します。Firebase AI LogicとAngularでの構築経験がない場合は、ここから始めましょう。 この例には、[機能の説明と新機能の追加方法を示す詳細なビデオウォークスルー](https://youtube.com/live/4vfDz2al_BI)が含まれています。 @@ -79,7 +79,7 @@ Firebase AI LogicとAngularで構築する方法の例を次に示します。 アプリケーションのアーキテクチャによって、選択するAI APIとツールが決まります。具体的には、アプリケーションがクライアントサイドかサーバーサイドかに基づいて選択します。Firebase AI Logicのようなツールは、クライアントサイドのコードに対してモデルAPIへの安全な接続を提供します。Firebase AI Logic以外のAPIを使用したい場合や、別のモデルプロバイダーを使用したい場合は、プロキシサーバー、あるいは[Cloud Functions for Firebase](https://firebase.google.com/docs/functions)をプロキシとして使用し、APIキーを公開しないことを検討してください。 -クライアントサイドアプリケーションを使用した接続の例については、コード: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example)を参照してください。 +クライアントサイドアプリケーションを使用した接続の例については、コード: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example)を参照してください。 APIキーを必要とするモデルAPIへのサーバーサイド接続には、`environments.ts`ではなく、シークレットマネージャーまたは環境変数を使用することを推奨します。APIキーと認証情報を保護するための標準的なベストプラクティスに従う必要があります。Firebaseは、Firebase App Hostingの最新アップデートにより、新しいシークレットマネージャーを提供するようになりました。詳細については、[公式ドキュメントを確認してください](https://firebase.google.com/docs/app-hosting/configure)。 @@ -91,7 +91,7 @@ APIキーを必要とするモデルAPIへのサーバーサイド接続には ツール呼び出しは、AI統合を質疑応答の形式のチャットボットよりもさらに拡張することで、ウェブアプリケーションをさらに強化します。実際、モデルプロバイダーの関数呼び出しAPIを使用して、モデルに関数呼び出しをリクエストさせることができます。利用可能なツールは、アプリケーションのコンテキスト内でより複雑なアクションを実行するために使用できます。 -[Angular examples repository](https://github.com/angular/examples)の[eコマースの例](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88)では、LLMは、店舗内の商品のグループがいくらになるかを計算するなどのより複雑なタスクを実行するために必要なコンテキストを得るために、在庫に関する関数呼び出しをリクエストします。利用可能なAPIの範囲は、LLMによってリクエストされた関数を呼び出すかどうかも含め、開発者であるあなた次第です。実行フローの制御はあなたが保持します。例えば、サービスの一部の関数は公開できますが、そのサービスのすべての関数を公開する必要はありません。 +[Angular examples repository](https://github.com/angular/examples)の[eコマースの例](https://github.com/angular/examples/blob/main/firebase-ai-logic-angular-example/src/app/ai.service.ts#L88)では、LLMは、店舗内の商品のグループがいくらになるかを計算するなどのより複雑なタスクを実行するために必要なコンテキストを得るために、在庫に関する関数呼び出しをリクエストします。利用可能なAPIの範囲は、LLMによってリクエストされた関数を呼び出すかどうかも含め、開発者であるあなた次第です。実行フローの制御はあなたが保持します。例えば、サービスの一部の関数は公開できますが、そのサービスのすべての関数を公開する必要はありません。 ### 非決定論的な応答の処理 {#handling-non-deterministic-responses} @@ -115,4 +115,5 @@ LLMプロンプトとAI IDE設定について学ぶには、以下のガイド + diff --git a/adev-ja/src/content/best-practices/performance/overview.md b/adev-ja/src/content/best-practices/performance/overview.md index 3ff3ac804..0280057f3 100644 --- a/adev-ja/src/content/best-practices/performance/overview.md +++ b/adev-ja/src/content/best-practices/performance/overview.md @@ -6,12 +6,12 @@ Angular includes many optimizations out of the box, but as applications grow, yo Loading performance determines how quickly your application becomes visible and interactive. Slow loading directly impacts [Core Web Vitals](https://web.dev/vitals/) like Largest Contentful Paint (LCP) and Time to First Byte (TTFB). -| Technique | What it does | When to use it | -| :------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | -| [Lazy-loaded routes](guide/routing/define-routes#lazily-loaded-components-and-routes) | Defers loading route components until navigation, reducing the initial bundle size | Applications with multiple routes where not all are needed on initial load | -| [Deferred loading with `@defer`](best-practices/performance/defer) | Splits components into separate bundles that load on demand | Components not visible on initial render, heavy third-party libraries, below-the-fold content | -| [Image optimization](best-practices/performance/image-optimization) | Prioritizes LCP images, lazy loads others, generates responsive `srcset` attributes | Any application that displays images | -| [Server-side rendering](best-practices/performance/ssr) | Renders pages on the server for faster first paint and better SEO, with [hydration](guide/hydration) to restore interactivity and [incremental hydration](guide/incremental-hydration) to defer hydrating sections until needed | Content-heavy applications, pages that need search engine indexing | +| Technique | What it does | When to use it | +| :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | +| [Lazy-loaded routes](best-practices/performance/lazy-loaded-routes#lazily-loaded-components-and-routes) | Defers loading route components until navigation, reducing the initial bundle size | Applications with multiple routes where not all are needed on initial load | +| [Deferred loading with `@defer`](best-practices/performance/defer) | Splits components into separate bundles that load on demand | Components not visible on initial render, heavy third-party libraries, below-the-fold content | +| [Image optimization](best-practices/performance/image-optimization) | Prioritizes LCP images, lazy loads others, generates responsive `srcset` attributes | Any application that displays images | +| [Server-side rendering](best-practices/performance/ssr) | Renders pages on the server for faster first paint and better SEO, with [hydration](guide/hydration) to restore interactivity and [incremental hydration](guide/incremental-hydration) to defer hydrating sections until needed | Content-heavy applications, pages that need search engine indexing | ## Runtime performance diff --git a/adev-ja/src/content/ecosystem/custom-build-pipeline.en.md b/adev-ja/src/content/ecosystem/custom-build-pipeline.en.md index 6670076a6..d2bab6eba 100644 --- a/adev-ja/src/content/ecosystem/custom-build-pipeline.en.md +++ b/adev-ja/src/content/ecosystem/custom-build-pipeline.en.md @@ -10,7 +10,7 @@ There are some niche use cases when you may want to maintain a custom build pipe - You have an existing app using a different toolchain and you’d like to add Angular to it - You’re strongly coupled to [module federation](https://module-federation.io/) and unable to adopt bundler-agnostic [native federation](https://www.npmjs.com/package/@angular-architects/native-federation) -- You’d like to create an short-lived experiment using your favorite build tool +- You’d like to create a short-lived experiment using your favorite build tool ## What are the options? diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md index 5990164ab..ae5abe871 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md @@ -37,7 +37,7 @@ The `outputToObservable` function lets you create an RxJS observable from a comp import {outputToObservable} from '@angular/core/rxjs-interop'; @Component(/*...*/) - class CustomSlider { +class CustomSlider { valueChange = output(); } diff --git a/adev-ja/src/content/events/v21.en.md b/adev-ja/src/content/events/v21.en.md index e1f5ace3b..46838e632 100644 --- a/adev-ja/src/content/events/v21.en.md +++ b/adev-ja/src/content/events/v21.en.md @@ -22,16 +22,4 @@ Angular v21 is being delivered to you as a brand new release adventure. With mod - Your first look at Signal Forms, our new streamlined, signal-based approach to forms in Angular - Exciting new details about the Angular Aria package -
- -
+ diff --git a/adev-ja/src/content/events/v21.md b/adev-ja/src/content/events/v21.md index 3e307640f..9f9bd7fa6 100644 --- a/adev-ja/src/content/events/v21.md +++ b/adev-ja/src/content/events/v21.md @@ -22,16 +22,4 @@ Angular v21は、まったく新しいリリース体験としてお届けしま - Angularのフォームに対する、新しい合理化されたシグナルベースのアプローチであるシグナルフォームを初公開 - Angular Ariaパッケージに関する注目の新情報 -
- -
+ diff --git a/adev-ja/src/content/guide/animations/css.en.md b/adev-ja/src/content/guide/animations/css.en.md index 8d08ff0e5..4ad6d5e0a 100644 --- a/adev-ja/src/content/guide/animations/css.en.md +++ b/adev-ja/src/content/guide/animations/css.en.md @@ -70,7 +70,7 @@ You can use css-grid to animate to auto height. -If you don't have to worry about supporting all browsers, you can also check out `calc-size()`, which is the true solution to animating auto height. See [MDN's docs](https://developer.mozilla.org/en-US/docs/Web/CSS/calc-size) and (this tutorial)[https://frontendmasters.com/blog/one-of-the-boss-battles-of-css-is-almost-won-transitioning-to-auto/] for more information. +If you don't have to worry about supporting all browsers, you can also check out `calc-size()`, which is the true solution to animating auto height. See [MDN's docs](https://developer.mozilla.org/en-US/docs/Web/CSS/calc-size) and [this tutorial](https://frontendmasters.com/blog/one-of-the-boss-battles-of-css-is-almost-won-transitioning-to-auto/) for more information. ### Animate entering and leaving a view diff --git a/adev-ja/src/content/guide/animations/enter-and-leave.en.md b/adev-ja/src/content/guide/animations/enter-and-leave.en.md index 73be130a0..8a8db2f3b 100644 --- a/adev-ja/src/content/guide/animations/enter-and-leave.en.md +++ b/adev-ja/src/content/guide/animations/enter-and-leave.en.md @@ -55,9 +55,9 @@ NOTE: When using multiple keyframe animations or transition properties on an ele -### Element removal order matters +### Element removal order -There is some nuance to how `animate.leave` animations are run and when an animation will occur. `animate.leave` works if it is placed on the element that is being removed. However, `animate.leave` will **not** animate if it is on an element that is a _descendent_ of the element being removed. This is because when a parent node is removed, it takes the entire sub tree with it, and since there are no animations on that parent node, it will be removed immediately. This means there's no element to animate away with `animate.leave`. You will need to consider this in your usage of `animate.leave`. +There is some nuance to how `animate.leave` animations are run and when an animation will occur. `animate.leave` works if it is placed on the element that is being removed, and if `animate.leave` is placed on an element that is a _descendent_ of the element being removed, those child animations will happen _before_ the parent node is removed from the DOM. This ensures that you can confidently animate away child elements without the parent node disappearing prematurely. diff --git a/adev-ja/src/content/guide/animations/enter-and-leave.md b/adev-ja/src/content/guide/animations/enter-and-leave.md index 8191fb972..152b1c03a 100644 --- a/adev-ja/src/content/guide/animations/enter-and-leave.md +++ b/adev-ja/src/content/guide/animations/enter-and-leave.md @@ -55,9 +55,9 @@ NOTE: 要素に複数のキーフレームアニメーションまたはtransiti -### 要素の削除順序が重要 {#element-removal-order-matters} +### 要素の削除順序 {#element-removal-order} -`animate.leave`アニメーションの実行方法とアニメーションが発生するタイミングには、いくつかの微妙な点があります。`animate.leave`は、削除される要素に配置されている場合に機能します。ただし、`animate.leave`は、削除される要素の_子孫_の要素にある場合はアニメーション**しません**。これは、親ノードが削除されると、サブツリー全体が一緒に削除され、その親ノードにアニメーションがないため、即座に削除されるためです。これは、`animate.leave`でアニメーション化する要素がないことを意味します。`animate.leave`の使用においてこのことを考慮する必要があります。 +`animate.leave`アニメーションの実行方法とアニメーションが発生するタイミングには、いくつかの微妙な点があります。`animate.leave`は、削除される要素に配置されている場合に機能します。また、`animate.leave`が削除される要素の_子孫_要素に配置されている場合、それらの子アニメーションは親ノードがDOMから削除される_前に_実行されます。これにより、親ノードが早期に消えることなく、子要素を確実にアニメーションで退場させることができます。 diff --git a/adev-ja/src/content/guide/aria/menubar.en.md b/adev-ja/src/content/guide/aria/menubar.en.md index 1abedb0c8..3d48c3e87 100644 --- a/adev-ja/src/content/guide/aria/menubar.en.md +++ b/adev-ja/src/content/guide/aria/menubar.en.md @@ -8,7 +8,7 @@ ## Overview -The manubar is a horizontal navigation bar that provides persistent access to application menus. Menubars organize commands into logical categories like File, Edit, and View, helping users discover and execute application features through keyboard or mouse interaction. +The menubar is a horizontal navigation bar that provides persistent access to application menus. Menubars organize commands into logical categories like File, Edit, and View, helping users discover and execute application features through keyboard or mouse interaction. diff --git a/adev-ja/src/content/guide/aria/overview.en.md b/adev-ja/src/content/guide/aria/overview.en.md index 9536e598e..380ca0d4f 100644 --- a/adev-ja/src/content/guide/aria/overview.en.md +++ b/adev-ja/src/content/guide/aria/overview.en.md @@ -3,9 +3,9 @@ ## What is Angular Aria? -Building accessible components seems straightforward, but implementing them according to the W3C Accessibility Guidelines requires significant effort and accessibility expertise. +Building accessible components seems straightforward, but implementing them according to the [W3C Accessibility Guidelines](https://www.w3.org/TR/wcag/) requires significant effort and accessibility expertise. -Angular Aria is a collection of headless, accessible directives that implement common WAI-ARIA patterns. The directives handle keyboard interactions, ARIA attributes, focus management, and screen reader support. All you have to do is provide the HTML structure, CSS styling, and business logic! +Angular Aria is a collection of headless, accessible directives that implement common [WAI-ARIA patterns](https://www.w3.org/WAI/ARIA/apg/patterns/). The directives handle keyboard interactions, ARIA attributes, focus management, and screen reader support. All you have to do is provide the HTML structure, CSS styling, and business logic! ## Installation diff --git a/adev-ja/src/content/guide/aria/overview.md b/adev-ja/src/content/guide/aria/overview.md index caea8c457..554654e25 100644 --- a/adev-ja/src/content/guide/aria/overview.md +++ b/adev-ja/src/content/guide/aria/overview.md @@ -3,9 +3,9 @@ ## Angular Ariaとは? {#what-is-angular-aria} -アクセシブルなコンポーネントの構築は一見簡単そうですが、W3Cアクセシビリティガイドラインに従って実装するには、多大な労力とアクセシビリティの専門知識が必要です。 +アクセシブルなコンポーネントの構築は一見簡単そうですが、[W3Cアクセシビリティガイドライン](https://www.w3.org/TR/wcag/)に従って実装するには、多大な労力とアクセシビリティの専門知識が必要です。 -Angular Ariaは、一般的なWAI-ARIAパターンを実装する、ヘッドレスでアクセシブルなディレクティブのコレクションです。ディレクティブはキーボードインタラクション、ARIA属性、フォーカス管理、スクリーンリーダーのサポートを処理します。あなたがすべきことは、HTML構造、CSSスタイリング、ビジネスロジックを提供することだけです! +Angular Ariaは、一般的な[WAI-ARIAパターン](https://www.w3.org/WAI/ARIA/apg/patterns/)を実装する、ヘッドレスでアクセシブルなディレクティブのコレクションです。ディレクティブはキーボードインタラクション、ARIA属性、フォーカス管理、スクリーンリーダーのサポートを処理します。あなたがすべきことは、HTML構造、CSSスタイリング、ビジネスロジックを提供することだけです! ## インストール {#installation} diff --git a/adev-ja/src/content/guide/components/advanced-configuration.en.md b/adev-ja/src/content/guide/components/advanced-configuration.en.md index 875e44eba..db7352678 100644 --- a/adev-ja/src/content/guide/components/advanced-configuration.en.md +++ b/adev-ja/src/content/guide/components/advanced-configuration.en.md @@ -7,7 +7,7 @@ TIP: This guide assumes you've already read the [Essentials Guide](essentials). The `@Component` decorator accepts a `changeDetection` option that controls the component's **change detection mode**. There are two change detection mode options. -**`ChangeDetectionStrategy.Default`** is, unsurprisingly, the default strategy. In this mode, +**`ChangeDetectionStrategy.Eager`/`Default`** is, unsurprisingly, the default strategy. In this mode, Angular checks whether the component's DOM needs an update whenever any activity may have occurred application-wide. Activities that trigger this checking include user interaction, network response, timers, and more. diff --git a/adev-ja/src/content/guide/components/advanced-configuration.md b/adev-ja/src/content/guide/components/advanced-configuration.md index d33a1340e..a48cf17e2 100644 --- a/adev-ja/src/content/guide/components/advanced-configuration.md +++ b/adev-ja/src/content/guide/components/advanced-configuration.md @@ -7,7 +7,7 @@ TIP: このガイドでは、すでに[基本概念のガイド](essentials)を `@Component` デコレーターは、コンポーネントの**変更検知モード**を制御する `changeDetection` オプションを受け取ります。 変更検知モードには2つのオプションがあります。 -**`ChangeDetectionStrategy.Default`** は、名前が示す通り、デフォルトの戦略です。 +**`ChangeDetectionStrategy.Eager`/`Default`** は、名前が示す通り、デフォルトの戦略です。 このモードでは、Angularは、アプリケーション全体で何か活動が行われた可能性があるたびに、 コンポーネントのDOMが更新を必要とするかどうかを確認します。 このチェックをトリガーする活動には、ユーザー操作、ネットワーク応答、タイマーなどがあります。 diff --git a/adev-ja/src/content/guide/components/anatomy-of-components.en.md b/adev-ja/src/content/guide/components/anatomy-of-components.en.md index 1bb8e6ae9..e08682b04 100644 --- a/adev-ja/src/content/guide/components/anatomy-of-components.en.md +++ b/adev-ja/src/content/guide/components/anatomy-of-components.en.md @@ -51,7 +51,7 @@ You can alternatively choose to write your template and styles in separate files export class ProfilePhoto {} ``` -This can help separate the concerns of _presentation_ from _behavior_ in your project. You can choose one approach for your entire project, or you decide which to use for each component. +This can help separate the concerns of _presentation_ from _behavior_ in your project. You can choose one approach for your entire project, or you can decide which to use for each component. Both `templateUrl` and `styleUrl` are relative to the directory in which the component resides. @@ -76,7 +76,7 @@ export class UserProfile {} By default, Angular components are _standalone_, meaning that you can directly add them to the `imports` array of other components. Components created with an earlier version of Angular may instead specify `standalone: false` in their `@Component` decorator. For these components, you instead import the `NgModule` in which the component is defined. See the full [`NgModule` guide](guide/ngmodules/overview) for details. -Important: In Angular versions before 19.0.0, the `standalone` option defaults to `false`. +IMPORTANT: In Angular versions before 19.0.0, the `standalone` option defaults to `false`. ### Showing components in a template diff --git a/adev-ja/src/content/guide/components/host-elements.en.md b/adev-ja/src/content/guide/components/host-elements.en.md index 082c2e0f6..e78c916f3 100644 --- a/adev-ja/src/content/guide/components/host-elements.en.md +++ b/adev-ja/src/content/guide/components/host-elements.en.md @@ -134,7 +134,7 @@ In cases like this, the following rules determine which value wins: ## Styling with CSS custom properties Developers often rely on [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties) to enable a flexible configuration of their component's styles. -You can set such custom properties on a host element with a [style binding][style binding](guide/templates/binding#css-style-properties). +You can set such custom properties on a host element with a [style binding](guide/templates/binding#css-style-properties). ```angular-ts @Component({ diff --git a/adev-ja/src/content/guide/components/styling.en.md b/adev-ja/src/content/guide/components/styling.en.md index 0355304f2..b2bad65c8 100644 --- a/adev-ja/src/content/guide/components/styling.en.md +++ b/adev-ja/src/content/guide/components/styling.en.md @@ -94,7 +94,7 @@ For example: So, in effect, the `` element may be in the component's template, or in any of its projected or child content. -- With `:host ::ng-deep p a`, both the `` and `

` elements must be decendants of the component's host element. +- With `:host ::ng-deep p a`, both the `` and `

` elements must be descendants of the component's host element. They can come from the component's template or the views of its child components, but not elsewhere in the app. diff --git a/adev-ja/src/content/guide/components/styling.md b/adev-ja/src/content/guide/components/styling.md index c8dca8ca9..896adad0c 100644 --- a/adev-ja/src/content/guide/components/styling.md +++ b/adev-ja/src/content/guide/components/styling.md @@ -68,9 +68,10 @@ export class ProfilePhoto {} モダンブラウザでは非推奨ですが、Angularのコンパイラは完全にサポートします。これらの擬似クラスは ネイティブの[Shadow DOM](https://developer.mozilla.org/docs/Web/Web_Components/Using_shadow_DOM) に依存せずに使用できます。 -コンパイル時に、フレームワークはこれらの擬似クラスを属性に変換するため、実行時にこれらのネイティブ擬似クラスのルール(ブラウザの互換性、特異性など)に準拠しません。Angularの -エミュレートされたカプセル化モードは、 -`::shadow`や`::part`など、Shadow DOMに関連するその他の擬似クラスをサポートしていません。 +コンパイル時に、フレームワークはこれらの擬似クラスを属性に変換するため、実行時にこれらのネイティブ擬似クラスの +ルール(ブラウザの互換性、特異性など)に準拠しません。Angularの +エミュレートされたカプセル化モードは、Shadow DOMに関連するその他の擬似クラス、たとえば +`::shadow`や`::part`などはサポートしていません。 #### `::ng-deep` @@ -141,4 +142,3 @@ Angularは、スタイル要素内のバインディングをサポートして [ `@import` at-rule](https://developer.mozilla.org/docs/Web/CSS/@import) を使用してCSSファイルを参照できます。 Angularはこれらの参照を*外部*スタイルとして扱います。外部スタイルは、エミュレートされたビューカプセル化の影響を受けません。 - diff --git a/adev-ja/src/content/guide/di/defining-dependency-providers.md b/adev-ja/src/content/guide/di/defining-dependency-providers.md index 84d4684f4..87217b8f4 100644 --- a/adev-ja/src/content/guide/di/defining-dependency-providers.md +++ b/adev-ja/src/content/guide/di/defining-dependency-providers.md @@ -731,6 +731,10 @@ export const routes: Routes = [ ]; ``` +Services provided at the route level are available to all components and directives within that route, as well as to its guards and resolvers. + +Since these services are instantiated independently of the route’s components, they do not have direct access to route-specific information. + ## Library author patterns When creating Angular libraries, you often need to provide flexible configuration options for consumers while maintaining clean APIs. Angular's own libraries demonstrate powerful patterns for achieving this. diff --git a/adev-ja/src/content/guide/di/hierarchical-dependency-injection.en.md b/adev-ja/src/content/guide/di/hierarchical-dependency-injection.en.md index 75e92df64..ccc5bf0b3 100644 --- a/adev-ja/src/content/guide/di/hierarchical-dependency-injection.en.md +++ b/adev-ja/src/content/guide/di/hierarchical-dependency-injection.en.md @@ -232,7 +232,7 @@ In this case, the injector looks no further than the current `ElementInjector` b providers: [{provide: FlowerService, useValue: {emoji: '🌷'}}], }) export class Self { - constructor(@Self() public flower: FlowerService) {} + public flower = inject(FlowerService, {self: true}); } ``` diff --git a/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md b/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md index afcaff207..51745375b 100644 --- a/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md +++ b/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md @@ -232,7 +232,7 @@ export class SelfNoData { providers: [{provide: FlowerService, useValue: {emoji: '🌷'}}], }) export class Self { - constructor(@Self() public flower: FlowerService) {} + public flower = inject(FlowerService, {self: true}); } ``` diff --git a/adev-ja/src/content/guide/directives/attribute-directives.en.md b/adev-ja/src/content/guide/directives/attribute-directives.en.md index f5af756de..0edbeb78b 100644 --- a/adev-ja/src/content/guide/directives/attribute-directives.en.md +++ b/adev-ja/src/content/guide/directives/attribute-directives.en.md @@ -14,7 +14,14 @@ This section walks you through creating a highlight directive that sets the back The CLI creates `src/app/highlight.directive.ts`, a corresponding test file `src/app/highlight.directive.spec.ts`. - + ```angular-ts + import {Directive} from '@angular/core'; + + @Directive({ + selector: '[appHighlight]', + }) + export class HighlightDirective {} + ``` The `@Directive()` decorator's configuration property specifies the directive's CSS attribute selector, `[appHighlight]`. @@ -25,11 +32,13 @@ This section walks you through creating a highlight directive that sets the back 1. Add logic to the `HighlightDirective` class that sets the background to yellow. - + -HELPFUL: Directives _do not_ support namespaces. +IMPORTANT: Directives _do not_ support namespaces. - +```angular-html {avoid} +

This is invalid

+``` ## Applying an attribute directive diff --git a/adev-ja/src/content/guide/directives/attribute-directives.md b/adev-ja/src/content/guide/directives/attribute-directives.md index dc35f294b..6e7bec394 100644 --- a/adev-ja/src/content/guide/directives/attribute-directives.md +++ b/adev-ja/src/content/guide/directives/attribute-directives.md @@ -14,7 +14,14 @@ CLIは`src/app/highlight.directive.ts`と、対応するテストファイル`src/app/highlight.directive.spec.ts`を作成します。 - + ```angular-ts + import {Directive} from '@angular/core'; + + @Directive({ + selector: '[appHighlight]', + }) + export class HighlightDirective {} + ``` `@Directive()`デコレーターの構成プロパティは、ディレクティブのCSS属性セレクター`[appHighlight]`を指定します。 @@ -25,11 +32,13 @@ 1. 背景を黄色に設定するロジックを`HighlightDirective`クラスに追加します。 - + -HELPFUL: ディレクティブは_ネームスペース_をサポートしていません。 +IMPORTANT: ディレクティブは_ネームスペース_をサポートしていません。 - +```angular-html {avoid} +

This is invalid

+``` ## 属性ディレクティブの適用 @@ -102,7 +111,6 @@ HELPFUL: ハンドラーは、ホストDOM要素`el`に色を設定するヘル 1. `highlight.directive.ts`で、`onMouseEnter`メソッドを修正して、最初に`appHighlight`でハイライトしようとします。`appHighlight`が`undefined`の場合は`red`にフォールバックします。 - 4. アプリケーションを起動して、ユーザーがラジオボタンで色を選択できることを確認します。 @@ -148,4 +156,4 @@ HELPFUL: ハンドラーは、ホストDOM要素`el`に色を設定するヘル -`ngNonBindable`を親要素に適用すると、Angularは要素の子要素に対して、プロパティバインディングやイベントバインディングなどあらゆる種類の補間とバインディングを無効にします。 \ No newline at end of file +`ngNonBindable`を親要素に適用すると、Angularは要素の子要素に対して、プロパティバインディングやイベントバインディングなどあらゆる種類の補間とバインディングを無効にします。 diff --git a/adev-ja/src/content/guide/directives/overview.en.md b/adev-ja/src/content/guide/directives/overview.en.md index 287f986e2..6deb5de41 100644 --- a/adev-ja/src/content/guide/directives/overview.en.md +++ b/adev-ja/src/content/guide/directives/overview.en.md @@ -38,7 +38,15 @@ HELPFUL: To add or remove a _single_ class, use [class binding](/guide/templates To use `NgClass`, add it to the component's `imports` list. - +```angular-ts +import {NgClass} from '@angular/common'; + +@Component({ + /* ... */ + imports: [NgClass], +}) +export class AppComponent {} +``` ### Using `NgClass` with an expression @@ -61,7 +69,7 @@ Because `isSpecial` is true, `ngClass` applies the class of `special` to the ` + For this use case, Angular applies the classes on initialization and in case of changes caused by reassigning the `currentClasses` object. The full example calls `setCurrentClasses()` initially with `ngOnInit()` when the user clicks on the `Refresh currentClasses` button. @@ -75,7 +83,15 @@ HELPFUL: To add or remove a _single_ style, use [style bindings](guide/templates To use `NgStyle`, add it to the component's `imports` list. - +```angular-ts +import {NgStyle} from '@angular/common'; + +@Component({ + /* ... */ + imports: [NgStyle], +}) +export class AppComponent {} +``` Use `NgStyle` to set multiple inline styles simultaneously, based on the state of the component. @@ -87,7 +103,7 @@ Use `NgStyle` to set multiple inline styles simultaneously, based on the state o 1. To set the element's styles, add an `ngStyle` property binding to `currentStyles`. - + For this use case, Angular applies the styles upon initialization and in case of changes. To do this, the full example calls `setCurrentStyles()` initially with `ngOnInit()` and when the dependent properties change through a button click. diff --git a/adev-ja/src/content/guide/directives/overview.md b/adev-ja/src/content/guide/directives/overview.md index 0058c3ed1..bc6e9ac2a 100644 --- a/adev-ja/src/content/guide/directives/overview.md +++ b/adev-ja/src/content/guide/directives/overview.md @@ -38,7 +38,15 @@ HELPFUL: _単一の_ クラスを追加または削除するには、`NgClass` `NgClass` を使用するには、コンポーネントの `imports` リストに追加します。 - +```angular-ts +import {NgClass} from '@angular/common'; + +@Component({ + /* ... */ + imports: [NgClass], +}) +export class AppComponent {} +``` ### 式を使用して `NgClass` を使用 @@ -75,7 +83,15 @@ HELPFUL: To add or remove a _single_ style, use [style bindings](guide/templates `NgStyle` を使用するには、コンポーネントの `imports` リストに追加します。 - +```angular-ts +import {NgStyle} from '@angular/common'; + +@Component({ + /* ... */ + imports: [NgStyle], +}) +export class AppComponent {} +``` `NgStyle` を使用して、コンポーネントの状態に基づいて、複数のインラインスタイルを同時に設定します。 @@ -93,7 +109,6 @@ HELPFUL: To add or remove a _single_ style, use [style bindings](guide/templates これを行うために、完全な例では、`ngOnInit()` を使用して最初に `setCurrentStyles()` を呼び出し、依存プロパティがボタンクリックを通じて変更されたときに呼び出します。 ただし、これらの手順は、`ngStyle` 自体を実装するために必要ではありません。 - ## DOM 要素のないディレクティブをホストする Angularの `` は、AngularがDOMに配置しないため、スタイルやレイアウトに影響を与えないグループ化要素です。 @@ -122,4 +137,4 @@ Angularの `` は、AngularがDOMに配置しないため、スタ - \ No newline at end of file + diff --git a/adev-ja/src/content/guide/directives/structural-directives.en.md b/adev-ja/src/content/guide/directives/structural-directives.en.md index 8751f2b8a..9a0f469bf 100644 --- a/adev-ja/src/content/guide/directives/structural-directives.en.md +++ b/adev-ja/src/content/guide/directives/structural-directives.en.md @@ -56,7 +56,7 @@ The second piece of syntax is a key-expression pair, `from source`. `from` is a ## One structural directive per element -You can only apply one structural directive per element when using the shorthand syntax. This is because there is only one `` element onto which that directive gets unwrapped. Multiple directives would require multiple nested ``, and it's unclear which directive should be first. `` can be used when to create wrapper layers when multiple structural directives need to be applied around the same physical DOM element or component, which allows the user to define the nested structure. +You can only apply one structural directive per element when using the shorthand syntax. This is because there is only one `` element onto which that directive gets unwrapped. Multiple directives would require multiple nested ``, and it's unclear which directive should be first. `` can be used to create wrapper layers when multiple structural directives need to be applied around the same physical DOM element or component, which allows the user to define the nested structure. ## Creating a structural directive @@ -76,7 +76,7 @@ Angular creates the directive class and specifies the CSS selector, `[select]`, Import `TemplateRef`, and `ViewContainerRef`. Inject `TemplateRef` and `ViewContainerRef` in the directive as private properties. ```ts -import {Directive, TemplateRef, ViewContainerRef} from '@angular/core'; +import {Directive, TemplateRef, ViewContainerRef, inject} from '@angular/core'; @Directive({ selector: '[select]', diff --git a/adev-ja/src/content/guide/directives/structural-directives.md b/adev-ja/src/content/guide/directives/structural-directives.md index 952a27507..0876d9911 100644 --- a/adev-ja/src/content/guide/directives/structural-directives.md +++ b/adev-ja/src/content/guide/directives/structural-directives.md @@ -76,7 +76,7 @@ Angularは、ディレクティブクラスを作成し、テンプレートで `TemplateRef`と`ViewContainerRef`をインポートし、ディレクティブ内で`TemplateRef`と`ViewContainerRef`をプライベートプロパティとしてインジェクトします。 ```ts -import {Directive, TemplateRef, ViewContainerRef} from '@angular/core'; +import {Directive, TemplateRef, ViewContainerRef, inject} from '@angular/core'; @Directive({ selector: '[select]', diff --git a/adev-ja/src/content/guide/drag-drop.md b/adev-ja/src/content/guide/drag-drop.md index 2e2aebbe3..91769cd4b 100644 --- a/adev-ja/src/content/guide/drag-drop.md +++ b/adev-ja/src/content/guide/drag-drop.md @@ -329,7 +329,7 @@ Combining `cdkDropListHasAnchor` with `cdkDropListSortingDisabled` makes it poss Drag and drop supports animations for both: -- Sorting an draggable element inside a list +- Sorting a draggable element inside a list - Moving the draggable element from the position that the user dropped it to the final position inside the list To set up your animations, define a CSS transition that targets the transform property. The following classes can be used for animations: diff --git a/adev-ja/src/content/guide/forms/dynamic-forms.en.md b/adev-ja/src/content/guide/forms/dynamic-forms.en.md index 4bd92aef6..be680110b 100644 --- a/adev-ja/src/content/guide/forms/dynamic-forms.en.md +++ b/adev-ja/src/content/guide/forms/dynamic-forms.en.md @@ -9,12 +9,12 @@ A typical use-case is a questionnaire. You might need to get input from users in different contexts. The format and style of the forms a user sees should remain constant, while the actual questions you need to ask vary with the context. -In this tutorial you will build a dynamic form that presents a basic questionnaire. +In this tutorial, you will build a dynamic form that presents a basic questionnaire. You build an online application for heroes seeking employment. The agency is constantly tinkering with the application process, but by using the dynamic form you can create the new forms on the fly without changing the application code. -The tutorial walks you through the following steps. +The tutorial walks you through the following steps: 1. Enable reactive forms for a project. 1. Establish a data model to represent form controls. @@ -30,7 +30,7 @@ The basic version can evolve to support a richer variety of questions, more grac Dynamic forms are based on reactive forms. -To give the application access reactive forms directives, import `ReactiveFormsModule` from the `@angular/forms` library into the necessary components. +To give the application access to reactive form directives, import `ReactiveFormsModule` from the `@angular/forms` package into the necessary components. diff --git a/adev-ja/src/content/guide/forms/dynamic-forms.md b/adev-ja/src/content/guide/forms/dynamic-forms.md index a78b8d050..66fe77349 100644 --- a/adev-ja/src/content/guide/forms/dynamic-forms.md +++ b/adev-ja/src/content/guide/forms/dynamic-forms.md @@ -14,7 +14,7 @@ エージェンシーは常にアプリケーションプロセスをいじっていますが、 動的フォームを使用することでアプリケーションコードを変更せずに新しいフォームをオンザフライで作成できます。 -このチュートリアルでは、次の手順について説明します。 +このチュートリアルでは、次の手順について説明します: 1. プロジェクトでリアクティブフォームを有効にする。 1. フォームコントロールを表すデータモデルを確立する。 @@ -30,7 +30,7 @@ 動的フォームはリアクティブフォームに基づいています。 -アプリケーションにリアクティブフォームディレクティブへのアクセス権を与えるには、必要なコンポーネントに `@angular/forms` ライブラリから `ReactiveFormsModule` をインポートします。 +アプリケーションにリアクティブフォームディレクティブへのアクセス権を与えるには、必要なコンポーネントに `@angular/forms` パッケージから `ReactiveFormsModule` をインポートします。 diff --git a/adev-ja/src/content/guide/forms/form-validation.en.md b/adev-ja/src/content/guide/forms/form-validation.en.md index 07d9d9d67..4cce9a1d4 100644 --- a/adev-ja/src/content/guide/forms/form-validation.en.md +++ b/adev-ja/src/content/guide/forms/form-validation.en.md @@ -8,7 +8,7 @@ This page shows how to validate user input from the UI and display useful valida To add validation to a template-driven form, you add the same validation attributes as you would with [native HTML form validation](https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5/Constraint_validation). Angular uses directives to match these attributes with validator functions in the framework. -Every time the value of a form control changes, Angular runs validation and generates either a list of validation errors that results in an `INVALID` status, or null, which results in a VALID status. +Every time the value of a form control changes, Angular runs validation and generates either a list of validation errors that results in an `INVALID` status, or `null`, which results in a `VALID` status. You can then inspect the control's state by exporting `ngModel` to a local template variable. The following example exports `NgModel` into a variable called `name`: @@ -237,7 +237,7 @@ Asynchronous validators implement the `AsyncValidatorFn` and `AsyncValidator` in These are very similar to their synchronous counterparts, with the following differences. - The `validate()` functions must return a Promise or an observable, -- The observable returned must be finite, meaning it must complete at some point. +- The observable returned must be finite, meaning that it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`. Asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful. @@ -339,6 +339,70 @@ With reactive forms, set the property in the `FormControl` instance. new FormControl('', {updateOn: 'blur'}); ``` +## Managing validators dynamically in reactive forms + +In complex reactive forms, you may need to add, remove, or modify validators based on user input or application state. +Angular provides several methods on `AbstractControl` to manage validators at runtime without recreating form controls. + +### Adding and removing validators + +The [`addValidators`](api/forms/AbstractControl#addValidators) and [`removeValidators`](api/forms/AbstractControl#removeValidators) methods allow you to modify a control's validators after initialization. + +```ts +onCountryChange(country: string) { + const postalCodeControl = this.profileForm.get('postalCode'); + + if (country === 'US') { + // Add validators for US postal codes + postalCodeControl.addValidators([Validators.required, Validators.pattern(/^\d{5}$/)]); + } else { + // Remove validators when not US + postalCodeControl.removeValidators([Validators.required]); + } + + postalCodeControl.updateValueAndValidity(); +} +``` + +### Replacing all validators + +Use [`setValidators`](api/forms/AbstractControl#setValidators) to replace all existing synchronous validators on a control, or [`clearValidators`](api/forms/AbstractControl#clearValidators) to remove all validators. + +```ts +toggleStrictNameValidation(isStrict: boolean) { + const nameControl = this.profileForm.get('name'); + + if (enable) { + // Set strict validation rules + nameControl.setValidators([ + Validators.required, + Validators.minLength(3), + Validators.pattern(/^[a-zA-Z]+$/), + ]); + } else { + // Clear all validators + nameControl.clearValidators(); + } + + nameControl.updateValueAndValidity(); +} +``` + +The same pattern applies to async validators using [`addAsyncValidators`](api/forms/AbstractControl#addAsyncValidators), [`removeAsyncValidators`](api/forms/AbstractControl#removeAsyncValidators), [`setAsyncValidators`](api/forms/AbstractControl#setAsyncValidators), and [`clearAsyncValidators`](api/forms/AbstractControl#clearAsyncValidators). + +### Triggering validation updates + +After modifying validators, call [`updateValueAndValidity`](api/forms/AbstractControl#updateValueAndValidity) to recalculate the control's validation status. +This method accepts options to control update behavior. + +```ts +// Update control and notify parent +control.updateValueAndValidity(); + +// Update control only, don't notify parent or emit events +control.updateValueAndValidity({onlySelf: true, emitEvent: false}); +``` + ## Interaction with native HTML form validation By default, Angular disables [native HTML form validation](https://developer.mozilla.org/docs/Web/Guide/HTML/Constraint_validation) by adding the `novalidate` attribute on the enclosing `
` and uses directives to match these attributes with validator functions in the framework. diff --git a/adev-ja/src/content/guide/forms/form-validation.md b/adev-ja/src/content/guide/forms/form-validation.md index 06aca2471..359d0fbec 100644 --- a/adev-ja/src/content/guide/forms/form-validation.md +++ b/adev-ja/src/content/guide/forms/form-validation.md @@ -339,6 +339,70 @@ interface ActorsService { new FormControl('', {updateOn: 'blur'}); ``` +## Managing validators dynamically in reactive forms + +In complex reactive forms, you may need to add, remove, or modify validators based on user input or application state. +Angular provides several methods on `AbstractControl` to manage validators at runtime without recreating form controls. + +### Adding and removing validators + +The [`addValidators`](api/forms/AbstractControl#addValidators) and [`removeValidators`](api/forms/AbstractControl#removeValidators) methods allow you to modify a control's validators after initialization. + +```ts +onCountryChange(country: string) { + const postalCodeControl = this.profileForm.get('postalCode'); + + if (country === 'US') { + // Add validators for US postal codes + postalCodeControl.addValidators([Validators.required, Validators.pattern(/^\d{5}$/)]); + } else { + // Remove validators when not US + postalCodeControl.removeValidators([Validators.required]); + } + + postalCodeControl.updateValueAndValidity(); +} +``` + +### Replacing all validators + +Use [`setValidators`](api/forms/AbstractControl#setValidators) to replace all existing synchronous validators on a control, or [`clearValidators`](api/forms/AbstractControl#clearValidators) to remove all validators. + +```ts +toggleStrictNameValidation(isStrict: boolean) { + const nameControl = this.profileForm.get('name'); + + if (enable) { + // Set strict validation rules + nameControl.setValidators([ + Validators.required, + Validators.minLength(3), + Validators.pattern(/^[a-zA-Z]+$/), + ]); + } else { + // Clear all validators + nameControl.clearValidators(); + } + + nameControl.updateValueAndValidity(); +} +``` + +The same pattern applies to async validators using [`addAsyncValidators`](api/forms/AbstractControl#addAsyncValidators), [`removeAsyncValidators`](api/forms/AbstractControl#removeAsyncValidators), [`setAsyncValidators`](api/forms/AbstractControl#setAsyncValidators), and [`clearAsyncValidators`](api/forms/AbstractControl#clearAsyncValidators). + +### Triggering validation updates + +After modifying validators, call [`updateValueAndValidity`](api/forms/AbstractControl#updateValueAndValidity) to recalculate the control's validation status. +This method accepts options to control update behavior. + +```ts +// Update control and notify parent +control.updateValueAndValidity(); + +// Update control only, don't notify parent or emit events +control.updateValueAndValidity({onlySelf: true, emitEvent: false}); +``` + ## ネイティブHTMLフォーム検証との相互作用 {#interaction-with-native-html-form-validation} デフォルトでは、Angularは囲んでいる``に`novalidate`属性を追加することで[ネイティブHTMLフォーム検証](https://developer.mozilla.org/docs/Web/Guide/HTML/Constraint_validation)を無効にし、これらの属性をフレームワーク内のバリデーター関数と一致させるためにディレクティブを使用します。 diff --git a/adev-ja/src/content/guide/forms/overview.en.md b/adev-ja/src/content/guide/forms/overview.en.md index ef499562a..4200cddfb 100644 --- a/adev-ja/src/content/guide/forms/overview.en.md +++ b/adev-ja/src/content/guide/forms/overview.en.md @@ -6,7 +6,7 @@ Applications use forms to enable users to log in, to update a profile, to enter Angular provides two different approaches to handling user input through forms: reactive and template-driven. -Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes. +Both capture user input events from the view, validate the input, create a form and data model, and provide a way to track changes. TIP: If you're looking for the new experimental Signal Forms, check out our [essential Signal Forms guide](/essentials/signal-forms)! @@ -86,7 +86,7 @@ The following component implements the same input field for a single control, us -IMPORTANT: In a template-driven form the source of truth is the template. The `NgModel` directive automatically manages the `FormControl` instance for you. +IMPORTANT: In a template-driven form, the source of truth is the template. The `NgModel` directive automatically manages the `FormControl` instance for you. ## Data flow in forms @@ -99,10 +99,10 @@ The following diagrams illustrate both kinds of data flow for each type of form, ### Data flow in reactive forms -In reactive forms each form element in the view is directly linked to the form model (a `FormControl` instance). +In reactive forms, each form element in the view is directly linked to the form model (a `FormControl` instance). Updates from the view to the model and from the model to the view are synchronous and do not depend on how the UI is rendered. -The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps. +The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps: 1. The user types a value into the input element, in this case the favorite color _Blue_. 1. The form input element emits an "input" event with the latest value. diff --git a/adev-ja/src/content/guide/forms/overview.md b/adev-ja/src/content/guide/forms/overview.md index ec8772abb..92dc6b4a1 100644 --- a/adev-ja/src/content/guide/forms/overview.md +++ b/adev-ja/src/content/guide/forms/overview.md @@ -6,7 +6,7 @@ Angularは、フォームを通じてユーザー入力を処理するために、リアクティブとテンプレート駆動の2つの異なるアプローチを提供します。 -どちらもビューからのユーザー入力イベントをキャプチャし、ユーザー入力を検証してフォームモデルとデータモデルを作成して更新し、変更を追跡する方法を提供します。 +どちらもビューからのユーザー入力イベントをキャプチャし、入力を検証してフォームモデルとデータモデルを作成し、変更を追跡する方法を提供します。 TIP: 新しい実験的なシグナルフォームをお探しの場合は、[基本ガイドのSignal Forms](/essentials/signal-forms)をご確認ください! @@ -102,7 +102,7 @@ IMPORTANT: テンプレート駆動フォームでは、真実の源はテンプ リアクティブフォームでは、ビュー内の各フォーム要素は、フォームモデル(`FormControl` インスタンス)に直接リンクされています。 ビューからモデルへの更新、およびモデルからビューへの更新は同期であり、UIのレンダリング方法に依存しません。 -ビューからモデルへの図は、入力フィールドの値がビューから変更された場合にデータがどのように流れるかを示しています。 +ビューからモデルへの図は、入力フィールドの値がビューから変更された場合にデータがどのように流れるかを示しています: 1. ユーザーは入力要素にお気に入りの色 _Blue_ を入力します。 1. フォーム入力要素は、最新の値を伴う "input" イベントを発行します。 diff --git a/adev-ja/src/content/guide/forms/reactive-forms.en.md b/adev-ja/src/content/guide/forms/reactive-forms.en.md index cd0474c37..d4fe7f488 100644 --- a/adev-ja/src/content/guide/forms/reactive-forms.en.md +++ b/adev-ja/src/content/guide/forms/reactive-forms.en.md @@ -1,28 +1,28 @@ # Reactive forms Reactive forms provide a model-driven approach to handling form inputs whose values change over time. -This guide shows you how to create and update a basic form control, progress to using multiple controls in a group, validate form values, and create dynamic forms where you can add or remove controls at run time. +This guide shows you how to create and update a basic form control, use multiple controls in a group, validate form values, and create dynamic forms where you can add or remove controls at runtime. ## Overview of reactive forms Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. -Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously. +Reactive forms are built around observable streams, where form inputs and values are provided as streams that can be accessed synchronously. Reactive forms also provide a straightforward path to testing because you are assured that your data is consistent and predictable when requested. -Any consumers of the streams have access to manipulate that data safely. +Any consumers of these streams can safely manipulate the data. Reactive forms differ from [template-driven forms](guide/forms/template-driven-forms) in distinct ways. Reactive forms provide synchronous access to the data model, immutability with observable operators, and change tracking through observable streams. -Template-driven forms let direct access modify data in your template, but are less explicit than reactive forms because they rely on directives embedded in the template, along with mutable data to track changes asynchronously. +Template-driven forms allow direct access to modify data in your template, but are less explicit than reactive forms because they rely on directives embedded in the template, along with mutable data to track changes asynchronously. See the [Forms Overview](guide/forms) for detailed comparisons between the two paradigms. ## Adding a basic form control There are three steps to using form controls. -1. Generate a new component and register the reactive forms module. This module declares the reactive-form directives that you need to use reactive forms. +1. Generate a new component and register the reactive forms module. This module declares the reactive-form directives required to use reactive forms. 1. Instantiate a new `FormControl`. 1. Register the `FormControl` in the template. @@ -62,7 +62,7 @@ The `FormControl` assigned to the `name` property is displayed when the ` this.cdr.markForCheck()); + } +} +``` + ### Using `FormArrayDirective` for top-level form arrays @@ -417,7 +437,7 @@ Initially, the form contains one `Alias` field. To add another field, click the ## Unified control state change events All form controls expose a single unified stream of **control state change events** through the `events` observable on `AbstractControl` (`FormControl`, `FormGroup`, `FormArray`, and `FormRecord`). -This unified stream lets you react to **value**, **status**, **pristine**, **touched** and **reset** state changes and also for **form-level actions** such as **submit** , allowing you to handle all updates with a one subscription instead of wiring multiple observables. +This unified stream lets you react to **value**, **status**, **pristine**, **touched**, and **reset** state changes, as well as **form-level actions** such as **submit**, allowing you to handle all updates with a single subscription instead of wiring multiple observables. ### Event types @@ -637,6 +657,8 @@ updatePostalCodeValidator(country: string) { } ``` +HELPFUL: For dynamically managing validators at runtime, see the [Managing validators dynamically in reactive forms](guide/forms/form-validation#managing-validators-dynamically-in-reactive-forms) section in the Form Validation guide. + ## Utility functions for narrowing form control types Angular provides four utility functions that help determine the concrete type of an `AbstractControl`. These functions act as **type guards** and narrow the control type when they return `true`, which lets you safely access subtype-specific properties inside the same block. diff --git a/adev-ja/src/content/guide/forms/reactive-forms.md b/adev-ja/src/content/guide/forms/reactive-forms.md index 272376627..74fb10f3b 100644 --- a/adev-ja/src/content/guide/forms/reactive-forms.md +++ b/adev-ja/src/content/guide/forms/reactive-forms.md @@ -231,7 +231,7 @@ Just as a form group contains a group of controls, the _profileForm_ `FormGroup` ユーザーがボタンをクリックすると、`profileForm` モデルが、`firstName` と `street` の新しい値で更新されます。`street` は、`address` プロパティ内のオブジェクトで提供されることに注意してください。 これは、`patchValue()` メソッドが、モデル構造に対して更新を適用するためです。 -`PatchValue()` は、フォームモデルで定義されているプロパティのみを更新します。 +`patchValue()` は、フォームモデルで定義されているプロパティのみを更新します。 ## FormBuilder サービスを使用してコントロールを生成する @@ -379,6 +379,26 @@ _フォーム検証_ は、ユーザー入力が完全で正しいことを確 新しいエイリアスインスタンスが追加されるたびに、新しいフォーム配列インスタンスに、インデックスに基づいてコントロールが提供されます。これにより、ルートコントロールのステータスと値を計算する際に、各個別のコントロールを追跡できます。 +NOTE: In zoneless applications, mutating a reactive forms model (for example calling `FormArray.push()`) does not automatically schedule component change detection. If your template depends on structural model changes such as `aliases.controls`, make sure the component notifies Angular to run change detection, for example by bridging a forms observable to `ChangeDetectorRef.markForCheck()`: + +```ts +import {ChangeDetectorRef, Component, inject} from '@angular/core'; +import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; + +@Component({ + /* ... */ +}) +export class ProfileEditor { + private readonly cdr = inject(ChangeDetectorRef); + + constructor() { + this.profileForm.valueChanges + .pipe(takeUntilDestroyed()) + .subscribe(() => this.cdr.markForCheck()); + } +} +``` + ### トップレベルフォーム配列での `FormArrayDirective` の使用 @@ -637,6 +657,8 @@ updatePostalCodeValidator(country: string) { } ``` +HELPFUL: For dynamically managing validators at runtime, see the [Managing validators dynamically in reactive forms](guide/forms/form-validation#managing-validators-dynamically-in-reactive-forms) section in the Form Validation guide. + ## フォームコントロールタイプを絞り込むユーティリティ関数 {#utility-functions-for-narrowing-form-control-types} Angularは、`AbstractControl` の具体的な型を判定するのに役立つ4つのユーティリティ関数を提供します。これらの関数は **型ガード** として機能し、`true` を返すときにコントロールタイプを絞り込むため、同じブロック内でサブタイプ固有のプロパティに安全にアクセスできます。 diff --git a/adev-ja/src/content/guide/forms/signals/async-operations.md b/adev-ja/src/content/guide/forms/signals/async-operations.md index ec4a837c2..47352590f 100644 --- a/adev-ja/src/content/guide/forms/signals/async-operations.md +++ b/adev-ja/src/content/guide/forms/signals/async-operations.md @@ -328,14 +328,14 @@ export class Registration { private createUsernameResource = (usernameSignal: Signal) => { return rxResource({ - request: () => usernameSignal(), - stream: ({request: username}) => this.usernameService.checkUsername(username), + params: () => usernameSignal(), + stream: ({params: username}) => this.usernameService.checkUsername(username), }); }; registrationForm = form(this.registrationModel, (schemaPath) => { validateAsync(schemaPath.username, { - params: ({value}) => value() || undefined, + params: ({value}) => value(), factory: this.createUsernameResource, onSuccess: (result) => result?.available ? null : {kind: 'usernameTaken', message: 'Username taken'}, diff --git a/adev-ja/src/content/guide/forms/signals/cross-field-logic.md b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md new file mode 100644 index 000000000..f244f3720 --- /dev/null +++ b/adev-ja/src/content/guide/forms/signals/cross-field-logic.md @@ -0,0 +1,251 @@ +# Cross-field logic + +**Cross-field logic** is necessary when any rule, validation, or behavior of one field depends on another field's value or state. + +Signal forms provide a **field context** to every rule function. The field context provides access to the current field's value and state, and lets you read other fields in the form using `valueOf()`, `stateOf()`, and `fieldTreeOf()`. + +This guide covers the field context API in depth and shows common cross-field patterns. For single-field validation, see the [Validation guide](/guide/forms/signals/validation). + +## Understanding the field context + +Every rule function in signal forms receives a **field context** parameter, which is an object that describes the current field and provides access to the rest of the form. + +There are three properties you can access for the current field: + +| Property | Type | Description | +| ----------- | -------------------- | -------------------------------------------------------------------- | +| `value` | `Signal` | The current field's value as a signal | +| `state` | `FieldState` | The current field's state (such as validity, errors, touched, dirty) | +| `fieldTree` | `FieldTree` | The current field's tree, for programmatic access to child fields | + +For cross-field logic, the following three properties allow you to access other parts of the form: + +| Property | Type | Description | +| --------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| `valueOf()` | `(path) => PValue` | Most common. Use when you need another field's raw value for comparisons or calculations. | +| `stateOf()` | `(path) => FieldState` | Use when your logic depends on another field's state, such as whether it's valid, touched, or dirty. | +| `fieldTreeOf()` | `(path) => FieldTree` | Use when you need programmatic access to another field's tree, such as pushing errors to a specific child field with validateTree. | + +Here is an example of using `value` and `valueOf()` to validate that the current field (end date) comes after the start date in the form: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class EventForm { + eventModel = signal({ + startDate: new Date('2026-06-01'), + endDate: new Date('2026-06-05'), + }); + + eventForm = form(this.eventModel, (schemaPath) => { + validate(schemaPath.endDate, ({value, valueOf}) => { + if (value() <= valueOf(schemaPath.startDate)) { + return { + kind: 'invalidDateRange', + message: 'End date must be after start date', + }; + } + + return null; + }); + }); +} +``` + +NOTE: The `fieldContext` parameter is typically destructured to pull out only what the rule needs. The remaining examples in this guide use this pattern. + +## Cross-field validation patterns + +The date range example from the previous section validates the end date against the start date. Because the rule reads `valueOf(schemaPath.startDate)`, it re-evaluates automatically whenever either date changes. In other words, a single validator is enough to keep the error state correct. + +However, that single validator only places the error on the end date field. If you want both fields to show an error when the range is invalid, add a matching validation rule to each field: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class EventForm { + eventModel = signal({ + startDate: new Date('2026-06-01'), + endDate: new Date('2026-06-05'), + }); + + eventForm = form(this.eventModel, (schemaPath) => { + validate(schemaPath.startDate, ({value, valueOf}) => { + if (value() >= valueOf(schemaPath.endDate)) { + return { + kind: 'invalidDateRange', + message: 'Start date must be before end date', + }; + } + return null; + }); + + validate(schemaPath.endDate, ({value, valueOf}) => { + if (value() <= valueOf(schemaPath.startDate)) { + return { + kind: 'invalidDateRange', + message: 'End date must be after start date', + }; + } + return null; + }); + }); +} +``` + +Both rules make use of `valueOf()` to read the other field. Because each rule is reactive, changing either date re-evaluates both validations automatically. + +NOTE: When a rule involves multiple fields, you need to decide where the error belongs: on a specific field, on multiple fields, or on the parent. In general, place the error where the user would most likely go to fix the problem. + +### Conditional requirements + +In some forms, certain fields are only required under certain conditions. For example, a registration form might require a company name only when the user selects a business account type: + +```ts +import {Component, signal} from '@angular/core'; +import {form, required} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class RegistrationForm { + registrationModel = signal({ + accountType: 'personal' as 'personal' | 'business', + companyName: '', + }); + + registrationForm = form(this.registrationModel, (schemaPath) => { + required(schemaPath.companyName, { + when: ({valueOf}) => valueOf(schemaPath.accountType) === 'business', + message: 'Company name is required for business accounts', + }); + }); +} +``` + +The `when` option receives the same field context as any other rule function, so `valueOf` works the same way. When the user switches back to `'personal'`, the condition re-evaluates and the requirement — along with its error — clears automatically. + +Using `required()` with `when` instead of a manual `validate()` check also adds proper required metadata to the field, which enables accessibility features like marking the field as required for screen readers. + +### Validating based on another field's state + +The examples so far use `valueOf()` to read another field's value. Sometimes your logic depends on another field's _state_ instead — whether it's valid, touched, or dirty. Use `stateOf()` for this. + +For example, a confirm-password field should only check for a match once the user has interacted with the password field. If the user hasn't touched the password yet, flagging a mismatch on the confirmation is premature: + +```ts +import {Component, signal} from '@angular/core'; +import {form, validate} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class PasswordForm { + passwordModel = signal({ + password: '', + confirmPassword: '', + }); + + passwordForm = form(this.passwordModel, (schemaPath) => { + validate(schemaPath.confirmPassword, ({value, valueOf, stateOf}) => { + if (!stateOf(schemaPath.password).touched()) { + return null; + } + if (value() !== valueOf(schemaPath.password)) { + return { + kind: 'passwordMismatch', + message: 'Passwords do not match', + }; + } + return null; + }); + }); +} +``` + +The `stateOf()` call returns the other field's [field state](api/forms/signals/FieldState), giving you access to signals like `invalid()`, `touched()`, and `dirty()`. Because these are signals, the rule re-evaluates whenever the password field's validity changes. + +WARNING: Be careful not to read state which depends on your field's validation, as that creates a circular loop. For example, a validator which checks whether the parent field is valid will create an infinite loop because the parent's validity depends on its children's validity (which includes your validator). + +## Using validateTree + +The examples so far use `validate()` to check individual fields. Sometimes you need to validate a group of fields where the logic is inherently about multiple fields in a group, and direct errors to specific children within it. `validateTree` handles is ideal for these kinds of scenarios. + +For example, in a Sudoku puzzle, each row must contain unique numbers. This is a group-level rule: you check the entire row, then flag the specific cells that violate it. This kind of validation can't be expressed cleanly with `validate` on individual fields, because each cell would need to know about every other cell. + +```ts +import {Component, signal} from '@angular/core'; +import {form, validateTree} from '@angular/forms/signals'; + +@Component({ + /* ... */ +}) +export class SudokuRow { + rowModel = signal({ + cell1: 1, + cell2: 3, + cell3: 1, + cell4: 4, + }); + + rowForm = form(this.rowModel, (schemaPath) => { + validateTree(schemaPath, ({value, fieldTreeOf}) => { + const row = value(); + const entries = [ + {val: row.cell1, fieldTree: fieldTreeOf(schemaPath.cell1)}, + {val: row.cell2, fieldTree: fieldTreeOf(schemaPath.cell2)}, + {val: row.cell3, fieldTree: fieldTreeOf(schemaPath.cell3)}, + {val: row.cell4, fieldTree: fieldTreeOf(schemaPath.cell4)}, + ]; + + const counts = new Map(); + for (const {val} of entries) { + if (val !== 0) { + counts.set(val, (counts.get(val) ?? 0) + 1); + } + } + + const errors = entries + .filter(({val}) => val !== 0 && (counts.get(val) ?? 0) > 1) + .map(({val, fieldTree}) => ({ + kind: 'duplicateInRow', + message: `${val} already appears in this row`, + fieldTree, + })); + + return errors.length > 0 ? errors : null; + }); + }); +} +``` + +The validator runs on the parent field (the row), reads all cell values, counts duplicates, and returns an error for each cell that contains a repeated number. The `fieldTree` property on each error tells Angular exactly which cell should show the error. Without `fieldTree`, the errors would apply to the row itself — not where the user needs to see them. + +Because `validateTree` can return an array of errors, a single validator can flag multiple cells at once. Each error includes a `fieldTree` pointing to its target, so Angular routes the errors to the correct fields. + +### When to use validateTree vs validate + +Prefer `validate()` with `valueOf()` when the error belongs on the field being validated — even if the rule reads from other fields. Reach for `validateTree` when: + +- The validation logic is inherently about a group of fields, not any single field +- The validator needs to return errors targeting different child fields + +TIP: For an introduction to `validateTree` and its return type, see the [Validation guide](/guide/forms/signals/validation). + +## Next steps + +This guide covered the field context API and common cross-field patterns. To learn more about related Signal Forms guide, check out: + + + + + + diff --git a/adev-ja/src/content/guide/forms/signals/field-state-management.en.md b/adev-ja/src/content/guide/forms/signals/field-state-management.en.md index 4107e939d..82b74aa00 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 @@ -523,6 +523,29 @@ export class Order { Hidden fields don't participate in validation, allowing the form to be submitted even if the hidden field would otherwise be invalid. +### Tracking values for array fields + +In signal forms, a `@for` block over a set of fields should be tracked by field identity. + +```angular-ts +@Component({ + imports: [FormField], + template: ` + @for (field of form.emails; track field) { + + } + `, +}) +export class App { + formModel = signal({emails: ['john.doe@mail.com', 'max.musterman@mail.com']}); + form = form(this.formModel); +} +``` + +The forms system is already tracking the model values within the array and maintaining a stable identity of the fields it creates automatically. + +When an item changes, it may represent a new logical entity even if some of its properties look the same. Tracking by identity ensures the framework treats it as a distinct item rather than reusing existing UI elements. This prevents stateful elements, like form inputs, from being incorrectly shared and keeps bindings aligned with the correct part of the model. + ## Using field state in component logic Field state signals work with Angular's reactive primitives like `computed()` and `effect()` for advanced form logic. @@ -595,14 +618,13 @@ While field state typically updates through user interactions (typing, focusing, #### Form submission -When a user submits a form, use the `submit()` function to handle validation and reveal errors. - -Signal Forms handles validation through its own system, so you need to prevent the browser's default form behavior. Add `novalidate` to the `` element to disable native HTML validation (such as browser tooltip popups for `required` or `type="email"` fields), and call `$event.preventDefault()` in your submit handler to prevent the browser from reloading the page: +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 `` element. ```angular-ts @Component({ + imports: [FormRoot, FormField], template: ` - + @@ -614,18 +636,19 @@ Signal Forms handles validation through its own system, so you need to prevent t export class Registration { registrationModel = signal({username: '', email: '', password: ''}); - registrationForm = form(this.registrationModel, (schemaPath) => { - required(schemaPath.username); - email(schemaPath.email); - required(schemaPath.password); - }); - - onSubmit(event: Event) { - event.preventDefault(); - submit(this.registrationForm, async () => { - this.submitToServer(); - }); - } + registrationForm = form( + this.registrationModel, + (schemaPath) => { + required(schemaPath.username); + email(schemaPath.email); + required(schemaPath.password); + }, + { + submission: { + action: async () => this.submitToServer(), + }, + }, + ); private submitToServer() { // Send data to server @@ -633,32 +656,31 @@ export class Registration { } ``` -The `submit()` function automatically marks all fields as touched (revealing validation errors) and only executes your callback if the form is valid. +When you use `FormRoot`, submitting the form automatically calls the `submit()` function, which marks all fields as touched (revealing validation errors) and executes your `action` callback if the form is valid. + +You can also submit a form manually, without using the directive, by calling `submit(this.registrationForm)`. When explicitly calling the `submit` function like this, you can pass a `FormSubmitOptions` to override the default `submission` logic for the form: `submit(this.registrationForm, {action: () => /* ... */ })`. #### Resetting forms after submission -After successfully submitting a form, you may want to return it to its initial state - clearing both user interaction history and field values. The `reset()` method clears the touched and dirty flags but doesn't change field values, so you need to update your model separately: +After successfully submitting a form, you may want to return it to its initial state - clearing both user interaction history and field values. The `reset()` method clears the touched and dirty flags. You can also pass an optional value to `reset()` to update the model data: ```ts export class Contact { - contactModel = signal({name: '', email: '', message: ''}); - contactForm = form(this.contactModel); - - async onSubmit() { - if (!this.contactForm().valid()) return; - - await this.api.sendMessage(this.contactModel()); - - // Clear interaction state (touched, dirty) - this.contactForm().reset(); - - // Clear values - this.contactModel.set({name: '', email: '', message: ''}); - } + private readonly INITIAL_MODEL = {name: '', email: '', message: ''}; + contactModel = signal({...this.INITIAL_MODEL}); + contactForm = form(this.contactModel, { + submission: { + action: async (f) => { + await this.api.sendMessage(this.contactModel()); + // Clear interaction state (touched, dirty) and reset to initial values + f().reset({...this.INITIAL_MODEL}); + }, + }, + }); } ``` -This two-step reset ensures the form is ready for new input without showing stale error messages or dirty state indicators. +This ensures the form is ready for new input without showing stale error messages or dirty state indicators. ## Styling based on validation state diff --git a/adev-ja/src/content/guide/forms/signals/field-state-management.md b/adev-ja/src/content/guide/forms/signals/field-state-management.md index 352b1f6a3..0c7631501 100644 --- a/adev-ja/src/content/guide/forms/signals/field-state-management.md +++ b/adev-ja/src/content/guide/forms/signals/field-state-management.md @@ -523,6 +523,29 @@ export class Order { 非表示のフィールドはバリデーションに参加しないため、たとえ非表示のフィールドがそうでなければ無効であってもフォームを送信できます。 +### Tracking values for array fields + +In signal forms, a `@for` block over a set of fields should be tracked by field identity. + +```angular-ts +@Component({ + imports: [FormField], + template: ` + @for (field of form.emails; track field) { + + } + `, +}) +export class App { + formModel = signal({emails: ['john.doe@mail.com', 'max.musterman@mail.com']}); + form = form(this.formModel); +} +``` + +The forms system is already tracking the model values within the array and maintaining a stable identity of the fields it creates automatically. + +When an item changes, it may represent a new logical entity even if some of its properties look the same. Tracking by identity ensures the framework treats it as a distinct item rather than reusing existing UI elements. This prevents stateful elements, like form inputs, from being incorrectly shared and keeps bindings aligned with the correct part of the model. + ## コンポーネントロジックでのフィールド状態の使用 {#using-field-state-in-component-logic} フィールド状態のシグナルは、Angularのリアクティブプリミティブである`computed()`や`effect()`と連携して、高度なフォームロジックを実現します。 @@ -595,14 +618,13 @@ export class Password { #### フォームの送信 {#form-submission} -ユーザーがフォームを送信するときは、`submit()`関数を使用してバリデーションを処理し、エラーを表示します。 - -シグナルフォームは独自のバリデーションシステムでバリデーションを処理するため、ブラウザのデフォルトのフォーム動作を防ぐ必要があります。``要素に`novalidate`を追加してネイティブHTMLバリデーション(`required`や`type="email"`フィールドに対するブラウザのツールチップポップアップなど)を無効にし、送信ハンドラーで`$event.preventDefault()`を呼び出してブラウザがページをリロードするのを防ぎます: +シグナルフォームprovides a `FormRoot` directive that simplifies form submission. It automatically prevents the default browser form submission behavior and sets the `novalidate` attribute on the `` element. ```angular-ts @Component({ + imports: [FormRoot, FormField], template: ` - + @@ -614,18 +636,19 @@ export class Password { export class Registration { registrationModel = signal({username: '', email: '', password: ''}); - registrationForm = form(this.registrationModel, (schemaPath) => { - required(schemaPath.username); - email(schemaPath.email); - required(schemaPath.password); - }); - - onSubmit(event: Event) { - event.preventDefault(); - submit(this.registrationForm, async () => { - this.submitToServer(); - }); - } + registrationForm = form( + this.registrationModel, + (schemaPath) => { + required(schemaPath.username); + email(schemaPath.email); + required(schemaPath.password); + }, + { + submission: { + action: async () => this.submitToServer(), + }, + }, + ); private submitToServer() { // Send data to server @@ -633,32 +656,31 @@ export class Registration { } ``` -`submit()`関数は、すべてのフィールドを自動的にtouchedとしてマークし(バリデーションエラーを表示)、フォームが有効な場合にのみコールバックを実行します。 +When you use `FormRoot`, submitting the form automatically calls the `submit()` function, which marks all fields as touched (revealing validation errors) and executes your `action` callback if the form is valid. + +You can also submit a form manually, without using the directive, by calling `submit(this.registrationForm)`. When explicitly calling the `submit` function like this, you can pass a `FormSubmitOptions` to override the default `submission` logic for the form: `submit(this.registrationForm, {action: () => /* ... */ })`. #### 送信後のフォームのリセット {#resetting-forms-after-submission} -フォームを正常に送信した後、ユーザーインタラクションの履歴とフィールドの値の両方をクリアして、初期状態に戻したい場合があります。`reset()`メソッドはtouchedフラグとdirtyフラグをクリアしますが、フィールドの値は変更しないため、モデルを別途更新する必要があります: +After successfully submitting a form, you may want to return it to its initial state - clearing both user interaction history and field values. The `reset()` method clears the touched and dirty flags. You can also pass an optional value to `reset()` to update the model data: ```ts export class Contact { - contactModel = signal({name: '', email: '', message: ''}); - contactForm = form(this.contactModel); - - async onSubmit() { - if (!this.contactForm().valid()) return; - - await this.api.sendMessage(this.contactModel()); - - // Clear interaction state (touched, dirty) - this.contactForm().reset(); - - // Clear values - this.contactModel.set({name: '', email: '', message: ''}); - } + private readonly INITIAL_MODEL = {name: '', email: '', message: ''}; + contactModel = signal({...this.INITIAL_MODEL}); + contactForm = form(this.contactModel, { + submission: { + action: async (f) => { + await this.api.sendMessage(this.contactModel()); + // Clear interaction state (touched, dirty) and reset to initial values + f().reset({...this.INITIAL_MODEL}); + }, + }, + }); } ``` -この2段階のリセットにより、古いエラーメッセージやdirty状態のインジケーターを表示することなく、フォームが新しい入力に対応できるようになります。 +This ensures the form is ready for new input without showing stale error messages or dirty state indicators. ## バリデーション状態に基づいたスタイリング {#styling-based-on-validation-state} diff --git a/adev-ja/src/content/guide/forms/signals/form-logic.md b/adev-ja/src/content/guide/forms/signals/form-logic.md index 5aa44718a..fe49c6e34 100644 --- a/adev-ja/src/content/guide/forms/signals/form-logic.md +++ b/adev-ja/src/content/guide/forms/signals/form-logic.md @@ -195,14 +195,14 @@ import {form, FormField, hidden} from '@angular/forms/signals'; imports: [FormField], template: ` @if (!profileForm.publicUrl().hidden()) { } `, @@ -239,12 +239,12 @@ import {form, FormField, readonly} from '@angular/forms/signals'; template: ` `, }) @@ -275,13 +275,13 @@ import {form, FormField, readonly} from '@angular/forms/signals'; imports: [FormField], template: ` `, }) @@ -359,7 +359,7 @@ import {form, FormField, debounce} from '@angular/forms/signals'; template: `

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

@@ -406,7 +406,7 @@ import {form, FormField, debounce} from '@angular/forms/signals'; template: ` `, }) @@ -483,7 +483,7 @@ import {form, FormField, required, min, max} from '@angular/forms/signals'; template: ` @if (ageForm.age().required()) { @@ -516,7 +516,7 @@ import {form, FormField, min, max, validate} from '@angular/forms/signals'; @Component({ selector: 'app-custom', - imports: [formField], + imports: [FormField], template: ` `, }) export class Custom { @@ -643,7 +643,7 @@ import {form, FormField, max} from '@angular/forms/signals'; @Component({ selector: 'app-inventory', - imports: [formField], + imports: [FormField], template: `
Go to Dashboard +``` + +You can also bind a `UrlTree` for more dynamic use cases: + +```angular-ts +import {Component, inject} from '@angular/core'; +import {Router, RouterLink, UrlTree} from '@angular/router'; + +@Component({ + template: ` + + {{ product.name }} + + `, + imports: [RouterLink], +}) +export class ProductList { + private router = inject(Router); + + product = {id: 42, name: 'Widget'}; + + // Create a UrlTree to display in the address bar + displayUrl: UrlTree = this.router.createUrlTree(['/products', 'widget']); +} +``` + ## Next steps Learn how to [read route state](/guide/routing/read-route-state) to create responsive and context-aware components. diff --git a/adev-ja/src/content/guide/routing/navigate-to-routes.md b/adev-ja/src/content/guide/routing/navigate-to-routes.md index b4834752e..00904ceb2 100644 --- a/adev-ja/src/content/guide/routing/navigate-to-routes.md +++ b/adev-ja/src/content/guide/routing/navigate-to-routes.md @@ -137,9 +137,39 @@ export class UserDetail { // To: /users this.router.navigate(['..'], {relativeTo: this.route}); } + + navigateToList() { + // Angular resolves the commands array as a single navigation path relative to the current route. + // From: /users/123 + // Result: /users/list + this.router.navigate(['..', 'list'], {relativeTo: this.route}); + } } ``` +When navigating multiple levels up, all `..` segments must be in the **first element** of the commands array. The router only parses `..` from the first command string — subsequent array elements are treated as literal path segments. + +```angular-ts {prefer} +// From: /team/123/users/456 +// Result: /team/123/settings +this.router.navigate(['../../settings'], {relativeTo: this.route}); +``` + +When using `relativeTo`, never prefix the first command with `/`. A leading `/` makes the navigation absolute and ignores `relativeTo` entirely. + +```angular-ts {prefer} +// From: /team/123/users/456 +// Result: /team/123/users/456/edit +this.router.navigate(['edit'], {relativeTo: this.route}); +``` + +```angular-ts {avoid} +// From: /team/123/users/456 +// Leading '/' causes absolute navigation — relativeTo is ignored +// Result: /edit +this.router.navigate(['/edit'], {relativeTo: this.route}); +``` + ### `router.navigateByUrl()` {#router-navigatebyurl} `router.navigateByUrl()`メソッドは、配列セグメントではなくURLパス文字列を使用してプログラムでナビゲートする直接的な方法を提供します。このメソッドは、完全なURLパスがあり、絶対ナビゲーションを実行する必要がある場合に最適です。特に、外部から提供されるURLやディープリンクのシナリオで役立ちます。 @@ -170,6 +200,53 @@ router.navigateByUrl('/checkout', { }); ``` +### Display a different URL in the address bar + +You can pass a browserUrl option to navigateByUrl to display a different URL in the browser's address bar than the one used for route matching. + +This is useful when you want to redirect a user to a different route—such as an error page—without changing the URL that the user originally tried to visit. + +```ts +router.navigateByUrl('/not-found', {browserUrl: '/products/missing-item'}); +``` + +Angular navigates to and renders the `/not-found` route, but the browser address bar shows `/products/missing-item`. + +NOTE: `browserUrl` only affects what appears in the browser's address bar. + +## Customizing the browser URL with RouterLink + +The `RouterLink` directive also supports a `browserUrl` input, which lets you control the URL displayed in the browser's address bar when a link is clicked, independently of the route Angular navigates to. + +```angular-html + +Go to Dashboard +``` + +You can also bind a `UrlTree` for more dynamic use cases: + +```angular-ts +import {Component, inject} from '@angular/core'; +import {Router, RouterLink, UrlTree} from '@angular/router'; + +@Component({ + template: ` + + {{ product.name }} + + `, + imports: [RouterLink], +}) +export class ProductList { + private router = inject(Router); + + product = {id: 42, name: 'Widget'}; + + // Create a UrlTree to display in the address bar + displayUrl: UrlTree = this.router.createUrlTree(['/products', 'widget']); +} +``` + ## 次のステップ {#next-steps} レスポンシブでコンテキストを認識するコンポーネントを作成するために、[ルートの状態を読み取る](/guide/routing/read-route-state)方法を学びましょう。 diff --git a/adev-ja/src/content/guide/routing/route-guards.en.md b/adev-ja/src/content/guide/routing/route-guards.en.md index 43dc343c9..0801de4f7 100644 --- a/adev-ja/src/content/guide/routing/route-guards.en.md +++ b/adev-ja/src/content/guide/routing/route-guards.en.md @@ -39,14 +39,16 @@ Angular provides four types of route guards, each serving different purposes: +All the guards have access to [services provided at the route level](guide/di/defining-dependency-providers#route-providers) as well as route-specific information via the `route` argument. + ### CanActivate The `CanActivate` guard determines whether a user can access a route. It is most commonly used for authentication and authorization. It has access to the following default arguments: -- `route: ActivatedRouteSnapshot` - Contains information about the route being activated -- `state: RouterStateSnapshot` - Contains the router's current state +- `route`: `ActivatedRouteSnapshot` - Contains information about the route being activated +- `state`: `RouterStateSnapshot` - Contains the router's current state It can return the [standard return guard types](#route-guard-return-types). @@ -70,8 +72,8 @@ The `CanActivateChild` guard determines whether a user can access child routes o It has access to the following default arguments: -- `childRoute: ActivatedRouteSnapshot` - Contains information about the "future" snapshot (i.e., state the router is attempting to navigate to) of the child route being activated -- `state: RouterStateSnapshot` - Contains the router's current state +- `childRoute`: `ActivatedRouteSnapshot` - Contains information about the "future" snapshot (i.e., state the router is attempting to navigate to) of the child route being activated +- `state`: `RouterStateSnapshot` - Contains the router's current state It can return the [standard return guard types](#route-guard-return-types). @@ -93,10 +95,10 @@ The `CanDeactivate` guard determines whether a user can leave a route. A common It has access to the following default arguments: -- `component: T` - The component instance being deactivated -- `currentRoute: ActivatedRouteSnapshot` - Contains information about the current route -- `currentState: RouterStateSnapshot` - Contains the current router state -- `nextState: RouterStateSnapshot` - Contains the next router state being navigated to +- `component`: `T` - The component instance being deactivated +- `currentRoute`: `ActivatedRouteSnapshot` - Contains information about the current route +- `currentState`: `RouterStateSnapshot` - Contains the current router state +- `nextState`: `RouterStateSnapshot` - Contains the next router state being navigated to It can return the [standard return guard types](#route-guard-return-types). @@ -121,8 +123,8 @@ The `CanMatch` guard determines whether a route can be matched during path match It has access to the following default arguments: -- `route: Route` - The route configuration being evaluated -- `segments: UrlSegment[]` - The URL segments that have not been consumed by previous parent route evaluations +- `route`: `Route` - The route configuration being evaluated +- `segments`: `UrlSegment[]` - The URL segments that have not been consumed by previous parent route evaluations It can return the [standard return guard types](#route-guard-return-types), but when it returns `false`, Angular tries other matching routes instead of completely blocking navigation. diff --git a/adev-ja/src/content/guide/routing/route-guards.md b/adev-ja/src/content/guide/routing/route-guards.md index 6ddfb09cb..2b1773a4c 100644 --- a/adev-ja/src/content/guide/routing/route-guards.md +++ b/adev-ja/src/content/guide/routing/route-guards.md @@ -39,14 +39,16 @@ Angularは、それぞれ異なる目的を持つ4種類のルートガードを +すべてのガードは、`route`引数を介して[ルートレベルで提供されるサービス](guide/di/defining-dependency-providers#route-providers)およびルート固有の情報にアクセスできます。 + ### CanActivate {#canactivate} `CanActivate`ガードは、ユーザーがルートにアクセスできるかどうかを決定します。これは、認証と認可に最も一般的に使用されます。 以下のデフォルト引数にアクセスできます。 -- `route: ActivatedRouteSnapshot` - アクティブ化されるルートに関する情報を含みます -- `state: RouterStateSnapshot` - ルーターの現在の状態を含みます +- `route`: `ActivatedRouteSnapshot` - アクティブ化されるルートに関する情報を含みます +- `state`: `RouterStateSnapshot` - ルーターの現在の状態を含みます [標準のガード戻り値の型](#route-guard-return-types)を返すことができます。 @@ -70,8 +72,8 @@ Tip: ユーザーをリダイレクトする必要がある場合は、[`URLTree 以下のデフォルト引数にアクセスできます。 -- `childRoute: ActivatedRouteSnapshot` - アクティブ化される子ルートの「将来の」スナップショット(つまり、ルーターがナビゲートしようとしている状態)に関する情報を含みます -- `state: RouterStateSnapshot` - ルーターの現在の状態を含みます +- `childRoute`: `ActivatedRouteSnapshot` - アクティブ化される子ルートの「将来の」スナップショット(つまり、ルーターがナビゲートしようとしている状態)に関する情報を含みます +- `state`: `RouterStateSnapshot` - ルーターの現在の状態を含みます [標準のガード戻り値の型](#route-guard-return-types)を返すことができます。 @@ -93,10 +95,10 @@ export const adminChildGuard: CanActivateChildFn = ( 以下のデフォルト引数にアクセスできます。 -- `component: T` - 非アクティブ化されるコンポーネントインスタンス -- `currentRoute: ActivatedRouteSnapshot` - 現在のルートに関する情報を含みます -- `currentState: RouterStateSnapshot` - 現在のルーターの状態を含みます -- `nextState: RouterStateSnapshot` - ナビゲート先の次のルーターの状態を含みます +- `component`: `T` - 非アクティブ化されるコンポーネントインスタンス +- `currentRoute`: `ActivatedRouteSnapshot` - 現在のルートに関する情報を含みます +- `currentState`: `RouterStateSnapshot` - 現在のルーターの状態を含みます +- `nextState`: `RouterStateSnapshot` - ナビゲート先の次のルーターの状態を含みます [標準のガード戻り値の型](#route-guard-return-types)を返すことができます。 @@ -121,8 +123,8 @@ export const unsavedChangesGuard: CanDeactivateFn
= ( 以下のデフォルト引数にアクセスできます。 -- `route: Route` - 評価されているルート設定 -- `segments: UrlSegment[]` - 以前の親ルート評価によって消費されていないURLセグメント +- `route`: `Route` - 評価されているルート設定 +- `segments`: `UrlSegment[]` - 以前の親ルート評価によって消費されていないURLセグメント [標準のガード戻り値の型](#route-guard-return-types)を返すことができますが、`false`を返した場合、Angularはナビゲーションを完全にブロックするのではなく、他のマッチングするルートを試行します。 diff --git a/adev-ja/src/content/guide/routing/route-transition-animations.en.md b/adev-ja/src/content/guide/routing/route-transition-animations.en.md index 4f7cafacc..7b776f0ac 100644 --- a/adev-ja/src/content/guide/routing/route-transition-animations.en.md +++ b/adev-ja/src/content/guide/routing/route-transition-animations.en.md @@ -27,7 +27,7 @@ For more details about the browser API, see the [Chrome Explainer](https://devel Angular Router integrates view transitions into the navigation lifecycle to create seamless route changes. During navigation, the Router: -1. **Completes navigation preparation** - Route matching, [lazy loading](/guide/routing/define-routes#lazily-loaded-components-and-routes), [guards](/guide/routing/route-guards), and [resolvers](/guide/routing/data-resolvers) execute +1. **Completes navigation preparation** - Route matching, [lazy loading](guide/routing/loading-strategies#lazily-loaded-components-and-routes), [guards](/guide/routing/route-guards), and [resolvers](/guide/routing/data-resolvers) execute 2. **Initiates the view transition** - Router calls `startViewTransition` when routes are ready for activation 3. **Updates the DOM** - Router activates new routes and deactivates old ones within the transition callback 4. **Finalizes the transition** - The transition Promise resolves when Angular completes rendering diff --git a/adev-ja/src/content/guide/routing/route-transition-animations.md b/adev-ja/src/content/guide/routing/route-transition-animations.md index 7103f41b4..8deacc5d9 100644 --- a/adev-ja/src/content/guide/routing/route-transition-animations.md +++ b/adev-ja/src/content/guide/routing/route-transition-animations.md @@ -27,7 +27,7 @@ document.startViewTransition(async () => { Angular Routerは、シームレスなルート変更を作成するために、ナビゲーションライフサイクルにビュー遷移を統合します。ナビゲーション中、Routerは以下を行います。 -1. **ナビゲーションの準備を完了する** - ルートのマッチング、[遅延ロード](/guide/routing/define-routes#lazily-loaded-components-and-routes)、[ガード](/guide/routing/route-guards)、および[リゾルバー](/guide/routing/data-resolvers)が実行されます +1. **ナビゲーションの準備を完了する** - ルートのマッチング、[遅延ロード](guide/routing/loading-strategies#lazily-loaded-components-and-routes)、[ガード](/guide/routing/route-guards)、および[リゾルバー](/guide/routing/data-resolvers)が実行されます 2. **ビュー遷移を開始する** - ルートがアクティベーションの準備ができたときに、Routerは`startViewTransition`を呼び出します 3. **DOMを更新する** - Routerは、遷移コールバック内で新しいルートをアクティブ化し、古いルートを非アクティブ化します 4. **遷移を完了する** - Angularがレンダリングを完了すると、遷移Promiseが解決されます diff --git a/adev-ja/src/content/guide/security.en.md b/adev-ja/src/content/guide/security.en.md index 699631a40..388ea1bd1 100644 --- a/adev-ja/src/content/guide/security.en.md +++ b/adev-ja/src/content/guide/security.en.md @@ -173,6 +173,10 @@ bootstrapApplication(AppComponent, { Always ensure that the nonces you provide are unique per request and that they are not predictable or guessable. If an attacker can predict future nonces, they can circumvent the protections offered by CSP. +Generating a nonce at the origin server is generally discouraged when using a CDN, as responses are frequently cached. If the server generates a nonce and the CDN caches that HTML response, every subsequent visitor receives the same "unique" value, allowing an attacker to discover the static value and bypass CSP protections. + +To maintain the "one-time-use" integrity of a nonce, it should ideally be generated at the Edge layer (e.g., CDN) just before the content is delivered to the user. + NOTE: If you want to [inline the critical CSS](/tools/cli/build#critical-css-inlining) of your application, you can not use the `CSP_NONCE` token, and should prefer the `autoCsp` option or set the `ngCspNonce` attribute on the root application element. @@ -363,6 +367,91 @@ Angular's `HttpClient` library recognizes this convention and automatically stri For more information, see the XSSI section of this [Google web security blog post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html). +## Preventing Server-Side Request Forgery (SSRF) + +Angular includes strict validation for `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Forwarded-Prefix` and `X-Forwarded-Port` headers in the request handling pipeline to prevent header-based [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF). + +The validation rules are: + +- `Host` and `X-Forwarded-Host` headers are validated against a strict allowlist and cannot contain path separators. +- `X-Forwarded-Port` header must be numeric. +- `X-Forwarded-Proto` header must be `http` or `https`. +- `X-Forwarded-Prefix` header must start with `/` and contain only alphanumeric characters, hyphens, and underscores, separated by single slashes. +- By default, all `X-Forwarded-*` headers are treated as untrusted and are removed from the request. To retain them, they must be explicitly allowed by configuring `trustProxyHeaders`. + +Invalid headers trigger an error log, and unallowed proxy headers are removed from the request. Requests with unrecognized hostnames will result in a `400 Bad Request` is issued. + +NOTE: Most cloud providers and CDN providers perform automatic validation of these headers before a request ever reaches the application origin. This inherent filtering significantly reduces the practical attack surface. + +### Configuring allowed hosts + +To allow specific hostnames, you need to add them to the allowlist. This is critical for ensuring your application works correctly and securely when deployed. The patterns support wildcards for flexible hostname matching. + +You can configure the `allowedHosts` option in your `angular.json`: + +```json {hideCopy} +{ + // ... + "projects": { + "your-project-name": { + // ... + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "security": { + "allowedHosts": [ + "example.com", + "*.example.com" // allows all subdomains of example.com + ] + } + // ... other options + } + } + } + } + } +} +``` + +You can also configure `allowedHosts` when initializing the application engine: + +```typescript +const appEngine = new AngularAppEngine({ + allowedHosts: ['example.com', '*.trusted-example.com'], +}); + +const nodeAppEngine = new AngularNodeAppEngine({ + allowedHosts: ['example.com', '*.trusted-example.com'], +}); +``` + +For the Node.js variant `AngularNodeAppEngine`, you can also provide `NG_ALLOWED_HOSTS` (comma-separated list) environment variable for authorizing hosts. + +```bash {hideDollar} +export NG_ALLOWED_HOSTS="example.com,*.trusted-example.com" +``` + +IMPORTANT: You can use `*` as a value in `allowedHosts` to allow all hostnames, though this is generally discouraged and presents a security risk. Accepting any host header can expose your application to host header injection and [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. This configuration should only be used when validation for `Host` and `X-Forwarded-Host` headers is performed in another layer, such as a load balancer or reverse proxy. For better security, we recommend using an explicit list of allowed hosts whenever possible. See [GHSA-x288-3778-4hhx](https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx) for more details. + +### Configuring trusted proxy headers + +By default, Angular ignores all `X-Forwarded-*` headers. If your application is behind a trusted reverse proxy (like a load balancer) that sets these headers, you can configure Angular to trust them. + +You can configure `trustProxyHeaders` when initializing the application engine: + +```typescript +const appEngine = new AngularAppEngine({ + trustProxyHeaders: ['x-forwarded-host', 'x-forwarded-proto'], // Trust specific headers +}); + +const nodeAppEngine = new AngularNodeAppEngine({ + trustProxyHeaders: true, // Trust all X-Forwarded-* headers +}); +``` + +IMPORTANT: Only enable `trustProxyHeaders` if your application is behind a trusted proxy that strictly validates or overrides these headers. Otherwise, attackers can spoof these headers to cause [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. + ## Auditing Angular applications Angular applications must follow the same security principles as regular web applications, and must be audited as such. diff --git a/adev-ja/src/content/guide/security.md b/adev-ja/src/content/guide/security.md index 7bbcb1485..ae2c13147 100644 --- a/adev-ja/src/content/guide/security.md +++ b/adev-ja/src/content/guide/security.md @@ -173,6 +173,10 @@ bootstrapApplication(AppComponent, { 提供するnonceは、**リクエストごとに一意である**こと、予測したり推測したりできないことを常に確認してください。 攻撃者が将来のnonceを予測できる場合、CSPによって提供される保護を回避できます。 +Generating a nonce at the origin server is generally discouraged when using a CDN, as responses are frequently cached. If the server generates a nonce and the CDN caches that HTML response, every subsequent visitor receives the same "unique" value, allowing an attacker to discover the static value and bypass CSP protections. + +To maintain the "one-time-use" integrity of a nonce, it should ideally be generated at the Edge layer (e.g., CDN) just before the content is delivered to the user. + NOTE: アプリケーションの[クリティカルCSSをインライン化](/tools/cli/build#critical-css-inlining)したい場合、`CSP_NONCE`トークンは使用できません。`autoCsp`オプションを使用するか、ルートアプリケーション要素に`ngCspNonce`属性を設定してください。 @@ -363,6 +367,91 @@ Angularの`HttpClient`ライブラリは、この規則を認識し、さらに 詳細については、[Google Webセキュリティブログの投稿](https://security.googleblog.com/2011/05/website-security-for-webmasters.html)のXSSIセクションを参照してください。 +## Preventing Server-Side Request Forgery (SSRF) + +Angular includes strict validation for `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Forwarded-Prefix` and `X-Forwarded-Port` headers in the request handling pipeline to prevent header-based [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF). + +The validation rules are: + +- `Host` and `X-Forwarded-Host` headers are validated against a strict allowlist and cannot contain path separators. +- `X-Forwarded-Port` header must be numeric. +- `X-Forwarded-Proto` header must be `http` or `https`. +- `X-Forwarded-Prefix` header must start with `/` and contain only alphanumeric characters, hyphens, and underscores, separated by single slashes. +- By default, all `X-Forwarded-*` headers are treated as untrusted and are removed from the request. To retain them, they must be explicitly allowed by configuring `trustProxyHeaders`. + +Invalid headers trigger an error log, and unallowed proxy headers are removed from the request. Requests with unrecognized hostnames will result in a `400 Bad Request` is issued. + +NOTE: Most cloud providers and CDN providers perform automatic validation of these headers before a request ever reaches the application origin. This inherent filtering significantly reduces the practical attack surface. + +### Configuring allowed hosts + +To allow specific hostnames, you need to add them to the allowlist. This is critical for ensuring your application works correctly and securely when deployed. The patterns support wildcards for flexible hostname matching. + +You can configure the `allowedHosts` option in your `angular.json`: + +```json {hideCopy} +{ + // ... + "projects": { + "your-project-name": { + // ... + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "security": { + "allowedHosts": [ + "example.com", + "*.example.com" // allows all subdomains of example.com + ] + } + // ... other options + } + } + } + } + } +} +``` + +You can also configure `allowedHosts` when initializing the application engine: + +```typescript +const appEngine = new AngularAppEngine({ + allowedHosts: ['example.com', '*.trusted-example.com'], +}); + +const nodeAppEngine = new AngularNodeAppEngine({ + allowedHosts: ['example.com', '*.trusted-example.com'], +}); +``` + +For the Node.js variant `AngularNodeAppEngine`, you can also provide `NG_ALLOWED_HOSTS` (comma-separated list) environment variable for authorizing hosts. + +```bash {hideDollar} +export NG_ALLOWED_HOSTS="example.com,*.trusted-example.com" +``` + +IMPORTANT: You can use `*` as a value in `allowedHosts` to allow all hostnames, though this is generally discouraged and presents a security risk. Accepting any host header can expose your application to host header injection and [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. This configuration should only be used when validation for `Host` and `X-Forwarded-Host` headers is performed in another layer, such as a load balancer or reverse proxy. For better security, we recommend using an explicit list of allowed hosts whenever possible. See [GHSA-x288-3778-4hhx](https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx) for more details. + +### Configuring trusted proxy headers + +By default, Angular ignores all `X-Forwarded-*` headers. If your application is behind a trusted reverse proxy (like a load balancer) that sets these headers, you can configure Angular to trust them. + +You can configure `trustProxyHeaders` when initializing the application engine: + +```typescript +const appEngine = new AngularAppEngine({ + trustProxyHeaders: ['x-forwarded-host', 'x-forwarded-proto'], // Trust specific headers +}); + +const nodeAppEngine = new AngularNodeAppEngine({ + trustProxyHeaders: true, // Trust all X-Forwarded-* headers +}); +``` + +IMPORTANT: Only enable `trustProxyHeaders` if your application is behind a trusted proxy that strictly validates or overrides these headers. Otherwise, attackers can spoof these headers to cause [Server-Side Request Forgery (SSRF)](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/SSRF) attacks. + ## Angularアプリケーションの監査 {#auditing-angular-applications} Angularアプリケーションは、通常のWebアプリケーションと同じセキュリティ原則に従う必要があり、同様に監査する必要があります。 diff --git a/adev-ja/src/content/guide/signals/overview.en.md b/adev-ja/src/content/guide/signals/overview.en.md index 3a2387df4..c558ec1b6 100644 --- a/adev-ja/src/content/guide/signals/overview.en.md +++ b/adev-ja/src/content/guide/signals/overview.en.md @@ -237,9 +237,9 @@ When you read a signal within an `OnPush` component's template, Angular tracks t When creating a signal, you can optionally provide an equality function, which will be used to check whether the new value is actually different than the previous one. ```ts -import _ from 'lodash'; +import isEqual from 'lodash/isEqual'; -const data = signal(['test'], {equal: _.isEqual}); +const data = signal(['test'], {equal: isEqual}); // Even though this is a different array instance, the deep equality // function will consider the values to be equal, and the signal won't diff --git a/adev-ja/src/content/guide/signals/overview.md b/adev-ja/src/content/guide/signals/overview.md index 338bd69d1..240357f08 100644 --- a/adev-ja/src/content/guide/signals/overview.md +++ b/adev-ja/src/content/guide/signals/overview.md @@ -237,9 +237,9 @@ effect(async () => { シグナルを作成する際には、オプションで等価関数を指定できます。これは、新しい値が前の値と実際に異なるかどうかを確認するために使用されます。 ```ts -import _ from 'lodash'; +import isEqual from 'lodash/isEqual'; -const data = signal(['test'], {equal: _.isEqual}); +const data = signal(['test'], {equal: isEqual}); // これは別の配列インスタンスですが、 // 深い等価関数を使用することで値は等しいと判断され、 diff --git a/adev-ja/src/content/guide/signals/resource.en.md b/adev-ja/src/content/guide/signals/resource.en.md index d21365185..ddff96f5f 100644 --- a/adev-ja/src/content/guide/signals/resource.en.md +++ b/adev-ja/src/content/guide/signals/resource.en.md @@ -125,3 +125,55 @@ You can use this status information to conditionally display user interface elem ## Reactive data fetching with `httpResource` [`httpResource`](/guide/http/http-resource) is a wrapper around `HttpClient` that gives you the request status and response as signals. It makes HTTP requests through the Angular HTTP stack, including interceptors. + +## Resource composition with snapshots + +A `ResourceSnapshot` is a structured representation of a resource's current state. Every resource has a `snapshot` property that provides a signal of its current state. + +```ts +const userId: Signal = getUserId(); + +const userResource = resource({ + params: () => ({id: userId()}), + loader: ({params}) => fetchUser(params), +}); + +const userSnapshot = userResource.snapshot; +``` + +Each snapshot contains a `status` and either a `value` or an `error`. + +### Composing resources with snapshots + +You can create new resources from snapshots using `resourceFromSnapshots`. This enables composition with signal APIs like `computed` and `linkedSignal` to transform resource behavior. + +```ts +import {linkedSignal, resourceFromSnapshots, Resource, ResourceSnapshot} from '@angular/core'; + +function withPreviousValue(input: Resource): Resource { + const derived = linkedSignal, ResourceSnapshot>({ + source: input.snapshot, + computation: (snap, previous) => { + if (snap.status === 'loading' && previous && previous.value.status !== 'error') { + // When the input resource enters loading state, we keep the value + // from its previous state, if any. + return {status: 'loading' as const, value: previous.value.value}; + } + + // Otherwise we simply forward the state of the input resource. + return snap; + }, + }); + + return resourceFromSnapshots(derived); +} + +@Component({ + /*... */ +}) +export class AwesomeProfile { + userId = input.required(); + user = withPreviousValue(httpResource(() => `/user/${this.userId()}`)); + // When userId changes, user.value() keeps the old user data until the new one loads +} +``` diff --git a/adev-ja/src/content/guide/signals/resource.md b/adev-ja/src/content/guide/signals/resource.md index 94d729074..6a796cabc 100644 --- a/adev-ja/src/content/guide/signals/resource.md +++ b/adev-ja/src/content/guide/signals/resource.md @@ -125,3 +125,55 @@ userResource.reload(); ## `httpResource` を使用したリアクティブデータ取得 {#reactive-data-fetching-with-httpresource} [`httpResource`](/guide/http/http-resource) は `HttpClient` のラッパーで、リクエストの状態とレスポンスをシグナルとして提供します。これはインターセプターを含むAngular HTTPスタックを通してHTTPリクエストを行います。 + +## Resource composition with snapshots + +A `ResourceSnapshot` is a structured representation of a resource's current state. Every resource has a `snapshot` property that provides a signal of its current state. + +```ts +const userId: Signal = getUserId(); + +const userResource = resource({ + params: () => ({id: userId()}), + loader: ({params}) => fetchUser(params), +}); + +const userSnapshot = userResource.snapshot; +``` + +Each snapshot contains a `status` and either a `value` or an `error`. + +### Composing resources with snapshots + +You can create new resources from snapshots using `resourceFromSnapshots`. This enables composition with signal APIs like `computed` and `linkedSignal` to transform resource behavior. + +```ts +import {linkedSignal, resourceFromSnapshots, Resource, ResourceSnapshot} from '@angular/core'; + +function withPreviousValue(input: Resource): Resource { + const derived = linkedSignal, ResourceSnapshot>({ + source: input.snapshot, + computation: (snap, previous) => { + if (snap.status === 'loading' && previous && previous.value.status !== 'error') { + // When the input resource enters loading state, we keep the value + // from its previous state, if any. + return {status: 'loading' as const, value: previous.value.value}; + } + + // Otherwise we simply forward the state of the input resource. + return snap; + }, + }); + + return resourceFromSnapshots(derived); +} + +@Component({ + /*... */ +}) +export class AwesomeProfile { + userId = input.required(); + user = withPreviousValue(httpResource(() => `/user/${this.userId()}`)); + // When userId changes, user.value() keeps the old user data until the new one loads +} +``` diff --git a/adev-ja/src/content/guide/ssr.en.md b/adev-ja/src/content/guide/ssr.en.md index 24ca1c780..eca4beb22 100644 --- a/adev-ja/src/content/guide/ssr.en.md +++ b/adev-ja/src/content/guide/ssr.en.md @@ -387,12 +387,17 @@ export class MyComponent { } ``` -IMPORTANT: The above tokens will be `null` in the following scenarios: + + -- During the build processes. -- When the application is rendered in the browser (CSR). -- When performing static site generation (SSG). -- During route extraction in development (at the time of the request). +IMPORTANT: The above tokens will be `null` in the following scenarios:
    +
  • During the build processes.
  • +
  • When the application is rendered in the browser (CSR).
  • +
  • When performing static site generation (SSG).
  • +
  • During route extraction in development (at the time of the request).
  • +
+ + ## Generate a fully static application @@ -521,7 +526,7 @@ bootstrapApplication(App, { }); ``` -#### `filter` +#### Filtering You can also selectively disable caching for certain requests using the [`filter`](api/common/http/HttpTransferCacheOptions) option in `withHttpTransferCacheOptions`. For example, you can disable caching for a specific API endpoint: @@ -545,7 +550,7 @@ bootstrapApplication(App, { Use this option to exclude endpoints with user‑specific or dynamic data (for example `/api/profile`). -#### Individually +#### Per-request To disable caching for an individual request, you can specify the [`transferCache`](api/common/http/HttpRequest#transferCache) option in an `HttpRequest`. @@ -611,3 +616,7 @@ export const reqHandler = createRequestHandler(async (req: Request) => { // ... }); ``` + +## Security + +For detailed information on preventing Server-Side Request Forgery (SSRF) and configuring allowed hosts, see the [Server-side security](best-practices/security#preventing-server-side-request-forgery-ssrf) guide. diff --git a/adev-ja/src/content/guide/ssr.md b/adev-ja/src/content/guide/ssr.md index 889c874db..52475a06f 100644 --- a/adev-ja/src/content/guide/ssr.md +++ b/adev-ja/src/content/guide/ssr.md @@ -387,12 +387,17 @@ export class MyComponent { } ``` -IMPORTANT: 上記のトークンは、次のシナリオでは `null` になります。 + + -- ビルドプロセス中。 -- アプリケーションがブラウザでレンダリングされるとき (CSR)。 -- 静的サイト生成 (SSG) を実行するとき。 -- 開発中のルート抽出時 (リクエスト時)。 +IMPORTANT: 上記のトークンは、次のシナリオでは `null` になります。
    +
  • ビルドプロセス中。
  • +
  • アプリケーションがブラウザでレンダリングされるとき (CSR)。
  • +
  • 静的サイト生成 (SSG) を実行するとき。
  • +
  • 開発中のルート抽出時 (リクエスト時)。
  • +
+ + ## 完全に静的なアプリケーションを生成する {#generate-a-fully-static-application} @@ -521,7 +526,7 @@ bootstrapApplication(App, { }); ``` -#### `filter` {#filter} +#### フィルタリング {#filtering} `withHttpTransferCacheOptions` の [`filter`](api/common/http/HttpTransferCacheOptions) オプションを使用して、特定のリクエストのキャッシュを選択的に無効にできます。たとえば、特定のAPIエンドポイントのキャッシュを無効にできます。 @@ -545,7 +550,7 @@ bootstrapApplication(App, { このオプションを使用して、ユーザー固有または動的なデータ(例: `/api/profile`)を持つエンドポイントを除外します。 -#### 個別 {#individually} +#### リクエストごと {#per-request} 個々のリクエストのキャッシュを無効にするには、`HttpRequest` で [`transferCache`](api/common/http/HttpRequest#transferCache) オプションを指定できます。 @@ -611,3 +616,7 @@ export const reqHandler = createRequestHandler(async (req: Request) => { // ... }); ``` + +## セキュリティ {#security} + +サーバーサイドリクエストフォージェリ (SSRF) の防止と許可ホストの設定に関する詳細については、[サーバーサイドセキュリティ](best-practices/security#preventing-server-side-request-forgery-ssrf)ガイドを参照してください。 diff --git a/adev-ja/src/content/guide/templates/binding.en.md b/adev-ja/src/content/guide/templates/binding.en.md index 27cfc658d..3fe72f50a 100644 --- a/adev-ja/src/content/guide/templates/binding.en.md +++ b/adev-ja/src/content/guide/templates/binding.en.md @@ -1,4 +1,4 @@ -# Binding dynamic text, properties and attributes +# Binding dynamic text, properties and attributes In Angular, a **binding** creates a dynamic connection between a component's template and its data. This connection ensures that changes to the component's data automatically update the rendered template. @@ -60,7 +60,7 @@ All expression values are converted to a string. Objects and arrays are converte Angular supports binding dynamic values into object properties and HTML attributes with square brackets. -You can bind to properties on an HTML element's DOM instance, a [component](guide/components) instance, or a [directive](guide/directives) instance. +You can bind to properties on an HTML element's DOM instance, a [component](/guide/components) instance, or a [directive](/guide/directives) instance. ### Native element properties @@ -244,7 +244,7 @@ The above example renders the following DOM.
...
``` -When binding `style` to an object, Angular compares the previous value to the current value with the triple-equals operator (`===`). You must create a new object instance when you modify these values in order to Angular to apply any updates. +When binding `style` to an object, Angular compares the previous value to the current value with the triple-equals operator (`===`). You must create a new object instance when you modify these values in order for Angular to apply any updates. If an element has multiple bindings for the same style property, Angular resolves collisions by following its style precedence order. @@ -260,4 +260,4 @@ Angular supports binding string values to ARIA attributes. Angular writes the string value to the element’s `aria-label` attribute and removes it when the bound value is `null`. -Some ARIA features expose DOM properties or directive inputs that accept structured values (such as element references). Use standard property bindings for those cases. See the [accessibility guide](best-practices/a11y#aria-attributes-and-properties) for examples and additional guidance. +Some ARIA features expose DOM properties or directive inputs that accept structured values (such as element references). Use standard property bindings for those cases. See the [accessibility guide](/best-practices/a11y#aria-attributes-and-properties) for examples and additional guidance. diff --git a/adev-ja/src/content/guide/templates/binding.md b/adev-ja/src/content/guide/templates/binding.md index 15d3d5e4b..452bb33ea 100644 --- a/adev-ja/src/content/guide/templates/binding.md +++ b/adev-ja/src/content/guide/templates/binding.md @@ -60,7 +60,7 @@ export class App { Angularは、角括弧を使用して、動的な値をオブジェクトのプロパティとHTML属性にバインドすることをサポートしています。 -HTML要素のDOMインスタンス、[コンポーネント](guide/components)インスタンス、または[ディレクティブ](guide/directives)インスタンスのプロパティにバインドできます。 +HTML要素のDOMインスタンス、[コンポーネント](/guide/components)インスタンス、または[ディレクティブ](/guide/directives)インスタンスのプロパティにバインドできます。 ### ネイティブ要素のプロパティ @@ -260,4 +260,4 @@ Angularは、ARIA属性への文字列値のバインディングをサポート Angularは、要素の`aria-label`属性に文字列値を書き込み、バインドされた値が`null`の場合は削除します。 -一部のARIA機能は、構造化された値(要素参照など)を受け取るDOMプロパティまたはディレクティブ入力を公開しています。これらの場合は、標準のプロパティバインディングを使用してください。例と追加のガイダンスについては、[アクセシビリティガイド](best-practices/a11y#aria-attributes-and-properties)を参照してください。 +一部のARIA機能は、構造化された値(要素参照など)を受け取るDOMプロパティまたはディレクティブ入力を公開しています。これらの場合は、標準のプロパティバインディングを使用してください。例と追加のガイダンスについては、[アクセシビリティガイド](/best-practices/a11y#aria-attributes-and-properties)を参照してください。 diff --git a/adev-ja/src/content/guide/templates/control-flow.en.md b/adev-ja/src/content/guide/templates/control-flow.en.md index a8408cc33..6d8122547 100644 --- a/adev-ja/src/content/guide/templates/control-flow.en.md +++ b/adev-ja/src/content/guide/templates/control-flow.en.md @@ -133,3 +133,30 @@ You can specify multiple conditions for a single block by have consecutive `@cas You can optionally include a `@default` block. The content of the `@default` block displays if none of the preceding case expressions match the switch value. If no `@case` matches the expression and there is no `@default` block, nothing is shown. + +### Exhaustive type checking + +`@switch` supports exhaustive type checking, allowing Angular to verify at compile time that all possible values of a union type are handled. + +By using `@default never;`, you explicitly declare that no remaining cases should exist. If the union type is later extended and a new case is not covered by an @case, Angular’s template type checker will report an error, helping you catch missing branches early. + +```angular-html +@Component({ + template: ` + @switch (state) { + @case ('loggedOut') { + + } + + @case ('loggedIn') { +

Welcome back!

+ } + + @default never; // throws because `@case ('loading')` is missing + } + `, +}) +export class AppComponent { + state: 'loggedOut' | 'loading' | 'loggedIn' = 'loggedOut'; +} +``` diff --git a/adev-ja/src/content/guide/templates/control-flow.md b/adev-ja/src/content/guide/templates/control-flow.md index 111cfcb61..0e2d008d1 100644 --- a/adev-ja/src/content/guide/templates/control-flow.md +++ b/adev-ja/src/content/guide/templates/control-flow.md @@ -133,3 +133,30 @@ NOTE: `*ngFor`とは異なり、`@for`ブロックはビューの再利用を優 オプションで `@default` ブロックを含めることができます。 `@default` ブロックのコンテンツは、前のケース式のいずれもスイッチ値と一致しない場合に表示されます。 `@case` が式と一致せず、`@default` ブロックがない場合は、何も表示されません。 + +### 網羅的な型チェック {#exhaustive-type-checking} + +`@switch` は網羅的な型チェックをサポートしており、Angularはコンパイル時にユニオン型のすべての可能な値が処理されていることを検証できます。 + +`@default never;` を使用することで、残りのケースが存在しないことを明示的に宣言します。ユニオン型が後で拡張され、新しいケースが `@case` でカバーされていない場合、Angularのテンプレート型チェッカーはエラーを報告し、見落としたブランチを早期に検出できます。 + +```angular-html +@Component({ + template: ` + @switch (state) { + @case ('loggedOut') { + + } + + @case ('loggedIn') { +

Welcome back!

+ } + + @default never; // throws because `@case ('loading')` is missing + } + `, +}) +export class AppComponent { + state: 'loggedOut' | 'loading' | 'loggedIn' = 'loggedOut'; +} +``` diff --git a/adev-ja/src/content/guide/templates/expression-syntax.en.md b/adev-ja/src/content/guide/templates/expression-syntax.en.md index b0d32c28d..298f5e40f 100644 --- a/adev-ja/src/content/guide/templates/expression-syntax.en.md +++ b/adev-ja/src/content/guide/templates/expression-syntax.en.md @@ -68,6 +68,7 @@ Angular supports the following operators from standard JavaScript. | typeof | `typeof 42` | | void | `void 1` | | in | `'model' in car` | +| instanceof | `car instanceof Automobile` | | Assignment | `a = b` | | Addition Assignment | `a += b` | | Subtraction Assignment | `a -= b` | @@ -100,7 +101,6 @@ NOTE: Optional chaining behaves differently from the standard JavaScript version | Object destructuring | `const { name } = person` | | Array destructuring | `const [firstItem] = items` | | Comma operator | `x = (x++, x)` | -| instanceof | `car instanceof Automobile` | | new | `new Car()` | ## Lexical context for expressions diff --git a/adev-ja/src/content/guide/templates/expression-syntax.md b/adev-ja/src/content/guide/templates/expression-syntax.md index 920890d0b..1ba1db71c 100644 --- a/adev-ja/src/content/guide/templates/expression-syntax.md +++ b/adev-ja/src/content/guide/templates/expression-syntax.md @@ -68,6 +68,7 @@ Angularは、標準JavaScriptの次の演算子をサポートしています。 | typeof | `typeof 42` | | void | `void 1` | | in | `'model' in car` | +| instanceof | `car instanceof Automobile` | | 代入 | `a = b` | | 加算代入 | `a += b` | | 減算代入 | `a -= b` | @@ -100,7 +101,6 @@ NOTE: オプショナルチェーンは、標準JavaScriptバージョンとは | オブジェクトのデストラクタリング | `const { name } = person` | | 配列のデストラクタリング | `const [firstItem] = items` | | カンマ演算子 | `x = (x++, x)` | -| instanceof | `car instanceof Automobile` | | new | `new Car()` | ## 式の字句コンテキスト diff --git a/adev-ja/src/content/guide/templates/ng-container.en.md b/adev-ja/src/content/guide/templates/ng-container.en.md index 4359ea648..5697e7854 100644 --- a/adev-ja/src/content/guide/templates/ng-container.en.md +++ b/adev-ja/src/content/guide/templates/ng-container.en.md @@ -1,4 +1,4 @@ -# Grouping elements with ng-container +# Grouping elements with ng-container `` is a special element in Angular that groups multiple elements together or marks a location in a template without rendering a real element in the DOM. @@ -71,11 +71,11 @@ export class UserProfile { In the example above, the `ngTemplateOutlet` directive dynamically renders one of two template fragments in the location of the `` element. -For more information regarding NgTemplateOutlet, see the [NgTemplateOutlets API documentation page](/api/common/NgTemplateOutlet). +For more information regarding `NgTemplateOutlet`, see the [NgTemplateOutlet API documentation page](/api/common/NgTemplateOutlet). ## Using `` with structural directives -You can also apply structural directives to `` elements. Common examples of this include `ngIf`and `ngFor`. +You can also apply structural directives to `` elements. Common examples of this include `ngIf` and `ngFor`. ```angular-html @@ -91,7 +91,7 @@ You can also apply structural directives to `` elements. Common ex ## Using `` for injection -See the Dependency Injection guide for more information on Angular's dependency injection system. +See the [Dependency Injection guide](/guide/di) for more information on Angular's dependency injection system. When you apply a directive to ``, descendant elements can inject the directive or anything that the directive provides. Use this when you want to declaratively provide a value to a specific part of your template. diff --git a/adev-ja/src/content/guide/templates/ng-container.md b/adev-ja/src/content/guide/templates/ng-container.md index 0c728cbe8..206ce73c5 100644 --- a/adev-ja/src/content/guide/templates/ng-container.md +++ b/adev-ja/src/content/guide/templates/ng-container.md @@ -71,7 +71,7 @@ export class UserProfile { 上記の例では、`ngTemplateOutlet` ディレクティブは、`` 要素の場所に2つのテンプレートフラグメントのいずれかを動的にレンダリングします。 -NgTemplateOutletの詳細については、[NgTemplateOutlets API ドキュメントページ](/api/common/NgTemplateOutlet) を参照してください。 +`NgTemplateOutlet`の詳細については、[NgTemplateOutlet API ドキュメントページ](/api/common/NgTemplateOutlet) を参照してください。 ## `` を構造ディレクティブで使用 @@ -91,7 +91,7 @@ NgTemplateOutletの詳細については、[NgTemplateOutlets API ドキュメ ## `` をインジェクションに使用する -Angularの依存性の注入システムの詳細については、依存性の注入ガイドを参照してください。 +Angularの依存性の注入システムの詳細については、[依存性の注入ガイド](/guide/di)を参照してください。 `` にディレクティブを適用すると、子孫の要素はディレクティブまたはディレクティブが提供するものを注入できます。テンプレートの特定の部分に値を宣言的に提供したい場合に使用します。 diff --git a/adev-ja/src/content/guide/templates/ng-content.en.md b/adev-ja/src/content/guide/templates/ng-content.en.md index 93713abef..9047bee9c 100644 --- a/adev-ja/src/content/guide/templates/ng-content.en.md +++ b/adev-ja/src/content/guide/templates/ng-content.en.md @@ -4,8 +4,7 @@ Here is an example of a `BaseButton` component that accepts any markup from its parent. -```angular-ts -// ./base-button/base-button.ts +```angular-ts {header:'base-button/base-button.ts'} import {Component} from '@angular/core'; @Component({ @@ -15,8 +14,7 @@ import {Component} from '@angular/core'; export class BaseButton {} ``` -```angular-ts -// ./app.ts +```angular-ts {header:'app.ts'} import {Component} from '@angular/core'; import {BaseButton} from './base-button'; diff --git a/adev-ja/src/content/guide/templates/ng-content.md b/adev-ja/src/content/guide/templates/ng-content.md index 903cce7af..b6c243a00 100644 --- a/adev-ja/src/content/guide/templates/ng-content.md +++ b/adev-ja/src/content/guide/templates/ng-content.md @@ -4,8 +4,7 @@ 以下は、親からマークアップを受け取る `BaseButton` コンポーネントの例です。 -```angular-ts -// ./base-button/base-button.ts +```angular-ts {header:'base-button/base-button.ts'} import {Component} from '@angular/core'; @Component({ @@ -15,8 +14,7 @@ import {Component} from '@angular/core'; export class BaseButton {} ``` -```angular-ts -// ./app.ts +```angular-ts {header:'app.ts'} import {Component} from '@angular/core'; import {BaseButton} from './base-button'; diff --git a/adev-ja/src/content/guide/templates/two-way-binding.en.md b/adev-ja/src/content/guide/templates/two-way-binding.en.md index 45c0ddc2a..62a6523d5 100644 --- a/adev-ja/src/content/guide/templates/two-way-binding.en.md +++ b/adev-ja/src/content/guide/templates/two-way-binding.en.md @@ -46,8 +46,7 @@ Leveraging two-way binding between a parent and child component requires more co Here is an example where the `App` is responsible for setting the initial count state, but the logic for updating and rendering the UI for the counter primarily resides inside its child `Counter`. -```angular-ts -// ./app.ts +```angular-ts {header: 'app.ts'} import {Component} from '@angular/core'; import {Counter} from './counter'; @@ -66,8 +65,7 @@ export class App { } ``` -```angular-ts -// './counter.ts'; +```angular-ts {header: 'counter.ts'} import {Component, model} from '@angular/core'; @Component({ @@ -95,8 +93,7 @@ The child component must contain a `model` property. Here is a simplified example: -```angular-ts -// './counter.ts'; +```angular-ts {header: 'counter.ts'} import {Component, model} from '@angular/core'; @Component({ @@ -118,8 +115,7 @@ The parent component must: Here is a simplified example: -```angular-ts -// ./app.ts +```angular-ts {header: 'app.ts'} import {Component} from '@angular/core'; import {Counter} from './counter'; diff --git a/adev-ja/src/content/guide/templates/two-way-binding.md b/adev-ja/src/content/guide/templates/two-way-binding.md index c504caf0a..8ed33025e 100644 --- a/adev-ja/src/content/guide/templates/two-way-binding.md +++ b/adev-ja/src/content/guide/templates/two-way-binding.md @@ -46,8 +46,7 @@ export class App { ここでは、`App`が初期カウント状態を設定しますが、カウンターのUIを更新およびレンダリングするためのロジックは、主にその子である`Counter`にある例を示します。 -```angular-ts -// ./app.ts +```angular-ts {header: 'app.ts'} import {Component} from '@angular/core'; import {Counter} from './counter'; @@ -66,8 +65,7 @@ export class App { } ``` -```angular-ts -// './counter.ts'; +```angular-ts {header: 'counter.ts'} import {Component, model} from '@angular/core'; @Component({ @@ -95,8 +93,7 @@ export class Counter { これは簡略化された例です。 -```angular-ts -// './counter.ts'; +```angular-ts {header: 'counter.ts'} import {Component, model} from '@angular/core'; @Component({ @@ -118,8 +115,7 @@ export class Counter { これは簡略化された例です。 -```angular-ts -// ./app.ts +```angular-ts {header: 'app.ts'} import {Component} from '@angular/core'; import {Counter} from './counter'; diff --git a/adev-ja/src/content/guide/testing/code-coverage.en.md b/adev-ja/src/content/guide/testing/code-coverage.en.md index b9b57d2d9..d50ed8ebb 100644 --- a/adev-ja/src/content/guide/testing/code-coverage.en.md +++ b/adev-ja/src/content/guide/testing/code-coverage.en.md @@ -86,6 +86,7 @@ Now, if your coverage drops below 80% when you run your tests, the command will You can configure several other coverage options in your `angular.json` file: - `coverageInclude`: Glob patterns of files to include in the coverage report. +- `coverageExclude`: Glob patterns of files to exclude in the coverage report. - `coverageReporters`: An array of reporters to use (e.g., `html`, `lcov`, `json`). - `coverageWatermarks`: An object specifying `[low, high]` watermarks for the HTML reporter, which can affect the color-coding of the report. diff --git a/adev-ja/src/content/guide/testing/code-coverage.md b/adev-ja/src/content/guide/testing/code-coverage.md index c9007704c..bee16feba 100644 --- a/adev-ja/src/content/guide/testing/code-coverage.md +++ b/adev-ja/src/content/guide/testing/code-coverage.md @@ -86,6 +86,7 @@ ng test --coverage `angular.json` ファイルで、他にもいくつかのカバレッジオプションを設定できます。 - `coverageInclude`: カバレッジレポートに含めるファイルのglobパターン。 +- `coverageExclude`: カバレッジレポートから除外するファイルのglobパターン。 - `coverageReporters`: 使用するレポーターの配列(例: `html`、`lcov`、`json`)。 - `coverageWatermarks`: HTMLレポーターの `[low, high]` ウォーターマークを指定するオブジェクト。レポートの色分けに影響を与える可能性があります。 diff --git a/adev-ja/src/content/guide/testing/components-scenarios.en.md b/adev-ja/src/content/guide/testing/components-scenarios.en.md index 193691d35..99453edf7 100644 --- a/adev-ja/src/content/guide/testing/components-scenarios.en.md +++ b/adev-ja/src/content/guide/testing/components-scenarios.en.md @@ -863,7 +863,7 @@ beforeEach(() => { }); ``` -HELPFUL: The `set` key in this example replaces all the exisiting imports on your component, make sure to imports all dependencies, not only the stubs. Alternatively you can use the `remove`/`add` keys to selectively remove and add imports. +HELPFUL: The `set` key in this example replaces all the existing imports on your component, make sure to import all dependencies, not only the stubs. Alternatively you can use the `remove`/`add` keys to selectively remove and add imports. ### `NO_ERRORS_SCHEMA` diff --git a/adev-ja/src/content/guide/testing/creating-component-harnesses.en.md b/adev-ja/src/content/guide/testing/creating-component-harnesses.en.md index 76c6bc4b8..6d0456e7b 100644 --- a/adev-ja/src/content/guide/testing/creating-component-harnesses.en.md +++ b/adev-ja/src/content/guide/testing/creating-component-harnesses.en.md @@ -133,7 +133,7 @@ class MyMenuItem {} class MyMenu { triggerText = input(''); - @ContentChildren(MyMenuItem) items: QueryList; + items = contentChildren(MyMenuItem); } ``` diff --git a/adev-ja/src/content/guide/testing/creating-component-harnesses.md b/adev-ja/src/content/guide/testing/creating-component-harnesses.md index 78923fa15..73b917b0a 100644 --- a/adev-ja/src/content/guide/testing/creating-component-harnesses.md +++ b/adev-ja/src/content/guide/testing/creating-component-harnesses.md @@ -133,7 +133,7 @@ class MyMenuItem {} class MyMenu { triggerText = input(''); - @ContentChildren(MyMenuItem) items: QueryList; + items = contentChildren(MyMenuItem); } ``` diff --git a/adev-ja/src/content/guide/testing/debugging.en.md b/adev-ja/src/content/guide/testing/debugging.en.md index 49012648e..c40d0d463 100644 --- a/adev-ja/src/content/guide/testing/debugging.en.md +++ b/adev-ja/src/content/guide/testing/debugging.en.md @@ -15,6 +15,6 @@ Debugging in the default Node.js environment is often the quickest way to diagno ## Debugging in a browser -Debugging with Vitest and [browser mode](/guide/testing/migrating-to-vitest#5-configure-browser-mode-optional) is not supported today. +The same way you start a debugging session with in Node, you can use `ng test` with the `--debug` flag with Vitest and [browser mode](/guide/testing/migrating-to-vitest#5-configure-browser-mode-optional). - +- **[Custom Controls Guide](guide/forms/signals/custom-controls)** - Building reusable form components ## Keep learning -Remember: Signal Forms is experimental, so check the [official documentation](guide/forms/signals/overview) for updates to the API. +IMPORTANT: Signal Forms is experimental, so check the [official documentation](guide/forms/signals/overview) for updates to the API. Happy coding! diff --git a/adev-ja/src/content/tutorials/signal-forms/steps/6-next-steps/README.md b/adev-ja/src/content/tutorials/signal-forms/steps/6-next-steps/README.md index ba1e73722..928c63143 100644 --- a/adev-ja/src/content/tutorials/signal-forms/steps/6-next-steps/README.md +++ b/adev-ja/src/content/tutorials/signal-forms/steps/6-next-steps/README.md @@ -26,6 +26,6 @@ ## 学び続ける {#keep-learning} -覚えておいてください: シグナルフォームは実験的であるため、APIの更新については[公式ドキュメント](guide/forms/signals/overview)を確認してください。 +IMPORTANT: シグナルフォームは実験的であるため、APIの更新については[公式ドキュメント](guide/forms/signals/overview)を確認してください。 楽しいコーディングを! diff --git a/origin b/origin index 31d3d5649..42d57c357 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit 31d3d564961b701bda96d94731fbed72c01975fa +Subproject commit 42d57c35781fb65fc4d44df59b6a85287664216a diff --git a/tools/adev-patches/fix-generate-routes-explicit-id.patch b/tools/adev-patches/fix-generate-routes-explicit-id.patch deleted file mode 100644 index 3c6f7c27a..000000000 --- a/tools/adev-patches/fix-generate-routes-explicit-id.patch +++ /dev/null @@ -1,17 +0,0 @@ -# Workaround until this issue has been resolved: https://github.com/angular/angular/pull/67201 -diff --git a/adev/scripts/routes/generate-routes.mts b/adev/scripts/routes/generate-routes.mts -index 605262d172..8ef9d7ead2 100644 ---- a/adev/scripts/routes/generate-routes.mts -+++ b/adev/scripts/routes/generate-routes.mts -@@ -75,6 +75,11 @@ main(); - - // TODO: refactor so this function is shared with the generation pipeline (adev/shared-docs/pipeline/shared/marked/transformations/heading.mts) - function getIdFromHeading(heading: string): string { -+ // Check for explicit ID in {#id} format (used in translations) -+ const explicitIdMatch = heading.match(/\{#([^}]+)\}/); -+ if (explicitIdMatch) { -+ return explicitIdMatch[1]; -+ } - return heading - .toLowerCase() - .replace(/\s|\//g, '-') // replace spaces and slashes with dashes