From 58f44e93431762a99609d03f9a7b81a4fdc58b3b Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 20 Nov 2024 20:26:05 +0900 Subject: [PATCH] feat: update to v19.0 (#990) * fix: update origin to 19.0.0 * fix: migrate untranslated contents * fix: migrate tutorial contents * fix: migrate reference contents * fix: migrate essentials contents * fix: migrate component guides * fix: migrate di guide contents * fix: migrate directives guide contents * fix: migrate http guide contents * fix: migrate pipes guide contents * fix: migrate routing guide contents * fix: migrate signals guide contents * fix: migrate templates guide contents * fix: migrate performance, ssr guide contents * fix: migrate best-practices contents * fix: migrate update guide contents * fix: migrate essentials * fix: migrate removed contents * fix: migrate navigation * fix: migrate adev patches * fix: address lint errors * chore: update translator scripts * fix: drop aio support from angular-ja * fix: change search index * fix: update current major version value * fix: update build scripts * fix: set production build flag --- .textlintrc | 6 +- .../app/features/update/recommendations.en.ts | 117 +++++ .../app/features/update/recommendations.ts | 117 +++++ .../features/update/update.component.en.ts | 3 +- .../app/features/update/update.component.ts | 3 +- adev-ja/src/app/sub-navigation-data.en.ts | 281 +++++------ adev-ja/src/app/sub-navigation-data.ts | 281 +++++------ .../content/best-practices/style-guide.en.md | 4 +- .../src/content/best-practices/style-guide.md | 4 +- .../ecosystem/rxjs-interop/output-interop.md | 52 ++ .../rxjs-interop/signals-interop.md} | 34 +- .../ecosystem/service-workers/config.md | 23 +- .../components/anatomy-of-components.en.md | 43 +- .../guide/components/anatomy-of-components.md | 59 ++- .../guide/components/content-projection.en.md | 32 ++ .../guide/components/content-projection.md | 87 ++-- .../content/guide/components/importing.en.md | 35 -- .../src/content/guide/components/importing.md | 35 -- .../src/content/guide/components/inputs.en.md | 285 +++++++++-- .../src/content/guide/components/inputs.md | 331 ++++++++++--- .../guide/components/output-function.en.md | 109 ----- .../guide/components/output-function.md | 109 ----- .../content/guide/components/outputs.en.md | 102 +++- .../src/content/guide/components/outputs.md | 126 +++-- .../content/guide/components/queries.en.md | 318 ++++++++---- .../src/content/guide/components/queries.md | 361 ++++++++++---- adev-ja/src/content/guide/defer.en.md | 299 ------------ adev-ja/src/content/guide/defer.md | 299 ------------ .../guide/di/dependency-injection.en.md | 3 +- .../content/guide/di/dependency-injection.md | 3 +- .../hierarchical-dependency-injection.en.md | 11 +- .../di/hierarchical-dependency-injection.md | 9 +- adev-ja/src/content/guide/di/overview.en.md | 2 +- adev-ja/src/content/guide/di/overview.md | 2 +- .../directive-composition-api.en.md | 10 +- .../directives/directive-composition-api.md | 10 +- .../directives/structural-directives.en.md | 1 - .../guide/directives/structural-directives.md | 1 - .../content/guide/http/making-requests.en.md | 1 - .../src/content/guide/http/making-requests.md | 1 - adev-ja/src/content/guide/hybrid-rendering.md | 322 +++++++++++++ adev-ja/src/content/guide/hydration.en.md | 14 +- adev-ja/src/content/guide/hydration.md | 14 +- .../src/content/guide/image-optimization.md | 2 + .../content/guide/incremental-hydration.md | 212 ++++++++ adev-ja/src/content/guide/ngmodules/api.md | 46 -- .../content/guide/ngmodules/bootstrapping.md | 152 ------ adev-ja/src/content/guide/ngmodules/faq.md | 428 ----------------- .../guide/ngmodules/feature-modules.md | 158 ------ .../src/content/guide/ngmodules/frequent.md | 53 -- .../content/guide/ngmodules/lazy-loading.md | 453 ------------------ .../content/guide/ngmodules/module-types.md | 107 ----- .../src/content/guide/ngmodules/overview.md | 209 ++++++-- .../src/content/guide/ngmodules/providers.md | 133 ----- .../src/content/guide/ngmodules/sharing.md | 51 -- .../guide/ngmodules/singleton-services.md | 185 ------- .../content/guide/ngmodules/vs-jsmodule.md | 86 ---- .../content/guide/performance/overview.en.md | 10 +- .../src/content/guide/performance/overview.md | 10 +- .../src/content/guide/pipes/template.en.md | 1 - adev-ja/src/content/guide/pipes/template.md | 1 - .../content/guide/pipes/transform-data.en.md | 2 - .../src/content/guide/pipes/transform-data.md | 2 - .../guide/routing/common-router-tasks.en.md | 1 - .../guide/routing/common-router-tasks.md | 1 - .../src/content/guide/signals/inputs.en.md | 147 ------ adev-ja/src/content/guide/signals/inputs.md | 147 ------ .../content/guide/signals/linked-signal.md | 109 +++++ adev-ja/src/content/guide/signals/model.en.md | 136 ------ adev-ja/src/content/guide/signals/model.md | 136 ------ .../src/content/guide/signals/overview.en.md | 10 +- adev-ja/src/content/guide/signals/overview.md | 8 +- .../src/content/guide/signals/queries.en.md | 191 -------- adev-ja/src/content/guide/signals/queries.md | 191 -------- adev-ja/src/content/guide/signals/resource.md | 115 +++++ .../src/content/guide/signals/rxjs-interop.md | 131 ----- adev-ja/src/content/guide/ssr.en.md | 44 +- adev-ja/src/content/guide/ssr.md | 44 +- .../src/content/guide/templates/defer.en.md | 2 +- adev-ja/src/content/guide/templates/defer.md | 2 +- .../content/guide/templates/ng-content.en.md | 2 - .../src/content/guide/templates/ng-content.md | 2 - .../content/guide/templates/ng-template.en.md | 7 +- .../content/guide/templates/ng-template.md | 7 +- .../content/guide/templates/overview.en.md | 2 +- .../src/content/guide/templates/overview.md | 2 +- .../src/content/guide/templates/pipes.en.md | 13 +- adev-ja/src/content/guide/templates/pipes.md | 13 +- .../guide/templates/two-way-binding.en.md | 4 - .../guide/templates/two-way-binding.md | 4 - .../content/guide/templates/variables.en.md | 2 - .../src/content/guide/templates/variables.md | 2 - adev-ja/src/content/guide/zoneless.md | 6 +- .../introduction/essentials/components.en.md | 142 +++--- .../introduction/essentials/components.md | 146 +++--- .../essentials/conditionals-and-loops.en.md | 112 ----- .../essentials/conditionals-and-loops.md | 112 ----- ...logic.en.md => dependency-injection.en.md} | 19 +- ...aring-logic.md => dependency-injection.md} | 25 +- .../handling-user-interaction.en.md | 59 --- .../essentials/handling-user-interaction.md | 58 --- .../essentials/managing-dynamic-data.en.md | 56 --- .../essentials/managing-dynamic-data.md | 56 --- .../introduction/essentials/next-steps.en.md | 6 +- .../introduction/essentials/next-steps.md | 6 +- .../introduction/essentials/overview.en.md | 15 +- .../introduction/essentials/overview.md | 13 +- .../rendering-dynamic-templates.en.md | 95 ---- .../essentials/rendering-dynamic-templates.md | 95 ---- .../introduction/essentials/signals.en.md | 80 ++++ .../introduction/essentials/signals.md | 80 ++++ .../introduction/essentials/templates.en.md | 148 ++++++ .../introduction/essentials/templates.md | 148 ++++++ .../src/content/reference/errors/NG0302.en.md | 3 +- .../src/content/reference/errors/NG0302.md | 3 +- .../src/content/reference/errors/NG0500.en.md | 1 - .../src/content/reference/errors/NG0500.md | 1 - .../src/content/reference/errors/NG0503.en.md | 2 - .../src/content/reference/errors/NG0503.md | 2 - .../src/content/reference/errors/NG0504.en.md | 3 - .../src/content/reference/errors/NG0504.md | 3 - .../src/content/reference/errors/NG0506.en.md | 3 +- .../src/content/reference/errors/NG0506.md | 3 +- .../reference/extended-diagnostics/NG8103.md | 3 - .../reference/extended-diagnostics/NG8113.md | 48 ++ .../extended-diagnostics/overview.md | 1 + .../content/reference/migrations/overview.md | 6 + .../reference/migrations/signal-inputs.md | 116 +++++ .../reference/migrations/signal-queries.md | 115 +++++ adev-ja/src/content/reference/roadmap.en.md | 106 ++-- adev-ja/src/content/reference/roadmap.md | 154 +++--- adev-ja/src/content/reference/versions.en.md | 5 +- adev-ja/src/content/reference/versions.md | 5 +- .../tools/cli/build-system-migration.md | 332 ++++++++++++- adev-ja/src/content/tools/cli/overview.md | 6 + .../tools/libraries/angular-package-format.md | 33 +- .../steps/11-optimizing-images/README.en.md | 2 - .../steps/11-optimizing-images/README.md | 2 - .../steps/12-enable-routing/README.en.md | 1 - .../steps/12-enable-routing/README.md | 1 - .../steps/14-routerLink/README.en.md | 2 - .../steps/14-routerLink/README.md | 2 - .../learn-angular/steps/15-forms/README.en.md | 1 - .../learn-angular/steps/15-forms/README.md | 1 - .../steps/17-reactive-forms/README.en.md | 1 - .../steps/17-reactive-forms/README.md | 1 - .../steps/24-create-a-pipe/README.en.md | 2 - .../steps/24-create-a-pipe/README.md | 2 - origin | 2 +- prh.yml | 4 + tools/adev-patches/localize-home.patch | 10 +- .../replace-environment-values.patch | 2 +- .../adev-patches/replace-version-links.patch | 3 +- .../set-current-major-verion.patch | 2 +- tools/lib/adev.ts | 2 +- tools/translate.ts | 12 +- 156 files changed, 4330 insertions(+), 5891 deletions(-) create mode 100644 adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md rename adev-ja/src/content/{guide/signals/rxjs-interop.en.md => ecosystem/rxjs-interop/signals-interop.md} (83%) delete mode 100644 adev-ja/src/content/guide/components/importing.en.md delete mode 100644 adev-ja/src/content/guide/components/importing.md delete mode 100644 adev-ja/src/content/guide/components/output-function.en.md delete mode 100644 adev-ja/src/content/guide/components/output-function.md delete mode 100644 adev-ja/src/content/guide/defer.en.md delete mode 100644 adev-ja/src/content/guide/defer.md create mode 100644 adev-ja/src/content/guide/hybrid-rendering.md create mode 100644 adev-ja/src/content/guide/incremental-hydration.md delete mode 100644 adev-ja/src/content/guide/ngmodules/api.md delete mode 100644 adev-ja/src/content/guide/ngmodules/bootstrapping.md delete mode 100644 adev-ja/src/content/guide/ngmodules/faq.md delete mode 100644 adev-ja/src/content/guide/ngmodules/feature-modules.md delete mode 100644 adev-ja/src/content/guide/ngmodules/frequent.md delete mode 100644 adev-ja/src/content/guide/ngmodules/lazy-loading.md delete mode 100644 adev-ja/src/content/guide/ngmodules/module-types.md delete mode 100644 adev-ja/src/content/guide/ngmodules/providers.md delete mode 100644 adev-ja/src/content/guide/ngmodules/sharing.md delete mode 100644 adev-ja/src/content/guide/ngmodules/singleton-services.md delete mode 100644 adev-ja/src/content/guide/ngmodules/vs-jsmodule.md delete mode 100644 adev-ja/src/content/guide/signals/inputs.en.md delete mode 100644 adev-ja/src/content/guide/signals/inputs.md create mode 100644 adev-ja/src/content/guide/signals/linked-signal.md delete mode 100644 adev-ja/src/content/guide/signals/model.en.md delete mode 100644 adev-ja/src/content/guide/signals/model.md delete mode 100644 adev-ja/src/content/guide/signals/queries.en.md delete mode 100644 adev-ja/src/content/guide/signals/queries.md create mode 100644 adev-ja/src/content/guide/signals/resource.md delete mode 100644 adev-ja/src/content/guide/signals/rxjs-interop.md delete mode 100644 adev-ja/src/content/introduction/essentials/conditionals-and-loops.en.md delete mode 100644 adev-ja/src/content/introduction/essentials/conditionals-and-loops.md rename adev-ja/src/content/introduction/essentials/{sharing-logic.en.md => dependency-injection.en.md} (73%) rename adev-ja/src/content/introduction/essentials/{sharing-logic.md => dependency-injection.md} (73%) delete mode 100644 adev-ja/src/content/introduction/essentials/handling-user-interaction.en.md delete mode 100644 adev-ja/src/content/introduction/essentials/handling-user-interaction.md delete mode 100644 adev-ja/src/content/introduction/essentials/managing-dynamic-data.en.md delete mode 100644 adev-ja/src/content/introduction/essentials/managing-dynamic-data.md delete mode 100644 adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.en.md delete mode 100644 adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.md create mode 100644 adev-ja/src/content/introduction/essentials/signals.en.md create mode 100644 adev-ja/src/content/introduction/essentials/signals.md create mode 100644 adev-ja/src/content/introduction/essentials/templates.en.md create mode 100644 adev-ja/src/content/introduction/essentials/templates.md create mode 100644 adev-ja/src/content/reference/extended-diagnostics/NG8113.md create mode 100644 adev-ja/src/content/reference/migrations/signal-inputs.md create mode 100644 adev-ja/src/content/reference/migrations/signal-queries.md diff --git a/.textlintrc b/.textlintrc index bf6acfa6e..45af8b558 100644 --- a/.textlintrc +++ b/.textlintrc @@ -30,7 +30,8 @@ "が", "に", "にも", - "は" + "は", + "でも" ] }, "no-exclamation-question-mark": false, @@ -40,7 +41,8 @@ "二重中括弧構文", "変更検知戦略", "推移的依存関係", - "三重等号演算子" + "三重等号演算子", + "入力変換関数" ] }, "ja-no-mixed-period": false diff --git a/adev-ja/src/app/features/update/recommendations.en.ts b/adev-ja/src/app/features/update/recommendations.en.ts index 1d5fe99a9..ccc6fe0e8 100644 --- a/adev-ja/src/app/features/update/recommendations.en.ts +++ b/adev-ja/src/app/features/update/recommendations.en.ts @@ -2397,4 +2397,121 @@ export const RECOMMENDATIONS: Step[] = [ action: 'You may experience tests failures if you have tests that rely on change detection execution order when using `ComponentFixture.autoDetect` because it now executes change detection for fixtures within `ApplicationRef.tick`. For example, this will cause test fixture to refresh before any dialogs that it creates whereas this may have been the other way around in the past.', }, + + { + action: + 'Angular directives, components and pipes are now standalone by default. Specify "standalone: false" for declarations that are currently declared in an NgModule. The Angular CLI will automatically update your code to reflect that.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-standalone-declarations', + }, + { + action: + 'Remove `this.` prefix when accessing template reference variables. For example, refactor `
{{ this.foo }}` to `
{{ foo }}`', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-this', + }, + { + action: + 'Replace usages of `BrowserModule.withServerTransition()` with injection of the `APP_ID` token to set the application `id` instead.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-browser-module-with-server-transition', + }, + { + action: 'The `factories` property in `KeyValueDiffers` has been removed.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-key-value-differs-factories', + }, + { + action: + 'In angular.json, replace the "name" option with "project" for the `@angular/localize` builder.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_localize_builder_project_option', + }, + { + action: 'Rename `ExperimentalPendingTasks` to `PendingTasks`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_rename_experimental_pending_tasks', + }, + { + action: + "Update tests that relied on the `Promise` timing of effects to use `await whenStable()` or call `.detectChanges()` to trigger effects. For effects triggered during change detection, ensure they don't depend on the application being fully rendered or consider using `afterRenderEffect()`. Tests using faked clocks may need to fast-forward/flush the clock.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.1', + }, + { + action: 'Upgrade to TypeScript version 5.5 or later.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.2', + }, + { + action: + 'Update tests using `fakeAsync` that rely on specific timing of zone coalescing and scheduling when a change happens outside the Angular zone (hybrid mode scheduling) as these timers are now affected by `tick` and `flush`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-timers-in-zone', + }, + { + action: + "When using `createComponent` API and not passing content for the first `ng-content`, provide `document.createTextNode('')` as a `projectableNode` to prevent rendering the default fallback content.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-render-default-fallback', + }, + { + action: + 'Update tests that rely on specific timing or ordering of change detection around custom elements, as the timing may have changed due to the switch to the hybrid scheduler.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-hybrid-scheduler-timing', + }, + { + action: + 'Migrate from using `Router.errorHandler` to `withNavigationErrorHandler` from `provideRouter` or `errorHandler` from `RouterModule.forRoot`.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-router-error-handler', + }, + { + action: + 'Update tests to handle errors thrown during `ApplicationRef.tick` by either triggering change detection synchronously or rejecting outstanding `ComponentFixture.whenStable` promises.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-testbed-error-handling', + }, + { + action: 'Update usages of `Resolve` interface to include `RedirectCommand` in its return type.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-resolve-interface-return-type', + }, + { + action: + '`fakeAsync` will flush pending timers by default. For tests that require the previous behavior, explicitly pass `{flush: false}` in the options parameter.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-fakeasync-to-flush-pending-timers', + }, ]; diff --git a/adev-ja/src/app/features/update/recommendations.ts b/adev-ja/src/app/features/update/recommendations.ts index d1d3c7377..4f98ba291 100644 --- a/adev-ja/src/app/features/update/recommendations.ts +++ b/adev-ja/src/app/features/update/recommendations.ts @@ -2397,4 +2397,121 @@ export const RECOMMENDATIONS: Step[] = [ action: '`ComponentFixture.autoDetect` が `ApplicationRef.tick` 内のフィクスチャの変更検出を実行するようになったため、`ComponentFixture.autoDetect` を使用しているときに変更検出の実行順序に依存しているテストがあると、テストに失敗することがあります。たとえば、これによってテストフィクスチャは、これまではその逆であったかもしれませんが、作成するダイアログの前にリフレッシュされます。', }, + + { + action: + 'Angular directives, components and pipes are now standalone by default. Specify "standalone: false" for declarations that are currently declared in an NgModule. The Angular CLI will automatically update your code to reflect that.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-standalone-declarations', + }, + { + action: + 'Remove `this.` prefix when accessing template reference variables. For example, refactor `
{{ this.foo }}` to `
{{ foo }}`', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-this', + }, + { + action: + 'Replace usages of `BrowserModule.withServerTransition()` with injection of the `APP_ID` token to set the application `id` instead.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-browser-module-with-server-transition', + }, + { + action: 'The `factories` property in `KeyValueDiffers` has been removed.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-key-value-differs-factories', + }, + { + action: + 'In angular.json, replace the "name" option with "project" for the `@angular/localize` builder.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_localize_builder_project_option', + }, + { + action: 'Rename `ExperimentalPendingTasks` to `PendingTasks`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_rename_experimental_pending_tasks', + }, + { + action: + "Update tests that relied on the `Promise` timing of effects to use `await whenStable()` or call `.detectChanges()` to trigger effects. For effects triggered during change detection, ensure they don't depend on the application being fully rendered or consider using `afterRenderEffect()`. Tests using faked clocks may need to fast-forward/flush the clock.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.1', + }, + { + action: 'Upgrade to TypeScript version 5.5 or later.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.2', + }, + { + action: + 'Update tests using `fakeAsync` that rely on specific timing of zone coalescing and scheduling when a change happens outside the Angular zone (hybrid mode scheduling) as these timers are now affected by `tick` and `flush`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-timers-in-zone', + }, + { + action: + "When using `createComponent` API and not passing content for the first `ng-content`, provide `document.createTextNode('')` as a `projectableNode` to prevent rendering the default fallback content.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-render-default-fallback', + }, + { + action: + 'Update tests that rely on specific timing or ordering of change detection around custom elements, as the timing may have changed due to the switch to the hybrid scheduler.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-hybrid-scheduler-timing', + }, + { + action: + 'Migrate from using `Router.errorHandler` to `withNavigationErrorHandler` from `provideRouter` or `errorHandler` from `RouterModule.forRoot`.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-router-error-handler', + }, + { + action: + 'Update tests to handle errors thrown during `ApplicationRef.tick` by either triggering change detection synchronously or rejecting outstanding `ComponentFixture.whenStable` promises.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-testbed-error-handling', + }, + { + action: 'Update usages of `Resolve` interface to include `RedirectCommand` in its return type.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-resolve-interface-return-type', + }, + { + action: + '`fakeAsync` will flush pending timers by default. For tests that require the previous behavior, explicitly pass `{flush: false}` in the options parameter.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-fakeasync-to-flush-pending-timers', + }, ]; 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 840f859cf..d82843a76 100644 --- a/adev-ja/src/app/features/update/update.component.en.ts +++ b/adev-ja/src/app/features/update/update.component.en.ts @@ -56,6 +56,7 @@ export default class AppComponent { protected afterRecommendations: Step[] = []; protected readonly versions = [ + {name: '19.0', number: 1900}, {name: '18.0', number: 1800}, {name: '17.0', number: 1700}, {name: '16.0', number: 1600}, @@ -93,7 +94,7 @@ export default class AppComponent { ]; protected from = this.versions.find((version) => version.name === '17.0')!; protected to = this.versions.find((version) => version.name === '18.0')!; - protected futureVersion = 1900; + protected futureVersion = 2000; protected readonly steps: Step[] = RECOMMENDATIONS; diff --git a/adev-ja/src/app/features/update/update.component.ts b/adev-ja/src/app/features/update/update.component.ts index ac1a956f8..6ecf6a5d2 100644 --- a/adev-ja/src/app/features/update/update.component.ts +++ b/adev-ja/src/app/features/update/update.component.ts @@ -56,6 +56,7 @@ export default class AppComponent { protected afterRecommendations: Step[] = []; protected readonly versions = [ + {name: '19.0', number: 1900}, {name: '18.0', number: 1800}, {name: '17.0', number: 1700}, {name: '16.0', number: 1600}, @@ -93,7 +94,7 @@ export default class AppComponent { ]; protected from = this.versions.find((version) => version.name === '17.0')!; protected to = this.versions.find((version) => version.name === '18.0')!; - protected futureVersion = 1900; + protected futureVersion = 2000; protected readonly steps: Step[] = RECOMMENDATIONS; diff --git a/adev-ja/src/app/sub-navigation-data.en.ts b/adev-ja/src/app/sub-navigation-data.en.ts index 23a733fb6..cad8b3168 100644 --- a/adev-ja/src/app/sub-navigation-data.en.ts +++ b/adev-ja/src/app/sub-navigation-data.en.ts @@ -46,34 +46,24 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'introduction/essentials/overview', }, { - label: 'Composing with Components', + label: 'Composition with components', path: 'essentials/components', contentPath: 'introduction/essentials/components', }, { - label: 'Managing Dynamic Data', - path: 'essentials/managing-dynamic-data', - contentPath: 'introduction/essentials/managing-dynamic-data', + label: 'Reactivity with signals', + path: 'essentials/signals', + contentPath: 'introduction/essentials/signals', }, { - label: 'Rendering Dynamic Templates', - path: 'essentials/rendering-dynamic-templates', - contentPath: 'introduction/essentials/rendering-dynamic-templates', + label: 'Dynamic interfaces with templates', + path: 'essentials/templates', + contentPath: 'introduction/essentials/templates', }, { - label: 'Conditionals and Loops', - path: 'essentials/conditionals-and-loops', - contentPath: 'introduction/essentials/conditionals-and-loops', - }, - { - label: 'Handling User Interaction', - path: 'essentials/handling-user-interaction', - contentPath: 'introduction/essentials/handling-user-interaction', - }, - { - label: 'Sharing Logic', - path: 'essentials/sharing-logic', - contentPath: 'introduction/essentials/sharing-logic', + label: 'Modular design with dependency injection', + path: 'essentials/dependency-injection', + contentPath: 'introduction/essentials/dependency-injection', }, { label: 'Next Steps', @@ -91,6 +81,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ { label: 'In-depth Guides', children: [ + { + label: 'Signals', + children: [ + { + label: 'Overview', + path: 'guide/signals', + contentPath: 'guide/signals/overview', + }, + { + label: 'linkedSignal', + path: 'guide/signals/linked-signal', + contentPath: 'guide/signals/linked-signal', + }, + { + label: 'Resource', + path: 'guide/signals/resource', + contentPath: 'guide/signals/resource', + }, + ], + }, { label: 'Components', children: [ @@ -99,11 +109,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components', contentPath: 'guide/components/anatomy-of-components', }, - { - label: 'Importing and using components', - path: 'guide/components/importing', - contentPath: 'guide/components/importing', - }, { label: 'Selectors', path: 'guide/components/selectors', @@ -124,11 +129,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components/outputs', contentPath: 'guide/components/outputs', }, - { - label: 'output() function', - path: 'guide/components/output-fn', - contentPath: 'guide/components/output-function', - }, { label: 'Content projection with ng-content', path: 'guide/components/content-projection', @@ -269,6 +269,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/directives/directive-composition-api', contentPath: 'guide/directives/directive-composition-api', }, + { + label: 'Optimizing images with NgOptimizedImage', + path: 'guide/image-optimization', + contentPath: 'guide/image-optimization', + }, ], }, { @@ -316,36 +321,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'Signals', - children: [ - { - label: 'Overview', - path: 'guide/signals', - contentPath: 'guide/signals/overview', - }, - { - label: 'RxJS Interop', - path: 'guide/signals/rxjs-interop', - contentPath: 'guide/signals/rxjs-interop', - }, - { - label: 'Inputs as signals', - path: 'guide/signals/inputs', - contentPath: 'guide/signals/inputs', - }, - { - label: 'Model inputs', - path: 'guide/signals/model', - contentPath: 'guide/signals/model', - }, - { - label: 'Queries as signals', - path: 'guide/signals/queries', - contentPath: 'guide/signals/queries', - }, - ], - }, { label: 'Routing', children: [ @@ -442,7 +417,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ ], }, { - label: 'Performance', + label: 'Server-side & hybrid-rendering', children: [ { label: 'Overview', @@ -450,17 +425,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'guide/performance/overview', }, { - label: 'Deferrable views', - path: 'guide/defer', - contentPath: 'guide/defer', - }, - { - label: 'Image Optimization', - path: 'guide/image-optimization', - contentPath: 'guide/image-optimization', - }, - { - label: 'Server-side Rendering', + label: 'Server-side rendering', path: 'guide/ssr', contentPath: 'guide/ssr', }, @@ -469,11 +434,21 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/prerendering', contentPath: 'guide/prerendering', }, + { + label: 'Hybrid rendering with server routing', + path: 'guide/hybrid-rendering', + contentPath: 'guide/hybrid-rendering', + }, { label: 'Hydration', path: 'guide/hydration', contentPath: 'guide/hydration', }, + { + label: 'Incremental Hydration', + path: 'guide/incremental-hydration', + contentPath: 'guide/incremental-hydration', + }, ], }, { @@ -586,36 +561,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'Animations', - children: [ - { - label: 'Overview', - path: 'guide/animations', - contentPath: 'guide/animations/overview', - }, - { - label: 'Transition and Triggers', - path: 'guide/animations/transition-and-triggers', - contentPath: 'guide/animations/transition-and-triggers', - }, - { - label: 'Complex Sequences', - path: 'guide/animations/complex-sequences', - contentPath: 'guide/animations/complex-sequences', - }, - { - label: 'Reusable Animations', - path: 'guide/animations/reusable-animations', - contentPath: 'guide/animations/reusable-animations', - }, - { - label: 'Route transition animations', - path: 'guide/animations/route-animations', - contentPath: 'guide/animations/route-animations', - }, - ], - }, { label: 'Experimental features', children: [ @@ -797,6 +742,56 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ { label: 'Extended Ecosystem', children: [ + { + label: 'NgModules', + path: 'guide/ngmodules/overview', + contentPath: 'guide/ngmodules/overview', + }, + { + label: 'Animations', + children: [ + { + label: 'Overview', + path: 'guide/animations', + contentPath: 'guide/animations/overview', + }, + { + label: 'Transition and Triggers', + path: 'guide/animations/transition-and-triggers', + contentPath: 'guide/animations/transition-and-triggers', + }, + { + label: 'Complex Sequences', + path: 'guide/animations/complex-sequences', + contentPath: 'guide/animations/complex-sequences', + }, + { + label: 'Reusable Animations', + path: 'guide/animations/reusable-animations', + contentPath: 'guide/animations/reusable-animations', + }, + { + label: 'Route transition animations', + path: 'guide/animations/route-animations', + contentPath: 'guide/animations/route-animations', + }, + ], + }, + { + label: 'Using RxJS with Angular', + children: [ + { + label: 'Signals interop', + path: 'ecosystem/rxjs-interop', + contentPath: 'ecosystem/rxjs-interop/signals-interop', + }, + { + label: 'Component output interop', + path: 'ecosystem/rxjs-interop/output-interop', + contentPath: 'ecosystem/rxjs-interop/output-interop', + }, + ], + }, { label: 'Service Workers & PWAs', children: [ @@ -1370,6 +1365,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'extended-diagnostics/NG8111', contentPath: 'reference/extended-diagnostics/NG8111', }, + { + label: 'NG8113: Unused Standalone Imports', + path: 'extended-diagnostics/NG8113', + contentPath: 'reference/extended-diagnostics/NG8113', + }, ], }, { @@ -1439,80 +1439,15 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'reference/migrations/route-lazy-loading', contentPath: 'reference/migrations/route-lazy-loading', }, - ], - }, - { - label: 'Concepts', - children: [ { - label: 'Overview', - path: 'reference/concepts', - contentPath: 'reference/concepts/overview', + label: 'Signal inputs', + path: 'reference/migrations/signal-inputs', + contentPath: 'reference/migrations/signal-inputs', }, { - label: 'NgModule', - children: [ - { - label: 'Overview', - path: 'guide/ngmodules', - contentPath: 'guide/ngmodules/overview', - }, - { - label: 'JS Modules vs NgModules', - path: 'guide/ngmodules/vs-jsmodule', - contentPath: 'guide/ngmodules/vs-jsmodule', - }, - { - label: 'Launching your app with a root module', - path: 'guide/ngmodules/bootstrapping', - contentPath: 'guide/ngmodules/bootstrapping', - }, - { - label: 'Sharing NgModules', - path: 'guide/ngmodules/sharing', - contentPath: 'guide/ngmodules/sharing', - }, - { - label: 'Frequently used NgModules', - path: 'guide/ngmodules/frequent', - contentPath: 'guide/ngmodules/frequent', - }, - { - label: 'Feature modules', - path: 'guide/ngmodules/feature-modules', - contentPath: 'guide/ngmodules/feature-modules', - }, - { - label: 'Types of feature modules', - path: 'guide/ngmodules/module-types', - contentPath: 'guide/ngmodules/module-types', - }, - { - label: 'Providing dependencies', - path: 'guide/ngmodules/providers', - contentPath: 'guide/ngmodules/providers', - }, - { - label: 'Singleton services', - path: 'guide/ngmodules/singleton-services', - contentPath: 'guide/ngmodules/singleton-services', - }, - { - label: 'Lazy-loading feature modules', - path: 'guide/ngmodules/lazy-loading', - contentPath: 'guide/ngmodules/lazy-loading', - }, - { - label: 'NgModule API', - path: 'guide/ngmodules/api', - contentPath: 'guide/ngmodules/api', - }, - { - label: 'NgModule FAQs', - path: 'guide/ngmodules/faq', - contentPath: 'guide/ngmodules/faq', - }, - ], + label: 'Signal queries', + path: 'reference/migrations/signal-queries', + contentPath: 'reference/migrations/signal-queries', }, ], }, diff --git a/adev-ja/src/app/sub-navigation-data.ts b/adev-ja/src/app/sub-navigation-data.ts index 93247d21d..cf1859524 100644 --- a/adev-ja/src/app/sub-navigation-data.ts +++ b/adev-ja/src/app/sub-navigation-data.ts @@ -11,6 +11,7 @@ import {NavigationItem} from '@angular/docs'; // These 2 imports are expected to be red because they are generated a build time import FIRST_APP_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/first-app/routes.json'; import LEARN_ANGULAR_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/learn-angular/routes.json'; +import DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/deferrable-views/routes.json'; import {DefaultPage} from './core/enums/pages'; import {getApiNavigationItems} from './features/references/helpers/manifest.helper'; @@ -50,29 +51,19 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'introduction/essentials/components', }, { - label: '動的なデータの管理', - path: 'essentials/managing-dynamic-data', - contentPath: 'introduction/essentials/managing-dynamic-data', + label: 'リアクティビティとシグナル', + path: 'essentials/signals', + contentPath: 'introduction/essentials/signals', }, { - label: '動的なテンプレート', - path: 'essentials/rendering-dynamic-templates', - contentPath: 'introduction/essentials/rendering-dynamic-templates', + label: 'テンプレートによる動的なインターフェース', + path: 'essentials/templates', + contentPath: 'introduction/essentials/templates', }, { - label: '条件分岐とループ', - path: 'essentials/conditionals-and-loops', - contentPath: 'introduction/essentials/conditionals-and-loops', - }, - { - label: 'ユーザーインタラクションの処理', - path: 'essentials/handling-user-interaction', - contentPath: 'introduction/essentials/handling-user-interaction', - }, - { - label: 'ロジックの共有', - path: 'essentials/sharing-logic', - contentPath: 'introduction/essentials/sharing-logic', + label: '依存性の注入によるモジュール設計', + path: 'essentials/dependency-injection', + contentPath: 'introduction/essentials/dependency-injection', }, { label: '次のステップ', @@ -90,6 +81,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ { label: '詳細ガイド', children: [ + { + label: 'シグナル', + children: [ + { + label: '概要', + path: 'guide/signals', + contentPath: 'guide/signals/overview', + }, + { + label: 'linkedSignal', + path: 'guide/signals/linked-signal', + contentPath: 'guide/signals/linked-signal', + }, + { + label: 'Resource', + path: 'guide/signals/resource', + contentPath: 'guide/signals/resource', + }, + ], + }, { label: 'コンポーネント', children: [ @@ -98,11 +109,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components', contentPath: 'guide/components/anatomy-of-components', }, - { - label: 'コンポーネントのインポートと使用', - path: 'guide/components/importing', - contentPath: 'guide/components/importing', - }, { label: 'セレクター', path: 'guide/components/selectors', @@ -123,11 +129,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components/outputs', contentPath: 'guide/components/outputs', }, - { - label: 'output()関数', - path: 'guide/components/output-fn', - contentPath: 'guide/components/output-function', - }, { label: 'ng-contentによるコンテンツ投影', path: 'guide/components/content-projection', @@ -268,6 +269,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/directives/directive-composition-api', contentPath: 'guide/directives/directive-composition-api', }, + { + label: 'NgOptimizedImageによる画像の最適化', + path: 'guide/image-optimization', + contentPath: 'guide/image-optimization', + }, ], }, { @@ -315,36 +321,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'シグナル', - children: [ - { - label: '概要', - path: 'guide/signals', - contentPath: 'guide/signals/overview', - }, - { - label: 'RxJSとの相互運用', - path: 'guide/signals/rxjs-interop', - contentPath: 'guide/signals/rxjs-interop', - }, - { - label: '入力としてのシグナル', - path: 'guide/signals/inputs', - contentPath: 'guide/signals/inputs', - }, - { - label: 'モデル入力', - path: 'guide/signals/model', - contentPath: 'guide/signals/model', - }, - { - label: 'シグナルによるクエリ', - path: 'guide/signals/queries', - contentPath: 'guide/signals/queries', - }, - ], - }, { label: 'ルーティング', children: [ @@ -441,23 +417,13 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ ], }, { - label: 'パフォーマンス', + label: 'サーバーサイドとハイブリッドレンダリング', children: [ { label: '概要', path: 'guide/performance', contentPath: 'guide/performance/overview', }, - { - label: '遅延可能なビュー', - path: 'guide/defer', - contentPath: 'guide/defer', - }, - { - label: '画像最適化', - path: 'guide/image-optimization', - contentPath: 'guide/image-optimization', - }, { label: 'サーバーサイドレンダリング', path: 'guide/ssr', @@ -468,11 +434,21 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/prerendering', contentPath: 'guide/prerendering', }, + { + label: 'サーバールーティングによるハイブリッドレンダリング', + path: 'guide/hybrid-rendering', + contentPath: 'guide/hybrid-rendering', + }, { label: 'ハイドレーション', path: 'guide/hydration', contentPath: 'guide/hydration', }, + { + label: 'インクリメンタルハイドレーション', + path: 'guide/incremental-hydration', + contentPath: 'guide/incremental-hydration', + }, ], }, { @@ -585,36 +561,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'アニメーション', - children: [ - { - label: '概要', - path: 'guide/animations', - contentPath: 'guide/animations/overview', - }, - { - label: 'トランジションとトリガー', - path: 'guide/animations/transition-and-triggers', - contentPath: 'guide/animations/transition-and-triggers', - }, - { - label: '複雑なシーケンス', - path: 'guide/animations/complex-sequences', - contentPath: 'guide/animations/complex-sequences', - }, - { - label: '再利用可能なアニメーション', - path: 'guide/animations/reusable-animations', - contentPath: 'guide/animations/reusable-animations', - }, - { - label: 'ルート遷移アニメーション', - path: 'guide/animations/route-animations', - contentPath: 'guide/animations/route-animations', - }, - ], - }, { label: '実験的機能', children: [ @@ -794,8 +740,58 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ ], }, { - label: '拡張されたエコシステム', + label: '拡張エコシステム', children: [ + { + label: 'NgModule', + path: 'guide/ngmodules/overview', + contentPath: 'guide/ngmodules/overview', + }, + { + label: 'アニメーション', + children: [ + { + label: '概要', + path: 'guide/animations', + contentPath: 'guide/animations/overview', + }, + { + label: 'トランジションとトリガー', + path: 'guide/animations/transition-and-triggers', + contentPath: 'guide/animations/transition-and-triggers', + }, + { + label: '複雑なシーケンス', + path: 'guide/animations/complex-sequences', + contentPath: 'guide/animations/complex-sequences', + }, + { + label: '再利用可能なアニメーション', + path: 'guide/animations/reusable-animations', + contentPath: 'guide/animations/reusable-animations', + }, + { + label: 'ルート遷移アニメーション', + path: 'guide/animations/route-animations', + contentPath: 'guide/animations/route-animations', + }, + ], + }, + { + label: 'RxJSとの併用', + children: [ + { + label: 'Signalとの相互運用', + path: 'ecosystem/rxjs-interop', + contentPath: 'ecosystem/rxjs-interop/signals-interop', + }, + { + label: 'コンポーネント出力との相互接続', + path: 'ecosystem/rxjs-interop/output-interop', + contentPath: 'ecosystem/rxjs-interop/output-interop', + }, + ], + }, { label: 'Service WorkerとPWA', children: [ @@ -872,6 +868,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ export const TUTORIALS_SUB_NAVIGATION_DATA: NavigationItem[] = [ FIRST_APP_TUTORIAL_NAV_DATA, LEARN_ANGULAR_TUTORIAL_NAV_DATA, + DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA, { path: DefaultPage.TUTORIALS, contentPath: 'tutorials/home', @@ -1376,6 +1373,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'extended-diagnostics/NG8111', contentPath: 'reference/extended-diagnostics/NG8111', }, + { + label: 'NG8113: Unused Standalone Imports', + path: 'extended-diagnostics/NG8113', + contentPath: 'reference/extended-diagnostics/NG8113', + }, ], }, { @@ -1445,80 +1447,15 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'reference/migrations/route-lazy-loading', contentPath: 'reference/migrations/route-lazy-loading', }, - ], - }, - { - label: 'コンセプト', - children: [ { - label: '概要', - path: 'reference/concepts', - contentPath: 'reference/concepts/overview', + label: 'Signal inputs', + path: 'reference/migrations/signal-inputs', + contentPath: 'reference/migrations/signal-inputs', }, { - label: 'NgModule', - children: [ - { - label: '概要', - path: 'guide/ngmodules', - contentPath: 'guide/ngmodules/overview', - }, - { - label: 'JS Modules vs NgModules', - path: 'guide/ngmodules/vs-jsmodule', - contentPath: 'guide/ngmodules/vs-jsmodule', - }, - { - label: 'Launching your app with a root module', - path: 'guide/ngmodules/bootstrapping', - contentPath: 'guide/ngmodules/bootstrapping', - }, - { - label: 'Sharing NgModules', - path: 'guide/ngmodules/sharing', - contentPath: 'guide/ngmodules/sharing', - }, - { - label: 'Frequently used NgModules', - path: 'guide/ngmodules/frequent', - contentPath: 'guide/ngmodules/frequent', - }, - { - label: 'Feature modules', - path: 'guide/ngmodules/feature-modules', - contentPath: 'guide/ngmodules/feature-modules', - }, - { - label: 'Types of feature modules', - path: 'guide/ngmodules/module-types', - contentPath: 'guide/ngmodules/module-types', - }, - { - label: 'Providing dependencies', - path: 'guide/ngmodules/providers', - contentPath: 'guide/ngmodules/providers', - }, - { - label: 'Singleton services', - path: 'guide/ngmodules/singleton-services', - contentPath: 'guide/ngmodules/singleton-services', - }, - { - label: 'Lazy-loading feature modules', - path: 'guide/ngmodules/lazy-loading', - contentPath: 'guide/ngmodules/lazy-loading', - }, - { - label: 'NgModule API', - path: 'guide/ngmodules/api', - contentPath: 'guide/ngmodules/api', - }, - { - label: 'NgModule FAQs', - path: 'guide/ngmodules/faq', - contentPath: 'guide/ngmodules/faq', - }, - ], + label: 'Signal queries', + path: 'reference/migrations/signal-queries', + contentPath: 'reference/migrations/signal-queries', }, ], }, diff --git a/adev-ja/src/content/best-practices/style-guide.en.md b/adev-ja/src/content/best-practices/style-guide.en.md index 4d80ceaf4..184899457 100644 --- a/adev-ja/src/content/best-practices/style-guide.en.md +++ b/adev-ja/src/content/best-practices/style-guide.en.md @@ -293,8 +293,8 @@ Provides a consistent way to quickly identify and reference pipes. | Symbol name | File name | |:--- |:--- | -| @Pipe({ standalone: true, name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
| ellipsis.pipe.ts | -| @Pipe({ standalone: true, name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | +| @Pipe({ name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
| ellipsis.pipe.ts | +| @Pipe({ name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | ### Unit test file names diff --git a/adev-ja/src/content/best-practices/style-guide.md b/adev-ja/src/content/best-practices/style-guide.md index 9c8ea3fd9..3ebc15bd3 100644 --- a/adev-ja/src/content/best-practices/style-guide.md +++ b/adev-ja/src/content/best-practices/style-guide.md @@ -293,8 +293,8 @@ Angular の HTML パーサーは大文字小文字を区別し、アッパーキ | シンボル名 | ファイル名 | |:--- |:--- | -| @Pipe({ standalone: true, name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
| ellipsis.pipe.ts | -| @Pipe({ standalone: true, name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | +| @Pipe({ name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
| ellipsis.pipe.ts | +| @Pipe({ name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | ### ユニットテストファイル名 diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md new file mode 100644 index 000000000..cfe278e81 --- /dev/null +++ b/adev-ja/src/content/ecosystem/rxjs-interop/output-interop.md @@ -0,0 +1,52 @@ +# RxJS interop with component and dirctive outputs + +IMPORTANT: The RxJS Interop package is available for [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. + +Tip: This guide assumes you're familiar with [component and directive outputs](guide/components/outputs). + +The `@angular/rxjs-interop` package offers two APIs related to component and directive outputs. + +## Creating an output based on an RxJs Observable + +The `outputFromObservable` lets you create a component or directive output that emits based on an RxJS observable: + + +import {Directive} from '@angular/core'; +import {outputFromObservable} from '@angular/core/rxjs-interop'; + +@Directive({/*...*/}) +class Draggable { + pointerMoves$: Observable = listenToPointerMoves(); + + // Whenever `pointerMoves$` emits, the `pointerMove` event fires. + pointerMove = outputFromObservable(this.pointerMoves$); +} + + +The `outputFromObservable` function has special meaning to the Angular compiler. **You may only call `outputFromObservable` in component and directive property initializers.** + +When you `subscribe` to the output, Angular automatically forwards the subscription to the underlying observable. Angular stops forwarding values when the component or directive is destroyed. + +HELPFUL: Consider using `output()` directly if you can emit values imperatively. + +## Creating an RxJS Observable from a component or directive output + +The `outputToObservable` function lets you create an RxJS observable from a component output. + + +import {outputToObservable} from '@angular/core/rxjs-interop'; + +@Component(/*...*/) +class CustomSlider { + valueChange = output(); +} + +// Instance reference to `CustomSlider`. +const slider: CustomSlider = createSlider(); + +outputToObservable(slider.valueChange) // Observable + .pipe(...) + .subscribe(...); + + +HELPFUL: Consider using the `subscribe` method on `OutputRef` directly if it meets your needs. diff --git a/adev-ja/src/content/guide/signals/rxjs-interop.en.md b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md similarity index 83% rename from adev-ja/src/content/guide/signals/rxjs-interop.en.md rename to adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md index 66132bc61..410507043 100644 --- a/adev-ja/src/content/guide/signals/rxjs-interop.en.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -1,10 +1,10 @@ -# RxJS Interop +# RxJS interop with Angular signals IMPORTANT: The RxJS Interop package is available for [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. -Angular's `@angular/core/rxjs-interop` package provides useful utilities to integrate [Angular Signals](guide/signals) with RxJS Observables. +The `@angular/rxjs-interop` package offers APIs that help you integrate RxJS and Angular signals. -## `toSignal` +## Create a signal from an RxJs Observable with `toSignal` Use the `toSignal` function to create a signal which tracks the value of an Observable. It behaves similarly to the `async` pipe in templates, but is more flexible and can be used anywhere in an application. @@ -63,7 +63,7 @@ If an Observable used in `toSignal` produces an error, that error is thrown when If an Observable used in `toSignal` completes, the signal continues to return the most recently emitted value before completion. -## `toObservable` +## Create an RxJS Observale from a signal with `toObservable` Use the `toObservable` utility to create an `Observable` which tracks the value of a signal. The signal's value is monitored with an `effect` which emits the value to the Observable when it changes. @@ -103,29 +103,3 @@ mySignal.set(3); ``` Here, only the last value (3) will be logged. - -### `outputFromObservable` - -`outputFromObservable(...)` declares an Angular output that emits values based on an RxJS observable. - -```ts -class MyDir { - nameChange$ = new Observable(/* ... */); - nameChange = outputFromObservable(this.nameChange$); // OutputRef -} -``` - -See more details in the [output() API guide](/guide/components/output-fn). - -### `outputToObservable` - -`outputToObservable(...)` converts an Angular output to an observable. -This allows you to integrate Angular outputs conveniently into RxJS streams. - -```ts -outputToObservable(myComp.instance.onNameChange) - .pipe(...) - .subscribe(...) -``` - -See more details in the [output() API guide](/guide/components/output-fn). diff --git a/adev-ja/src/content/ecosystem/service-workers/config.md b/adev-ja/src/content/ecosystem/service-workers/config.md index da5239f6a..a4d16eea2 100644 --- a/adev-ja/src/content/ecosystem/service-workers/config.md +++ b/adev-ja/src/content/ecosystem/service-workers/config.md @@ -197,6 +197,7 @@ export interface DataGroup { maxSize: number; maxAge: string; timeout?: string; + refreshAhead?: string; strategy?: 'freshness' | 'performance'; }; cacheQueryOptions?: { @@ -270,6 +271,22 @@ The network timeout is how long the Angular service worker waits for the network For example, the string `5s30u` translates to five seconds and 30 milliseconds of network timeout. + +##### `refreshAhead` + +This duration string specifies the time ahead of the expiration of a cached resource when the Angular service worker should proactively attempt to refresh the resource from the network. +The `refreshAhead` duration is an optional configuration that determines how much time before the expiration of a cached response the service worker should initiate a request to refresh the resource from the network. + +| Suffixes | Details | +|:--- |:--- | +| `d` | Days | +| `h` | Hours | +| `m` | Minutes | +| `s` | Seconds | +| `u` | Milliseconds | + +For example, the string `1h30m` translates to one hour and 30 minutes ahead of the expiration time. + ##### `strategy` The Angular service worker can use either of two caching strategies for data resources. @@ -330,7 +347,7 @@ A request is considered to be a navigation request if: * The URL must not contain a file extension (that is, a `.`) in the last path segment * The URL must not contain `__` -HELPFUL: To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigationrequeststrategy) section. +HELPFUL: To configure whether navigation requests are sent through to the network or not, see the [navigationRequestStrategy](#navigationrequeststrategy) section and [applicationMaxAge](#application-max-age) sections. #### Matching navigation request URLs @@ -374,3 +391,7 @@ This optional property enables you to configure how the service worker handles n | `'freshness'` | Passes the requests through to the network and falls back to the `performance` behavior when offline. This value is useful when the server redirects the navigation requests elsewhere using a `3xx` HTTP redirect status code. Reasons for using this value include:
  • Redirecting to an authentication website when authentication is not handled by the application
  • Redirecting specific URLs to avoid breaking existing links/bookmarks after a website redesign
  • Redirecting to a different website, such as a server-status page, while a page is temporarily down
| IMPORTANT: The `freshness` strategy usually results in more requests sent to the server, which can increase response latency. It is recommended that you use the default performance strategy whenever possible. + +### `applicationMaxAge` + +This optional property enables you to configure how long the service worker will cache any requests. Within the `maxAge`, files will be served from cache. Beyond it, all requests will only be served from the network, including asset and data requests. 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 c26d74939..5aa2f664d 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 @@ -19,7 +19,7 @@ You provide Angular-specific information for a component by adding a `@Component export class ProfilePhoto { } -For full details on writing Angular templates, see the [Templates guide](guide/templates). +For full details on writing Angular templates, including data binding, event handling, and control flow, see the [Templates guide](guide/templates). The object passed to the `@Component` decorator is called the component's **metadata**. This includes the `selector`, `template`, and other properties described throughout this guide. @@ -53,6 +53,29 @@ Both `templateUrl` and `styleUrl` are relative to the directory in which the com ## Using components +### Imports in the `@Component` decorator + +To use a component, [directive](guide/directives), or [pipe](guide/templates/pipes), you must add +it to the `imports` array in the `@Component` decorator: + +```angular-ts +import {ProfilePhoto} from './profile-photo'; + +@Component({ + // Import the `ProfilePhoto` component in + // order to use it in this component's template. + imports: [ProfilePhoto], + /* ... */ +}) +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 `NgMdoule` in which the component is defined. See the full [`NgModule` guide](guide/ngmodules) for details. + +Important: In Angular versions before 19.0.0, the `standalone` option defaults to `false`. + +### Showing components in a template + Every component defines a [CSS selector](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors): @@ -65,21 +88,21 @@ export class ProfilePhoto { } See [Component Selectors](guide/components/selectors) for details about which types of selectors Angular supports and guidance on choosing a selector. -You use a component by creating a matching HTML element in the template of _other_ components: +You show a component by creating a matching HTML element in the template of _other_ components: - + @Component({ - selector: 'user-profile', - template: ` - - `, - ..., + selector: 'profile-photo', +}) +export class ProfilePhoto { } + +@Component({ + imports: [ProfilePhoto], + template: `` }) export class UserProfile { } -See [Importing and using components](guide/components/importing) for details on how to reference and use other components in your template. - Angular creates an instance of the component for every matching HTML element it encounters. The DOM element that matches a component's selector is referred to as that component's **host element**. The contents of a component's template are rendered inside its host element. The DOM rendered by a component, corresponding to that component's template, is called that diff --git a/adev-ja/src/content/guide/components/anatomy-of-components.md b/adev-ja/src/content/guide/components/anatomy-of-components.md index 6c2e4fbb1..9b2b3522e 100644 --- a/adev-ja/src/content/guide/components/anatomy-of-components.md +++ b/adev-ja/src/content/guide/components/anatomy-of-components.md @@ -19,7 +19,7 @@ TypeScriptクラスの上部に `@Component` [デコレーター](https://www.ty export class ProfilePhoto { } -Angularテンプレートの書き方については、[テンプレートガイド](guide/templates)を参照してください。 +データバインディング、イベント処理、制御フローなど、Angularテンプレート作成に関する詳細は、[テンプレートガイド](guide/templates)を参照してください。 `@Component` デコレーターに渡されるオブジェクトは、コンポーネントの**メタデータ**と呼ばれます。これには、このガイドで説明されている `selector`、`template`、その他のプロパティが含まれています。 @@ -36,7 +36,7 @@ export class ProfilePhoto { } デフォルトでは、コンポーネントのスタイルは、そのコンポーネントのテンプレートで定義された要素にのみ影響を与えます。Angularのスタイリングアプローチの詳細については、[コンポーネントのスタイリング](guide/components/styling)を参照してください。 -代わりに、テンプレートとスタイルを別々のファイルに書くこともできます。 +テンプレートとスタイルを別々のファイルに記述することもできます。 @Component({ @@ -47,12 +47,35 @@ export class ProfilePhoto { } export class ProfilePhoto { } -これにより、プロジェクト内の*プレゼンテーション*と*動作*の懸念を分離できます。プロジェクト全体で一貫したアプローチを選択することも、コンポーネントごとに使用するものを決定できます。 +これにより、プロジェクト内の_表示_と_動作_の懸念事項を分離できます。プロジェクト全体に対して1つのアプローチを選択するか、コンポーネントごとにどちらを使用するかを決定できます。 -`templateUrl` と `styleUrl` はどちらも、コンポーネントが存在するディレクトリを基準とした相対パスです。 +`templateUrl`と`styleUrl`の両方は、コンポーネントが存在するディレクトリを基準とした相対パスです。 ## コンポーネントの使用 +### `@Component`デコレーターでのインポート + +コンポーネント、[ディレクティブ](guide/directives)、または[パイプ](guide/templates/pipes)を使用するには、 +`@Component`デコレーターの`imports`配列に追加する必要があります。 + +```angular-ts +import {ProfilePhoto} from './profile-photo'; + +@Component({ + // このコンポーネントのテンプレートで使用するために、 + // `ProfilePhoto`コンポーネントをインポートします。 + imports: [ProfilePhoto], + /* ... */ +}) +export class UserProfile { } +``` + +デフォルトでは、Angularコンポーネントは*スタンドアロン*です。つまり、他のコンポーネントの`imports`配列に直接追加できます。以前のバージョンのAngularで作成されたコンポーネントは、代わりに`@Component`デコレーターで`standalone: false`を指定している場合があります。これらのコンポーネントの場合、代わりにコンポーネントが定義されている`NgModule`をインポートします。詳細は、完全な[`NgModule`ガイド](guide/ngmodules)を参照してください。 + +IMPORTANT: 19.0.0より前のAngularバージョンでは、`standalone`オプションはデフォルトで`false`です。 + +### テンプレートでのコンポーネントの表示 + すべてのコンポーネントは[CSSセレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors)を定義します。 @@ -63,29 +86,29 @@ export class ProfilePhoto { } export class ProfilePhoto { } -Angularがサポートするセレクターの種類と、セレクターを選択する際のガイダンスについては、[コンポーネントセレクター](guide/components/selectors)を参照してください。 +Angularがサポートするセレクターの種類と、セレクターの選択に関するガイダンスについては、[コンポーネントセレクター](guide/components/selectors)を参照してください。 -他のコンポーネントのテンプレートに一致するHTML要素を作成することで、コンポーネントを使用します。 +_他の_コンポーネントのテンプレートで一致するHTML要素を作成することで、コンポーネントを表示します。 - + @Component({ - selector: 'user-profile', - template: ` - - `, - ..., + selector: 'profile-photo', +}) +export class ProfilePhoto { } + +@Component({ + imports: [ProfilePhoto], + template: `` }) export class UserProfile { } -テンプレートで他のコンポーネントを参照して使用する方法は、[コンポーネントのインポートと使用](guide/components/importing)を参照してください。 - -Angularは、遭遇した一致するHTML要素ごとに、コンポーネントのインスタンスを作成します。コンポーネントのセレクターに一致するDOM要素は、そのコンポーネントの**ホスト要素**と呼ばれます。コンポーネントのテンプレートの内容は、そのホスト要素内にレンダリングされます。 +Angularは、遭遇する一致するHTML要素ごとにコンポーネントのインスタンスを作成します。コンポーネントのセレクターと一致するDOM要素は、そのコンポーネントの**ホスト要素**と呼ばれます。コンポーネントのテンプレートの内容はそのホスト要素内にレンダリングされます。 -コンポーネントによってレンダリングされたDOM (コンポーネントのテンプレートに対応)は、 +コンポーネントによってレンダリングされるDOM(コンポーネントのテンプレートに対応)は、 そのコンポーネントの**ビュー**と呼ばれます。 -このようにコンポーネントを組み合わせることで、**Angular アプリケーションはコンポーネントのツリーとして考えることができます**。 +このようにコンポーネントを構成することで、**Angularアプリケーションをコンポーネントのツリーと考えることができます**。 ```mermaid flowchart TD @@ -99,4 +122,4 @@ flowchart TD ``` -このツリー構造は、[依存性の注入](guide/di)や[子クエリ](guide/components/queries)など、その他のAngularの概念を理解する上で重要です。 +このツリー構造は、[依存性の注入](guide/di)や[子クエリ](guide/components/queries)など、他のいくつかのAngularの概念を理解する上で重要です。 diff --git a/adev-ja/src/content/guide/components/content-projection.en.md b/adev-ja/src/content/guide/components/content-projection.en.md index a91950897..59895427b 100644 --- a/adev-ja/src/content/guide/components/content-projection.en.md +++ b/adev-ja/src/content/guide/components/content-projection.en.md @@ -148,6 +148,38 @@ did not match a `select` attribute: If a component does not include an `` placeholder without a `select` attribute, any elements that don't match one of the component's placeholders do not render into the DOM. +## Fallback content + +Angular can show *fallback content* for a component's `` placeholder if that component doesn't have any matching child content. You can specify fallback content by adding child content to the `` element itself. + +```angular-html + +
+ Default Title +
+ Default Body +
+``` + +```angular-html + + + Hello + + +``` + +```angular-html + + +
+ Hello +
+ Default Body +
+
+``` + ## Aliasing content for projection Angular supports a special attribute, `ngProjectAs`, that allows you to specify a CSS selector on diff --git a/adev-ja/src/content/guide/components/content-projection.md b/adev-ja/src/content/guide/components/content-projection.md index 2014599a0..58b1078c3 100644 --- a/adev-ja/src/content/guide/components/content-projection.md +++ b/adev-ja/src/content/guide/components/content-projection.md @@ -13,7 +13,7 @@ Tip: このガイドは、すでに [基本概念のガイド](essentials) を export class CustomCard {/* ... */} ``` -**`` 要素は、コンテンツを配置する場所を示すプレースホルダーとして使用できます。**: +**``要素は、コンテンツを配置する場所を示すプレースホルダーとして使用できます。**: ```angular-ts @Component({ @@ -23,12 +23,12 @@ export class CustomCard {/* ... */} export class CustomCard {/* ... */} ``` -Tip: `` は、 -[ネイティブの `` 要素](https://developer.mozilla.org/docs/Web/HTML/Element/slot) と似ていますが、 +Tip: ``は、 +[ネイティブの``要素](https://developer.mozilla.org/docs/Web/HTML/Element/slot)と似ていますが、 Angular固有の機能も備えています。 -`` を使用したコンポーネントを使用する場合、 -コンポーネントホスト要素の子要素はすべて、その `` の場所にレンダリング、あるいは **投影されます**: +``を使用したコンポーネントを使用する場合、 +コンポーネントホスト要素の子要素はすべて、その``の場所にレンダリング、あるいは**投影されます**: ```angular-ts // コンポーネントソース @@ -59,24 +59,25 @@ export class CustomCard {/* ... */} ``` -Angularは、このように渡されるコンポーネントの子要素を、そのコンポーネントの **コンテンツ** と呼びます。 -これはコンポーネントの **ビュー** とは異なります。 +Angularは、このように渡されるコンポーネントの子要素を、そのコンポーネントの**コンテンツ**と呼びます。 +これはコンポーネントの**ビュー**とは異なります。 ビューは、コンポーネントのテンプレートで定義された要素を指します。 -**`` 要素は、コンポーネントでも DOM 要素でもありません。** +**``要素は、コンポーネントでもDOM要素でもありません。** 代わりに、コンテンツをレンダリングする場所をAngularに伝える特別なプレースホルダーです。 -Angularのコンパイラは、ビルド時にすべての `` 要素を処理します。 -実行時に `` を挿入、削除、または変更できません。ディレクティブ、スタイル、または任意の属性を `` に追加できません。 +Angularのコンパイラは、ビルド時にすべての``要素を処理します。 +実行時に``の挿入や削除、変更はできません。ディレクティブやスタイル、任意の属性も``には追加できません。 -`` を `@if`、`@for`、または `@switch` で条件付きで含めるべきではありません。 -Angularは常ににレンダリングされたコンテンツのDOMノードをインスタンス化して作成します。 -その `` プレースホルダが非表示であってもです。コンポーネントコンテンツの条件付きレンダリングについては -[テンプレートフラグメント](api/core/ng-template) を参照してください。 +``を`@if`、`@for`、または`@switch`によって条件付きで含めるべきではありません。 +Angularは常にレンダリングされたコンテンツのDOMノードをインスタンス化して作成します。 +その``プレースホルダが非表示であってもです。コンポーネントコンテンツの条件付きレンダリングについては +[テンプレートフラグメント](api/core/ng-template)を参照してください。 ## 複数のコンテンツプレースホルダー -Angularは、CSSセレクターに基づいて、複数の異なる要素を異なる `` プレースホルダーへの投影をサポートしています。 -上記のカードの例を拡張して、`select` 属性を使用して、カードのタイトルと本文の2つのプレースホルダーを作成できます。 +Angularは、CSSセレクターに基づいて、複数の異なる要素を異なる``プレースホルダーへの投影をサポートしています。 +上記のカードの例を拡張して、`select`属性を使用して、 +カードのタイトルと本文の2つのプレースホルダーを作成できます。 ```angular-html @@ -106,19 +107,19 @@ Angularは、CSSセレクターに基づいて、複数の異なる要素を異 ``` -`` プレースホルダーは、 -[コンポーネントセレクター](guide/components/selectors) と同じCSSセレクターをサポートしています。 +``プレースホルダーは、 +[コンポーネントセレクター](guide/components/selectors)と同じCSSセレクターをサポートしています。 -`select` 属性を持つ `` プレースホルダーを1つ以上、 -`select` 属性を持たない `` プレースホルダーを1つ含める場合、 -後者は `select` 属性に一致しなかったすべての要素をキャプチャします。 +`select`属性を持つ``プレースホルダーを1つ以上、 +`select`属性を持たない``プレースホルダーを1つ含める場合、 +後者は`select`属性に一致しなかったすべての要素をキャプチャします。 ```angular-html
- +
``` @@ -144,14 +145,46 @@ Angularは、CSSセレクターに基づいて、複数の異なる要素を異 ``` -コンポーネントに `select` 属性を持たない `` プレースホルダーが含まれていない場合、 +コンポーネントに`select`属性を持たない``プレースホルダーが含まれていない場合、 コンポーネントのいずれかのプレースホルダーに一致しない要素はDOMにレンダリングされません。 +## フォールバックコンテンツ + +Angularは、コンポーネントの``プレースホルダーに一致する子コンテンツがない場合、コンポーネントの``プレースホルダーに*フォールバックコンテンツ*を表示できます。``要素自体に子コンテンツを追加することで、フォールバックコンテンツを指定できます。 + +```angular-html + +
+ Default Title +
+ Default Body +
+``` + +```angular-html + + + Hello + + +``` + +```angular-html + + +
+ Hello +
+ Default Body +
+
+``` + ## 投影のためのコンテンツのエイリアシング -Angularは、任意の要素にCSSセレクターを指定できる特殊な属性 `ngProjectAs` をサポートしています。 -`ngProjectAs` を持つ要素が `` プレースホルダーに対してチェックされると、 -Angularは要素のIDではなく `ngProjectAs` の値と比較します。 +Angularは、任意の要素にCSSセレクターを指定できる特殊な属性`ngProjectAs`をサポートしています。 +`ngProjectAs`を持つ要素が``プレースホルダーに対してチェックされると、 +Angularは要素のIDではなく`ngProjectAs`の値と比較します。 ```angular-html @@ -182,4 +215,4 @@ Angularは要素のIDではなく `ngProjectAs` の値と比較します。 ``` -`ngProjectAs` は静的な値のみをサポートし、動的な式にはバインドできません。 +`ngProjectAs`は静的な値のみをサポートし、動的な式にはバインドできません。 diff --git a/adev-ja/src/content/guide/components/importing.en.md b/adev-ja/src/content/guide/components/importing.en.md deleted file mode 100644 index 7978df771..000000000 --- a/adev-ja/src/content/guide/components/importing.en.md +++ /dev/null @@ -1,35 +0,0 @@ -# Importing and using components - -Tip: This guide assumes you've already read the [Essentials Guide](essentials). Read that first if you're new to Angular. - -Angular supports two ways of making a component available to other components: as a standalone component or in an `NgModule`. - -## Standalone components - -A **standalone component** is a component that sets `standalone: true` in its component metadata. -Standalone components directly import other components, directives, and pipes used in their -templates: - - -@Component({ - standalone: true, - selector: 'profile-photo', -}) -export class ProfilePhoto { } - -@Component({ - standalone: true, - imports: [ProfilePhoto], - template: `` -}) -export class UserProfile { } - - -Standalone components are directly importable into other standalone components. - -The Angular team recommends using standalone components for all new development. - -## NgModules - -Angular code that predates standalone components uses `NgModule` as a mechanism for importing and -using other components. See the full [`NgModule` guide](guide/ngmodules) for details. diff --git a/adev-ja/src/content/guide/components/importing.md b/adev-ja/src/content/guide/components/importing.md deleted file mode 100644 index 3587bbf2d..000000000 --- a/adev-ja/src/content/guide/components/importing.md +++ /dev/null @@ -1,35 +0,0 @@ -# コンポーネントのインポートと使用 - -Tip: このガイドでは、すでに[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 - -Angularでは、他のコンポーネントでコンポーネントを使用可能にするために、2つの方法がサポートされています。スタンドアロンコンポーネントとして、または `NgModule` で。 - -## スタンドアロンコンポーネント - -**スタンドアロンコンポーネント** は、コンポーネントメタデータで `standalone: true` を設定したコンポーネントです。 -スタンドアロンコンポーネントは、テンプレートで使用されている他のコンポーネント、 -ディレクティブ、パイプを直接インポートします。 - - -@Component({ - standalone: true, - selector: 'profile-photo', -}) -export class ProfilePhoto { } - -@Component({ - standalone: true, - imports:[ProfilePhoto], - template: `` -}) -export class UserProfile { } - - -スタンドアロンコンポーネントは、他のスタンドアロンコンポーネントに直接インポートできます。 - -Angularチームでは、新規開発にはスタンドアロンコンポーネントを使用することを推奨しています。 - -## NgModules - -スタンドアロンコンポーネントが導入される前のAngularコードでは、`NgModule` を使用して、他のコンポーネントをインポートし、使用していました。 -詳細については、[`NgModule` ガイド](guide/ngmodules) を参照してください。 diff --git a/adev-ja/src/content/guide/components/inputs.en.md b/adev-ja/src/content/guide/components/inputs.en.md index 164c673ca..2ca91966c 100644 --- a/adev-ja/src/content/guide/components/inputs.en.md +++ b/adev-ja/src/content/guide/components/inputs.en.md @@ -4,12 +4,16 @@ Tip: This guide assumes you've already read the [Essentials Guide](essentials). Tip: If you're familiar with other web frameworks, input properties are similar to _props_. -When creating a component, you can mark specific class properties as **bindable** by adding the `@Input` decorator on the property: +When you use a component, you commonly want to pass some data to it. A component specifies the data that it accepts by declaring +**inputs**: - -@Component({...}) + +import {Component, input} from '@angular/core'; + +@Component({/*...*/}) export class CustomSlider { - @Input() value = 0; + // Declare an input named 'value' with a default value of zero. + value = input(0); } @@ -19,30 +23,74 @@ This lets you bind to the property in a template: ``` -Angular refers to properties marked with the `@Input` decorator as **inputs**. When using a component, you pass data to it by setting its inputs. +If an input has a default value, TypeScript infers the type from the default value: + +```typescript +@Component({/*...*/}) +export class CustomSlider { + // TypeScript infers that this input is a number, returning InputSignal. + value = input(0); +} +``` + +You can explicitly declare a type for the input by specifying a generic parameter to the function. + +If an input without a default value is not set, its value is `undefined`: + +```typescript +@Component({/*...*/}) +export class CustomSlider { + // Produces an InputSignal because `value` may not be set. + value = input(); +} +``` **Angular records inputs statically at compile-time**. Inputs cannot be added or removed at run-time. +The `input` function has special meaning to the Angular compiler. **You can exclusively call `input` in component and directive property initializers.** + When extending a component class, **inputs are inherited by the child class.** **Input names are case-sensitive.** -## Customizing inputs +## Reading inputs -The `@Input` decorator accepts a config object that lets you change the way that input works. +The `input` function returns an `InputSignal`. You can read the value by calling the signal: -### Required inputs + +import {Component, input} from '@angular/core'; -You can specify the `required` option to enforce that a given input must always have a value. +@Component({/*...*/}) +export class CustomSlider { + // Declare an input named 'value' with a default value of zero. + value = input(0); + + // Create a computed expression that reads the value input + label = computed(() => `The slider's value is ${this.value()}`); +} + + +Signals created by the `input` function are read-only. + +## Required inputs + +You can declare that an input is `required` by calling `input.required` instead of `input`: -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({required: true}) value = 0; + // Declare a required input named value. Returns an `InputSignal`. + value = input.required(); } -If you try to use a component without specifying all of its required inputs, Angular reports an error at build-time. +Angular enforces that required inputs _must_ be set when the component is used in a template. If you try to use a component without specifying all of its required inputs, Angular reports an error at build-time. + +Required inputs do not automatically include `undefined` in the generic parameter of the returned `InputSignal`. + +## Configuring inputs + +The `input` function accepts a config object as a second parameter that lets you change the way that input works. ### Input transforms @@ -51,13 +99,13 @@ You can specify a `transform` function to change the value of an input when it's @Component({ selector: 'custom-slider', - ... + /*...*/ }) export class CustomSlider { - @Input({transform: trimString}) label = ''; + label = input('', {transform: trimString}); } -function trimString(value: string | undefined) { +function trimString(value: string | undefined): string { return value?.trim() ?? ''; } @@ -72,41 +120,41 @@ The most common use-case for input transforms is to accept a wider range of valu **Input transform function must be statically analyzable at build-time.** You cannot set transform functions conditionally or as the result of an expression evaluation. -**Input transform functions should always be [pure functions](https://en.wikipedia.org/wiki/Pure_function).** Relying on state outside of the transform function can lead to unpredictable behavior. +**Input transform functions should always be [pure functions](https://en.wikipedia.org/wiki/Pure_function).** Relying on state outside the transform function can lead to unpredictable behavior. #### Type checking When you specify an input transform, the type of the transform function's parameter determines the types of values that can be set to the input in a template. -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({transform: appendPx}) widthPx: string = ''; + widthPx = input('', {transform: appendPx}); } -function appendPx(value: number) { +function appendPx(value: number): string { return `${value}px`; } -In the example above, the `widthPx` input accepts a `number` while the property on the class is a `string`. +In the example above, the `widthPx` input accepts a `number` while the `InputSignal` property returns a `string`. #### Built-in transformations Angular includes two built-in transform functions for the two most common scenarios: coercing values to boolean and numbers. -import {Component, Input, booleanAttribute, numberAttribute} from '@angular/core'; +import {Component, input, booleanAttribute, numberAttribute} from '@angular/core'; -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({transform: booleanAttribute}) disabled = false; - @Input({transform: numberAttribute}) number = 0; + disabled = input(false, {transform: booleanAttribute}); + value = input(0, {transform: numberAttribute}); } -`booleanAttribute` imitates the behavior of standard -HTML [boolean attributes](https://developer.mozilla.org/docs/Glossary/Boolean/HTML), where the _presence_ of the attribute indicates a "true" value. However, Angular's `booleanAttribute` treats the literal string `"false"` as the boolean `false`. +`booleanAttribute` imitates the behavior of standard HTML [boolean attributes](https://developer.mozilla.org/docs/Glossary/Boolean/HTML), where the +_presence_ of the attribute indicates a "true" value. However, Angular's `booleanAttribute` treats the literal string `"false"` as the boolean `false`. `numberAttribute` attempts to parse the given value to a number, producing `NaN` if parsing fails. @@ -115,9 +163,9 @@ HTML [boolean attributes](https://developer.mozilla.org/docs/Glossary/Boolean/HT You can specify the `alias` option to change the name of an input in templates. -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({alias: 'sliderValue'}) value = 0; + value = input(0, {alias: 'sliderValue'}); } @@ -129,11 +177,170 @@ This alias does not affect usage of the property in TypeScript code. While you should generally avoid aliasing inputs for components, this feature can be useful for renaming properties while preserving an alias for the original name or for avoiding collisions with the name of native DOM element properties. +## Model inputs + +**Model inputs** are a special type of input that enable a component to propagate new values back to its parent component. + +When creating a component, you can define a model input similarly to how you create a standard input. + +Both types of input allow someone to bind a value into the property. However, **model inputs allow the component author to write values into the property**. If the property is bound with a two-way binding, the new value propagates to that binding. + +```typescript +@Component({ /* ... */}) +export class CustomSlider { + // Define a model input named "value". + value = model(0); + + increment() { + // Update the model input with a new value, propagating the value to any bindings. + this.value.update(oldValue => oldValue + 10); + } +} + +@Component({ + /* ... */ + // Using the two-way binding syntax means that any changes to the slider's + // value automatically propagate back to the `volume` signal. + // Note that this binding uses the signal *instance*, not the signal value. + template: ``, +}) +export class MediaControls { + // Create a writable signal for the `volume` local state. + volume = signal(0); +} +``` + +In the above example, the `CustomSlider` can write values into its `value` model input, which then propagates those values back to the `volume` signal in `MediaControls`. This binding keeps the values of `value` and `volume` in sync. Notice that the binding passes the `volume` signal instance, not the _value_ of the signal. + +In other respects, model inputs work similarly to standard inputs. You can read the value by calling the signal function, including in reactive contexts like `computed` and `effect`. + +See [Two-way binding](guide/templates/two-way-binding) for more details on two-way binding in templates. + +### Two-way binding with plain properties + +You can bind a plain JavaScript property to a model input. + +```angular-ts +@Component({ + /* ... */ + // `value` is a model input. + // The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding + template: '', +}) +export class MediaControls { + protected volume = 0; +} +``` + +In the example above, the `CustomSlider` can write values into its `checked` model input, which then propagates those values back to the `isAdmin` property in `UserProfile`. This binding keeps that values of `checked` and `isAdmin` in sync. + +### Implicit `change` events + +When you declare a model input in a component or directive, Angular automatically creates a corresponding [output](guide/components/outputs) for that model. The output's name is the model input's name suffixed with "Change". + +```angular-ts +@Directive({ /* ... */ }) +export class CustomCheckbox { + // This automatically creates an output named "checkedChange". + // Can be subscribed to using `(checkedChange)="handler()"` in the template. + checked = model(false); +} +``` + +Angular emits this change event whenever you write a new value into the model input by calling its `set` or `update` methods. + +See [Custom events with outputs](guide/components/outputs) for more details on outputs. + +### Customizing model inputs + +You can mark a model input as required or provide an alias in the same way as a [standard input](guide/signals/inputs). + +Model inputs do not support input transforms. + +### When to use model inputs + +Use model inputs when you want a component to support two-way binding. This is typically appropriate when a component exists to modify a value based on user interaction. Most commonly, custom form controls, such as a date picker or combobox, should use model inputs for their primary value. + +## Choosing input names + +Avoid choosing input names that collide with properties on DOM elements like HTMLElement. Name collisions introduce confusion about whether the bound property belongs to the component or the DOM element. + +Avoid adding prefixes for component inputs like you would with component selectors. Since a given element can only host one component, any custom properties can be assumed to belong to the component. + +## Declaring inputs with the `@Input` decorator + +Tip: While the Angular team recommends using the signal-based `input` function for new projects, the original decorator-based `@Input` API remains fully supported. + +You can alternatively declare component inputs by adding the `@Input` decorator to a property: + + +@Component({...}) +export class CustomSlider { + @Input() value = 0; +} + + +Binding to an input is the same in both signal-based and decorator-based inputs: + +```angular-html + +``` + +### Customizing decorator-based inputs + +The `@Input` decorator accepts a config object that lets you change the way that input works. + +#### Required inputs + +You can specify the `required` option to enforce that a given input must always have a value. + + +@Component({...}) +export class CustomSlider { + @Input({required: true}) value = 0; +} + + +If you try to use a component without specifying all of its required inputs, Angular reports an error at build-time. + +#### Input transforms + +You can specify a `transform` function to change the value of an input when it's set by Angular. This transform function works identically to transform functions for signal-based inputs described above. + + +@Component({ + selector: 'custom-slider', + ... +}) +export class CustomSlider { + @Input({transform: trimString}) label = ''; +} + +function trimString(value: string | undefined) { return value?.trim() ?? ''; } + + +#### Input aliases + +You can specify the `alias` option to change the name of an input in templates. + + +@Component({...}) +export class CustomSlider { + @Input({alias: 'sliderValue'}) value = 0; +} + + +```angular-html + +``` + The `@Input` decorator also accepts the alias as its first parameter in place of the config object. -## Inputs with getters and setters +Input aliases work the same way as for signal-based inputs described above. + +### Inputs with getters and setters -A property implemented with a getter and setter can be an input: +When using decorator-based inputs, a property implemented with a getter and setter can be an input: export class CustomSlider { @@ -142,12 +349,9 @@ export class CustomSlider { return this.internalValue; } - set value(newValue: number) { - this.internalValue = newValue; - } +set value(newValue: number) { this.internalValue = newValue; } - private internalValue = 0; -} +private internalValue = 0; } You can even create a _write-only_ input by only defining a public setter: @@ -159,11 +363,10 @@ export class CustomSlider { this.internalValue = newValue; } - private internalValue = 0; -} +private internalValue = 0; } -Prefer using input transforms instead of getters and setters if possible. +**Prefer using input transforms instead of getters and setters** if possible. Avoid complex or costly getters and setters. Angular may invoke an input's setter multiple times, which may negatively impact application performance if the setter performs any costly behaviors, such as DOM manipulation. @@ -190,9 +393,3 @@ You can additionally specify an input alias in the `inputs` list by putting the }) export class CustomSlider extends BaseSlider { }
- -## Choosing input names - -Avoid choosing input names that collide with properties on DOM elements like HTMLElement. Name collisions introduce confusion about whether the bound property belongs to the component or the DOM element. - -Avoid adding prefixes for component inputs like you would with component selectors. Since a given element can only host one component, any custom properties can be assumed to belong to the component. diff --git a/adev-ja/src/content/guide/components/inputs.md b/adev-ja/src/content/guide/components/inputs.md index 26fec0277..e11b470f3 100644 --- a/adev-ja/src/content/guide/components/inputs.md +++ b/adev-ja/src/content/guide/components/inputs.md @@ -1,63 +1,111 @@ -# 入力プロパティでデータを受け取る +# 入力プロパティによるデータの受け取り -Tip: このガイドは、[基本概念のガイド](essentials) を既読していることを前提としています。Angularを初めて使う場合は、まずそちらをお読みください。 +TIP: このガイドは、既に[基本概念ガイド](essentials)を読んでいることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 -Tip: 他のウェブフレームワークに精通している場合は、入力プロパティは*props*に似ています。 +TIP: 他のウェブフレームワークに精通している場合は、入力プロパティは_props_に似ています。 -コンポーネントを作成する際、特定のクラスプロパティに `@Input` デコレーターを追加することで、そのプロパティを **バインド可能** にできます。 +コンポーネントを使用する際に、一般的にいくつかのデータを渡したいことがあります。 +コンポーネントは、**入力**を宣言することで、受け入れるデータを指定します。 - -@Component({...}) + +import {Component, input} from '@angular/core'; + +@Component({/*...*/}) export class CustomSlider { - @Input() value = 0; + // Declare an input named 'value' with a default value of zero. + value = input(0); } -これにより、テンプレートでプロパティにバインドできます。 +これにより、テンプレートのプロパティにバインドできます。 ```angular-html ``` -Angularは、`@Input` デコレーターでマークされたプロパティを **入力** と呼びます。コンポーネントを使用する際、入力に値を設定することでコンポーネントにデータを渡します。 +入力にデフォルト値がある場合、TypeScriptはデフォルト値から型を推論します。 + +```typescript +@Component({/*...*/}) +export class CustomSlider { + // TypeScriptは、この入力が数値であると推論し、InputSignalを返します。 + value = input(0); +} +``` + +関数のジェネリックパラメーターを指定することで、入力の型を明示的に宣言できます。 -**Angular はコンパイル時に静的に入力を記録します。** 入力は、実行時に追加または削除はできません。 +デフォルト値のない入力が設定されていない場合、その値は`undefined`になります。 + +```typescript +@Component({/*...*/}) +export class CustomSlider { + // `value`は設定されない可能性があるため、InputSignalを生成します。 + value = input(); +} +``` + +**Angularはコンパイル時に静的に入力を記録します。**実行時に入力の追加や削除はできません。 + +`input`関数は、Angularコンパイラにとって特別な意味を持ちます。**`input`は、コンポーネントとディレクティブのプロパティ初期化子でのみ呼び出すことができます。** コンポーネントクラスを拡張する場合、**入力は子クラスによって継承されます。** -**入力名は、大文字と小文字が区別されます。** +**入力名は大文字と小文字が区別されます。** + +## 入力の読み取り + +`input`関数は`InputSignal`を返します。シグナルを呼び出すことで値を読み取ることができます。 -## 入力のカスタマイズ + +import {Component, input} from '@angular/core'; -`@Input` デコレーターは、入力の動作を変更できる設定オブジェクトを受け取ります。 +@Component({/*...*/}) +export class CustomSlider { + // Declare an input named 'value' with a default value of zero. + value = input(0); + + // Create a computed expression that reads the value input + label = computed(() => `The slider's value is ${this.value()}`); +} + + +`input`関数によって作成されたシグナルは読み取り専用です。 -### 必須入力 +## 必須入力 -`required` オプションを指定することで、特定の入力に常に値が設定されていることを強制できます。 +`input`の代わりに`input.required`を呼び出すことで、入力が`required`であることを宣言できます。 -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({required: true}) value = 0; + // Declare a required input named value. Returns an `InputSignal`. + value = input.required(); } -必須入力をすべて指定せずにコンポーネントを使用しようとすると、Angularはビルド時にエラーを報告します。 +Angularは、テンプレートでコンポーネントを使用する際に、必須入力が_必ず_設定されていることを強制します。すべての必須入力を指定せずにコンポーネントを使用しようとすると、Angularはビルド時にエラーを報告します。 + +必須入力は、返される`InputSignal`のジェネリックパラメーターに`undefined`を自動的に含めません。 + +## 入力の構成 + +`input`関数は、入力の動作を変更できる2番目のパラメーターとしてconfigオブジェクトを受け取ります。 ### 入力変換 -`transform` 関数を指定することで、Angularによって入力値が設定されるときに、入力値を変更できます。 +入力がAngularによって設定されるときに、その値を変更する`transform`関数を指定できます。 @Component({ selector: 'custom-slider', - ... + /*...*/ }) export class CustomSlider { - @Input({transform: trimString}) label = ''; + label = input('', {transform: trimString}); } -function trimString(value: string | undefined) { +function trimString(value: string | undefined): string { return value?.trim() ?? ''; } @@ -66,58 +114,58 @@ function trimString(value: string | undefined) { ``` -上記の例では、`systemVolume` の値が変更されるたびに、Angularは `trimString` を実行し、`label` に結果を設定します。 +上記の例では、`systemVolume`の値が変更されるたびに、Angularは`trimString`を実行し、`label`に結果を設定します。 -入力変換の最も一般的なユースケースは、テンプレートでより幅広い値の種類(多くの場合、`null` や `undefined` を含む)を受け入れることです。 +入力変換の最も一般的なユースケースは、テンプレートでより広い範囲の値型を受け入れることであり、多くの場合`null`と`undefined`を含みます。 -**入力変換の関数は、ビルド時に静的に解析可能である必要があります。** 変換関数を条件付きで設定したり、式評価の結果として設定したりできません。 +**入力変換関数は、ビルド時に静的に分析可能でなければなりません。**条件付きで、または式の評価の結果として、変換関数は設定できません。 -**入力変換の関数は、常に [純粋関数](https://en.wikipedia.org/wiki/Pure_function) である必要があります。** 変換関数の外の状態に依存すると、予期しない動作につながる可能性があります。 +**入力変換関数は常に[純粋関数](https://en.wikipedia.org/wiki/Pure_function)でなければなりません。**変換関数外の状態に依存すると、予期せぬ動作につながる可能性があります。 #### 型チェック -入力変換を指定すると、変換関数のパラメーターの型によって、テンプレートで入力に設定できる値の型が決まります。 +入力変換を指定すると、変換関数の引数の型によって、テンプレートで入力に設定できる値の型が決まります。 -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({transform: appendPx}) widthPx: string = ''; + widthPx = input('', {transform: appendPx}); } -function appendPx(value: number) { +function appendPx(value: number): string { return `${value}px`; } -上記の例では、`widthPx` 入力値は `number` を受け取りますが、クラスのプロパティは `string` です。 +上記の例では、`widthPx`入力は`number`を受け入れる一方、`InputSignal`プロパティは`string`を返します。 #### 組み込み変換 -Angularには、最も一般的な2つのシナリオに対応する2つの組み込み変換関数が含まれています。ブール値と数値への値の強制変換です。 +Angularには、最も一般的な2つのシナリオのための2つの組み込み変換関数が含まれています。ブール値と数値への値の強制変換です。 -import {Component, Input, booleanAttribute, numberAttribute} from '@angular/core'; +import {Component, input, booleanAttribute, numberAttribute} from '@angular/core'; -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({transform: booleanAttribute}) disabled = false; - @Input({transform: numberAttribute}) number = 0; + disabled = input(false, {transform: booleanAttribute}); + value = input(0, {transform: numberAttribute}); } -`booleanAttribute` は、標準のHTML [ブール属性](https://developer.mozilla.org/docs/Glossary/Boolean/HTML) の動作を模倣します。 -属性の存在は "true" 値を示します。ただし、Angularの `booleanAttribute` は、リテラル文字列 `"false"` をブール値 `false` として扱います。 +`booleanAttribute`は、属性の_存在_が「true」値を示す標準的なHTML[ブール属性](https://developer.mozilla.org/docs/Glossary/Boolean/HTML)の動作を模倣します。 +ただし、Angularの`booleanAttribute`は、リテラル文字列`"false"`をブール値`false`として扱います。 -`numberAttribute` は、指定された値を数値に解析しようとします。解析に失敗すると、`NaN` を生成します。 +`numberAttribute`は、与えられた値を数値として解析しようと試み、解析に失敗した場合は`NaN`を生成します。 ### 入力エイリアス -`alias` オプションを指定することで、テンプレートでの入力の名前を変更できます。 +`alias`オプションを指定して、テンプレートでの入力の名前を変更できます。 -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Input({alias: 'sliderValue'}) value = 0; + value = input(0, {alias: 'sliderValue'}); } @@ -127,13 +175,172 @@ export class CustomSlider { このエイリアスは、TypeScriptコードでのプロパティの使用には影響しません。 -一般的にコンポーネントの入力にエイリアスを使用することは避けるべきですが、この機能はプロパティの名前を変更しながら元の名前のエイリアスを保持する場合や、ネイティブDOM要素プロパティの名前との衝突を避ける場合に役立ちます。 +一般的にコンポーネントの入力をエイリアス化することは避けるべきですが、この機能は、元の名前のエイリアスを保持しながらプロパティの名前を変更したり、ネイティブDOM要素プロパティの名前との競合を回避したりする場合に役立ちます。 + +## モデル入力 + +**モデル入力**は、コンポーネントが新しい値を親コンポーネントに伝播できるようにする特殊なタイプの入力です。 + +コンポーネントを作成する際に、通常の入力と同様にモデル入力を定義できます。 + +どちらの種類の入力も、値をプロパティにバインドすることを可能にします。しかし、**モデル入力は、コンポーネントの作者がプロパティに値を書き込むことを可能にします。**プロパティが双方向バインディングでバインドされている場合、新しい値はそのバインディングに伝播します。 + +```typescript +@Component({ /* ... */}) +export class CustomSlider { + // "value"という名前のモデル入力を定義します。 + value = model(0); + + increment() { + // 新しい値でモデル入力を更新し、値をすべてのバインディングに伝播します。 + this.value.update(oldValue => oldValue + 10); + } +} + +@Component({ + /* ... */ + // 双方向バインディング構文を使用すると、スライダーの値の変更は、 + // `volume`シグナルに自動的に伝播します。 + // このバインディングは、シグナルの値ではなく、シグナルのインスタンスを使用していることに注意してください。 + template: ``, +}) +export class MediaControls { + // `volume`ローカル状態の書き込み可能なシグナルを作成します。 + volume = signal(0); +} +``` + +上記の例では、`CustomSlider`は`value`モデル入力に値を書き込むことができ、それによって`MediaControls`の`volume`シグナルに値が伝播します。このバインディングにより、`value`と`volume`の値が同期します。バインディングはシグナルの_値_ではなく、`volume`シグナルのインスタンスを渡していることに注意してください。 + +他の点では、モデル入力は標準入力と同様に機能します。`computed`や`effect`のようなリアクティブなコンテキスト内を含め、シグナル関数を呼び出すことで値を読み取ることができます。 + +テンプレートでの双方向バインディングの詳細については、[双方向バインディング](guide/templates/two-way-binding)を参照してください。 + +### プレーンプロパティとの双方向バインディング + +プレーンなJavaScriptプロパティをモデル入力にバインドできます。 + +```angular-ts +@Component({ + /* ... */ + // `value`はモデル入力です。 + // 角かっこ内のかっこ記法(別名「バナナインアボックス」)は、双方向バインディングを作成します + template: '', +}) +export class MediaControls { + protected volume = 0; +} +``` + +上記の例では、`CustomSlider`は`checked`モデル入力に値を書き込むことができ、それによって`UserProfile`の`isAdmin`プロパティに値が伝播します。このバインディングにより、`checked`と`isAdmin`の値が同期します。 + +### 暗黙的な`change`イベント + +コンポーネントまたはディレクティブでモデル入力を宣言すると、Angularはそのモデルに対応する[出力](guide/components/outputs)を自動的に作成します。出力の名前は、モデル入力の名前に「Change」が付いたものです。 + +```angular-ts +@Directive({ /* ... */ }) +export class CustomCheckbox { + // これにより、「checkedChange」という名前の出力が自動的に作成されます。 + // テンプレートで `(checkedChange)="handler()"` を使用して購読できます。 + checked = model(false); +} +``` + +`set`メソッドまたは`update`メソッドを呼び出すことで、モデル入力に新しい値を書き込むたびに、Angularはこの変更イベントを発生させます。 + +出力の詳細については、[出力によるカスタムイベント](guide/components/outputs)を参照してください。 + +### モデル入力のカスタマイズ + +[通常の入力](guide/signals/inputs)と同様に、モデル入力を必須としてマークしたり、エイリアスを提供したりできます。 + +モデル入力は入力変換をサポートしません。 -`@Input` デコレーターは、設定オブジェクトの代わりにエイリアスを最初の引数として受け取ります。 +### モデル入力を使用する場合 -## ゲッターとセッターを使用した入力 +コンポーネントが双方向バインディングをサポートする場合に、モデル入力を使用します。これは通常、ユーザーの操作に基づいて値を変更するためにコンポーネントが存在する場合に適しています。最も一般的には、日付ピッカーやコンボボックスなどのカスタムフォームコントロールは、主要な値にモデル入力を使用する必要があります。 -ゲッターとセッターを使用して実装されたプロパティは、入力にできます。 +# 入力名の選択 + +DOM要素のプロパティ(HTMLElementなど)と競合する入力名を選択することは避けてください。名前の競合は、バインドされたプロパティがコンポーネントのものであるか、DOM要素のものであるかについて混乱を招きます。 + +コンポーネントセレクターのように、コンポーネント入力にプレフィックスを追加することは避けてください。特定の要素は1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属すると見なすことができます。 + +# `@Input`デコレーターによる入力の宣言 + +TIP: Angularチームは新しいプロジェクトにはシグナルベースの`input`関数の使用を推奨していますが、元のデコレーターベースの`@Input` APIは引き続き完全にサポートされています。 + +コンポーネント入力を宣言する代わりに、プロパティに`@Input`デコレーターを追加できます。 + + +@Component({...}) +export class CustomSlider { + @Input() value = 0; +} + + +入力へのバインディングは、シグナルベースの入力とデコレーターベースの入力の両方で同じです。 + +```angular-html + +``` + +### デコレーターベースの入力のカスタマイズ + +`@Input`デコレーターは、入力の動作を変更できるconfigオブジェクトを受け取ります。 + +#### 必須入力 + +`required`オプションを指定して、特定の入力が常に値を持つ必要があることを強制できます。 + + +@Component({...}) +export class CustomSlider { + @Input({required: true}) value = 0; +} + + +すべての必須入力を指定せずにコンポーネントを使用しようとすると、Angularはビルド時にエラーを報告します。 + +#### 入力変換 + +Angularによって入力が設定されるときにその値を変更する`transform`関数を指定できます。この変換関数は、上記で説明したシグナルベースの入力の変換関数と同様に機能します。 + + +@Component({ + selector: 'custom-slider', + ... +}) +export class CustomSlider { + @Input({transform: trimString}) label = ''; +} + +function trimString(value: string | undefined) { return value?.trim() ?? ''; } + + +#### 入力エイリアス + +`alias`オプションを指定して、テンプレートでの入力の名前を変更できます。 + + +@Component({...}) +export class CustomSlider { + @Input({alias: 'sliderValue'}) value = 0; +} + + +```angular-html + +``` + +`@Input`デコレーターは、configオブジェクトの代わりに、エイリアスを最初のパラメーターとして受け取ります。 + +入力エイリアスは、上記で説明したシグナルベースの入力と同じように機能します。 + +### ゲッターとセッターを持つ入力 + +デコレーターベースの入力を使用する場合、ゲッターとセッターで実装されたプロパティを入力にできます。 export class CustomSlider { @@ -142,15 +349,12 @@ export class CustomSlider { return this.internalValue; } - set value(newValue: number) { - this.internalValue = newValue; - } +set value(newValue: number) { this.internalValue = newValue; } - private internalValue = 0; -} +private internalValue = 0; } -公開セッターのみを定義することで、*書き込み専用*の入力を作成できます。 +パブリックセッターのみを定義することで、_書き込み専用_の入力を作成できます。 export class CustomSlider { @@ -159,20 +363,19 @@ export class CustomSlider { this.internalValue = newValue; } - private internalValue = 0; -} +private internalValue = 0; } -可能な場合は、ゲッターとセッターの代わりに 入力変換 を使用することをお勧めします。 +可能な場合は、**ゲッターとセッターの代わりに入力変換を使用することをお勧めします。** -複雑なゲッターやセッターは避けてください。Angularは、入力のセッターを複数回呼び出す場合があります。セッターがDOM操作などのコストのかかる動作をする場合、アプリケーションのパフォーマンスに悪影響を及ぼす可能性があります。 +複雑なゲッターとセッター、またはコストの高いゲッターとセッターは避けてください。Angularは入力のセッターを複数回呼び出す可能性があり、セッターがDOM操作などのコストの高い処理を実行する場合、アプリケーションのパフォーマンスに悪影響を与える可能性があります。 -## `@Component` デコレーターで入力を指定する +## `@Component`デコレーターでの入力の指定 -`@Input` デコレーターに加えて、`@Component` デコレーターの `inputs` プロパティでコンポーネントの入力を指定できます。これは、コンポーネントが基本クラスからプロパティを継承する場合に役立ちます。 +`@Input`デコレーターに加えて、`@Component`デコレーターの`inputs`プロパティを使用して、コンポーネントの入力を指定できます。これは、コンポーネントが基本クラスからプロパティを継承する場合に役立ちます。 -// `CustomSlider` は、`BaseSlider` から `disabled` プロパティを継承します。 +// `CustomSlider` inherits the `disabled` property from `BaseSlider`. @Component({ ..., inputs: ['disabled'], @@ -180,19 +383,13 @@ export class CustomSlider { export class CustomSlider extends BaseSlider { } -さらに、`inputs` リストで入力エイリアスを指定できます。エイリアスをコロンの後に文字列に記述します。 +文字列の後にコロンを付けてエイリアスを`inputs`リストに指定できます。 -// `CustomSlider` は、`BaseSlider` から `disabled` プロパティを継承します。 +// `CustomSlider` inherits the `disabled` property from `BaseSlider`. @Component({ ..., inputs: ['disabled: sliderDisabled'], }) export class CustomSlider extends BaseSlider { } - -## 入力名の選択 - -DOM要素(HTMLElementなど)のプロパティと衝突する入力名は避けてください。名前の衝突は、バインドされたプロパティがコンポーネントに属しているのか、DOM要素に属しているのか混乱を生じさせます。 - -コンポーネントセレクターのように、コンポーネント入力にプレフィックスを追加することは避けてください。特定の要素は、1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属すると見なすことができます。 diff --git a/adev-ja/src/content/guide/components/output-function.en.md b/adev-ja/src/content/guide/components/output-function.en.md deleted file mode 100644 index 9840eaab5..000000000 --- a/adev-ja/src/content/guide/components/output-function.en.md +++ /dev/null @@ -1,109 +0,0 @@ -# Function-based outputs - -The `output()` function declares an output in a directive or component. -Outputs allow you to emit values to parent components. - -HELPFUL: The `output()` function is currently in [developer preview](/reference/releases#developer-preview). - - -import {Component, output} from '@angular/core'; - -@Component({...}) -export class MyComp { - nameChange = output() // OutputEmitterRef - - setNewName(newName: string) { - this.nameChange.emit(newName); - } -} - - -An output is automatically recognized by Angular whenever you use the `output` function as an initializer of a class member. -Parent components can listen to outputs in templates by using the event binding syntax. - -```angular-html - -``` - -## Aliasing an output - -Angular uses the class member name as the name of the output. -You can alias outputs to change their public name to be different. - -```typescript -class MyComp { - nameChange = output({alias: 'ngxNameChange'}); -} -``` - -This allows users to bind to your output using `(ngxNameChange)`, while inside your component you can access the output emitter using `this.nameChange`. - -## Subscribing programmatically - -Consumers may create your component dynamically with a reference to a `ComponentRef`. -In those cases, parents can subscribe to outputs by directly accessing the property of type `OutputRef`. - -```ts -const myComp = viewContainerRef.createComponent(...); - -myComp.instance.nameChange.subscribe(newName => { - console.log(newName); -}); -``` - -Angular will automatically clean up the subscription when `myComp` is destroyed. -Alternatively, an object with a function to explicitly unsubscribe earlier is returned. - -## Using RxJS observables as source - -In some cases, you may want to emit output values based on RxJS observables. -Angular provides a way to use RxJS observables as source for outputs. - -The `outputFromObservable` function is a compiler primitive, similar to the `output()` function, and declares outputs that are driven by RxJS observables. - - -import {Directive} from '@angular/core'; -import {outputFromObservable} from '@angular/core/rxjs-interop'; - -@Directive(...) -class MyDir { - nameChange$ = this.dataService.get(); // Observable - nameChange = outputFromObservable(this.nameChange$); -} - - -Angular will forward subscriptions to the observable, but will stop forwarding values when the owning directive is destroyed. -In the example above, if `MyDir` is destroyed, `nameChange` will no longer emit values. - -HELPFUL: Most of the time, using `output()` is sufficient and you can emit values imperatively. - -## Converting an output to an observable - -You can subscribe to outputs by calling `.subscribe` method on `OutputRef`. -In other cases, Angular provides a helper function that converts an `OutputRef` to an observable. - - -import {outputToObservable} from '@angular/core/rxjs-interop'; - -@Component(...) -class MyComp { - nameChange = output(); -} - -// Instance reference to `MyComp`. -const myComp: MyComp; - -outputToObservable(this.myComp.instance.nameChange) // Observable - .pipe(...) - .subscribe(...); - - -## Why you should use `output()` over decorator-based `@Output()`? - -The `output()` function provides numerous benefits over decorator-based `@Output` and `EventEmitter`: - -1. Simpler mental model and API: -
• No concept of error channel, completion channels, or other APIs from RxJS. -
• Outputs are simple emitters. You can emit values using the `.emit` function. -2. More accurate types. -
• `OutputEmitterRef.emit(value)` is now correctly typed, while `EventEmitter` has broken types and can cause runtime errors. diff --git a/adev-ja/src/content/guide/components/output-function.md b/adev-ja/src/content/guide/components/output-function.md deleted file mode 100644 index add6e588d..000000000 --- a/adev-ja/src/content/guide/components/output-function.md +++ /dev/null @@ -1,109 +0,0 @@ -# 関数ベースの出力 - -`output()` 関数は、ディレクティブまたはコンポーネントで出力を宣言します。 -出力を使用すると、親コンポーネントに出力を送信できます。 - -HELPFUL: `output()` 関数は現在、[開発プレビュー](/reference/releases#developer-preview)です。 - - -import {Component, output} from '@angular/core'; - -@Component({...}) -export class MyComp { - nameChange = output() // OutputEmitterRef - - setNewName(newName: string) { - this.nameChange.emit(newName); - } -} - - -出力は、`output` 関数をクラスメンバーのイニシャライザーとして使用すると、Angularによって自動的に認識されます。 -親コンポーネントは、イベントバインディング構文を使用して、テンプレート内の出力を購読できます。 - -```angular-html - -``` - -## 出力のエイリアス - -Angularは、クラスメンバーの名前を出力の名前として使用します。 -出力にエイリアスを付けることで、公開名を変更できます。 - -```typescript -class MyComp { - nameChange = output({alias: 'ngxNameChange'}); -} -``` - -これにより、ユーザーは `(ngxNameChange)` を使用して出力にバインドできます。コンポーネント内では、`this.nameChange` を使用して出力エミッターにアクセスできます。 - -## プログラムによる購読 - -コンシューマーは、`ComponentRef` への参照を使用して、コンポーネントを動的に作成できます。 -そのような場合、親は `OutputRef` タイプのプロパティに直接アクセスすることで出力に購読できます。 - -```ts -const myComp = viewContainerRef.createComponent(...); - -myComp.instance.nameChange.subscribe(newName => { - console.log(newName); -}); -``` - -`myComp` が破棄されると、Angularは自動的に購読をクリーンアップします。 -または、より早く購読を解除するための関数を含むオブジェクトが返されます。 - -## RxJS Observableをソースとして使用 - -場合によっては、RxJS Observableに基づいて出力値を送信したいことがあります。 -Angularは、RxJS Observableをアウトプットのソースとして使用する方法を提供します。 - -`outputFromObservable` 関数は、`output()` 関数と同様にコンパイラのプリミティブであり、RxJS Observableによって駆動される出力を宣言します。 - - -import {Directive} from '@angular/core'; -import {outputFromObservable} from '@angular/core/rxjs-interop'; - -@Directive(...) -class MyDir { - nameChange$ = this.dataService.get(); // Observable - nameChange = outputFromObservable(this.nameChange$); -} - - -AngularはObservableへの購読を転送しますが、所有するディレクティブが破棄されると値の転送を停止します。 -上記の例では、`MyDir` が破棄されると、`nameChange` は値を送信しなくなります。 - -HELPFUL: ほとんどの場合、`output()` を使用すれば十分で、値を命令的に送信できます。 - -## 出力をObservableに変換する - -`OutputRef` の `.subscribe` メソッドを呼び出すことで、出力に購読できます。 -他のケースでは、Angularは `OutputRef` をObservableに変換するヘルパー関数を提供します。 - - -import {outputToObservable} from '@angular/core/rxjs-interop'; - -@Component(...) -class MyComp { - nameChange = output(); -} - -// `MyComp` へのインスタンス参照。 -const myComp: MyComp; - -outputToObservable(this.myComp.instance.nameChange) // Observable - .pipe(...) - .subscribe(...); - - -## なぜデコレーターベースの `@Output()` よりも `output()` を使用する必要があるのか? - -`output()` 関数は、デコレーターベースの `@Output` と `EventEmitter` に比べて、多くの利点があります。 - -1. よりシンプルなメンタルモデルとAPI: -
• RxJSのエラーチャネル、完了チャネル、またはその他のAPIの概念はありません。 -
• 出力は単純なエミッターです。 `.emit` 関数を使用して値を送信できます。 -2. より正確な型。 -
• `OutputEmitterRef.emit(value)` は、正しく型付けされていますが、`EventEmitter` の型は壊れており、ランタイムエラーが発生する可能性があります。 diff --git a/adev-ja/src/content/guide/components/outputs.en.md b/adev-ja/src/content/guide/components/outputs.en.md index 140cb1971..4d910cd6a 100644 --- a/adev-ja/src/content/guide/components/outputs.en.md +++ b/adev-ja/src/content/guide/components/outputs.en.md @@ -2,12 +2,12 @@ Tip: This guide assumes you've already read the [Essentials Guide](essentials). Read that first if you're new to Angular. -Angular components can define custom events by assigning a property to a new `EventEmitter` and adding the `@Output` decorator: +Angular components can define custom events by assigning a property to the `output` function: - -@Component({...}) + +@Component({/*...*/}) export class ExpandablePanel { - @Output() panelClosed = new EventEmitter(); + panelClosed = output(); } @@ -15,13 +15,13 @@ export class ExpandablePanel { ``` -You can emit an event by calling the `emit` method on the `EventEmitter`: +The `output` function returns an `OutputEmitterRef`. You can emit an event by calling the `emit` method on the `OutputEmitterRef`: this.panelClosed.emit(); -Angular refers to properties marked with the `@Output` decorator as **outputs**. You can use outputs to pass data to other components, similar to native browser events like `click`. +Angular refers to properties initialized with the `output` function as **outputs**. You can use outputs to raise custom events, similar to native browser events like `click`. **Angular custom events do not bubble up the DOM**. @@ -29,6 +29,8 @@ Angular refers to properties marked with the `@Output` decorator as **outputs**. When extending a component class, **outputs are inherited by the child class.** +The `output` function has special meaning to the Angular compiler. **You can exclusively call `output` in component and directive property initializers.** + ## Emitting event data You can pass event data when calling `emit`: @@ -52,12 +54,12 @@ When defining an event listener in a template, you can access the event data fro ## Customizing output names -The `@Output` decorator accepts a parameter that lets you specify a different name for the event in a template: +The `output` function accepts a parameter that lets you specify a different name for the event in a template: -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Output('valueChanged') changed = new EventEmitter(); + changed = output({alias: 'valueChanged'}); } @@ -69,6 +71,76 @@ This alias does not affect usage of the property in TypeScript code. While you should generally avoid aliasing outputs for components, this feature can be useful for renaming properties while preserving an alias for the original name or for avoiding collisions with the name of native DOM events. +## Subscribing to outputs programmatically + +When creating a component dynamically, you can programmatically subscribe to output events +from the component instance. The `OutputRef` type includes a `subscribe` method: + +```ts +const someComponentRef: ComponentRef = viewContainerRef.createComponent(/*...*/); + +someComponentRef.instance.someEventProperty.subscribe(eventData => { + console.log(eventData); +}); +``` + +Angular automatically cleans up event subscriptions when it destroys components with subscribers. Alternatively, you can manually unsubscribe from an event. The `subscribe` function returns an `OutputRefSubscription` with an `unsubscribe` method: + +```typescript +const eventSubscription = someComponent.someEventProperty.subscribe(eventData => { + console.log(eventData); +}); + +// ... + +eventSubscription.unsubscribe(); +``` + +## Choosing event names + +Avoid choosing output names that collide with events on DOM elements like HTMLElement. Name collisions introduce confusion about whether the bound property belongs to the component or the DOM element. + +Avoid adding prefixes for component outputs like you would with component selectors. Since a given element can only host one component, any custom properties can be assumed to belong to the component. + +Always use [camelCase](https://en.wikipedia.org/wiki/Camel_case) output names. Avoid prefixing output names with "on". + +## Using outputs with RxJS + +See [RxJS interop with component and directive outputs](ecosystem/rxjs-interop/output-interop) for details on interoperability between outputs and RxJS. + +## Declaring outputs with the `@Output` decorator + +Tip: While the Angular team recommends using the `output` function for new projects, the +original decorator-based `@Output` API remains fully supported. + +You can alternatively define custom events by assigning a property to a new `EventEmitter` and adding the `@Output` decorator: + + +@Component({/*...*/}) +export class ExpandablePanel { + @Output() panelClosed = new EventEmitter(); +} + + +You can emit an event by calling the `emit` method on the `EventEmitter`. + +### Aliases with the `@Output` decorator + +The `@Output` decorator accepts a parameter that lets you specify a different name for the event in a template: + + +@Component({/*...*/}) +export class CustomSlider { + @Output('valueChanged') changed = new EventEmitter(); +} + + +```angular-html + +``` + +This alias does not affect usage of the property in TypeScript code. + ## Specify outputs in the `@Component` decorator In addition to the `@Output` decorator, you can also specify a component's outputs with the `outputs` property in the `@Component` decorator. This can be useful when a component inherits a property from a base class: @@ -76,7 +148,7 @@ In addition to the `@Output` decorator, you can also specify a component's outpu // `CustomSlider` inherits the `valueChanged` property from `BaseSlider`. @Component({ - ..., + /*...*/ outputs: ['valueChanged'], }) export class CustomSlider extends BaseSlider {} @@ -87,16 +159,8 @@ You can additionally specify an output alias in the `outputs` list by putting th // `CustomSlider` inherits the `valueChanged` property from `BaseSlider`. @Component({ - ..., + /*...*/ outputs: ['valueChanged: volumeChanged'], }) export class CustomSlider extends BaseSlider {} - -## Choosing event names - -Avoid choosing output names that collide with events on DOM elements like HTMLElement. Name collisions introduce confusion about whether the bound property belongs to the component or the DOM element. - -Avoid adding prefixes for component outputs like you would with component selectors. Since a given element can only host one component, any custom properties can be assumed to belong to the component. - -Always use [camelCase](https://en.wikipedia.org/wiki/Camel_case) output names. Avoid prefixing output names with "on". diff --git a/adev-ja/src/content/guide/components/outputs.md b/adev-ja/src/content/guide/components/outputs.md index 325f4445c..33133af81 100644 --- a/adev-ja/src/content/guide/components/outputs.md +++ b/adev-ja/src/content/guide/components/outputs.md @@ -2,12 +2,12 @@ Tip: このガイドは、[基本概念のガイド](essentials) を既読していることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 -Angularコンポーネントは、新しい `EventEmitter` にプロパティを割り当てて `@Output` デコレーターを追加することで、カスタムイベントを定義できます。 +Angularコンポーネントは、`output`関数にプロパティを割り当てることでカスタムイベントを定義できます。 - -@Component({...}) + +@Component({/*...*/}) export class ExpandablePanel { - @Output() panelClosed = new EventEmitter(); + panelClosed = output(); } @@ -15,36 +15,38 @@ export class ExpandablePanel { ``` -`EventEmitter` の `emit` メソッドを呼び出すことで、イベントを送信できます。 +`output`関数は`OutputEmitterRef`を返します。`OutputEmitterRef`の`emit`メソッドを呼び出すことで、イベントを発生させることができます。 this.panelClosed.emit(); -Angularは、`@Output` デコレーターでマークされたプロパティを**出力**と呼びます。出力を使用して、`click` のようなネイティブブラウザイベントと同様に、他のコンポーネントにデータを渡すことができます。 +Angularでは、`output`関数で初期化されたプロパティを**出力**と呼びます。出力を使用すると、`click`などのネイティブブラウザイベントと同様に、カスタムイベントを発生させることができます。 **Angular カスタムイベントは DOM を伝播しません。** **出力名は、大文字と小文字が区別されます。** -コンポーネントクラスを拡張する場合、**出力は子クラスによって継承されます。** +コンポーネントクラスを拡張する場合、**outputsは子クラスによって継承されます。** -## イベントデータの送信 +`output`関数は、Angularコンパイラにとって特別な意味を持ちます。**`output`は、コンポーネントとディレクティブのプロパティ初期化子でのみ呼び出すことができます。** -`emit` を呼び出す際にイベントデータを渡すことができます。 +## イベントデータの送出 + +`emit`を呼び出す際に、イベントデータを渡すことができます。 -// プリミティブ値を送信できます。 +// プリミティブ値を送出できます。 this.valueChanged.emit(7); -// カスタムイベントオブジェクトを送信できます +// カスタムイベントオブジェクトを送出できます this.thumbDropped.emit({ pointerX: 123, pointerY: 456, }) -テンプレートでイベントリスナーを定義する場合、`$event` 変数からイベントデータにアクセスできます。 +テンプレートでイベントリスナーを定義する場合、`$event`変数からイベントデータにアクセスできます。 ```angular-html @@ -52,12 +54,12 @@ this.thumbDropped.emit({ ## 出力名のカスタマイズ -`@Output` デコレーターは、テンプレートでイベントに異なる名前を指定できるパラメータを受け取ります。 +`output`関数は、テンプレートでイベントに異なる名前を指定できるパラメーターを受け入れます。 -@Component({...}) +@Component({/*...*/}) export class CustomSlider { - @Output('valueChanged') changed = new EventEmitter(); + changed = output({alias: 'valueChanged'}); } @@ -67,36 +69,98 @@ export class CustomSlider { このエイリアスは、TypeScriptコードでのプロパティの使用には影響しません。 -コンポーネントの出力のエイリアスは一般的に避けるべきですが、この機能は、元の名前のエイリアスを保持しながらプロパティの名前を変更したり、ネイティブDOMイベントの名前との衝突を回避したりするのに役立ちます。 +一般的に、コンポーネントの出力のエイリアスは避けるべきですが、この機能は元の名前のエイリアスを保持しながらプロパティの名前を変更する場合や、ネイティブDOMイベントの名前との衝突を避ける場合に役立ちます。 + +## プログラムによる出力の購読 + +コンポーネントを動的に作成する場合は、コンポーネントインスタンスから出力イベントをプログラムで購読できます。 +`OutputRef`型には`subscribe`メソッドが含まれています。 + +```ts +const someComponentRef: ComponentRef = viewContainerRef.createComponent(/*...*/); + +someComponentRef.instance.someEventProperty.subscribe(eventData => { + console.log(eventData); +}); +``` + +Angularは、サブスクライバーを持つコンポーネントを破棄するときに、イベントサブスクリプションを自動的にクリーンアップします。または、イベントから手動で購読解除できます。`subscribe`関数は、`unsubscribe`メソッドを持つ`OutputRefSubscription`を返します。 + +```typescript +const eventSubscription = someComponent.someEventProperty.subscribe(eventData => { + console.log(eventData); +}); + +// ... + +eventSubscription.unsubscribe(); +``` + +## イベント名の選択 + +HTMLElementなどのDOM要素のイベントと衝突する出力名を選択することは避けてください。名前の衝突は、バインドされたプロパティがコンポーネントのものであるか、DOM要素のものであるかについて混乱を招きます。 + +コンポーネントセレクターのように、コンポーネント出力にプレフィックスを追加することは避けてください。特定の要素は1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属すると見なすことができます。 + +常に[camelCase](https://en.wikipedia.org/wiki/Camel_case)出力名を使用してください。「on」で始まる出力名は避けてください。 + +## RxJSを使用したoutputs + +outputsとRxJSの相互運用性については、[RxJS interop with component and directive outputs](ecosystem/rxjs-interop/output-interop)を参照してください。 + +## `@Output`デコレーターを使用した出力の宣言 + +Tip: Angularチームは新規プロジェクトでは`output`関数の使用を推奨していますが、 +元のデコレーターベースの`@Output`APIは引き続き完全にサポートされています。 + +代替として、新しい`EventEmitter`にプロパティを割り当て、`@Output`デコレーターを追加することで、カスタムイベントを定義できます。 + + +@Component({/*...*/}) +export class ExpandablePanel { + @Output() panelClosed = new EventEmitter(); +} + + +`EventEmitter`の`emit`メソッドを呼び出すことで、イベントを発生させることができます。 -## `@Component` デコレーターで出力名を指定する +### `@Output`デコレーターを使用したエイリアス -`@Output` デコレーターに加えて、`@Component` デコレーターの `outputs` プロパティを使用して、コンポーネントの出力名を指定できます。これは、コンポーネントが基本クラスからプロパティを継承する場合に役立ちます。 +`@Output`デコレーターは、テンプレートでイベントに異なる名前を指定できるパラメーターを受け入れます。 -// `CustomSlider` は、`BaseSlider` から `valueChanged` プロパティを継承します。 +@Component({/*...*/}) +export class CustomSlider { + @Output('valueChanged') changed = new EventEmitter(); +} + + +```angular-html + +``` + +このエイリアスは、TypeScriptコードでのプロパティの使用には影響しません。 + +## `@Component`デコレーターでの出力の指定 + +`@Output`デコレーターに加えて、`@Component`デコレーターの`outputs`プロパティを使用して、コンポーネントの出力を指定できます。これは、コンポーネントが基底クラスからプロパティを継承する場合に役立ちます。 + + +// `CustomSlider`は`BaseSlider`から`valueChanged`プロパティを継承します。 @Component({ - ..., + /*...*/ outputs: ['valueChanged'], }) export class CustomSlider extends BaseSlider {} -さらに、`outputs` リストでコロンの後にエイリアスを置くことで、出力のエイリアスを指定できます。 +`outputs`リストにエイリアスも指定できます。エイリアスは文字列の後にコロンを付けて記述します。 -// `CustomSlider` は、`BaseSlider` から `valueChanged` プロパティを継承します。 +// `CustomSlider`は`BaseSlider`から`valueChanged`プロパティを継承します。 @Component({ - ..., + /*...*/ outputs: ['valueChanged: volumeChanged'], }) export class CustomSlider extends BaseSlider {} - -## イベント名の選択 - -`HTMLElement` などのDOM要素のイベントと衝突する出力名を選ぶことは避けてください。名前が衝突すると、バインドされているプロパティがコンポーネントに属しているのか、DOM要素に属しているのかがわかりにくくなります。 - -コンポーネントセレクターのように、コンポーネント出力にプレフィックスを追加することは避けてください。特定の要素には、1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属していると見なすことができます。 - -出力名には常に[キャメルケース](https://en.wikipedia.org/wiki/Camel_case)を使用してください。出力名の前に「on」を付けることは避けてください。 diff --git a/adev-ja/src/content/guide/components/queries.en.md b/adev-ja/src/content/guide/components/queries.en.md index 3374d40df..b1df7f992 100644 --- a/adev-ja/src/content/guide/components/queries.en.md +++ b/adev-ja/src/content/guide/components/queries.en.md @@ -6,16 +6,19 @@ A component can define **queries** that find child elements and read values from Developers most commonly use queries to retrieve references to child components, directives, DOM elements, and more. +All query functions return signals that reflect the most up-to-date results. You can read the +result by calling the signal function, including in reactive contexts like `computed` and `effect`. + There are two categories of query: **view queries** and **content queries.** ## View queries -View queries retrieve results from the elements in the component's _view_ — the elements defined in the component's own template. You can query for a single result with the `@ViewChild` decorator. +View queries retrieve results from the elements in the component's _view_ — the elements defined in the component's own template. You can query for a single result with the `viewChild` function. - + @Component({ selector: 'custom-card-header', - ... + /*...*/ }) export class CustomCardHeader { text: string; @@ -26,26 +29,21 @@ export class CustomCardHeader { template: 'Visit sunny California!', }) export class CustomCard { - @ViewChild(CustomCardHeader) header: CustomCardHeader; - - ngAfterViewInit() { - console.log(this.header.text); - } + header = viewChild(CustomCardHeader); + headerText = computed(() => this.header()?.text); } -In this example, the `CustomCard` component queries for a child `CustomCardHeader` and accesses the result in `ngAfterViewInit`. +In this example, the `CustomCard` component queries for a child `CustomCardHeader` and uses the result in a `computed`. -If the query does not find a result, its value is `undefined`. This may occur if the target element is hidden by `NgIf`. Angular keeps the result of `@ViewChild` up to date as your application state changes. +If the query does not find a result, its value is `undefined`. This may occur if the target element is hidden by `@if`. Angular keeps the result of `viewChild` up to date as your application state changes. -**View query results become available in the `ngAfterViewInit` lifecycle method**. Before this point, the value is `undefined`. See the [Lifecycle](guide/components/lifecycle) section for details on the component lifecycle. - -You can also query for multiple results with the `@ViewChildren` decorator. +You can also query for multiple results with the `viewChildren` function. @Component({ selector: 'custom-card-action', - ..., + /*...*/ }) export class CustomCardAction { text: string; @@ -59,28 +57,23 @@ export class CustomCardAction { `, }) export class CustomCard { - @ViewChildren(CustomCardAction) actions: QueryList; - - ngAfterViewInit() { - this.actions.forEach(action => { - console.log(action.text); - }); - } + actions = viewChildren(CustomCardAction); + actionsTexts = computed(() => this.actions().map(action => action.text); } -`@ViewChildren` creates a `QueryList` object that contains the query results. You can subscribe to changes to the query results over time via the `changes` property. +`viewChildren` creates a signal with an `Array` of the query results. **Queries never pierce through component boundaries.** View queries can only retrieve results from the component's template. ## Content queries -Content queries retrieve results from the elements in the component's _content_— the elements nested inside the component in the template where it's used. You can query for a single result with the `@ContentChild` decorator. +Content queries retrieve results from the elements in the component's _content_— the elements nested inside the component in the template where it's used. You can query for a single result with the `contentChild` function. - + @Component({ selector: 'custom-toggle', - ... + /*...*/ }) export class CustomToggle { text: string; @@ -88,18 +81,16 @@ export class CustomToggle { @Component({ selector: 'custom-expando', - ... + /*...*/ }) export class CustomExpando { - @ContentChild(CustomToggle) toggle: CustomToggle; - - ngAfterContentInit() { - console.log(this.toggle.text); - } + toggle = contentChild(CustomToggle); + toggleText = computed(() => this.toggle()?.text); } -@Component({ - selector: 'user-profile', +@Component({ + /* ... */ + // CustomToggle is used inside CustomExpando as content. template: ` Show @@ -109,20 +100,18 @@ export class CustomExpando { export class UserProfile { } -In this example, the `CustomExpando` component queries for a child `CustomToggle` and accesses the result in `ngAfterContentInit`. +In this example, the `CustomExpando` component queries for a child `CustomToggle` and accesses the result in a `computed`. -If the query does not find a result, its value is `undefined`. This may occur if the target element is absent or hidden by `NgIf`. Angular keeps the result of `@ContentChild` up to date as your application state changes. +If the query does not find a result, its value is `undefined`. This may occur if the target element is absent or hidden by `@if`. Angular keeps the result of `contentChild` up to date as your application state changes. By default, content queries find only _direct_ children of the component and do not traverse into descendants. -**Content query results become available in the `ngAfterContentInit` lifecycle method**. Before this point, the value is `undefined`. See the [Lifecycle](guide/components/lifecycle) section for details on the component lifecycle. - -You can also query for multiple results with the `@ContentChildren` decorator. +You can also query for multiple results with the `contentChildren` function. @Component({ selector: 'custom-menu-item', - ... + /*...*/ }) export class CustomMenuItem { text: string; @@ -130,16 +119,11 @@ export class CustomMenuItem { @Component({ selector: 'custom-menu', - ..., + /*...*/ }) export class CustomMenu { - @ContentChildren(CustomMenuItem) items: QueryList; - - ngAfterContentInit() { - this.items.forEach(item => { - console.log(item.text); - }); - } + items = contentChildren(CustomMenuItem); + itemTexts = computed(() => this.items().map(item => item.text)); } @Component({ @@ -154,10 +138,26 @@ export class CustomMenu { export class UserProfile { } -`@ContentChildren` creates a `QueryList` object that contains the query results. You can subscribe to changes to the query results over time via the `changes` property. +`contentChildren` creates a signal with an `Array` of the query results. **Queries never pierce through component boundaries.** Content queries can only retrieve results from the same template as the component itself. +## Required queries + +If a child query (`viewChild` or `contentChild`) does not find a result, its value is `undefined`. This may occur if the target element is hidden by a control flow statement like `@if` or `@for`. Because of this, the child queries return a signal that include `undefined` in their value type. + +If some cases, especially with `viewChild`, you know with certainty that a specific child is always available. In other cases, you may want to strictly enforce that a specific child is present. For these cases, you can use a *required query*. + +```angular-ts +@Component({/* ... */}) +export class CustomCard { + header = viewChild.required(CustomCardHeader); + body = contentChild.required(CustomCardBody); +} +``` + +If a required query does not find a matching result, Angular reports an error. Because this guarantees that a result is available, require queries do not automatially include `undefined` in the signal's value type. + ## Query locators This first parameter for each query decorator is its **locator**. @@ -169,14 +169,14 @@ a [template reference variable](guide/templates/variables#template-reference-var ```angular-ts @Component({ - ..., + /*...*/ template: ` ` }) export class ActionBar { - @ViewChild('save') saveButton: ElementRef; + saveButton = viewChild>('save'); } ``` @@ -194,14 +194,14 @@ For more advanced cases, you can use any `ProviderToken` as a locator. This lets const SUB_ITEM = new InjectionToken('sub-item'); @Component({ - ..., + /*...*/ providers: [{provide: SUB_ITEM, useValue: 'special-item'}], }) export class SpecialItem { } -@Component({...}) +@Component({/*...*/}) export class CustomList { - @ContentChild(SUB_ITEM) subItemType: string; + subItemType = contentChild(SUB_ITEM); } ``` @@ -209,31 +209,23 @@ The above example uses an `InjectionToken` as a locator, but you can use any `Pr ## Query options -All query decorators accept an options object as a second parameter. These options control how the query finds its results. +All query functions accept an options object as a second parameter. These options control how the query finds its results. -### Static queries - -`@ViewChild` and `@ContentChild` queries accept the `static` option. +### Reading specific values from an element's injector -```angular-ts -@Component({ - selector: 'custom-card', - template: 'Visit sunny California!', -}) -export class CustomCard { - @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader; +By default, the query locator indicates both the element you're searching for and the value retrieved. You can alternatively specify the `read` option to retrieve a different value from the element matched by the locator. - ngOnInit() { - console.log(this.header.text); - } +```ts +@Component({/*...*/}) +export class CustomExpando { + toggle = contentChild(ExpandoContent, {read: TemplateRef}); } ``` -By setting `static: true`, you guarantee to Angular that the target of this query is _always_ present and is not conditionally rendered. This makes the result available earlier, in the `ngOnInit` lifecycle method. - -Static query results do not update after initialization. +The above example, locates an element with the directive `ExpandoContent` and retrieves +the `TemplateRef` associated with that element. -The `static` option is not available for `@ViewChildren` and `@ContentChildren` queries. +Developers most commonly use `read` to retrieve `ElementRef` and `TemplateRef`. ### Content descendants @@ -242,10 +234,10 @@ By default, content queries find only _direct_ children of the component and do @Component({ selector: 'custom-expando', - ... + /*...*/ }) export class CustomExpando { - @ContentChild(CustomToggle) toggle: CustomToggle; + toggle = contentChild(CustomToggle); } @Component({ @@ -266,23 +258,185 @@ In the example above, `CustomExpando` cannot find `` because it i View queries do not have this option because they _always_ traverse into descendants. -### Reading specific values from an element's injector +## Decorator-based queries +Tip: While the Angular team recommends using the signal-based query function for new projects, the +original decorator-based query APIs remain fully supported. -By default, the query locator indicates both the element you're searching for and the value retrieved. You can alternatively specify the `read` option to retrieve a different value from the element matched by the locator. +You can alternatively declare queries by adding the corresponding decorator to a property. Decorator-based queries behave the same way as signal-based queries except as described below. -```ts -@Component({...}) +### View queries + +You can query for a single result with the `@ViewChild` decorator. + + +@Component({ + selector: 'custom-card-header', + /*...*/ +}) +export class CustomCardHeader { + text: string; +} + +@Component({ + selector: 'custom-card', + template: 'Visit sunny California!', +}) +export class CustomCard { + @ViewChild(CustomCardHeader) header: CustomCardHeader; + + ngAfterViewInit() { + console.log(this.header.text); + } +} + + +In this example, the `CustomCard` component queries for a child `CustomCardHeader` and accesses the result in `ngAfterViewInit`. + +Angular keeps the result of `@ViewChild` up to date as your application state changes. + +**View query results become available in the `ngAfterViewInit` lifecycle method**. Before this point, the value is `undefined`. See the [Lifecycle](guide/components/lifecycle) section for details on the component lifecycle. + +You can also query for multiple results with the `@ViewChildren` decorator. + + +@Component({ + selector: 'custom-card-action', + /*...*/ +}) +export class CustomCardAction { + text: string; +} + +@Component({ + selector: 'custom-card', + template: ` + Save + Cancel + `, +}) +export class CustomCard { + @ViewChildren(CustomCardAction) actions: QueryList; + + ngAfterViewInit() { + this.actions.forEach(action => { + console.log(action.text); + }); + } +} + + +`@ViewChildren` creates a `QueryList` object that contains the query results. You can subscribe to changes to the query results over time via the `changes` property. + +### Content queries + +You can query for a single result with the `@ContentChild` decorator. + + +@Component({ + selector: 'custom-toggle', + /*...*/ +}) +export class CustomToggle { + text: string; +} + +@Component({ + selector: 'custom-expando', + /*...*/ +}) export class CustomExpando { - @ContentChild(ExpandoContent, {read: TemplateRef}) toggle: TemplateRef; + @ContentChild(CustomToggle) toggle: CustomToggle; + + ngAfterContentInit() { + console.log(this.toggle.text); + } +} + +@Component({ + selector: 'user-profile', + template: ` + + Show + + ` +}) +export class UserProfile { } + + +In this example, the `CustomExpando` component queries for a child `CustomToggle` and accesses the result in `ngAfterContentInit`. + +Angular keeps the result of `@ContentChild` up to date as your application state changes. + +**Content query results become available in the `ngAfterContentInit` lifecycle method**. Before this point, the value is `undefined`. See the [Lifecycle](guide/components/lifecycle) section for details on the component lifecycle. + +You can also query for multiple results with the `@ContentChildren` decorator. + + +@Component({ + selector: 'custom-menu-item', + /*...*/ +}) +export class CustomMenuItem { + text: string; +} + +@Component({ + selector: 'custom-menu', + /*...*/ +}) +export class CustomMenu { + @ContentChildren(CustomMenuItem) items: QueryList; + + ngAfterContentInit() { + this.items.forEach(item => { + console.log(item.text); + }); + } +} + +@Component({ + selector: 'user-profile', + template: ` + + Cheese + Tomato + + ` +}) +export class UserProfile { } + + +`@ContentChildren` creates a `QueryList` object that contains the query results. You can subscribe to changes to the query results over time via the `changes` property. + +### Decorator-based query options + +All query decorators accept an options object as a second parameter. These options work the same way as signal-based queries except where described below. + +### Static queries + +`@ViewChild` and `@ContentChild` decorators accept the `static` option. + +```angular-ts +@Component({ + selector: 'custom-card', + template: 'Visit sunny California!', +}) +export class CustomCard { + @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader; + + ngOnInit() { + console.log(this.header.text); + } } ``` -The above example, locates an element with the directive `ExpandoContent` and retrieves -the `TemplateRef` associated with that element. +By setting `static: true`, you guarantee to Angular that the target of this query is _always_ present and is not conditionally rendered. This makes the result available earlier, in the `ngOnInit` lifecycle method. -Developers most commonly use `read` to retrieve `ElementRef` and `TemplateRef`. +Static query results do not update after initialization. + +The `static` option is not available for `@ViewChildren` and `@ContentChildren` queries. -## Using QueryList +### Using QueryList `@ViewChildren` and `@ContentChildren` both provide a `QueryList` object that contains a list of results. diff --git a/adev-ja/src/content/guide/components/queries.md b/adev-ja/src/content/guide/components/queries.md index 1bae801bc..5a8a4e072 100644 --- a/adev-ja/src/content/guide/components/queries.md +++ b/adev-ja/src/content/guide/components/queries.md @@ -6,16 +6,19 @@ Tip: このガイドでは、[基本概念のガイド](essentials)を読んで 開発者は、クエリを使用して、子コンポーネント、ディレクティブ、DOM要素などの参照を取得することがほとんどです。 -クエリには、**ビュークエリ**と**コンテンツクエリ**の2種類があります。 +すべてのクエリ関数は、最新の結果を反映するシグナルを返します。 +`computed`や`effect`などのリアクティブなコンテキストでも、シグナル関数を呼び出すことで結果を読み取れます。 + +クエリには、**ビュークエリ**と**コンテンツクエリ**の2つのカテゴリーがあります。 ## ビュークエリ -ビュークエリは、コンポーネントの*ビュー*(コンポーネント自身のテンプレートで定義されている要素)にある要素から結果を取得します。`@ViewChild` デコレーターを使用して、単一の結果をクエリできます。 +ビュークエリは、コンポーネントの_ビュー_(コンポーネント自身のテンプレートで定義された要素)内の要素から結果を取得します。`viewChild`関数を使用して単一の結果をクエリできます。 - + @Component({ selector: 'custom-card-header', - ... + /*...*/ }) export class CustomCardHeader { text: string; @@ -26,26 +29,21 @@ export class CustomCardHeader { template: 'Visit sunny California!', }) export class CustomCard { - @ViewChild(CustomCardHeader) header: CustomCardHeader; - - ngAfterViewInit() { - console.log(this.header.text); - } + header = viewChild(CustomCardHeader); + headerText = computed(() => this.header()?.text); } -この例では、`CustomCard`コンポーネントは子要素の`CustomCardHeader`をクエリし、`ngAfterViewInit`で結果にアクセスしています。 +この例では、`CustomCard`コンポーネントは子`CustomCardHeader`をクエリし、`computed`で結果を使用しています。 -クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が`NgIf`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、`@ViewChild`の結果を最新の状態に保ちます。 +クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が`@if`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化するにつれて`viewChild`の結果を最新の状態に保ちます。 -**ビュークエリの結果は、`ngAfterViewInit`ライフサイクルメソッドで利用可能になります**。この時点以前は、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションをご覧ください。 - -`@ViewChildren` デコレーターを使用して、複数の結果をクエリできます。 +`viewChildren`関数を使用して、複数結果をクエリできます。 @Component({ selector: 'custom-card-action', - ..., + /*...*/ }) export class CustomCardAction { text: string; @@ -59,28 +57,23 @@ export class CustomCardAction { `, }) export class CustomCard { - @ViewChildren(CustomCardAction) actions: QueryList; - - ngAfterViewInit() { - this.actions.forEach(action => { - console.log(action.text); - }); - } + actions = viewChildren(CustomCardAction); + actionsTexts = computed(() => this.actions().map(action => action.text); } -`@ViewChildren`は、クエリの結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。 +`viewChildren`は、クエリ結果の`Array`を含むシグナルを作成します。 -**クエリはコンポーネントの境界を越えることは決してありません。** ビュークエリは、コンポーネントのテンプレートからのみ結果を取得できます。 +**クエリはコンポーネントの境界を貫通することはありません。**ビュークエリは、コンポーネントのテンプレートからの結果のみを取得できます。 ## コンテンツクエリ -コンテンツクエリは、コンポーネントの_コンテンツ_(コンポーネントが使用されているテンプレート内でコンポーネントにネストされた要素)にある要素から結果を取得します。`@ContentChild` デコレーターを使用して、単一の結果をクエリできます。 +コンテンツクエリは、コンポーネントの_コンテンツ_(コンポーネントが使用されているテンプレート内でコンポーネントの中にネストされた要素)内の要素から結果を取得します。`contentChild`関数を使用して単一の結果をクエリできます。 - + @Component({ selector: 'custom-toggle', - ... + /*...*/ }) export class CustomToggle { text: string; @@ -88,18 +81,16 @@ export class CustomToggle { @Component({ selector: 'custom-expando', - ... + /*...*/ }) export class CustomExpando { - @ContentChild(CustomToggle) toggle: CustomToggle; - - ngAfterContentInit() { - console.log(this.toggle.text); - } + toggle = contentChild(CustomToggle); + toggleText = computed(() => this.toggle()?.text); } -@Component({ - selector: 'user-profile', +@Component({ + /* ... */ + // CustomToggle is used inside CustomExpando as content. template: ` Show @@ -109,20 +100,18 @@ export class CustomExpando { export class UserProfile { } -この例では、`CustomExpando`コンポーネントは子要素の`CustomToggle`をクエリし、`ngAfterContentInit`で結果にアクセスしています。 - -クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が存在しないか、`NgIf`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、`@ContentChild`の結果を最新の状態に保ちます。 +この例では、`CustomExpando`コンポーネントは子`CustomToggle`をクエリし、`computed`で結果にアクセスしています。 -デフォルトでは、コンテンツクエリはコンポーネントの*直接*の子要素のみを見つけ、子孫にトラバースすることはありません。 +クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が存在しないか、`@if`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化するにつれて`contentChild`の結果を最新の状態に保ちます。 -**コンテンツクエリの結果は、`ngAfterContentInit`ライフサイクルメソッドで利用可能になります**。この時点以前は、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションをご覧ください。 +デフォルトでは、コンテンツクエリはコンポーネントの_直接_の子のみを見つけ、子孫にはトラバースしません。 -`@ContentChildren` デコレーターを使用して、複数の結果をクエリできます。 +`contentChildren`関数を使用して、複数結果をクエリできます。 @Component({ selector: 'custom-menu-item', - ... + /*...*/ }) export class CustomMenuItem { text: string; @@ -130,16 +119,11 @@ export class CustomMenuItem { @Component({ selector: 'custom-menu', - ..., + /*...*/ }) export class CustomMenu { - @ContentChildren(CustomMenuItem) items: QueryList; - - ngAfterContentInit() { - this.items.forEach(item => { - console.log(item.text); - }); - } + items = contentChildren(CustomMenuItem); + itemTexts = computed(() => this.items().map(item => item.text)); } @Component({ @@ -154,148 +138,319 @@ export class CustomMenu { export class UserProfile { } -`@ContentChildren`は、クエリの結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。 +`contentChildren`は、クエリ結果の`Array`を含むシグナルを作成します。 + +**クエリはコンポーネントの境界を貫通することはありません。**コンテンツクエリは、コンポーネント自体と同じテンプレートからの結果のみを取得できます。 -**クエリはコンポーネントの境界を越えることは決してありません。** コンテンツクエリは、コンポーネント自身と同じテンプレートからのみ結果を取得できます。 +## 必須クエリ + +子クエリ(`viewChild`または`contentChild`)が結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が`@if`や`@for`などの制御フロー文によって非表示になっている場合に発生する可能性があります。このため、子クエリは`undefined`を含む値型を持つシグナルを返します。 + +場合によっては、特に`viewChild`を使用する場合、特定の子が常に利用可能であることが確実な場合があります。他の場合では、特定の子が存在することを厳格に適用したい場合があります。これらの場合、*必須クエリ*を使用できます。 + +```angular-ts +@Component({/* ... */}) +export class CustomCard { + header = viewChild.required(CustomCardHeader); + body = contentChild.required(CustomCardBody); +} +``` + +必須クエリが一致する結果を見つけられない場合、Angularはエラーを報告します。これは結果が利用可能であることを保証するため、必須クエリは自動的にシグナルの値型に`undefined`を含めません。 ## クエリロケーター -各クエリのデコレーターの最初の引数は、**ロケーター**です。 +各クエリデコレーターの最初の引数は、その**ロケーター**です。 -ほとんどの場合、コンポーネントまたはディレクティブをロケーターとして使用します。 +ほとんどの場合、ロケーターとしてコンポーネントまたはディレクティブを使用することをお勧めします。 -代わりに、[テンプレート参照変数](guide/templates/variables#template-reference-variables) -に対応する文字列ロケーターを指定できます。 +[テンプレート参照変数](guide/templates/variables#template-reference-variables)に対応する +文字列ロケーターも指定できます。 ```angular-ts @Component({ - ..., + /*...*/ template: ` ` }) export class ActionBar { - @ViewChild('save') saveButton: ElementRef; + saveButton = viewChild>('save'); } ``` -同じテンプレート参照変数を定義している要素が複数ある場合、クエリは最初に一致する要素を取得します。 +複数の要素が同じテンプレート参照変数を定義している場合、クエリは最初に一致する要素を取得します。 -Angularは、CSSセレクターをクエリのロケーターとしてサポートしていません。 +Angularは、CSSセレクターをクエリロケーターとしてサポートしていません。 ### クエリとインジェクターツリー -Tip: プロバイダーとAngularのインジェクションツリーの詳細については、[依存性注入](guide/di)を参照してください。 +Tip: プロバイダーとAngularのインジェクションツリーについては、[依存性の注入](guide/di)を参照してください。 -より高度なケースでは、`ProviderToken`をロケーターとして使用できます。これにより、コンポーネントとディレクティブのプロバイダーに基づいて要素を特定できます。 +より高度なケースでは、ロケーターとして任意の`ProviderToken`を使用できます。これにより、コンポーネントとディレクティブのプロバイダーに基づいて要素を見つけることができます。 ```angular-ts const SUB_ITEM = new InjectionToken('sub-item'); @Component({ - ..., + /*...*/ providers: [{provide: SUB_ITEM, useValue: 'special-item'}], }) export class SpecialItem { } -@Component({...}) +@Component({/*...*/}) export class CustomList { - @ContentChild(SUB_ITEM) subItemType: string; + subItemType = contentChild(SUB_ITEM); } ``` -上記の例では、`InjectionToken`をロケーターとして使用していますが、特定の要素を特定するために、任意の`ProviderToken`を使用できます。 +上記の例では、ロケーターとして`InjectionToken`を使用していますが、任意の`ProviderToken`を使用して特定の要素を見つけることができます。 ## クエリオプション -すべてのクエリデコレーターは、2番目のパラメーターとしてオプションオブジェクトを受け取ります。これらのオプションは、クエリが結果をどのように見つけるかを制御します。 +すべてのクエリ関数は、第2引数としてオプションオブジェクトを受け取ります。これらのオプションは、クエリが結果を見つける方法を制御します。 -### 静的クエリ +### 要素のインジェクターからの特定の値の読み取り -`@ViewChild`および`@ContentChild`クエリは、`static`オプションを受け取ります。 +デフォルトでは、クエリロケーターは、検索対象の要素と取得される値の両方を示します。代わりに、`read`オプションを指定して、ロケーターによって一致した要素から別の値を取得できます。 + +```ts +@Component({/*...*/}) +export class CustomExpando { + toggle = contentChild(ExpandoContent, {read: TemplateRef}); +} +``` + +上記の例では、`ExpandoContent`ディレクティブを持つ要素を見つけて、 +その要素に関連付けられた`TemplateRef`を取得します。 + +開発者は、`read`を使用して`ElementRef`と`TemplateRef`を取得することが最も一般的です。 + +### コンテンツの子孫 + +デフォルトでは、コンテンツクエリはコンポーネントの_直接_の子のみを見つけ、子孫にはトラバースしません。 + + +@Component({ + selector: 'custom-expando', + /*...*/ +}) +export class CustomExpando { + toggle = contentChild(CustomToggle); +} + +@Component({ + selector: 'user-profile', + template: ` + + + + Show + + + ` +}) +export class UserProfile { } + + +上記の例では、`CustomExpando`は``を見つけられません。これは、``の直接の子ではないためです。`descendants: true`を設定することで、同じテンプレート内のすべての子孫をトラバースするようにクエリを構成できます。ただし、クエリは他のテンプレートの要素をトラバースするためにコンポーネントを_決して_貫通することはありません。 + +ビュークエリにはこのオプションはありません。これは、常に子孫をトラバースするためです。 + +## デコレーターベースのクエリ +Tip: Angularチームは新規プロジェクトにはシグナルベースのクエリ関数の使用を推奨していますが、 +元のデコレーターベースのクエリAPIは引き続き完全にサポートされています。 + +代わりに、対応するデコレーターをプロパティに追加することでもクエリを宣言できます。デコレーターベースのクエリは、下記で説明する点を除いて、シグナルベースのクエリと同じように動作します。 + +### ビュークエリ + +`@ViewChild`デコレーターを使用して、単一の結果をクエリできます。 + + +@Component({ + selector: 'custom-card-header', + /*...*/ +}) +export class CustomCardHeader { + text: string; +} -```angular-ts @Component({ selector: 'custom-card', template: 'Visit sunny California!', }) export class CustomCard { - @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader; + @ViewChild(CustomCardHeader) header: CustomCardHeader; - ngOnInit() { + ngAfterViewInit() { console.log(this.header.text); } } -``` + -`static: true`を設定することで、Angularにこのクエリのターゲットが*常に*存在し、条件付きでレンダリングされていないことを保証します。これにより、結果はより早い段階で、`ngOnInit`ライフサイクルメソッドで利用可能になります。 +この例では、`CustomCard`コンポーネントは子`CustomCardHeader`をクエリし、`ngAfterViewInit`で結果にアクセスしています。 -静的クエリの結果は、初期化後に更新されません。 +Angularは、アプリケーションの状態が変化するにつれて`@ViewChild`の結果を最新の状態に保ちます。 + +**ビュークエリの結果は`ngAfterViewInit`ライフサイクルメソッドで使用可能になります。**この時点より前では、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションを参照してください。 + +`@ViewChildren`デコレーターを使用して、複数の結果をクエリできます。 + + +@Component({ + selector: 'custom-card-action', + /*...*/ +}) +export class CustomCardAction { + text: string; +} -`static`オプションは、`@ViewChildren`および`@ContentChildren`クエリでは使用できません。 +@Component({ + selector: 'custom-card', + template: ` + Save + Cancel + `, +}) +export class CustomCard { + @ViewChildren(CustomCardAction) actions: QueryList; + + ngAfterViewInit() { + this.actions.forEach(action => { + console.log(action.text); + }); + } +} + -### コンテンツ子孫 +`@ViewChildren`は、クエリ結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、時間の経過とともにクエリ結果の変更を購読できます。 -デフォルトでは、コンテンツクエリはコンポーネントの_直接_の子要素のみを見つけ、子孫にトラバースすることはありません。 +### コンテンツクエリ + +`@ContentChild`デコレーターを使用して、単一の結果をクエリできます。 + + +@Component({ + selector: 'custom-toggle', + /*...*/ +}) +export class CustomToggle { + text: string; +} - @Component({ selector: 'custom-expando', - ... + /*...*/ }) export class CustomExpando { @ContentChild(CustomToggle) toggle: CustomToggle; + + ngAfterContentInit() { + console.log(this.toggle.text); + } } @Component({ selector: 'user-profile', template: ` - - - Show - + Show ` }) export class UserProfile { } -上記の例では、`CustomExpando`は、``が``の直接の子要素ではないため、``を見つけることができません。`descendants: true`を設定することで、クエリが同じテンプレート内のすべての子孫をトラバースするように構成できます。ただし、クエリは、_決して_コンポーネントに侵入して他のテンプレート内の要素をトラバースすることはありません。 +この例では、`CustomExpando`コンポーネントは子`CustomToggle`をクエリし、`ngAfterContentInit`で結果にアクセスしています。 -ビュークエリには、子孫を_常に_トラバースするため、このオプションはありません。 +Angularは、アプリケーションの状態が変化するにつれて`@ContentChild`の結果を最新の状態に保ちます。 -### 要素のインジェクターからの特定の値の読み取り +**コンテンツクエリの結果は`ngAfterContentInit`ライフサイクルメソッドで使用可能になります。**この時点より前では、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションを参照してください。 -デフォルトでは、クエリロケーターは、検索する要素と取得する値の両方を示します。代わりに、`read`オプションを指定して、ロケーターによって一致する要素から別の値を取得できます。 +`@ContentChildren`デコレーターを使用して、複数の結果をクエリできます。 -```ts -@Component({...}) -export class CustomExpando { - @ContentChild(ExpandoContent, {read: TemplateRef}) toggle: TemplateRef; + +@Component({ + selector: 'custom-menu-item', + /*...*/ +}) +export class CustomMenuItem { + text: string; +} + +@Component({ + selector: 'custom-menu', + /*...*/ +}) +export class CustomMenu { + @ContentChildren(CustomMenuItem) items: QueryList; + + ngAfterContentInit() { + this.items.forEach(item => { + console.log(item.text); + }); + } +} + +@Component({ + selector: 'user-profile', + template: ` + + Cheese + Tomato + + ` +}) +export class UserProfile { } + + +`@ContentChildren`は、クエリ結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、時間の経過とともにクエリ結果の変更を購読できます。 + +### デコレーターベースのクエリオプション + +すべてのクエリデコレーターは、第2引数としてオプションオブジェクトを受け取ります。これらのオプションは、シグナルベースのクエリと同じように動作しますが、下記で説明する点を除きます。 + +### 静的クエリ + +`@ViewChild`と`@ContentChild`デコレーターは、`static`オプションを受け取ります。 + +```angular-ts +@Component({ + selector: 'custom-card', + template: 'Visit sunny California!', +}) +export class CustomCard { + @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader; + + ngOnInit() { + console.log(this.header.text); + } } ``` -上記の例では、`ExpandoContent`ディレクティブを持つ要素を特定し、 -その要素に関連付けられた`TemplateRef`を取得します。 +`static: true`を設定することで、このクエリのターゲットは_常に_存在し、条件付きでレンダリングされないことをAngularに保証します。これにより、`ngOnInit`ライフサイクルメソッドで早く結果を利用できます。 -開発者は、`read`を使用して、`ElementRef`と`TemplateRef`を取得することがほとんどです。 +静的クエリの結果は、初期化後に更新されません。 + +`static`オプションは、`@ViewChildren`と`@ContentChildren`クエリでは使用できません。 -## QueryList の使用 +### QueryListの使用 `@ViewChildren`と`@ContentChildren`はどちらも、結果のリストを含む`QueryList`オブジェクトを提供します。 -`QueryList`は、`map`、`reduce`、`forEach`などの配列のような方法で結果を操作するための便利なAPIをいくつか提供します。`toArray`を呼び出すことで、現在の結果の配列を取得できます。 +`QueryList`は、`map`、`reduce`、`forEach`など、配列のような方法で結果を操作するための多くの便利なAPIを提供します。`toArray`を呼び出すことで、現在の結果の配列を取得できます。 + +`changes`プロパティを購読して、結果が変更されるたびに何かを実行できます。 -`changes`プロパティを購読して、結果が変更されるたびに何かを行うことができます。 +## 一般的なクエリの落とし穴 -## クエリの一般的な落とし穴 +クエリを使用する際、一般的な落とし穴により、コードの理解と保守が難しくなる可能性があります。 -クエリを使用する際に、コードの理解と保守を難しくする一般的な落とし穴があります。 +複数のコンポーネント間で共有される状態については、常に単一の真実の源を維持してください。これにより、異なるコンポーネントで状態が繰り返し同期されなくなるシナリオを回避できます。 -複数のコンポーネント間で共有される状態には、常に単一の真実の源を維持します。これにより、異なるコンポーネントで状態が繰り返し使用され、同期が乱れるシナリオを防ぐことができます。 +子コンポーネントに直接状態を書き込むことは避けてください。このパターンは、理解しにくく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい壊れやすいコードにつながる可能性があります。 -子コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい、もろいコードにつながる可能性があります。 +親コンポーネントまたは祖先コンポーネントに直接状態を書き込むことは決してしないでください。このパターンは、理解しにくく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい壊れやすいコードにつながる可能性があります。 -親または祖先コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい、もろいコードにつながる可能性があります。 diff --git a/adev-ja/src/content/guide/defer.en.md b/adev-ja/src/content/guide/defer.en.md deleted file mode 100644 index 21a5537af..000000000 --- a/adev-ja/src/content/guide/defer.en.md +++ /dev/null @@ -1,299 +0,0 @@ -# Deferrable Views - -## Overview - -Deferrable views can be used in component template to defer the loading of select dependencies within that template. Those dependencies include components, directives, and pipes, and any associated CSS. To use this feature, you can declaratively wrap a section of your template in a `@defer` block which specifies the loading conditions. - -Deferrable views support a series of [triggers](guide/defer#triggers), [prefetching](guide/defer#prefetching), and several sub blocks used for [placeholder](guide/defer#placeholder), [loading](guide/defer#loading), and [error](guide/defer#error) state management. You can also create custom conditions with [`when`](guide/defer#when) and [`prefetch when`](guide/defer#prefetching). - -```angular-html -@defer { - -} -``` - -## Why use Deferrable Views? - -Deferrable views, also known as `@defer` blocks, are a powerful tool that can be used to reduce the initial bundle size of your application or defer heavy components that may not ever be loaded until a later time. This should result in a faster initial load and an improvement in your Core Web Vitals (CWV) results. Deferring some of your components until later should specifically improve Largest Contentful Paint (LCP) and Time to First Byte (TTFB). - -Note: It is highly recommended that any defer loaded component that might result in layout shift once the dependencies have loaded be below the fold or otherwise not yet visible to the user. - -## Which dependencies are defer-loadable? - -In order for dependencies within a `@defer` block to be deferred, they need to meet two conditions: - -1. They must be standalone. Non-standalone dependencies cannot be deferred and will still be eagerly loaded, even inside of `@defer` blocks. - -2. They must not be directly referenced from the same file, outside of `@defer` blocks; this includes ViewChild queries. - -Transitive dependencies of the components, directives, and pipes used in the defer block can be standalone or NgModule based and will still be deferred. - -## Blocks - -`@defer` blocks have several sub blocks to allow you to gracefully handle different stages in the deferred loading process. - -### `@defer` - -The content of the main `@defer` block is the section of content that is lazily loaded. It will not be rendered initially, and all of the content will appear once the specified [trigger](guide/defer#triggers) or `when` condition is met and the dependencies have been fetched. By default, a `@defer` block is triggered when the browser state becomes [idle](guide/defer#on-idle). - -### `@placeholder` - -By default, defer blocks do not render any content before they are triggered. The `@placeholder` is an optional block that declares content to show before the defer block is triggered. This placeholder content is replaced with the main content once the loading is complete. You can use any content in the placeholder section including plain HTML, components, directives, and pipes; however keep in mind the dependencies of the placeholder block are eagerly loaded. - -Note: For the best user experience, you should always specify a `@placeholder` block. - -The `@placeholder` block accepts an optional parameter to specify the `minimum` amount of time that this placeholder should be shown. This `minimum` parameter is specified in time increments of milliseconds (ms) or seconds (s). This parameter exists to prevent fast flickering of placeholder content in the case that the deferred dependencies are fetched quickly. The `minimum` timer for the `@placeholder` block begins after the initial render of this `@placeholder` block completes. - -```angular-html -@defer { - -} @placeholder (minimum 500ms) { -

Placeholder content

-} -``` - -Note: Certain triggers may require the presence of either a `@placeholder` or a [template reference variable](guide/templates/variables#template-reference-variables) to function. See the [Triggers](guide/defer#triggers) section for more details. - -### `@loading` - -The `@loading` block is an optional block that allows you to declare content that will be shown during the loading of any deferred dependencies. Its dependences are eagerly loaded (similar to `@placeholder`). - -For example, you could show a loading spinner. Once loading has been triggered, the `@loading` block replaces the `@placeholder` block. - -The `@loading` block accepts two optional parameters to specify the `minimum` amount of time that this placeholder should be shown and amount of time to wait `after` loading begins before showing the loading template. `minimum` and `after` parameters are specified in time increments of milliseconds (ms) or seconds (s). Just like `@placeholder`, these parameters exist to prevent fast flickering of content in the case that the deferred dependencies are fetched quickly. Both the `minimum` and `after` timers for the `@loading` block begins immediately after the loading has been triggered. - -```angular-html -@defer { - -} @loading (after 100ms; minimum 1s) { - loading... -} -``` - -### `@error` - -The `@error` block allows you to declare content that will be shown if deferred loading fails. Similar to `@placeholder` and `@loading`, the dependencies of the `@error` block are eagerly loaded. The `@error` block is optional. - -```angular-html -@defer { - -} @error { -

Failed to load the calendar

-} -``` - -## Triggers - -When a `@defer` block is triggered, it replaces placeholder content with lazily loaded content. There are two options for configuring when this swap is triggered: `on` and `when`. - - -`on` specifies a trigger condition using a trigger from the list of available triggers below. An example would be on interaction or on viewport. - -Multiple event triggers can be defined at once. For example: `on interaction; on timer(5s)` means that the defer block will be triggered if the user interacts with the placeholder, or after 5 seconds. - -Note: Multiple `on` triggers are always OR conditions. Similarly, `on` mixed with `when` conditions are also OR conditions. - -```angular-html -@defer (on viewport; on timer(5s)) { - -} @placeholder { - -} -``` - - -`when` specifies a condition as an expression that returns a boolean. When this expression becomes truthy, the placeholder is swapped with the lazily loaded content (which may be an asynchronous operation if the dependencies need to be fetched). - -Note: if the `when` condition switches back to `false`, the defer block is not reverted back to the placeholder. The swap is a one-time operation. If the content within the block should be conditionally rendered, an `if` condition can be used within the block itself. - -```angular-html -@defer (when cond) { - -} -``` - -You could also use both `when` and `on` together in one statement, and the swap will be triggered if either condition is met. - -```angular-html -@defer (on viewport; when cond) { - -} @placeholder { - -} -``` - -### on idle - -`idle` will trigger the deferred loading once the browser has reached an idle state (detected using the `requestIdleCallback` API under the hood). This is the default behavior with a defer block. - -### on viewport - -`viewport` would trigger the deferred block when the specified content enters the viewport using the [`IntersectionObserver` API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). This could be the placeholder content or an element reference. - -By default, the placeholder will act as the element watched for entering viewport as long as it is a single root element node. - -```angular-html -@defer (on viewport) { - -} @placeholder { -
Calendar placeholder
-} -``` - -Alternatively, you can specify a [template reference variable](guide/templates/variables#template-reference-variables) in the same template as the `@defer` block as the element that is watched to enter the viewport. This variable is passed in as a parameter on the viewport trigger. - -```angular-html -
Hello!
- -@defer (on viewport(greeting)) { - -} -``` - -### on interaction - -`interaction` will trigger the deferred block when the user interacts with the specified element through `click` or `keydown` events. - -By default, the placeholder will act as the interaction element as long as it is a single root element node. - -```angular-html -@defer (on interaction) { - -} @placeholder { -
Calendar placeholder
-} -``` - -Alternatively, you can specify a [template reference variable](guide/templates/variables#template-reference-variables) as the element that triggers interaction. This variable is passed in as a parameter on the interaction trigger. - -```angular-html - - -@defer (on interaction(greeting)) { - -} @placeholder { -
Calendar placeholder
-} -``` - -### on hover - -`hover` triggers deferred loading when the mouse has hovered over the trigger area. Events used for this are `mouseenter` and `focusin`. - -By default, the placeholder will act as the hover element as long as it is a single root element node. - -```angular-html -@defer (on hover) { - -} @placeholder { -
Calendar placeholder
-} -``` - -Alternatively, you can specify a [template reference variable](guide/templates/variables#template-reference-variables) as the hover element. This variable is passed in as a parameter on the hover trigger. - -```angular-html -
Hello!
- -@defer (on hover(greeting)) { - -} @placeholder { -
Calendar placeholder
-} -``` - -### on immediate - -`immediate` triggers the deferred load immediately, meaning once the client has finished rendering, the defer chunk would then start fetching right away. - -```angular-html -@defer (on immediate) { - -} @placeholder { -
Calendar placeholder
-} -``` - -### on timer - -`timer(x)` would trigger after a specified duration. The duration is required and can be specified in `ms` or `s`. - -```angular-html -@defer (on timer(500ms)) { - -} -``` - -## Prefetching - -`@defer` allows to specify conditions when prefetching of the dependencies should be triggered. You can use a special `prefetch` keyword. `prefetch` syntax works similarly to the main defer conditions, and accepts `when` and/or `on` to declare the trigger. - -In this case, `when` and `on` associated with defer controls when to render, and `prefetch when` and `prefetch on` controls when to fetch the resources. This enables more advanced behaviors, such as letting you start to prefetch resources before a user has actually seen or interacted with a defer block, but might interact with it soon, making the resources available faster. - -In the example below, the prefetching starts when a browser becomes idle and the contents of the block is rendered on interaction. - -```angular-html -@defer (on interaction; prefetch on idle) { - -} @placeholder { - -} -``` - -## Testing - -Angular provides TestBed APIs to simplify the process of testing `@defer` blocks and triggering different states during testing. By default, `@defer` blocks in tests will play through like a defer block would behave in a real application. If you want to manually step through states, you can switch the defer block behavior to `Manual` in the TestBed configuration. - -```typescript -it('should render a defer block in different states', async () => { - // configures the defer block behavior to start in "paused" state for manual control. - TestBed.configureTestingModule({deferBlockBehavior: DeferBlockBehavior.Manual}); - - @Component({ - // ... - template: ` - @defer { - - } @placeholder { - Placeholder - } @loading { - Loading... - } - ` - }) - class ComponentA {} - - // Create component fixture. - const componentFixture = TestBed.createComponent(ComponentA); - - // Retrieve the list of all defer block fixtures and get the first block. - const deferBlockFixture = (await componentFixture.getDeferBlocks())[0]; - - // Renders placeholder state by default. - expect(componentFixture.nativeElement.innerHTML).toContain('Placeholder'); - - // Render loading state and verify rendered output. - await deferBlockFixture.render(DeferBlockState.Loading); - expect(componentFixture.nativeElement.innerHTML).toContain('Loading'); - - // Render final state and verify the output. - await deferBlockFixture.render(DeferBlockState.Complete); - expect(componentFixture.nativeElement.innerHTML).toContain('large works!'); -}); -``` - -## Behavior with Server-side rendering (SSR) and Static site generation (SSG) - -When rendering an application on the server (either using SSR or SSG), defer blocks always render their `@placeholder` (or nothing if a placeholder is not specified). Triggers are ignored on the server. - -## Behavior with `NgModule` - -`@defer` blocks can be used in both standalone and NgModule-based components, directives and pipes. You can use standalone and NgModule-based dependencies inside of a `@defer` block, however **only standalone components, directives, and pipes can be deferred**. The NgModule-based dependencies would be included into the eagerly loaded bundle. - -## Nested `@defer` blocks and avoiding cascading loads - -There are cases where nesting multiple `@defer` blocks may cause cascading requests. An example of this would be when a `@defer` block with an immediate trigger has a nested `@defer` block with another immediate trigger. When you have nested `@defer` blocks, make sure that an inner one has a different set of conditions, so that they don't trigger at the same time, causing cascading requests. - -## Avoiding Layout Shifts - -It is a recommended best practice to not defer components that will be visible in the user's viewport on initial load. This will negatively affect Core Web Vitals by causing an increase in cumulative layout shift (CLS). If you choose to defer components in this area, it's best to avoid `immediate`, `timer`, `viewport`, and custom `when` conditions that would cause the content to be loaded during the initial render of the page. diff --git a/adev-ja/src/content/guide/defer.md b/adev-ja/src/content/guide/defer.md deleted file mode 100644 index 6a1b2f77c..000000000 --- a/adev-ja/src/content/guide/defer.md +++ /dev/null @@ -1,299 +0,0 @@ -# 遅延可能なビュー - -## 概要 - -遅延可能なビューは、コンポーネントテンプレート内で、そのテンプレート内の特定の依存関係の読み込みを遅延させるために使用できます。これらの依存関係には、コンポーネントやディレクティブ、パイプおよび関連するCSSが含まれます。この機能を使用するには、テンプレートのセクションを、読み込み条件を指定する `@defer` ブロックで宣言的にラップできます。 - -遅延可能なビューは一連の[トリガー](guide/defer#triggers)と[プリフェッチ](guide/defer#prefetching)、そして[プレースホルダー](guide/defer#placeholder)や[読み込み中](guide/defer#loading)、[エラー](guide/defer#error)の状態管理に使用されるいくつかのサブブロックをサポートしています。また、[`when`](guide/defer#when)と[`prefetch when`](guide/defer#prefetching)を使用してカスタム条件を作成できます。 - -```angular-html -@defer { - -} -``` - -## 遅延可能なビューを使用する理由 - -遅延可能なビュー(`@defer` ブロックとも呼ばれます)は、アプリケーションの初期バンドルサイズを削減したり、後で読み込まれる可能性のある重いコンポーネントを遅延させたりするために使用できる強力なツールです。これにより、初期読み込みが高速化され、Core Web Vitals(CWV)の結果が向上するはずです。コンポーネントの一部を後で読み込むように遅延させると、特に最大コンテンツフルペイント(LCP)と最初のバイトまでの時間(TTFB)が向上します。 - -Note: レイアウトシフトが発生する可能性のある遅延読み込みコンポーネントは、依存関係が読み込まれた後に折り畳み以下にあるか、ユーザーにまだ表示されていない場所に配置することを強くお勧めします。 - -## どの依存関係が遅延読み込み可能ですか? - -`@defer` ブロック内の依存関係が遅延されるためには、次の2つの条件を満たす必要があります。 - -1. スタンドアロンである必要があります。スタンドアロンではない依存関係は遅延できず、`@defer` ブロック内であっても、引き続き即時的に読み込まれます。 - -2. 同じファイルから、`@defer` ブロックの外側では直接参照されていない必要があります。これには、ViewChildクエリも含まれます。 - -遅延ブロックで使用されるコンポーネント、ディレクティブ、パイプの推移的な依存関係は、スタンドアロンまたはNgModuleベースにでき、引き続き遅延されます。 - -## ブロック - -`@defer` ブロックには、遅延読み込みプロセス中のさまざまな段階を適切に処理できるように、いくつかのサブブロックがあります。 - -### `@defer` - -メインの `@defer` ブロックの内容は、遅延読み込みされるコンテンツのセクションです。最初はレンダリングされず、すべてのコンテンツは、指定された[トリガー](guide/defer#triggers)または `when` 条件が満たされ、依存関係がフェッチされると表示されます。デフォルトでは、`@defer` ブロックは、ブラウザの状態が[アイドル状態](guide/defer#on-idle)になるとトリガーされます。 - -### `@placeholder` - -デフォルトでは、遅延ブロックは、トリガーされる前にコンテンツをレンダリングしません。`@placeholder` は、遅延ブロックがトリガーされる前に表示されるコンテンツを宣言するオプションのブロックです。このプレースホルダーコンテンツは、読み込みが完了すると、メインコンテンツに置き換えられます。プレースホルダーセクションには、プレーンHTML、コンポーネント、ディレクティブ、パイプなど、あらゆるコンテンツを使用できます。ただし、プレースホルダーブロックの依存関係は即時的に読み込まれることに注意してください。 - -Note: 最良のユーザー体験を実現するためには、常に `@placeholder` ブロックを指定する必要があります。 - -`@placeholder` ブロックは、プレースホルダーを表示する `minimum` 時間を指定するオプションのパラメーターを受け取ります。この `minimum` パラメーターは、ミリ秒 (ms) または秒 (s) の時間増分で指定されます。このパラメーターは、遅延された依存関係がすばやくフェッチされた場合に、プレースホルダーコンテンツの高速なちらつきを防ぐために存在します。`@placeholder` ブロックの `minimum` タイマーは、この `@placeholder` ブロックの初期レンダリングが完了した後に開始されます。 - -```angular-html -@defer { - -} @placeholder (minimum 500ms) { -

プレースホルダーコンテンツ

-} -``` - -Note: 特定のトリガーでは、[テンプレート参照変数](guide/templates/variables#template-reference-variables)のいずれか、または `@placeholder` のいずれかの存在が必要な場合があります。詳細については、[トリガー](guide/defer#triggers)セクションを参照してください。 - -### `@loading` - -`@loading` ブロックは、遅延された依存関係の読み込み中に表示されるコンテンツを宣言できるオプションのブロックです。その依存関係は即時的に読み込まれます(`@placeholder` と同様)。 - -たとえば、読み込みスピナーを表示できます。読み込みがトリガーされると、`@loading` ブロックは `@placeholder` ブロックを置き換えます。 - -`@loading` ブロックは、プレースホルダーを表示する `minimum` 時間と、読み込みを開始してから読み込みテンプレートを表示するまでの待ち時間 `after` を指定する2つのオプションのパラメーターを受け取ります。`minimum` と `after` パラメーターは、ミリ秒 (ms) または秒 (s) の時間増分で指定されます。`@placeholder` と同様に、これらのパラメーターは、遅延された依存関係がすばやくフェッチされた場合に、コンテンツの高速なちらつきを防ぐために存在します。`@loading` ブロックの `minimum` と `after` の両方のタイマーは、読み込みがトリガーされた直後に開始されます。 - -```angular-html -@defer { - -} @loading (after 100ms; minimum 1s) { - 読み込み中... -} -``` - -### `@error` - -`@error` ブロックを使用すると、遅延読み込みが失敗した場合に表示されるコンテンツを宣言できます。`@placeholder` や `@loading` と同様に、`@error` ブロックの依存関係は即時的に読み込まれます。`@error` ブロックはオプションです。 - -```angular-html -@defer { - -} @error { -

カレンダーを読み込めませんでした

-} -``` - -## トリガー - -`@defer` ブロックがトリガーされると、プレースホルダーコンテンツが遅延読み込みされたコンテンツに置き換えられます。このスワップがトリガーされるタイミングを構成するオプションは、`on` と `when` の2つです。 - - -`on` は、以下の利用可能なトリガーのリストからトリガーを使用して、トリガー条件を指定します。例としては、インタラクション時やビューポート時などがあります。 - -複数のイベントトリガーを一度に定義できます。たとえば、`on interaction; on timer(5s)` は、ユーザーがプレースホルダーとインタラクションした場合、または5秒後に遅延ブロックがトリガーされることを意味します。 - -Note: 複数の `on` トリガーは、常にOR条件です。同様に、`on` と `when` 条件を組み合わせた場合も、OR条件になります。 - -```angular-html -@defer (on viewport; on timer(5s)) { - -} @placeholder { - -} -``` - - -`when` は、ブール値を返す式として条件を指定します。この式が真になると、プレースホルダーは遅延読み込みされたコンテンツに置き換えられます(依存関係をフェッチする必要がある場合は非同期操作になる可能性があります)。 - -Note: `when` 条件が `false` に戻った場合、遅延ブロックはプレースホルダーに戻りません。スワップは1回限りの操作です。ブロック内のコンテンツを条件付きでレンダリングする必要がある場合は、ブロック自体内に `if` 条件を使用できます。 - -```angular-html -@defer (when cond) { - -} -``` - -`when` と `on` の両方を1つのステートメントで一緒に使用できます。いずれかの条件が満たされると、スワップがトリガーされます。 - -```angular-html -@defer (on viewport; when cond) { - -} @placeholder { - -} -``` - -### on idle - -`idle` は、ブラウザがアイドル状態になったときに遅延読み込みをトリガーします(内部的には `requestIdleCallback` APIを使用して検出されます)。これは、遅延ブロックのデフォルトの動作です。 - -### on viewport - -`viewport` は、指定されたコンテンツが[`IntersectionObserver` API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API)を使用してビューポートに入ったときに遅延ブロックをトリガーします。これは、プレースホルダーコンテンツまたは要素参照にできます。 - -デフォルトでは、プレースホルダーは、単一のルート要素ノードである限り、ビューポートに入るために監視される要素として機能します。 - -```angular-html -@defer (on viewport) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -または、`@defer` ブロックと同じテンプレート内の[テンプレート参照変数](guide/templates/variables#template-reference-variables)を、ビューポートに入るために監視される要素として指定できます。この変数は、ビューポートトリガーのパラメーターとして渡されます。 - -```angular-html -
こんにちは!
- -@defer (on viewport(greeting)) { - -} -``` - -### on interaction - -`interaction` は、ユーザーが `click` または `keydown` イベントで指定された要素とインタラクションしたときに遅延ブロックをトリガーします。 - -デフォルトでは、プレースホルダーは、単一のルート要素ノードである限り、インタラクション要素として機能します。 - -```angular-html -@defer (on interaction) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -または、[テンプレート参照変数](guide/templates/variables#template-reference-variables)をインタラクションをトリガーする要素として指定できます。この変数は、インタラクショントリガーのパラメーターとして渡されます。 - -```angular-html - - -@defer (on interaction(greeting)) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -### on hover - -`hover` は、マウスがトリガーエリアにホバーしたときに遅延読み込みをトリガーします。これに使用されるイベントは、`mouseenter` と `focusin` です。 - -デフォルトでは、プレースホルダーは、単一のルート要素ノードである限り、ホバー要素として機能します。 - -```angular-html -@defer (on hover) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -または、[テンプレート参照変数](guide/templates/variables#template-reference-variables)をホバー要素として指定できます。この変数は、ホバートリガーのパラメーターとして渡されます。 - -```angular-html -
こんにちは!
- -@defer (on hover(greeting)) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -### on immediate - -`immediate` は、遅延読み込みをすぐにトリガーします。つまり、クライアントがレンダリングを完了すると、遅延チャンクがすぐにフェッチを開始します。 - -```angular-html -@defer (on immediate) { - -} @placeholder { -
カレンダーのプレースホルダー
-} -``` - -### on timer - -`timer(x)` は、指定された期間後にトリガーされます。期間は必須であり、`ms` または `s` で指定できます。 - -```angular-html -@defer (on timer(500ms)) { - -} -``` - -## プリフェッチ - -`@defer` を使用すると、依存関係のプリフェッチをトリガーする条件を指定できます。`prefetch` キーワードを使用できます。`prefetch` 構文は、メインの遅延条件と同様に機能し、`when` または `on` を受け取ってトリガーを宣言します。 - -この場合、遅延に関連付けられた `when` と `on` はレンダリングするタイミングを制御し、`prefetch when` と `prefetch on` はリソースをフェッチするタイミングを制御します。これにより、ユーザーが実際に遅延ブロックを見たか、またはインタラクションしないうちにリソースのプリフェッチを開始してすぐにインタラクションする可能性があるため、リソースをより高速に利用できるようにするためのより高度な動作が可能になります。 - -次の例では、プリフェッチはブラウザがアイドル状態になったときに開始され、ブロックの内容はインタラクション時にレンダリングされます。 - -```angular-html -@defer (on interaction; prefetch on idle) { - -} @placeholder { - -} -``` - -## テスト - -Angularは、`@defer` ブロックのテストと、テスト中のさまざまな状態のトリガーを簡素化するTestBed APIを提供します。デフォルトでは、テスト内の `@defer` ブロックは、実際のアプリケーションで遅延ブロックが動作するのと同じように動作します。状態を手動でステップ実行する場合は、TestBedの構成で遅延ブロックの動作を `Manual` に切り替えることができます。 - -```typescript -it('should render a defer block in different states', async () => { - // configures the defer block behavior to start in "paused" state for manual control. - TestBed.configureTestingModule({deferBlockBehavior: DeferBlockBehavior.Manual}); - - @Component({ - // ... - template: ` - @defer { - - } @placeholder { - プレースホルダー - } @loading { - 読み込み中... - } - ` - }) - class ComponentA {} - - // Create component fixture. - const componentFixture = TestBed.createComponent(ComponentA); - - // Retrieve the list of all defer block fixtures and get the first block. - const deferBlockFixture = (await componentFixture.getDeferBlocks())[0]; - - // Renders placeholder state by default. - expect(componentFixture.nativeElement.innerHTML).toContain('プレースホルダー'); - - // Render loading state and verify rendered output. - await deferBlockFixture.render(DeferBlockState.Loading); - expect(componentFixture.nativeElement.innerHTML).toContain('読み込み中'); - - // Render final state and verify the output. - await deferBlockFixture.render(DeferBlockState.Complete); - expect(componentFixture.nativeElement.innerHTML).toContain('large works!'); -}); -``` - -## サーバーサイドレンダリング(SSR)と静的サイト生成(SSG)での動作 - -サーバーでアプリケーションをレンダリングする場合(SSRまたはSSGを使用)、遅延ブロックは常に `@placeholder` をレンダリングします(プレースホルダーが指定されていない場合は何もレンダリングしません)。トリガーはサーバーでは無視されます。 - -## `NgModule` での動作 - -`@defer` ブロックは、スタンドアロンコンポーネントとNgModuleベースのコンポーネント、ディレクティブ、パイプの両方で使用できます。スタンドアロンとNgModuleベースの依存関係を `@defer` ブロック内で使用できますが、**遅延できるのはスタンドアロンコンポーネント、ディレクティブ、パイプのみです**。NgModuleベースの依存関係は、即時的に読み込まれたバンドルに含まれます。 - -## ネストされた `@defer` ブロックとカスケード読み込みの回避 - -複数の `@defer` ブロックをネストすると、カスケード要求が発生することがあります。その例としては、immediateトリガーを持つ `@defer` ブロックに、別のimmediateトリガーを持つネストされた `@defer` ブロックがある場合が挙げられます。ネストされた `@defer` ブロックがある場合は、内側のブロックに異なる条件を設定して、同時にトリガーされないようにし、カスケード要求が発生しないようにしてください。 - -## レイアウトシフトの回避 - -初期読み込み時にユーザーのビューポートに表示されるコンポーネントは遅延させないことをお勧めします。これにより、累積レイアウトシフト(CLS)が増加し、Core Web Vitalsに悪影響を及ぼします。この領域のコンポーネントを遅延させる場合は、初期レンダリング中にコンテンツが読み込まれる原因となる `immediate`、`timer`、`viewport`、およびカスタム `when` 条件を避けるのが最適です。 diff --git a/adev-ja/src/content/guide/di/dependency-injection.en.md b/adev-ja/src/content/guide/di/dependency-injection.en.md index b35e72b0d..ce9aeb79a 100644 --- a/adev-ja/src/content/guide/di/dependency-injection.en.md +++ b/adev-ja/src/content/guide/di/dependency-injection.en.md @@ -50,9 +50,8 @@ In this case the `HeroService` becomes available to all instances of this compon For example: - + @Component({ - standalone: true, selector: 'hero-list', template: '...', providers: [HeroService] diff --git a/adev-ja/src/content/guide/di/dependency-injection.md b/adev-ja/src/content/guide/di/dependency-injection.md index 3074eb6fb..7b3616dd6 100644 --- a/adev-ja/src/content/guide/di/dependency-injection.md +++ b/adev-ja/src/content/guide/di/dependency-injection.md @@ -50,9 +50,8 @@ class HeroService {} 例: - + @Component({ - standalone: true, selector: 'hero-list', template: '...', providers: [HeroService] 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 6ae15ab2a..b3d5da195 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 @@ -816,9 +816,8 @@ Because the injector has only to look at the `ElementInjector` of the `` for the `AnimalService`. Instead, the injector will begin at the `` `ElementInjector`. - + @Component({ - standalone: true, selector: 'app-child', … viewProviders: [ @@ -853,9 +852,8 @@ If you just use `@Host()` for the injection of `AnimalService`, the result is do The `ChildComponent` configures the `viewProviders` so that the dog emoji is provided as `AnimalService` value. You can also see `@Host()` in the constructor: - + @Component({ - standalone: true selector: 'app-child', … viewProviders: [ @@ -886,9 +884,8 @@ export class ChildComponent { Add a `viewProviders` array with a third animal, hedgehog 🦔, to the `app.component.ts` `@Component()` metadata: - + @Component({ - standalone: true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ], @@ -1177,4 +1174,4 @@ style RootInjector fill:#BDD7EE,color:#000 - \ No newline at end of file + 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 4a61451b3..ea9e2c708 100644 --- a/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md +++ b/adev-ja/src/content/guide/di/hierarchical-dependency-injection.md @@ -816,9 +816,8 @@ Emoji from FlowerService: 🌺 `FlowerService` の例と同様に、コンストラクターに `@SkipSelf()` を追加すると、インジェクターは現在の `` の `ElementInjector` を `AnimalService` について検索しません。 代わりに、インジェクターは `` の `ElementInjector` で検索を開始します。 - + @Component({ - standalone: true, selector: 'app-child', … viewProviders: [ @@ -853,9 +852,8 @@ Emoji from FlowerService: 🌺 `ChildComponent` は、 `viewProviders` を構成して、犬の絵文字が `AnimalService` 値として提供されるようにします。 また、コンストラクターに `@Host()` があることもわかります。 - + @Component({ - standalone: true selector: 'app-child', … viewProviders: [ @@ -886,9 +884,8 @@ export class ChildComponent { 3番目の動物、ハリネズミ 🦔 を含む `viewProviders` 配列を、 `app.component.ts` の `@Component()` メタデータに追加します。 - + @Component({ - standalone: true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ], diff --git a/adev-ja/src/content/guide/di/overview.en.md b/adev-ja/src/content/guide/di/overview.en.md index 105c02042..d640e1d7b 100644 --- a/adev-ja/src/content/guide/di/overview.en.md +++ b/adev-ja/src/content/guide/di/overview.en.md @@ -2,7 +2,7 @@ "DI" is a design pattern and mechanism for creating and delivering some parts of an app to other parts of an app that require them. -Tip: Check out Angular's [Essentials](essentials/sharing-logic) before diving into this comprehensive guide. +Tip: Check out Angular's [Essentials](essentials/dependency-injection) before diving into this comprehensive guide. When you develop a smaller part of your system, like a module or a class, you may need to use features from other classes. For example, you may need an HTTP service to make backend calls. Dependency Injection, or DI, is a design pattern and mechanism for creating and delivering some parts of an application to other parts of an application that require them. Angular supports this design pattern and you can use it in your applications to increase flexibility and modularity. diff --git a/adev-ja/src/content/guide/di/overview.md b/adev-ja/src/content/guide/di/overview.md index 13e274ba0..f09899ec0 100644 --- a/adev-ja/src/content/guide/di/overview.md +++ b/adev-ja/src/content/guide/di/overview.md @@ -2,7 +2,7 @@ "DI" は、アプリケーションの一部を作成して、それらを必要とするアプリケーションの他の部分に提供するための設計パターンとメカニズムです。 -Tip: この包括的なガイドに取り組む前に、Angularの [基本概念](essentials/sharing-logic) を確認してください。 +Tip: この包括的なガイドに取り組む前に、Angularの [基本概念](essentials/dependency-injection) を確認してください。 モジュールやクラスなどのシステムのより小さな部分を開発する場合、他のクラスの機能を使用する必要がある場合があります。たとえば、バックエンドを呼び出すためにHTTPサービスが必要になる場合があります。依存性の注入 (DI) は、アプリケーションの一部を作成して、それらを必要とするアプリケーションの他の部分に提供するための設計パターンとメカニズムです。Angularはこの設計パターンをサポートしており、アプリケーションでこれを利用することで、柔軟性とモジュール性を高めることができます。 diff --git a/adev-ja/src/content/guide/directives/directive-composition-api.en.md b/adev-ja/src/content/guide/directives/directive-composition-api.en.md index bfb37cf50..c89832689 100644 --- a/adev-ja/src/content/guide/directives/directive-composition-api.en.md +++ b/adev-ja/src/content/guide/directives/directive-composition-api.en.md @@ -16,7 +16,6 @@ works similarly to applying the `MenuBehavior` to the `` element in ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior], @@ -32,7 +31,7 @@ and outputs are not exposed as part of the component's public API. See **Angular applies host directives statically at compile time.** You cannot dynamically add directives at runtime. -**Directives used in `hostDirectives` must be `standalone: true`.** +**Directives used in `hostDirectives` may not specify `standalone: false`.** **Angular ignores the `selector` of directives applied in the `hostDirectives` property.** @@ -44,7 +43,6 @@ in your component's API by expanding the entry in `hostDirectives`: ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ @@ -69,7 +67,6 @@ component: ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ @@ -108,14 +105,12 @@ export class Tooltip { } // MenuWithTooltip can compose behaviors from multiple other directives @Directive({ - standalone: true, hostDirectives: [Tooltip, Menu], }) export class MenuWithTooltip { } // CustomWidget can apply the already-composed behaviors from MenuWithTooltip @Directive({ - standalone: true, hostDirectives: [MenuWithTooltip], }) export class SpecializedMenuWithTooltip { } @@ -132,7 +127,6 @@ The following example shows minimal use of a host directive: ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior], @@ -160,13 +154,11 @@ example. export class Tooltip { } @Directive({ - standalone: true, hostDirectives: [Tooltip], }) export class CustomTooltip { } @Directive({ - standalone: true, hostDirectives: [CustomTooltip], }) export class EvenMoreCustomTooltip { } diff --git a/adev-ja/src/content/guide/directives/directive-composition-api.md b/adev-ja/src/content/guide/directives/directive-composition-api.md index e61481e29..a8036dbfc 100644 --- a/adev-ja/src/content/guide/directives/directive-composition-api.md +++ b/adev-ja/src/content/guide/directives/directive-composition-api.md @@ -16,7 +16,6 @@ Angularディレクティブは、再利用可能な動作をカプセル化す ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior], @@ -32,7 +31,7 @@ export class AdminMenu { } **Angularはコンパイル時に静的にホストディレクティブを適用します。** ランタイム時には動的にディレクティブを追加できません。 -**`hostDirectives` で使用されるディレクティブは `standalone: true` でなければなりません。** +**`hostDirectives` で使用されるディレクティブは `standalone: false` を指定できません。** **Angularは `hostDirectives` プロパティで適用されたディレクティブの `selector` を無視します。** @@ -44,7 +43,6 @@ export class AdminMenu { } ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ @@ -69,7 +67,6 @@ export class AdminMenu { } ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [{ @@ -108,14 +105,12 @@ export class Tooltip { } // MenuWithTooltip は、他の複数のディレクティブから動作を構成できます @Directive({ - standalone: true, hostDirectives: [Tooltip, Menu], }) export class MenuWithTooltip { } // CustomWidget は、すでに構成されている MenuWithTooltip からの動作を適用できます @Directive({ - standalone: true, hostDirectives: [MenuWithTooltip], }) export class SpecializedMenuWithTooltip { } @@ -132,7 +127,6 @@ export class SpecializedMenuWithTooltip { } ```typescript @Component({ - standalone: true, selector: 'admin-menu', template: 'admin-menu.html', hostDirectives: [MenuBehavior], @@ -160,13 +154,11 @@ export class AdminMenu { } export class Tooltip { } @Directive({ - standalone: true, hostDirectives: [Tooltip], }) export class CustomTooltip { } @Directive({ - standalone: true, hostDirectives: [CustomTooltip], }) export class EvenMoreCustomTooltip { } 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 08be31f42..4b25af1e4 100644 --- a/adev-ja/src/content/guide/directives/structural-directives.en.md +++ b/adev-ja/src/content/guide/directives/structural-directives.en.md @@ -79,7 +79,6 @@ Import `TemplateRef`, and `ViewContainerRef`. Inject `TemplateRef` and `ViewCont import {Directive, TemplateRef, ViewContainerRef} from '@angular/core'; @Directive({ - standalone: true, selector: '[select]', }) export class SelectDirective { diff --git a/adev-ja/src/content/guide/directives/structural-directives.md b/adev-ja/src/content/guide/directives/structural-directives.md index 4f6be22f3..df56f0da0 100644 --- a/adev-ja/src/content/guide/directives/structural-directives.md +++ b/adev-ja/src/content/guide/directives/structural-directives.md @@ -79,7 +79,6 @@ Angularは、ディレクティブクラスを作成し、テンプレートで import {Directive, TemplateRef, ViewContainerRef} from '@angular/core'; @Directive({ - standalone: true, selector: '[select]', }) export class SelectDirective { diff --git a/adev-ja/src/content/guide/http/making-requests.en.md b/adev-ja/src/content/guide/http/making-requests.en.md index 810cd99af..c29bb8ba4 100644 --- a/adev-ja/src/content/guide/http/making-requests.en.md +++ b/adev-ja/src/content/guide/http/making-requests.en.md @@ -247,7 +247,6 @@ Within a component, you can combine `@if` with the `async` pipe to render the UI import { AsyncPipe } from '@angular/common'; @Component({ - standalone: true, imports: [AsyncPipe], template: ` @if (user$ | async; as user) { diff --git a/adev-ja/src/content/guide/http/making-requests.md b/adev-ja/src/content/guide/http/making-requests.md index 5bb94b6e3..b52955944 100644 --- a/adev-ja/src/content/guide/http/making-requests.md +++ b/adev-ja/src/content/guide/http/making-requests.md @@ -247,7 +247,6 @@ export class UserService { import { AsyncPipe } from '@angular/common'; @Component({ - standalone: true, imports: [AsyncPipe], template: ` @if (user$ | async; as user) { diff --git a/adev-ja/src/content/guide/hybrid-rendering.md b/adev-ja/src/content/guide/hybrid-rendering.md new file mode 100644 index 000000000..7206518bc --- /dev/null +++ b/adev-ja/src/content/guide/hybrid-rendering.md @@ -0,0 +1,322 @@ +# Hybrid rendering + +## What is hybrid rendering? + +Hybrid rendering combines the benefits of server-side rendering (SSR), pre-rendering (also known as "static site generation" or SSG) and client-side rendering (CSR) to optimize your Angular application. It allows you to render different parts of your application using different strategies, giving you fine-grained control over how your app is delivered to users. + +Angular’s new **developer preview** server rendering APIs offer a more efficient and adaptable approach to building modern web applications. These APIs give you complete control over your app’s rendering, allowing for optimizations that enhance performance, Search Engine Optimization (SEO), and overall user experience. + +**Benefits of these new APIs:** + +- **Greater flexibility:** + - Leverage fine-grained control over rendering allows you to optimize for performance and user experience in different parts of your application. + - Choose the best rendering strategy for each route, whether it's server-side rendering for fast initial load times, client-side rendering for dynamic interactivity, or a hybrid approach. +- **Built-in internationalization (i18n):** + - Easily adapt your application to different languages and regions with out-of-the-box i18n support. +- **Environment agnostic:** + - Use these APIs with any JavaScript runtime environment, not just Node.js. + - Enjoy the benefits of enhanced rendering capabilities regardless of your technology stack. +- **Seamless dev server integration:** + - Take advantage of a smooth and efficient development experience from a fully integrated development server. + +This developer preview gives you a first look at these powerful new features. The Angular team encourages you to explore them and provide feedback to help shape the future of Angular server rendering. + +## Setting up hybrid rendering + +You can create a **new** project with server-side routing with the Angular CLI: + +```shell +ng new --ssr --server-routing +``` + +You can also add server-side routing to an existing project with the `ng add` command: + +```shell +ng add @angular/ssr --server-routing +``` + +## Server routing + +### Configuring server routes + +You can create a server route config by declaring an array of [`ServerRoute`](api/ssr/ServerRoute 'API reference') objects. This configuration typically lives in a file named `app.routes.server.ts`. + +```typescript +// app.routes.server.ts +import { RenderMode, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + { + path: '', // This renders the "/" route on the client (CSR) + renderMode: RenderMode.Client, + }, + { + path: 'about', // This page is static, so we prerender it (SSG) + renderMode: RenderMode.Prerender, + }, + { + path: 'profile', // This page requires user-specific data, so we use SSR + renderMode: RenderMode.Server, + }, + { + path: '**', // All other routes will be rendered on the server (SSR) + renderMode: RenderMode.Server, + }, +]; +``` + +You can add this config to your application using the [`provideServerRoutesConfig`](api/ssr/provideServerRoutesConfig 'API reference') function. + +```typescript +import { provideServerRoutesConfig } from '@angular/ssr'; +import { serverRoutes } from './app.routes.server'; + +// app.config.server.ts +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering(), + provideServerRoutesConfig(serverRoutes), + // ... other providers ... + ] +}; +``` + +When using the [App shell pattern](ecosystem/service-workers/app-shell), you must specify the route to be used as the app shell for client-side rendered routes. To do this, provide an options object with the `appShellRoute` property to [`provideServerRoutesConfig`](api/ssr/provideServerRoutesConfig 'API reference'): + +```typescript +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRoutesConfig(serverRoutes, { appShellRoute: 'shell' }), + // ... other providers ... + ] +}; +``` + +### Rendering modes + +The server routing configuration lets you specify how each route in your application should render by setting a [`RenderMode`](api/ssr/RenderMode 'API reference'): + +| Rendering mode | Description | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Server (SSR)** | Renders the application on the server for each request, sending a fully populated HTML page to the browser. See the [Server-Side Rendering (SSR) guide](guide/ssr) for more information. | +| **Client (CSR)** | Renders the application in the browser. This is the default Angular behavior. | +| **Prerender (SSG)** | Prerenders the application at build time, generating static HTML files for each route. See the [Prerendering guide](guide/prerendering) for more information. | + +#### Choosing a rendering mode + +Each rendering mode has different benefits and drawbacks. You can choose rendering modes based on the specific needs of your application. + +##### Client-side rendering + +Client-side rendering has the simplest development model, as you can write code that assumes it always runs in a web browser. This lets you use a wide range of client-side libraries that also assume they run in a browser. + +Client-side rendering generally has worse performance than other rendering modes, as it must download, parse, and execute your page's JavaScript before the user can see any rendered content. If your page fetches more data from the server as it renders, users also have to wait for those additional requests before they can view the complete content. + +If your page is indexed by search crawlers, client-side rendering may negatively affect search engine optimization (SEO), as search crawlers have limits to how much JavaScript they execute when indexing a page. + +When client-side rendering, the server does not need to do any work to render a page beyond serving static JavaScript assets. You may consider this factor if server cost is a concern. + +Applications that support installable, offline experiences with [service workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can rely on client-side rendering without needing to communicate with a server. + +##### Server-side rendering + +Server-side rendering offers faster page loads than client-side rendering. Instead of waiting for JavaScript to download and run, the server directly renders an HTML document upon receiving a request from the browser. The user experiences only the latency necessary for the server to fetch data and render the requested page. This mode also eliminates the need for additional network requests from the browser, as your code can fetch data during rendering on the server. + +Server-side rendering generally has excellent search engine optimization (SEO), as search crawlers receive a fully rendered HTML document. + +Server-side rendering requires you to author code that does not strictly depend on browser APIs and limits your selection of JavaScript libraries that assume they run in a browser. + +When server-side rendering, your server runs Angular to produce an HTML response for every request. This additional cost may affect server hosting costs. + +##### Build-time prerendering + +Prerendering offers faster page loads than both client-side rendering and server-side rendering. Because prerendering creates HTML documents at _build-time_, the server can directly respond to requests with the static HTML document without any additional work. + +Prerendering requires that all information necessary to render a page is available at _build-time_. This means that prerendered pages cannot include any data to the specific user loading the page. This means that prerendering is primarily useful for pages that are the same for all users of your application. + +Because prerendering occurs at build-time, it may add significant time to your production builds. Using [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') to produce a large number of HTML documents may affect the total file size of your deployments, and thus lead to slower deployments. + +It may also add time to your deployments based on the number of static HTML documents included in your build output. + +Prerendering generally has excellent search engine optimization (SEO), as search crawlers receive a fully rendered HTML document. + +Prerendering requires you to author code that does not strictly depend on browser APIs and limits your selection of JavaScript libraries that assume they run in a browser. + +Prerendering incurs extremely little overhead per server request, as your server responds with static HTML documents. Static files are also easily cached by Content Delivery Networks (CDNs), browsers, and intermediate caching layers for even faster subsequent page loads. Deploying static HTML files to a CDN improves scalability by offloading work from your application web server, which is impactful for high-traffic applications. + +### Setting headers and status codes + +You can set custom headers and status codes for individual server routes using the `headers` and `status` properties in the `ServerRoute` configuration. + +```typescript +// app.routes.server.ts +import { RenderMode, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + { + path: 'profile', + renderMode: RenderMode.Server, + headers: { + 'X-My-Custom-Header': 'some-value', + }, + status: 201, + }, + // ... other routes +]; +``` + +### Redirects + +Angular handles redirects specified by the [`redirectTo`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') property in route configurations, differently on the server-side. + +**Server-Side Rendering (SSR)** +Redirects are performed using standard HTTP redirects (e.g., 301, 302) within the server-side rendering process. + +**Prerendering (SSG)** +Redirects are implemented as "soft redirects" using `` tags in the prerendered HTML. This allows for redirects without requiring a round trip to the server. + +### Customizing build-time prerendering (SSG) + +When using [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), you can specify several configuration options to customize the prerendering and serving process. + +#### Parameterized routes + +For each route with [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), you can specify a [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') function. This function lets you control which specific parameters produce separate prerendered documents. + +The [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') function returns a `Promise` that resolves to an array of objects. Each object is a key-value map of route parameter name to value. For example, if you define a route like `posts/:id`, `getPrerenderParams ` could return the array `[{id: 123}, {id: 456}]`, and thus render separate documents for `posts/123` and `posts/456`. + +The body of [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') can use Angular's [`inject`](api/core/inject 'API reference') function to inject dependencies and perform any work to determine which routes to prerender. This typically includes making requests to fetch data to construct the array of parameter values. + +```typescript +// app.routes.server.ts +import { RenderMode, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + { + path: 'post/:id', + renderMode: RenderMode.Prerender, + async getPrerenderParams() { + const dataService = inject(PostService); + const ids = await dataService.getIds(); // Assuming this returns ['1', '2', '3'] + + return ids.map(id => ({ id })); // Transforms IDs into an array of objects for prerendering + + // This will prerender the paths: `/post/1`, `/post/2` and `/post/3` + }, + }, +]; +``` + +Because [`getPrerenderParams`](api/ssr/ServerRoutePrerenderWithParams#getPrerenderParams 'API reference') exclusively applies to [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference'), this function always runs at _build-time_. `getPrerenderParams` must not rely on any browser-specific or server-specific APIs for data. If the route does not specify a [`fallback`](api/ssr/ServerRoutePrerenderWithParams#fallback 'API reference') option, the route falls back to [`PrerenderFallback.Server`](api/ssr/PrerenderFallback#Server 'API reference') (SSR) by default. + +IMPORTANT: When using [`inject`](api/core/inject 'API reference') inside `getPrerenderParams`, please remember that `inject` must be used synchronously. It cannot be invoked within asynchronous callbacks or following any `await` statements. For more information, refer to [`runInInjectionContext` API reference](api/core/runInInjectionContext). + +#### Fallback strategies + +When using [`RenderMode.Prerender`](api/ssr/RenderMode#Prerender 'API reference') mode, you can specify a fallback strategy to handle requests for paths that haven't been prerendered. + +The available fallback strategies are: + +- **Server:** Fallback to server-side rendering. This is the **default** behavior if no `fallback` property is specified. +- **Client:** Fallback to client-side rendering. +- **None:** No fallback. Angular will not handle requests for paths that are not prerendered. + +```typescript +// app.routes.server.ts +import { RenderMode, PrerenderFallback, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + { + path: 'post/:id', + renderMode: RenderMode.Prerender, + fallback: PrerenderFallback.Client, // Fallback to CSR if not prerendered + async getPrerenderParams() { + // This function returns an array of objects representing prerendered posts at the paths: + // `/post/1`, `/post/2`, and `/post/3`. + // The path `/post/4` will utilize the fallback behavior if it's requested. + return [{ id: 1 }, { id: 2 }, { id: 3 }]; + }, + }, +]; +``` + +## Accessing Request and Response via DI + +The `@angular/core` package provides several tokens for interacting with the server-side rendering environment. These tokens give you access to crucial information and objects within your Angular application during SSR. + +- **[`REQUEST`](api/core/REQUEST 'API reference'):** Provides access to the current request object, which is of type [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) from the Web API. This allows you to access headers, cookies, and other request information. +- **[`RESPONSE_INIT`](api/core/RESPONSE_INIT 'API reference'):** Provides access to the response initialization options, which is of type [`ResponseInit`](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#parameters) from the Web API. This allows you to set headers and the status code for the response dynamically. Use this token to set headers or status codes that need to be determined at runtime. +- **[`REQUEST_CONTEXT`](api/core/REQUEST_CONTEXT 'API reference'):** Provides access to additional context related to the current request. This context can be passed as the second parameter of the [`handle`](api/ssr/AngularAppEngine#handle 'API reference') function. Typically, this is used to provide additional request-related information that is not part of the standard Web API. + +```angular-ts +import { inject, REQUEST } from '@angular/core'; + +@Component({ + selector: 'app-my-component', + template: `

My Component

`, +}) +export class MyComponent { + constructor() { + const request = inject(REQUEST); + console.log(request?.url); + } +} +``` + +IMPORTANT: The above tokens will be `null` in the following scenarios:

+ +- During the build processes. +- When the application is rendered in the browser (client-side rendering). +- When performing static site generation (SSG). +- During route extraction in development (at the time of the request). + +## Configuring a non-Node.js Server + +The `@angular/ssr` provides essential APIs for server-side rendering your Angular application on platforms other than Node.js. It leverages the standard [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects from the Web API, enabling you to integrate Angular SSR into various server environments. For detailed information and examples, refer to the [`@angular/ssr` API reference](api/ssr/node/AngularAppEngine). + +```typescript +// server.ts +import { AngularAppEngine, createRequestHandler } from '@angular/ssr'; + +const angularApp = new AngularAppEngine(); + +/** + * This is a request handler used by the Angular CLI (dev-server and during build). + */ +const reqHandler = createRequestHandler(async (req: Request) => { + const res: Response|null = await angularApp.render(req); + + // ... +}); +``` + +## Configuring a Node.js Server + +The `@angular/ssr/node` extends `@angular/ssr` specifically for Node.js environments. It provides APIs that make it easier to implement server-side rendering within your Node.js application. For a complete list of functions and usage examples, refer to the [`@angular/ssr/node` API reference](api/ssr/node/AngularNodeAppEngine) API reference. + +```typescript +// server.ts +import { AngularNodeAppEngine, createNodeRequestHandler, writeResponseToNodeResponse } from '@angular/ssr/node'; +import express from 'express'; + +const app = express(); +const angularApp = new AngularNodeAppEngine(); + +app.get('*', (req, res, next) => + angularApp + .handle(req) + .then(response => { + if (response) { + writeResponseToNodeResponse(response, res); + } else { + next(); // Pass control to the next middleware + } + }) + .catch(next); +}); + +/** + * The request handler used by the Angular CLI (dev-server and during build). + */ +export const reqHandler = createNodeRequestHandler(app); +```v diff --git a/adev-ja/src/content/guide/hydration.en.md b/adev-ja/src/content/guide/hydration.en.md index 966d16ae2..b5baa63d9 100644 --- a/adev-ja/src/content/guide/hydration.en.md +++ b/adev-ja/src/content/guide/hydration.en.md @@ -75,8 +75,6 @@ bootstrapApplication(App, { }); ``` -IMPORTANT: the Event Replay feature is currently in [Developer Preview](/reference/releases#developer-preview). - ## Constraints Hydration imposes a few constraints on your application that are not present without hydration enabled. Your application must have the same generated DOM structure on both the server and the client. The process of hydration expects the DOM tree to have the same structure in both places. This also includes whitespaces and comment nodes that Angular produces during the rendering on the server. Those whitespaces and nodes must be present in the HTML generated by the server-side rendering process. @@ -99,10 +97,10 @@ There are a few cases where if you have a component template that does not have As an example, here are some of the most common cases of this issue. -* `` without a `` -* `
` without a `` +- `
` inside a `

` +- `` inside an `

` +- `` inside another `` If you are uncertain about whether your HTML is valid, you can use a [syntax validator](https://validator.w3.org/) to check it. @@ -174,3 +172,7 @@ bootstrapApplication(AppComponent, { ## Third Party Libraries with DOM Manipulation There are a number of third party libraries that depend on DOM manipulation to be able to render. D3 charts is a prime example. These libraries worked without hydration, but they may cause DOM mismatch errors when hydration is enabled. For now, if you encounter DOM mismatch errors using one of these libraries, you can add the `ngSkipHydration` attribute to the component that renders using that library. + +## Incremental Hydration + +Incremental hydration is an advanced form of hydration that allows for more granular control over when hydration happens. See the [incremental hydration guide](guide/incremental-hydration) for more information. diff --git a/adev-ja/src/content/guide/hydration.md b/adev-ja/src/content/guide/hydration.md index 0185ca914..a86cb685a 100644 --- a/adev-ja/src/content/guide/hydration.md +++ b/adev-ja/src/content/guide/hydration.md @@ -75,8 +75,6 @@ bootstrapApplication(App, { }); ``` -IMPORTANT: イベント再生機能は現在[開発者プレビュー](/reference/releases#developer-preview)です。 - ## 制約 {#constraints} ハイドレーションは、ハイドレーションが有効になっていない場合に存在しない、アプリケーションにいくつかの制約を課します。アプリケーションは、サーバーとクライアントの両方で同じDOM構造を生成する必要があります。ハイドレーションのプロセスは、DOMツリーが両方の場所で同じ構造を持つことを期待します。これには、Angularがサーバーでのレンダリング中に生成する空白やコメントノードも含まれます。これらの空白とノードは、サーバーサイドレンダリングプロセスによって生成されたHTMLに存在する必要があります。 @@ -99,10 +97,10 @@ IMPORTANT: サーバーサイドレンダリング操作によって生成され たとえば、以下は、この問題の最も一般的なケースです。 -* `

`なしの`
` -* `

`内の`

`なしの`
` +- `

`内の`

` +- `

`内の`` +- 別の``内の`` HTMLの有効性について不確かであれば、[構文バリデーター](https://validator.w3.org/)を使用して確認できます。 @@ -174,3 +172,7 @@ bootstrapApplication(AppComponent, { ## DOM操作を行うサードパーティライブラリ DOM操作に依存してレンダリングを行うサードパーティライブラリがいくつかあります。D3チャートが代表的な例です。これらのライブラリはハイドレーションなしで動作していましたが、ハイドレーションが有効になっているとDOMミスマッチエラーが発生する可能性があります。現時点では、これらのライブラリのいずれかを使用してDOMミスマッチエラーが発生した場合は、そのライブラリを使用してレンダリングを行うコンポーネントに`ngSkipHydration`属性を追加できます。 + +## インクリメンタルハイドレーション + +インクリメンタルハイドレーションはハイドレーションの高度な形態であり、ハイドレーションが起こるタイミングをより細かくコントロールできます。詳しくは[インクリメンタルハイドレーションのガイド](guide/incremental-hydration)を参照してください。 diff --git a/adev-ja/src/content/guide/image-optimization.md b/adev-ja/src/content/guide/image-optimization.md index 44c82a78c..2d80daa07 100644 --- a/adev-ja/src/content/guide/image-optimization.md +++ b/adev-ja/src/content/guide/image-optimization.md @@ -248,6 +248,8 @@ If you haven't used `sizes` before, a good place to start is to set it based on If you find that the above does not cover your desired image behavior, see the documentation on [advanced sizes values](#advanced-sizes-values). +Note that `NgOptimizedImage` automatically prepends `"auto"` to the provided `sizes` value. This is an optimization that increases the accuracy of srcset selection on browsers which support `sizes="auto"`, and is ignored by browsers which do not. + By default, the responsive breakpoints are: `[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]` diff --git a/adev-ja/src/content/guide/incremental-hydration.md b/adev-ja/src/content/guide/incremental-hydration.md new file mode 100644 index 000000000..6ba5a5534 --- /dev/null +++ b/adev-ja/src/content/guide/incremental-hydration.md @@ -0,0 +1,212 @@ +# Incremental Hydration + +Tip: Incremental hydration is currently in [developer preview](/reference/releases#developer-preview). + +**Incremental hydration** is an advanced type of [hydration](guide/hydration) that can leave sections of your application dehydrated and _incrementally_ trigger hydration of those sections as they are needed. + +## Why use incremental hydration? + +Incremental hydration is a performance improvement that builds on top of full application hydration. It can produce smaller initial bundles while still providing an end-user experience that is comparable to a full application hydration experience. Smaller bundles improve initial load times, reducing [First Input Delay (FID)](<(https://web.dev/fid)>) and [Cumulative Layout Shift (CLS)](https://web.dev/cls). + +Incremental hydration also lets you use deferrable views (`@defer`) for content that may not have been deferrable before. Specifically, you can now use deferrable views for content that is above the fold. Prior to incremental hydration, putting a `@defer` block above the fold would result in placeholder content rendering and then being replaced by the `@defer` block's main template content. This would result in a layout shift. Incremental hydration means the main template of the `@defer` block will render with no layout shift on hydration. + +## How do you enable incremental hydration in Angular? + +You can enable incremental hydration for applications that already use server-side rendering (SSR) with hydration. Follow the [Angular SSR Guide](guide/ssr) to enable server-side rendering and the [Angular Hydration Guide](guide/hydration) to enable hydration first. + +Enable incremental hydration by adding the `withIncrementalHydration()` function to the `provideClientHydration` provider. + +```typescript +import { + bootstrapApplication, + provideClientHydration, + withIncrementalHydration, +} from '@angular/platform-browser'; +... + +bootstrapApplication(AppComponent, { + providers: [provideClientHydration(withIncrementalHydration())] +}); +``` + +Incremental Hydration depends on and enables event replay automatically. If you already have `withEventReplay()` in your list, you can safely remove it after enabling incremental hydration. + +## How does incremental hydration work? + +Incremental hydration builds on top of full-application [hydration](guide/hydration), [deferrable views](guide/defer), and [event replay](guide/hydration#capturing-and-replaying-events). With incremental hydration, you can add additional triggers to `@defer` blocks that define incremental hydration boundaries. Adding a `hydrate` trigger to a defer block tells Angular that it should load that defer block's dependencies during server-side rendering and render the main template rather than the `@placeholder`. When client-side rendering, the dependencies are still deferred, and the defer block content stays dehydrated until its `hydrate` trigger fires. That trigger tells the defer block to fetch its dependencies and hydrate the content. Any browser events, specifically those that match listeners registered in your component, that are triggered by the user prior to hydration are queued up and replayed once the hydration process is complete. + +## Controlling hydration of content with triggers + +You can specify **hydrate triggers** that control when Angular loads and hydrates deferred content. These are additional triggers that can be used alongside regular `@defer` triggers. + +Each `@defer` block may have multiple hydrate event triggers, separated with a semicolon (`;`). Angular triggers hydration when _any_ of the triggers fire. + +There are three types of hydrate triggers: `hydrate on`, `hydrate when`, and `hydrate never`. + +### `hydrate on` + +`hydrate on` specifies a condition for when hydration is triggered for the `@defer` block. + +The available triggers are as follows: + +| Trigger | Description | +| --------------------------------------------------- | ---------------------------------------------------------------------- | +| [`hydrate on idle`](#hydrate-on-idle) | Triggers when the browser is idle. | +| [`hydrate on viewport`](#hydrate-on-viewport) | Triggers when specified content enters the viewport | +| [`hydrate on interaction`](#hydrate-on-interaction) | Triggers when the user interacts with specified element | +| [`hydrate on hover`](#hydrate-on-hover) | Triggers when the mouse hovers over specified area | +| [`hydrate on immediate`](#hydrate-on-immediate) | Triggers immediately after non-deferred content has finished rendering | +| [`hydrate on timer`](#hydrate-on-timer) | Triggers after a specific duration | + +#### `hydrate on idle` + +The `hydrate on idle` trigger loads the deferrable view's dependencies and hydrates the content once the browser has reached an idle state, based on `requestIdleCallback`. + +```angular-html +@defer (hydrate on idle) { + +} @placeholder { +
Large component placeholder
+} +``` + +#### `hydrate on viewport` + +The `hydrate on viewport` trigger loads the deferrable view's dependencies and hydrates the corresponding page of the app when the specified content enters the viewport using the +[Intersection Observer API](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API). + +```angular-html +@defer (hydrate on viewport) { + +} @placeholder { +
Large component placeholder
+} +``` + +#### `hydrate on interaction` + +The `hydrate on interaction` trigger loads the deferrable view's dependencies and hydrates the content when the user interacts with the specified element through +`click` or `keydown` events. + +```angular-html +@defer (hydrate on interaction) { + +} @placeholder { +
Large component placeholder
+} +``` + +#### `hydrate on hover` + +The `hydrate on hover` trigger loads the deferrable view's dependencies and hydrates the content when the mouse has hovered over the triggered area through the +`mouseover` and `focusin` events. + +```angular-html +@defer (hydrate on hover) { + +} @placeholder { +
Large component placeholder
+} +``` + +#### `hydrate on immediate` + +The `hydrate on immediate` trigger loads the deferrable view's dependencies and hydrates the content immediately. This means that the deferred block loads as soon +as all other non-deferred content has finished rendering. + +```angular-html +@defer (hydrate on immediate) { + +} @placeholder { +
Large component placeholder
+} +``` + +#### `hydrate on timer` + +The `hydrate on timer` trigger loads the deferrable view's dependencies and hydrates the content after a specified duration. + +```angular-html +@defer (hydrate on timer(500ms)) { + +} @placeholder { +
Large component placeholder
+} +``` + +The duration parameter must be specified in milliseconds (`ms`) or seconds (`s`). + +### `hydrate when` + +The `hydrate when` trigger accepts a custom conditional expression and loads the deferrable view's dependencies and hydrates the content when the +condition becomes truthy. + +```angular-html +@defer (hydrate when condition) { + +} @placeholder { +
Large component placeholder
+} +``` + +Note: `hydrate when` conditions only trigger when they are the top-most dehydrated `@defer` block. The condition provided for the trigger is +specified in the parent component, which needs to exist before it can be triggered. If the parent block is dehydrated, that expression will not yet +be resolvable by Angular. + +### `hydrate never` + +The `hydrate never` allows users to specify that the content in the defer block should remain dehydrated indefinitely, effectively becoming static +content. Note that this applies to the initial render only. During a subsequent client-side render, a `@defer` block with `hydrate never` would +still fetch dependencies, as hydration only applies to initial load of server-side rendered content. In the example below, subsequent client-side +renders would load the `@defer` block dependencies on viewport. + +```angular-html +@defer (on viewport; hydrate never) { + +} @placeholder { +
Large component placeholder
+} +``` + +Note: Using `hydrate never` prevents hydration of the entire nested subtree of a given `@defer` block. No other `hydrate` triggers fire for content nested underneath that block. + +## Hydrate triggers alongside regular triggers + +Hydrate triggers are additional triggers that are used alongside regular triggers on a `@defer` block. Hydration is an initial load optimization, and that means hydrate triggers only apply to that initial load. Any subsequent client side render will still use the regular trigger. + +```angular-html +@defer (on idle; hydrate on interaction) { + +} @placeholder{ +
Example Placeholder
+} +``` + +In this example, on the initial load, the `hydrate on interaction` applies. Hydration will be triggered on interaction with the `` component. On any subsequent page load that is client-side rendered, for example when a user clicks a routerLink that loads a page with this component, the `on idle` will apply. + +## How does incremental hydration work with nested `@defer` blocks? + +Angular's component and dependency system is hierarchical, which means hydrating any component requires all of its parents also be hydrated. So if hydration is triggered for a child `@defer` block of a nested set of dehydrated `@defer` blocks, hydration is triggered from the top-most dehydrated `@defer` block down to the triggered child and fire in that order. + +```angular-html +@defer (hydrate on interaction) { + + @defer (hydrate on hover) { + + } @placeholder { +
Child placeholder
+ } +} @placeholder{ +
Parent Placeholder
+} +``` + +In the above example, hovering over the nested `@defer` block triggers hydration. The parent `@defer` block with the `` hydrates first, then the child `@defer` block with `` hydrates after. + +## Constraints + +Incremental hydration has the same constraints as full-application hydration, including limits on direct DOM manipulation and requiring valid HTML structure. Visit the [Hydration guide constraints](guide/hydration#constraints) section for more details. + +## Do I still need to specify `@placeholder` blocks? + +Yes. `@placeholder` block content is not used for incremental hydration, but a `@placeholder` is still necessary for subsequent client-side rendering cases. If your content was not on the route that was part of the initial load, then any navigation to the route that has your `@defer` block content renders like a regular `@defer` block. So the `@placeholder` is rendered in those client-side rendering cases. diff --git a/adev-ja/src/content/guide/ngmodules/api.md b/adev-ja/src/content/guide/ngmodules/api.md deleted file mode 100644 index b2c00b3fc..000000000 --- a/adev-ja/src/content/guide/ngmodules/api.md +++ /dev/null @@ -1,46 +0,0 @@ -# NgModule API - -At a high level, NgModules are a way to organize Angular applications and they accomplish this through the metadata in the `@NgModule` decorator. -The metadata falls into three categories: - -| Category | Details | -|:--- |:--- | -| Static | Compiler configuration which tells the compiler about directive selectors and where in templates the directives should be applied through selector matching. This is configured using the `declarations` array. | -| Runtime | Injector configuration using the `providers` array. | -| Composability / Grouping | Bringing NgModules together and making them available using the `imports` and `exports` arrays. | - - - -@NgModule({ - // Static, that is compiler configuration - declarations: [], // Configure the selectors - - // Runtime, or injector configuration - providers: [], // Runtime injector configuration - - // Composability / Grouping - imports: [], // composing NgModules together - exports: [] // making NgModules available to other parts of the app -}) - - - -## `@NgModule` metadata - -The following table summarizes the `@NgModule` metadata properties. - -| Property | Details | -|:--- |:--- | -| `declarations` | A list of [declarable](guide/ngmodules/faq#what-is-a-declarable?) classes (*components*, *directives*, and *pipes*) that *belong to this module*.
  1. When compiling a template, you need to determine a set of selectors which should be used for triggering their corresponding directives.
  2. The template is compiled within the context of an NgModule —the NgModule within which the template's component is declared— which determines the set of selectors using the following rules:
    • All selectors of directives listed in `declarations`.
    • All selectors of directives exported from imported NgModules.
Components, directives, and pipes must belong to *exactly* one module. The compiler emits an error if you try to declare the same class in more than one module. Be careful not to re-declare a class that is imported directly or indirectly from another module. | -| `providers` | A list of dependency-injection providers.
Angular registers these providers with the NgModule's injector. If it is the NgModule used for bootstrapping then it is the root injector.
These services become available for injection into any component, directive, pipe or service which is a child of this injector.
A lazy-loaded module has its own injector which is typically a child of the application root injector.
Lazy-loaded services are scoped to the lazy module's injector. If a lazy-loaded module also provides the `UserService`, any component created within that module's context (such as by router navigation) gets the local instance of the service, not the instance in the root application injector.
Components in external modules continue to receive the instance provided by their injectors.
For more information on injector hierarchy and scoping, see [Providers](guide/ngmodules/providers) and the [DI Guide](guide/di). | -| `imports` | A list of modules which should be folded into this module. Folded means it is as if all the imported NgModule's exported properties were declared here.
Specifically, it is as if the list of modules whose exported components, directives, or pipes are referenced by the component templates were declared in this module.
A component template can [reference](guide/ngmodules/faq#how-does-angular-find-components,-directives,-and-pipes-in-a-template?-what-is-a-template-reference?) another component, directive, or pipe when the reference is declared in this module or if the imported module has exported it. For example, a component can use the `NgIf` and `NgFor` directives only if the module has imported the Angular `CommonModule` (perhaps indirectly by importing `BrowserModule`).
You can import many standard directives from the `CommonModule` but some familiar directives belong to other modules. For example, you can use `[(ngModel)]` only after importing the Angular `FormsModule`. | -| `exports` | A list of declarations —*component*, *directive*, and *pipe* classes— that an importing module can use.
Exported declarations are the module's *public API*. A component in another module can use *this* module's `UserComponent` if it imports this module and this module exports `UserComponent`.
Declarations are private by default. If this module does *not* export `UserComponent`, then only the components within *this* module can use `UserComponent`.
Importing a module does *not* automatically re-export the imported module's imports. Module 'B' can't use `ngIf` just because it imported module 'A' which imported `CommonModule`. Module 'B' must import `CommonModule` itself.
A module can list another module among its `exports`, in which case all of that module's public components, directives, and pipes are exported.
[Re-export](guide/ngmodules/faq#what-should-i-export?) makes module transitivity explicit. If Module 'A' re-exports `CommonModule` and Module 'B' imports Module 'A', Module 'B' components can use `ngIf` even though 'B' itself didn't import `CommonModule`. | -| `bootstrap` | A list of components that are automatically bootstrapped.
Usually there's only one component in this list, the *root component* of the application.
Angular can launch with multiple bootstrap components, each with its own location in the host web page. | - -## More on NgModules - - - - - - diff --git a/adev-ja/src/content/guide/ngmodules/bootstrapping.md b/adev-ja/src/content/guide/ngmodules/bootstrapping.md deleted file mode 100644 index b2d0b12af..000000000 --- a/adev-ja/src/content/guide/ngmodules/bootstrapping.md +++ /dev/null @@ -1,152 +0,0 @@ -# Launching your app with a root module - -An NgModule describes how the application parts fit together. -Every application has at least one Angular module, the *root* module, which must be present for bootstrapping the application on launch. -By convention and by default, this NgModule is named `AppModule`. - -When you use the [Angular CLI](/tools/cli) `ng new` command with the `no-standalone` option to generate an app, the default `AppModule` looks like the following: - - -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } - - - -The `@NgModule` decorator identifies `AppModule` as an `NgModule` class. -`@NgModule` takes a metadata object that tells Angular how to compile and launch the application. - -| Metadata field | Details | -|:--- |:--- | -| `declarations` | Includes the *root* application component. | -| `imports` | Imports `BrowserModule` to enable browser-specific services (such as DOM rendering, sanitization) | -| `providers` | The service providers. | -| `bootstrap` | The *root* component that Angular creates and inserts into the `index.html` host web page. | - -## The `declarations` array - -The module's `declarations` array tells Angular which components belong to that module. -As you create more components, add them to `declarations`. - -The `declarations` array only takes declarables. -Declarables are [components](guide/components), [directives](guide/directives), and [pipes](guide/templates/pipes). -All of a module's declarables must be in the `declarations` array. -Declarables must belong to exactly one module. -The compiler returns an error if declare the same class in multiple modules. - -These declared classes are usable within the module but private to components in a different module, unless they are exported from this module and the other module imports this one. - -An example of what goes into a declarations array follows: - - - -declarations: [ - YourComponent, - YourPipe, - YourDirective -], - - - -### Using directives with `@NgModule` - -Use the `declarations` array for directives. -To use a directive, component, or pipe in a module, you must do a few things: - -1. Export it from the TypeScript file where you wrote it -2. Import it into the appropriate file containing the `@NgModule` class. -3. Declare it in the `@NgModule` `declarations` array. - -Those three steps look like the following. In the file where you create your directive, export it. -The following example shows an empty directive named `ItemDirective`. - - -import { Directive } from '@angular/core'; - -@Directive({ - selector: '[appItem]' -}) -export class ItemDirective { - // your code here -} - - -The key point here is that you have to export it, so that you can import it elsewhere. -Next, import it into the file in which your `NgModule` lives. In this example, this is the `app.module.ts` file. - - -import { ItemDirective } from './item.directive'; - - -And in the same file, add it to the `@NgModule` `declarations` array: - - - declarations: [ - AppComponent, - ItemDirective - ], - - -Now you can use `ItemDirective` in a component. -This example uses `AppModule`, but you would follow the same steps for a feature module. -For more about directives, see [Attribute Directives](guide/directives/attribute-directives) and [Structural Directives](guide/directives/structural-directives). -You'd also use the same technique for [pipes](guide/templates/pipes) and [components](guide/components). - -Remember, components, directives, and pipes belong to one module only. -You only need to declare them once in your application because you share them by importing the necessary modules. -This saves you time and helps keep your application lean. - -## The `imports` array - -Modules accept an `imports` array in the `@NgModule` metadata object. -It tells Angular about other NgModules that this particular module needs to function properly. - - - imports: [ - BrowserModule, - FormsModule, - HttpClientModule - ], - - -This list of modules are those that export components, directives, or pipes that component templates in this module reference. -In this case, the component is `AppComponent`, which references components, directives, or pipes in `BrowserModule`, `FormsModule`, or `HttpClientModule`. -A component template can reference another component, directive, or pipe when the referenced class is declared in this module, or the class was imported from another module. - -## The `providers` array - -The providers array is where you list the services the application needs. -When you list services here, they are available app-wide. -You can scope them when using feature modules and lazy loading. -For more information, see [Providers in modules](guide/ngmodules/providers). - -## The `bootstrap` array - -The application launches by bootstrapping the root `AppModule`. -The bootstrapping process creates the component(s) listed in the `bootstrap` array and inserts each one into the browser DOM, if it finds an element matching the component's `selector`. - -Each bootstrapped component is the base of its own tree of components. -Inserting a bootstrapped component usually triggers a cascade of component creations that builds up that tree. -While you can put more than one component tree on a host web page, most applications have only one component tree and bootstrap a single root component. - -The root component is commonly called `AppComponent` and is in the root module's `bootstrap` array. - -In a situation where you want to bootstrap a component based on an API response, -or you want to mount the `AppComponent` in a different DOM node that doesn't match the component selector, please refer to `ApplicationRef.bootstrap()` documentation. - -## More about Angular Modules - -See [Frequently Used Modules](guide/ngmodules/frequent) to learn more about modules you will commonly see in applications. diff --git a/adev-ja/src/content/guide/ngmodules/faq.md b/adev-ja/src/content/guide/ngmodules/faq.md deleted file mode 100644 index 8e7d4f49a..000000000 --- a/adev-ja/src/content/guide/ngmodules/faq.md +++ /dev/null @@ -1,428 +0,0 @@ -# NgModule FAQ - -NgModules help organize an application into cohesive blocks of functionality. - -This page answers the questions many developers ask about NgModule design and implementation. - -## What classes should I add to the `declarations` array? - -Add [declarable](guide/ngmodules/bootstrapping#the-declarations-array) classes —components, directives, and pipes— to a `declarations` list. - -Declare these classes in *exactly one* module of the application. -Declare them in a module if they belong to that particular module. - -## What is a `declarable`? - -Declarables are the class types —components, directives, and pipes— that you can add to a module's `declarations` list. -They're the only classes that you can add to `declarations`. - -## What classes should I *not* add to `declarations`? - -Add only [declarable](guide/ngmodules/bootstrapping#the-declarations-array) classes to an NgModule's `declarations` list. - -Do *not* declare the following: - -* A class that's already declared in another module, whether an application module, `@NgModule`, or third-party module. -* An array of directives imported from another module. - For example, don't declare `FORMS_DIRECTIVES` from `@angular/forms` because the `FormsModule` already declares it. -* Module classes. -* Service classes. -* Non-Angular classes and objects, such as strings, numbers, functions, entity models, configurations, business logic, and helper classes. - -## Why list the same component in multiple `NgModule` properties? - -`AppComponent` is often listed in both `declarations` and `bootstrap`. -You might see the same component listed in `declarations` and `exports`. - -While that seems redundant, these properties have different functions. -Membership in one list doesn't imply membership in another list. - -* `AppComponent` could be declared in this module but not bootstrapped. -* `AppComponent` could be bootstrapped in this module but declared in a different feature module. -* A component could be imported from another application module (so you can't declare it) and re-exported by this module. -* A component could be exported for inclusion in an external component's template as well as dynamically loaded in a pop-up dialog. - -## What does "Can't bind to 'x' since it isn't a known property of 'y'" mean? - -This error often means that you haven't declared the directive "x" or haven't imported the NgModule to which "x" belongs. - -HELPFUL: Perhaps you declared "x" in an application submodule but forgot to export it. -The "x" class isn't visible to other modules until you add it to the `exports` list. - -## What should I import? - -Import NgModules whose public (exported) [declarable classes](guide/ngmodules/bootstrapping#the-declarations-array) -you need to reference in this module's component templates. - -This always means importing `CommonModule` from `@angular/common` for access to -the Angular directives such as `NgIf` and `NgFor`. -You can import it directly or from another NgModule that [re-exports](#can-i-re-export-classes-and-modules?) it. - -Import [BrowserModule](#should-i-import-browsermodule-or-commonmodule?) only in the root `AppModule`. - -Import `FormsModule` from `@angular/forms` if your components have `[(ngModel)]` two-way binding expressions. - -Import *shared* and *feature* modules when your components use their components, directives, and pipes. - -## Should I import `BrowserModule` or `CommonModule`? - -The root application module, `AppModule`, of almost every browser application should import `BrowserModule` from `@angular/platform-browser`. -`BrowserModule` provides services that are essential to launch and run a browser application. - -`BrowserModule` also re-exports `CommonModule` from `@angular/common`, -which means that components in the `AppModule` also have access to -the Angular directives every application needs, such as `NgIf` and `NgFor`. - -Do not import `BrowserModule` in any other module. -*Feature modules* and *lazy-loaded modules* should import `CommonModule` instead. -They need the common directives. -They don't need to re-install the app-wide providers. - -Note: Importing `CommonModule` also frees feature modules for use on *any* target platform, not just browsers. - -## What if I import the same module twice? - -That's not a problem. -When three modules all import Module 'A', Angular evaluates Module 'A' once, the first time it encounters it, and doesn't do so again. - -That's true at whatever level `A` appears in a hierarchy of imported NgModules. -When Module 'B' imports Module 'A', Module 'C' imports 'B', and Module 'D' imports `[C, B, A]`, then 'D' triggers the evaluation of 'C', which triggers the evaluation of 'B', which evaluates 'A'. -When Angular gets to the 'B' and 'A' in 'D', they're already cached and ready to go. - -Angular doesn't like NgModules with circular references, so don't let Module 'A' import Module 'B', which imports Module 'A'. - -## What should I export? - -Export [declarable](guide/ngmodules/bootstrapping#the-declarations-array) classes that components in *other* NgModules should be able to use in their templates. -These are your *public* classes. -If you don't export a declarable class, it stays *private*, visible only to other components declared in this NgModule. - -You *can* export any declarable class —components, directives, and pipes— whether -it's declared in this NgModule or in an imported NgModule. - -You *can* re-export entire imported NgModules, which effectively re-export all of their exported classes. -An NgModule can even export a module that it doesn't import. - -## What should I *not* export? - -Don't export the following: - -* Private components, directives, and pipes that you need only within components declared in this NgModule. - If you don't want another NgModule to see it, don't export it. - -* Non-declarable objects such as services, functions, configurations, and entity models. -* Components that are only loaded dynamically by the router or by bootstrapping. - Such components can never be selected in another component's template. - While there's no harm in exporting them, there's also no benefit. - -* Pure service modules that don't have public (exported) declarations. - For example, there's no point in re-exporting `HttpClientModule` because it doesn't export anything. - Its only purpose is to add http service providers to the application as a whole. - -## Can I re-export classes and modules? - -Absolutely. - -NgModules are a great way to selectively aggregate classes from other NgModules and re-export them in a consolidated, convenience module. - -An NgModule can re-export entire NgModules, which effectively re-exports all of their exported classes. -Angular's own `BrowserModule` exports a couple of NgModules like this: - - - -exports: [CommonModule, ApplicationModule] - - - -An NgModule can export a combination of its own declarations, selected imported classes, and imported NgModules. - -Don't bother re-exporting pure service modules. -Pure service modules don't export [declarable](guide/ngmodules/bootstrapping#the-declarations-array) classes that another NgModule could use. -For example, there's no point in re-exporting `HttpClientModule` because it doesn't export anything. -Its only purpose is to add http service providers to the application as a whole. - -## What is the `forRoot()` method? - -The `forRoot()` static method is a convention that makes it easy for developers to configure services and providers that are intended to be singletons. -A good example of `forRoot()` is the `RouterModule.forRoot()` method. - -For more information on `forRoot()` see [the `forRoot()` pattern](guide/ngmodules/singleton-services#the-forroot()-pattern) section of the [Singleton Services](guide/ngmodules/singleton-services) guide. - -## Why is a service provided in a feature module visible everywhere? - -Providers listed in the `@NgModule.providers` of a bootstrapped module have application scope. -Adding a service provider to `@NgModule.providers` effectively publishes the service to the entire application. - -When you import an NgModule, Angular adds the module's service providers (the contents of its `providers` list) to the application root injector. - -This makes the provider visible to every class in the application that knows the provider's lookup token, or name. - -Extensibility through NgModule imports is a primary goal of the NgModule system. -Merging NgModule providers into the application injector makes it easy for a module library to enrich the entire application with new services. -By adding the `HttpClientModule` once, every application component can make HTTP requests. - -However, this might feel like an unwelcome surprise if you expect the module's services to be visible only to the components declared by that feature module. -If the `HeroModule` provides the `HeroService` and the root `AppModule` imports `HeroModule`, any class that knows the `HeroService` *type* can inject that service, not just the classes declared in the `HeroModule`. - -To limit access to a service, consider lazy loading the NgModule that provides that service. -See [How do I restrict service scope to a module?](#how-do-i-restrict-service-scope-to-a-module?) for more information. - -## Why is a service provided in a lazy-loaded module visible only to that module? - -Unlike providers of the modules loaded at launch, providers of lazy-loaded modules are *module-scoped*. - -When the Angular router lazy-loads a module, it creates a new execution context. -That [context has its own injector](#why-does-lazy-loading-create-a-child-injector? "Why Angular creates a child injector"), which is a direct child of the application injector. -The router adds the lazy module's providers and the providers of its imported NgModules to this child injector. - -These providers are insulated from changes to application providers with the same lookup token. -When the router creates a component within the lazy-loaded context, -Angular prefers service instances created from these providers to the service instances of the application root injector. - -## What if two modules provide the same service? - -When two imported modules, loaded at the same time, list a provider with the same token, the second module's provider "wins". -That's because both providers are added to the same injector. - -When Angular looks to inject a service for that token, it creates and delivers the instance created by the second provider. - -*Every* class that injects this service gets the instance created by the second provider. -Even classes declared within the first module get the instance created by the second provider. - -If NgModule A provides a service for token 'X' and imports an NgModule B that also provides a service for token 'X', then NgModule A's service definition "wins". - -The service provided by the root `AppModule` takes precedence over services provided by imported NgModules. -The `AppModule` always wins. - -## How do I restrict service scope to a module? - -When a module is loaded at application launch, its `@NgModule.providers` have *application-wide scope*; that is, they are available for injection throughout the application. - -Imported providers are easily replaced by providers from another imported NgModule. -Such replacement might be by design. -It could be unintentional and have adverse consequences. - -As a general rule, import modules with providers *exactly once*, preferably in the application's *root module*. -That's also usually the best place to configure, wrap, and override them. - -Suppose a module requires a customized `HttpBackend` that adds a special header for all Http requests. -If another module elsewhere in the application also customizes `HttpBackend` or merely imports the `HttpClientModule`, it could override this module's `HttpBackend` provider, losing the special header. -The server will reject http requests from this module. - -To avoid this problem, import the `HttpClientModule` only in the `AppModule`, the application *root module*. - -If you must guard against this kind of "provider corruption", *don't rely on a launch-time module's `providers`*. - -Load the module lazily if you can. -Angular gives a [lazy-loaded module](#why-is-a-service-provided-in-a-lazy-loaded-module-visible-only-to-that-module?) its own child injector. -The module's providers are visible only within the component tree created with this injector. - -### Alternative: Restricting scope to a component and its children - -Continuing with the same example, suppose the components of a module truly require a private, custom `HttpBackend`. - -Create a "top component" that acts as the root for all of the module's components. -Add the custom `HttpBackend` provider to the top component's `providers` list rather than the module's `providers`. -Recall that Angular creates a child injector for each component instance and populates the injector with the component's own providers. - -When a child of this component asks for the `HttpBackend` service, -Angular provides the local `HttpBackend` service, not the version provided in the application root injector. -Child components can then make configured HTTP requests no matter how other modules configure `HttpBackend`. - -Make sure to create components needing access to this special-configuration `HttpBackend` as children of this component. - -You can embed the child components in the top component's template. -Alternatively, make the top component a routing host by giving it a ``. -Define child routes and let the router load module components into that outlet. - -Though you can limit access to a service by providing it in a lazy loaded module or providing it in a component, providing services in a component can lead to multiple instances of those services. -Thus, the lazy loading is preferable. - -## Should I add application-wide providers to the root `AppModule` or the root `AppComponent`? - -Define application-wide providers by specifying `providedIn: 'root'` on its `@Injectable()` decorator (in the case of services) or at `InjectionToken` construction (in the case where tokens are provided). -Providers that are created this way automatically are made available to the entire application and don't need to be listed in any module. - -If a provider cannot be configured in this way \(perhaps because it has no sensible default value\), then register application-wide providers in the root `AppModule`, not in the `AppComponent`. - -Lazy-loaded modules and their components can inject `AppModule` services; they can't inject `AppComponent` services. - -Register a service in `AppComponent` providers *only* if the service must be hidden -from components outside the `AppComponent` tree. -This is a rare use case. - -More generally, [prefer registering providers in NgModules](#should-i-add-other-providers-to-a-module-or-a-component?) to registering in components. - -### Discussion - -Angular registers all startup module providers with the application root injector. -The services that root injector providers create have application scope, which means they are available to the entire application. - -Certain services, such as the `Router`, only work when you register them in the application root injector. - -By contrast, Angular registers `AppComponent` providers with the `AppComponent`'s own injector. -`AppComponent` services are available only to that component and its component tree. -They have component scope. - -The `AppComponent`'s injector is a child of the root injector, one down in the injector hierarchy. -For applications that don't use the router, that's almost the entire application. -But in routed applications, routing operates at the root level where `AppComponent` services don't exist. -This means that lazy-loaded modules can't reach them. - -## Should I add other providers to a module or a component? - -Providers should be configured using `@Injectable` syntax. -If possible, they should be provided in the application root (`providedIn: 'root'`). -Services that are configured this way are lazily loaded if they are only used from a lazily loaded context. - -If it's the consumer's decision whether a provider is available application-wide or not, then register providers in modules (`@NgModule.providers`) instead of registering in components (`@Component.providers`). - -Register a provider with a component when you *must* limit the scope of a service instance to that component and its component tree. -Apply the same reasoning to registering a provider with a directive. - -For example, an editing component that needs a private copy of a caching service should register the service with the component. -Then each new instance of the component gets its own cached service instance. -The changes that editor makes in its service don't touch the instances elsewhere in the application. - -[Always register *application-wide* services with the root `AppModule`](#should-i-add-application-wide-providers-to-the-root-appmodule-or-the-root-appcomponent?), not the root `AppComponent`. - -## Why is it bad if a shared module provides a service to a lazy-loaded module? - -### The eagerly loaded scenario - -When an eagerly loaded module provides a service, for example a `UserService`, that service is available application-wide. -If the root module provides `UserService` and imports another module that provides the same `UserService`, Angular registers one of them in the root application injector (see [What if I import the same module twice?](#what-if-i-import-the-same-module-twice?)). - -Then, when some component injects `UserService`, Angular finds it in the application root injector, and delivers the app-wide singleton service. -No problem. - -### The lazy loaded scenario - -Now consider a lazy loaded module that also provides a service called `UserService`. - -When the router lazy loads a module, it creates a child injector and registers the `UserService` provider with that child injector. -The child injector is *not* the root injector. - -When Angular creates a lazy component for that module and injects `UserService`, it finds a `UserService` provider in the lazy module's *child injector* -and creates a *new* instance of the `UserService`. -This is an entirely different `UserService` instance than the app-wide singleton version that Angular injected in one of the eagerly loaded components. - -This scenario causes your application to create a new instance every time, instead of using the singleton. - -## Why does lazy loading create a child injector? - -Angular adds `@NgModule.providers` to the application root injector, unless the NgModule is lazy-loaded. -For a lazy-loaded NgModule, Angular creates a *child injector* and adds the module's providers to the child injector. - -This means that an NgModule behaves differently depending on whether it's loaded during application start or lazy-loaded later. -Neglecting that difference can lead to [adverse consequences](#why-is-it-bad-if-a-shared-module-provides-a-service-to-a-lazy-loaded-module?). - -Why doesn't Angular add lazy-loaded providers to the application root injector as it does for eagerly loaded NgModules? - -The answer is grounded in a fundamental characteristic of the Angular dependency-injection system. -An injector can add providers *until it's first used*. -Once an injector starts creating and delivering services, its provider list is frozen; no new providers are allowed. - -When an application starts, Angular first configures the root injector with the providers of all eagerly loaded NgModules *before* creating its first component and injecting any of the provided services. -Once the application begins, the application root injector is closed to new providers. - -Time passes and application logic triggers lazy loading of an NgModule. -Angular must add the lazy-loaded module's providers to an injector somewhere. -It can't add them to the application root injector because that injector is closed to new providers. -So Angular creates a new child injector for the lazy-loaded module context. - -## How can I tell if an NgModule or service was previously loaded? - -Some NgModules and their services should be loaded only once by the root `AppModule`. -Importing the module a second time by lazy loading a module could [produce errant behavior](#why-is-it-bad-if-a-shared-module-provides-a-service-to-a-lazy-loaded-module?) that may be difficult to detect and diagnose. - -To prevent this issue, write a constructor that attempts to inject the module or service from the root application injector. -If the injection succeeds, the class has been loaded a second time. -You can throw an error or take other remedial action. - -Certain NgModules, such as `BrowserModule`, implement such a guard. -Here is a custom constructor for an NgModule called `GreetingModule`. - - -@NgModule({...}) -export class GreetingModule { - constructor(@Optional() @SkipSelf() parentModule?: GreetingModule) { - if (parentModule) { - throw new Error( - 'GreetingModule is already loaded. Import it in the AppModule only'); - } - } -} - - -## What kinds of modules should I have and how should I use them? - -Every application is different. -Developers have various levels of experience and comfort with the available choices. -Some suggestions and guidelines appear to have wide appeal. - -### `SharedModule` - -`SharedModule` is a conventional name for an `NgModule` with the components, directives, and pipes that you use everywhere in your application. -This module should consist entirely of `declarations`, most of them exported. - -The `SharedModule` may re-export other widget modules, such as `CommonModule`, `FormsModule`, and NgModules with the UI controls that you use most widely. - -The `SharedModule` should not have `providers` for reasons [explained previously](#why-is-it-bad-if-a-shared-module-provides-a-service-to-a-lazy-loaded-module?). -Nor should any of its imported or re-exported modules have `providers`. - -Import the `SharedModule` in your *feature* modules. - -### Feature Modules - -Feature modules are modules you create around specific application business domains, user workflows, and utility collections. -They support your application by containing a particular feature, such as routes, services, widgets, etc. -To conceptualize what a feature module might be in your app, consider that if you would put the files related to a certain functionality, like a search, in one folder, that the contents of that folder would be a feature module that you might call your `SearchModule`. -It would contain all of the components, routing, and templates that would make up the search functionality. - -For more information, see [Feature Modules](guide/ngmodules/feature-modules) and [Module Types](guide/ngmodules/module-types) - -## What's the difference between NgModules and JavaScript Modules? - -In an Angular app, NgModules and JavaScript modules work together. - -In modern JavaScript, every file is a module (see the [Modules](https://exploringjs.com/es6/ch_modules.html) page of the Exploring ES6 website). -Within each file you write an `export` statement to make parts of the module public. - -An Angular NgModule is a class with the `@NgModule` decorator —JavaScript modules don't have to have the `@NgModule` decorator. -Angular's `NgModule` has `imports` and `exports` and they serve a similar purpose. - -You *import* other NgModules so you can use their exported classes in component templates. -You *export* this NgModule's classes so they can be imported and used by components of *other* NgModules. - -For more information, see [JavaScript Modules vs. NgModules](guide/ngmodules/vs-jsmodule). - -## What is a template reference? - -How does Angular find components, directives, and pipes in a template? - -The [Angular compiler](#what-is-the-angular-compiler?) looks inside component templates for other components, directives, and pipes. -When it finds one, that's a template reference. - -The Angular compiler finds a component or directive in a template when it can match the *selector* of that component or directive to some HTML in that template. - -The compiler finds a pipe if the pipe's *name* appears within the pipe syntax of the template HTML. - -Angular only matches selectors and pipe names for classes that are declared by this module or exported by a module that this module imports. - -## What is the Angular compiler? - -The Angular compiler converts the application code you write into highly performant JavaScript code. -The `@NgModule` metadata plays an important role in guiding the compilation process. - -The code you write isn't immediately executable. -For example, components have templates that contain custom elements, attribute directives, Angular binding declarations, and some peculiar syntax that clearly isn't native HTML. - -The Angular compiler reads the template markup, combines it with the corresponding component class code, and emits *component factories*. - -A component factory creates a pure, 100% JavaScript representation of the component that incorporates everything described in its `@Component` metadata: -The HTML, the binding instructions, the attached styles. - -Because directives and pipes appear in component templates, the Angular compiler incorporates them into compiled component code too. - -`@NgModule` metadata tells the Angular compiler what components to compile for this module and how to link this module with other modules. diff --git a/adev-ja/src/content/guide/ngmodules/feature-modules.md b/adev-ja/src/content/guide/ngmodules/feature-modules.md deleted file mode 100644 index e26378f35..000000000 --- a/adev-ja/src/content/guide/ngmodules/feature-modules.md +++ /dev/null @@ -1,158 +0,0 @@ -# Feature modules - -Feature modules are NgModules for the purpose of organizing code. - -As your application grows, you can organize code relevant for a specific feature. -This helps apply clear boundaries for features. -With feature modules, you can keep code related to a specific functionality or feature separate from other code. -Delineating areas of your application helps with collaboration between developers and teams, separating directives, and managing the size of the root module. - -## Feature modules vs. root modules - -A feature module is an organizational best practice, as opposed to a concept of the core Angular API. -A feature module delivers a cohesive set of functionality focused on a specific application need such as a user workflow, routing, or forms. -While you can do everything within the root module, feature modules help you partition the application into focused areas. -A feature module collaborates with the root module and with other modules through the services it provides and the components, directives, and pipes that it shares. - -## How to make a feature module - -Assuming you already have an application that you created with the [Angular CLI](/tools/cli), create a feature module using the CLI by entering the following command in the root project directory. -You can omit the "Module" suffix from the name because the CLI appends it: - - - -ng generate module CustomerDashboard - - - -This causes the CLI to create a folder called `customer-dashboard` with a file inside called `customer-dashboard.module.ts` with the following contents: - - - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -@NgModule({ - imports: [ - CommonModule - ], - declarations: [] -}) -export class CustomerDashboardModule { } - - - -The structure of an NgModule is the same whether it is a root module or a feature module. -In the CLI generated feature module, there are two JavaScript import statements at the top of the file: the first imports `NgModule`, which, like the root module, lets you use the `@NgModule` decorator; the second imports `CommonModule`, which contributes many common directives such as `ngIf` and `ngFor`. - -Note: Feature modules import `CommonModule` instead of `BrowserModule`, which is only imported once in the root module. -`CommonModule` only contains information for common directives such as `ngIf` and `ngFor` which are needed in most templates, whereas `BrowserModule` configures the Angular application for the browser which needs to be done only once. - -The `declarations` array is available for you to add declarables, which are components, directives, and pipes that belong exclusively to this particular module. -To add a component, enter the following command at the command line where `customer-dashboard` is the directory where the CLI generated the feature module and `CustomerDashboard` is the name of the component: - - - -ng generate component customer-dashboard/CustomerDashboard - - - -This generates a folder for the new component within the `customer-dashboard` folder and updates `CustomerDashboardModule`. - - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { CustomerDashboardComponent } from './customer-dashboard/customer-dashboard.component'; - -@NgModule({ - imports: [ - CommonModule - ], - declarations: [ - CustomerDashboardComponent - ], - exports: [ - CustomerDashboardComponent - ] -}) -export class CustomerDashboardModule { } - - -The `CustomerDashboardComponent` is now in the JavaScript import list at the top and added to the `declarations` array, which lets Angular know to associate this new component with this feature module. - -## Importing a feature module - -To incorporate the feature module into your app, you have to let the root module, `app.module.ts`, know about it. -Notice the `CustomerDashboardModule` export at the bottom of `customer-dashboard.module.ts`. -This exposes it so that other modules can get to it. -To import it into the `AppModule`, add it to the imports in `app.module.ts` and to the `imports` array: - - -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; - -// import the feature module here so you can add it to the imports array below -import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - BrowserModule, - CustomerDashboardModule // add the feature module here - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } - - -Now the `AppModule` knows about the feature module and `AppComponent` can use the customer dashboard component. -More details on this in the section below. - -If you were to add any service providers to the feature module, `AppModule` would know about those too, as would any other imported feature modules. - -## Rendering a feature module's component template - -When the CLI generated the `CustomerDashboardComponent` for the feature module, it included a template, `customer-dashboard.component.html`, with the following markup: - - -

- customer-dashboard works! -

-
- -To see this HTML in the `AppComponent`, you first have to export the `CustomerDashboardComponent` in the `CustomerDashboardModule`. -In `customer-dashboard.module.ts`, just beneath the `declarations` array, add an `exports` array containing `CustomerDashboardComponent`: - - - exports: [ - CustomerDashboardComponent - ] - - -Next, in the `AppComponent`, `app.component.html`, add the tag ``: - - -

- {{title}} -

- - -
- -Now, in addition to the title that renders by default, the `CustomerDashboardComponent` template renders too: - -feature module component - -## More on NgModules - - - - - - diff --git a/adev-ja/src/content/guide/ngmodules/frequent.md b/adev-ja/src/content/guide/ngmodules/frequent.md deleted file mode 100644 index dea264009..000000000 --- a/adev-ja/src/content/guide/ngmodules/frequent.md +++ /dev/null @@ -1,53 +0,0 @@ -# Frequently-used modules - -A Module-based Angular application needs at least one module that serves as the root module. -As you add features to your app, you can add them in modules. -The following are frequently used Angular modules with examples of some of the things they contain: - -| NgModule | Import it from | Why you use it | -|:--- |:--- |:--- | -| `BrowserModule` | `@angular/platform-browser` | To run your application in a browser. | -| `CommonModule` | `@angular/common` | To use `NgIf` and `NgFor`. | -| `FormsModule` | `@angular/forms` | To build template driven forms \(includes `NgModel`\). | -| `ReactiveFormsModule` | `@angular/forms` | To build reactive forms. | -| `RouterModule` | `@angular/router` | To use `RouterLink`, `.forRoot()`, and `.forChild()`. | -| `HttpClientModule` | `@angular/common/http` | To communicate with a server using the HTTP protocol. | - -## Importing modules - -When you use these Angular modules, import them in `AppModule`, or your feature module as appropriate, and list them in the `@NgModule` `imports` array. - -For example, in a new application generated by the [Angular CLI](/tools/cli) with the `--no-standalone` option, `BrowserModule` is imported into the `AppModule`. - - -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; - -@NgModule({ - declarations: [ - AppComponent - ], - imports: [ - /* add modules here so Angular knows to use them */ - BrowserModule, - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } - - - -The imports at the top of the array are JavaScript import statements while the `imports` array within `@NgModule` is Angular specific. -For more information on the difference, see [JavaScript Modules vs. NgModules](guide/ngmodules/vs-jsmodule). - -## `BrowserModule` and `CommonModule` - -`BrowserModule` re-exports `CommonModule`, which exposes many common directives such as `ngIf` and `ngFor`. -These directives are available to any module that imports the browser module, given the re-export. - -For applications that run in the browser, import `BrowserModule` in the root `AppModule` because it provides services that are essential to launch and render your application in browsers. - -Note: `BrowserModule`'s providers are for the whole application so it should only be in the root module, not in feature modules. Feature modules only need the common directives in `CommonModule`; they don't need to re-install app-wide providers. diff --git a/adev-ja/src/content/guide/ngmodules/lazy-loading.md b/adev-ja/src/content/guide/ngmodules/lazy-loading.md deleted file mode 100644 index 39282f43b..000000000 --- a/adev-ja/src/content/guide/ngmodules/lazy-loading.md +++ /dev/null @@ -1,453 +0,0 @@ -# Lazy-loading feature modules - -By default, NgModules are eagerly loaded. This means that as soon as the application loads, so do all the NgModules, whether they are immediately necessary or not. -For large applications with lots of routes, consider lazy loading —a design pattern that loads NgModules as needed. -Lazy loading helps keep initial bundle sizes smaller, which in turn helps decrease load times. - - - - - -## Lazy loading basics - -This section introduces the basic procedure for configuring a lazy-loaded route. -For a step-by-step example, see the [step-by-step setup](#step-by-step-setup) section on this page. - -To lazy load Angular modules, use `loadChildren` (instead of `component`) in your `AppRoutingModule` `routes` configuration as follows. - - - -const routes: Routes = [ - { - path: 'items', - loadChildren: () => import('./items/items.module').then(m => m.ItemsModule) - } -]; - - - -In the lazy-loaded module's routing module, add a route for the component. - - - -const routes: Routes = [ - { - path: '', - component: ItemsComponent - } -]; - - - -Also be sure to remove the `ItemsModule` from the `AppModule`. -For step-by-step instructions on lazy loading modules, continue with the following sections of this page. - -## Step-by-step setup - -Setting up a lazy-loaded feature module requires two main steps: - -1. Create the feature module with the Angular CLI, using the `--route` flag. -1. Configure the routes. - -### Set up an application - -If you don't already have an application, follow the following steps to create one with the Angular CLI. -If you already have an application, skip to [Configure the routes](#imports-and-route-configuration). - -Enter the following command where `customer-app` is the name of your app: - - - -ng new customer-app --no-standalone --routing - - - -This creates an application called `customer-app`, `--no-standalone` flag makes the app module-based, and the `--routing` flag generates a file called `app-routing.module.ts`. -This is one of the files you need for setting up lazy loading for your feature module. -Navigate into the project by issuing the command `cd customer-app`. - -### Create a feature module with routing - -Next, you need a feature module with a component to route to. -To make one, enter the following command in the command line tool, where `customers` is the name of the feature module. -The path for loading the `customers` feature modules is also `customers` because it is specified with the `--route` option: - - - -ng generate module customers --route customers --module app.module - - - -This creates a `customers` directory having the new lazy-loadable feature module `CustomersModule` defined in the `customers.module.ts` file and the routing module `CustomersRoutingModule` defined in the `customers-routing.module.ts` file. -The command automatically declares the `CustomersComponent` and imports `CustomersRoutingModule` inside the new feature module. - -Because the new module is meant to be lazy-loaded, the command does **not** add a reference to it in the application's root module file, `app.module.ts`. -Instead, it adds the declared route, `customers` to the `routes` array declared in the module provided as the `--module` option. - - -const routes: Routes = [ - { - path: 'customers', - loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) - } -]; - - -Notice that the lazy-loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports. -The import path is the relative path to the module. - - - -In Angular version 8, the string syntax for the `loadChildren` route specification was deprecated in favor of the `import()` syntax. -You can opt into using string-based lazy loading (`loadChildren: './path/to/module#Module'`) by including the lazy-loaded routes in your `tsconfig` file, which includes the lazy-loaded files in the compilation. - -By default the Angular CLI generates projects with stricter file inclusions intended to be used with the `import()` syntax. - - - -### Add another feature module - -Use the same command to create a second lazy-loaded feature module with routing, along with its stub component. - - - -ng generate module orders --route orders --module app.module - - - -This creates a new directory called `orders` containing the `OrdersModule` and `OrdersRoutingModule`, along with the new `OrdersComponent` source files. -The `orders` route, specified with the `--route` option, is added to the `routes` array inside the `app-routing.module.ts` file, using the lazy-loading syntax. - - -const routes: Routes = [ - { - path: 'customers', - loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) - }, - { - path: 'orders', - loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) - } -]; - - -### Set up the UI - -Though you can type the URL into the address bar, a navigation UI is straightforward for the user and more common. -Replace the default placeholder markup in `app.component.html` with a custom nav, so you can navigate to your modules in the browser: - - -

- {{title}} -

- - - - -
- -To see your application in the browser so far, enter the following command in the command line tool window: - - - -ng serve - - - -Then go to `localhost:4200` where you should see "customer-app" and three buttons. - -three buttons in the browser - -These buttons work, because the Angular CLI automatically added the routes for the feature modules to the `routes` array in `app-routing.module.ts`. - -### Imports and route configuration - -The Angular CLI automatically added each feature module to the routes map at the application level. -Finish this off by adding the default route. -In the `app-routing.module.ts` file, update the `routes` array with the following: - - -const routes: Routes = [ - { - path: 'customers', - loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule) - }, - { - path: 'orders', - loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule) - }, - { - path: '', - redirectTo: '', - pathMatch: 'full' - } -]; - - -The first two paths are the routes to the `CustomersModule` and the `OrdersModule`. -The final entry defines a default route. -The empty path matches everything that doesn't match an earlier path. - -### Inside the feature module - -Next, take a look at the `customers.module.ts` file. -If you're using the Angular CLI and following the steps outlined in this page, you don't have to do anything here. - - -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { CustomersRoutingModule } from './customers-routing.module'; -import { CustomersComponent } from './customers.component'; - -@NgModule({ - imports: [ - CommonModule, - CustomersRoutingModule - ], - declarations: [CustomersComponent] -}) -export class CustomersModule { } - - -The `customers.module.ts` file imports the `customers-routing.module.ts` and `customers.component.ts` files. -`CustomersRoutingModule` is listed in the `@NgModule` `imports` array giving `CustomersModule` access to its own routing module. -`CustomersComponent` is in the `declarations` array, which means `CustomersComponent` belongs to the `CustomersModule`. - -The `app-routing.module.ts` then imports the feature module, `customers.module.ts` using JavaScript's dynamic import. - -The feature-specific route definition file `customers-routing.module.ts` imports its own feature component defined in the `customers.component.ts` file, along with the other JavaScript import statements. -It then maps the empty path to the `CustomersComponent`. - - -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; - -import { CustomersComponent } from './customers.component'; - -const routes: Routes = [ - { - path: '', - component: CustomersComponent - } -]; -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class CustomersRoutingModule { } - - -The `path` here is set to an empty string because the path in `AppRoutingModule` is already set to `customers`, so this route in the `CustomersRoutingModule`, is already within the `customers` context. -Every route in this routing module is a child route. - -The other feature module's routing module is configured similarly. - - -import { OrdersComponent } from './orders.component'; - -const routes: Routes = [ - { - path: '', - component: OrdersComponent - } -]; - - -### Verify lazy loading - -You can verify that a module is indeed being lazy loaded with the Chrome developer tools. -In Chrome, open the developer tools by pressing ⌘ Cmd+Option+i on a Mac or Ctrl+Shift+j on a PC and go to the Network Tab. - -lazy loaded modules diagram - -Click on the Orders or Customers button. -If you see a chunk appear, everything is wired up properly and the feature module is being lazy loaded. -A chunk should appear for Orders and for Customers but only appears once for each. - -lazy loaded modules diagram - -To see it again, or to test after making changes, click the circle with a line through it in the upper left of the Network Tab: - -lazy loaded modules diagram - -Then reload with ⌘ Cmd+R or Ctrl+R, depending on your platform. - -## `forRoot()` and `forChild()` - -You might have noticed that the Angular CLI adds `RouterModule.forRoot(routes)` to the `AppRoutingModule` `imports` array. -This lets Angular know that the `AppRoutingModule` is a routing module and `forRoot()` specifies that this is the root routing module. -It configures all the routes you pass to it, gives you access to the router directives, and registers the `Router` service. -Use `forRoot()` only once in the application, inside the `AppRoutingModule`. - -The Angular CLI also adds `RouterModule.forChild(routes)` to feature routing modules. -This way, Angular knows that the route list is only responsible for providing extra routes and is intended for feature modules. -You can use `forChild()` in multiple modules. - -The `forRoot()` method takes care of the *global* injector configuration for the Router. -The `forChild()` method has no injector configuration. -It uses directives such as `RouterOutlet` and `RouterLink`. -For more information, see the [`forRoot()` pattern](guide/ngmodules/singleton-services#forRoot) section of the singleton services guide. - -## Preloading - -Preloading improves UX by loading parts of your application in the background. -You can preload modules, standalone components or component data. - -### Preloading modules and standalone components - -Preloading modules and standalone components improves UX by loading parts of your application in the background. By doing this, users don't have to wait for the elements to download when they activate a route. - -To enable preloading of all lazy loaded modules and standalone components, import the `PreloadAllModules` token from the Angular `router`. - -### Module based application - - - -import { PreloadAllModules } from '@angular/router'; - - - -Then, specify your preloading strategy in the `AppRoutingModule`'s `RouterModule.forRoot()` call. - - -RouterModule.forRoot( - appRoutes, - { - preloadingStrategy: PreloadAllModules - } -) - - -### Standalone application - -For standalone applications configure preloading strategies by adding `withPreloading` to `provideRouter`s RouterFeatures in `app.config.ts` - - -import { ApplicationConfig } from '@angular/core'; -import { - PreloadAllModules, - provideRouter - withPreloading, -} from '@angular/router'; - -import { routes } from './app.routes'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideRouter( - routes, - withPreloading(PreloadAllModules) - ), - ], -}; - - -### Preloading component data - -To preload component data, use a `resolver`. -Resolvers improve UX by blocking the page load until all necessary data is available to fully display the page. - -#### Resolvers - -Create a resolver service. -With the Angular CLI, the command to create a service is as follows: - - -ng generate service - - -In the newly created service, implement the `Resolve` interface provided by the `@angular/router` package: - - - -import { Resolve } from '@angular/router'; - -… - -/*An interface that represents your data model*/ -export interface Crisis { - id: number; - name: string; -} - -export class CrisisDetailResolverService implements Resolve { - resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - // your logic goes here - } -} - - - -Import this resolver into your module's routing module. - - - -import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; - - - -Add a `resolve` object to the component's `route` configuration. - - -{ - path: '/your-path', - component: YourComponent, - resolve: { - crisis: CrisisDetailResolverService - } -} - - -In the component's constructor, inject an instance of the `ActivatedRoute` class that represents the current route. - - -import { ActivatedRoute } from '@angular/router'; - -@Component({ … }) -class YourComponent { - constructor(private route: ActivatedRoute) {} -} - - -Use the injected instance of the `ActivatedRoute` class to access `data` associated with a given route. - - -import { ActivatedRoute } from '@angular/router'; - -@Component({ … }) -class YourComponent { - constructor(private route: ActivatedRoute) {} - - ngOnInit() { - this.route.data - .subscribe(data => { - const crisis: Crisis = data.crisis; - // … - }); - } -} - - -## Troubleshooting lazy-loading modules - -A common error when lazy-loading modules is importing common modules in multiple places within an application. -Test for this condition by first generating the module using the Angular CLI and including the `--route route-name` parameter, where `route-name` is the name of your module. -Next, create the module without the `--route` parameter. -If `ng generate module` with the `--route` parameter returns an error, but runs correctly without it, you might have imported the same module in multiple places. - -Remember, many common Angular modules should be imported at the base of your application. - -For more information on Angular Modules, see [NgModules](guide/ngmodules). - -## More on NgModules and routing - -You might also be interested in the following: - -* [Routing and Navigation](guide/routing) -* [Providers](guide/ngmodules/providers) -* [Types of Feature Modules](guide/ngmodules/module-types) -* [Route-level code-splitting in Angular](https://web.dev/route-level-code-splitting-in-angular) -* [Route preloading strategies in Angular](https://web.dev/route-preloading-in-angular) diff --git a/adev-ja/src/content/guide/ngmodules/module-types.md b/adev-ja/src/content/guide/ngmodules/module-types.md deleted file mode 100644 index 2fbc8b366..000000000 --- a/adev-ja/src/content/guide/ngmodules/module-types.md +++ /dev/null @@ -1,107 +0,0 @@ -# Guidelines for creating NgModules - -This topic provides a conceptual overview of the different categories of NgModules you can create in order to organize your code in a modular structure. -These categories are not cast in stone —they are suggestions. -You may want to create NgModules for other purposes, or combine the characteristics of some of these categories. - -NgModules are a great way to organize an application and keep code related to a specific functionality or feature separate from other code. -Use NgModules to consolidate components, directives, and pipes into cohesive blocks of functionality. -Focus each block on a feature or business domain, a workflow or navigation flow, a common collection of utilities, or one or more providers for services. - -## Summary of NgModule categories - -All module-based applications start by [bootstrapping a root NgModule](guide/ngmodules/bootstrapping "Launching an app with a root NgModule"). -You can organize your other NgModules any way you want. - -This topic provides some guidelines for the following general categories of NgModules: - -| Category | Details | -|:--- |:--- | -| [Domain](#domain-ngmodules) | Is organized around a feature, business domain, or user experience. | -| [Routing](#routing-ngmodules) | Provides the routing configuration for another NgModule. | -| [Service](#service-ngmodules) | Provides utility services such as data access and messaging. | -| [Widget](#widget-ngmodules) | Makes a component, directive, or pipe available to other NgModules. | -| [Shared](#shared-ngmodules) | Makes a set of components, directives, and pipes available to other NgModules. | - -The following table summarizes the key characteristics of each category. - -| NgModule | Declarations | Providers | Exports | Imported by | -|:--- |:--- |:--- |:--- |:--- | -| Domain | Yes | Rare | Top component | Another domain, `AppModule` | -| Routed | Yes | Rare | No | None | -| Routing | No | Yes \(Guards\) | RouterModule | Another domain \(for routing\) | -| Service | No | Yes | No | `AppModule` | -| Widget | Yes | Rare | Yes | Another domain | -| Shared | Yes | No | Yes | Another domain | - -## Domain NgModules - -Use a domain NgModule to deliver a user experience dedicated to a particular feature or application domain, such as editing a customer or placing an order. - -A domain NgModule organizes the code related to a certain function, containing all of the components, routing, and templates that make up the function. -Your top component in the domain NgModule acts as the feature or domain's root, and is the only component you export. -Private supporting subcomponents descend from it. - -Import a domain NgModule exactly once into another NgModule, such as a domain NgModule, or into the root NgModule (`AppModule`) of an application that contains only a few NgModules. - -Domain NgModules consist mostly of declarations. -You rarely include providers. -If you do, the lifetime of the provided services should be the same as the lifetime of the NgModule. - -## Routing NgModules - -Use a routing NgModule to provide the routing configuration for a domain NgModule, thereby separating routing concerns from its companion domain NgModule. - -HELPFUL: For an overview and details about routing, see [In-app navigation: routing to views](guide/routing "In-app navigation: routing to views"). - -Use a routing NgModule to do the following tasks: - -* Define routes -* Add router configuration to the NgModule via `imports` -* Add guard and resolver service providers to the NgModule's providers - -The name of the routing NgModule should parallel the name of its companion NgModule, using the suffix `Routing`. -For example, consider a `ContactModule` in `contact.module.ts` has a routing NgModule named `ContactRoutingModule` in `contact-routing.module.ts`. - -Import a routing NgModule only into its companion NgModule. -If the companion NgModule is the root `AppModule`, the `AppRoutingModule` adds router configuration to its imports with `RouterModule.forRoot(routes)`. -All other routing NgModules are children that import using `RouterModule.forChild(routes)`. - -In your routing NgModule, re-export the `RouterModule` as a convenience so that components of the companion NgModule have access to router directives such as `RouterLink` and `RouterOutlet`. - -Don't use declarations in a routing NgModule. -Components, directives, and pipes are the responsibility of the companion domain NgModule, not the routing NgModule. - -## Service NgModules - -Use a service NgModule to provide a utility service such as data access or messaging. -Ideal service NgModules consist entirely of providers and have no declarations. -Angular's `HttpClientModule` is a good example of a service NgModule. - -Use only the root `AppModule` to import service NgModules. - -## Widget NgModules - -Use a widget NgModule to make a component, directive, or pipe available to external NgModules. -Import widget NgModules into any NgModules that need the widgets in their templates. -Many third-party UI component libraries are provided as widget NgModules. - -A widget NgModule should consist entirely of declarations, most of them exported. -It would rarely have providers. - -## Shared NgModules - -Put commonly used directives, pipes, and components into one NgModule, typically named `SharedModule`, and then import just that NgModule wherever you need it in other parts of your application. -You can import the shared NgModule in your domain NgModules, including [lazy-loaded NgModules](guide/ngmodules/lazy-loading "Lazy-loading an NgModule"). - -Note: Shared NgModules should not include providers, nor should any of its imported or re-exported NgModules include providers. - -To learn how to use shared modules to organize and streamline your code, see [Sharing NgModules in an app](guide/ngmodules/sharing "Sharing NgModules in an app"). - -## Next steps - -If you want to manage NgModule loading and the use of dependencies and services, see the following: - -* To learn about loading NgModules eagerly when the application starts, or lazy-loading NgModules asynchronously by the router, see [Lazy-loading feature modules](guide/ngmodules/lazy-loading) -* To understand how to provide a service or other dependency for your app, see [Providing Dependencies for an NgModule](guide/ngmodules/providers "Providing Dependencies for an NgModule") -* To learn how to create a singleton service to use in NgModules, see [Making a service a singleton](guide/ngmodules/singleton-services "Making a service a singleton") diff --git a/adev-ja/src/content/guide/ngmodules/overview.md b/adev-ja/src/content/guide/ngmodules/overview.md index 00c0ee444..9a4e61b73 100644 --- a/adev-ja/src/content/guide/ngmodules/overview.md +++ b/adev-ja/src/content/guide/ngmodules/overview.md @@ -1,67 +1,188 @@ # NgModules -**NgModules** configure the injector, the compiler and help organize related things together. +IMPORTANT: The Angular team recommends using [standalone components](guide/components/anatomy-of-components#-imports-in-the-component-decorator) instead of `NgModule` for all new code. Use this guide to understand existing code built with `@NgModule`. -An NgModule is a class marked by the `@NgModule` decorator. -`@NgModule` takes a metadata object that describes how to compile a component's template and how to create an injector at runtime. -It identifies the module's own components, directives, and pipes, making some of them public, through the `exports` property, so that external components can use them. -`@NgModule` can also add service providers to the application dependency injectors. +An NgModule is a class marked by the `@NgModule` decorator. This decorator accepts *metadata* that tells Angular how to compile component templates and configure dependency injection. -## Angular modularity +```typescript +import {NgModule} from '@angular/core'; -Modules are a great way to organize an application and extend it with capabilities from external libraries. +@NgModule({ + // Metadata goes here +}) +export class CustomMenuModule { } +``` + +An NgModule has two main responsibilities: +* Declaring components, directives, and pipes that belong to the NgModule +* Add providers to the injector for components, directives, and pipes that import the NgModule + +## Declarations + +The `declarations` property of the `@NgModule` metadata declares the components, directives, and pipes that belong to the NgModule. + +```typescript +@NgModule({ + /* ... */ + // CustomMenu and CustomMenuItem are components. + declarations: [CustomMenu, CustomMenuItem], +}) +export class CustomMenuModule { } +``` + +In the example above, the components `CustomMenu` and `CustomMenuItem` belong to `CustomMenuModule`. + +The `declarations` property additionally accepts _arrays_ of components, directives, and pipes. These arrays, in turn, may also contain other arrays. + +```typescript +const MENU_COMPONENTS = [CustomMenu, CustomMenuItem]; +const WIDGETS = [MENU_COMPONENTS, CustomSlider]; + +@NgModule({ + /* ... */ + // This NgModule declares all of CustomMenu, CustomMenuItem, + // CustomSlider, and CustomCheckbox. + declarations: [WIDGETS, CustomCheckbox], +}) +export class CustomMenuModule { } +``` + +If Angular discovers any components, directives, or pipes declared in more than one NgModule, it reports an error. + +Any components, directives, or pipes must be explicitly marked as `standalone: false` in order to be declared in an NgModule. + +```typescript +@Component({ + // Mark this component as `standalone: false` so that it can be declared in an NgModule. + standalone: false, + /* ... */ +}) +export class CustomMenu { /* ... */ } +``` + +### imports + +Components declared in an NgModule may depend on other components, directives, and pipes. Add these dependencies to the `imports` property of the `@NgModule` metadata. + +```typescript +@NgModule({ + /* ... */ + // CustomMenu and CustomMenuItem depend on the PopupTrigger and SelectorIndicator components. + imports: [PopupTrigger, SelectionIndicator], + declarations: [CustomMenu, CustomMenuItem], +}) +export class CustomMenuModule { } +``` -Angular libraries are NgModules, such as `FormsModule`, `HttpClientModule`, and `RouterModule`. -Many third-party libraries are available as NgModules such as the [Material Design component library](https://material.angular.io), [Ionic](https://ionicframework.com), or [Angular's Firebase integration](https://github.com/angular/angularfire). +The `imports` array accepts other NgModules, as well as standalone components, directives, and pipes. -NgModules consolidate components, directives, and pipes into cohesive blocks of functionality, each focused on a feature area, application business domain, workflow, or common collection of utilities. +### exports -Modules can also add services to the application. -Such services might be internally developed, like something you'd develop yourself or come from outside sources, such as the Angular router and HTTP client. +An NgModule can _export_ its declared components, directives, and pipes such that they're available to other components and NgModules. -Modules can be loaded eagerly when the application starts or lazy loaded asynchronously by the router. + ```typescript +@NgModule({ + imports: [PopupTrigger, SelectionIndicator], + declarations: [CustomMenu, CustomMenuItem], + + // Make CustomMenu and CustomMenuItem available to + // components and NgModules that import CustomMenuModule. + exports: [CustomMenu, CustomMenuItem], +}) +export class CustomMenuModule { } +``` + +The `exports` property is not limited to declarations, however. An NgModule can also export any other components, directives, pipes, and NgModules that it imports. + + ```typescript +@NgModule({ + imports: [PopupTrigger, SelectionIndicator], + declarations: [CustomMenu, CustomMenuItem], -NgModule metadata does the following: + // Also make PopupTrigger available to any component or NgModule that imports CustomMenuModule. + exports: [CustomMenu, CustomMenuItem, PopupTrigger], +}) +export class CustomMenuModule { } +``` + +## `NgModule` providers + +Tip: See the [Dependency Injection guide](guides/di) for information on dependency injection and providers. -* Declares which components, directives, and pipes belong to the module -* Makes some of those components, directives, and pipes public so that other module's component templates can use them -* Imports other modules with the components, directives, and pipes that components in the current module need -* Provides services that other application components can use +An `NgModule` can specify `providers` for injected dependencies. These providers are available to: +* Any standalone component, directive, or pipe that imports the NgModule, and +* The `declarations` and `providers` of any _other_ NgModule that imports the NgModule. -Every Module-based Angular application has at least one module, the root module. -You [bootstrap](guide/ngmodules/bootstrapping) that module to launch the application. +```typescript +@NgModule({ + imports: [PopupTrigger, SelectionIndicator], + declarations: [CustomMenu, CustomMenuItem], + + // Provide the OverlayManager service + providers: [OverlayManager], + /* ... */ +}) +export class CustomMenuModule { } + +@NgModule({ + imports: [CustomMenuModule], + declarations: [UserProfile], + providers: [UserDataClient], +}) +export class UserProfileModule { } +``` + +In the example above: +* The `CustomMenuModule` provides `OverlayManager`. +* The `CustomMenu` and `CustomMenuItem` components can inject `OverlayManager` because they're declared in `CustomMenuModule`. +* `UserProfile` can inject `OverlayManager` because its NgModule imports `CustomMenuModule`. +* `UserDataClient` can inject `OverlayManager` because its NgModule imports `CustomMenuModule`. + +### The `forRoot` and `forChild` pattern + +Some NgModules define a static `forRoot` method that accepts some configuration and returns an array of providers. The name "`forRoot`" is a convention that indicates that these providers are intended to be added exclusively to the _root_ of your application during bootstrap. + +Any providers included in this way are eagerly loaded, increasing the JavaScript bundle size of your initial page load. + +```typescript +boorstrapApplication(MyApplicationRoot, { + providers: [ + CustomMenuModule.forRoot(/* some config */), + ], +}); +``` + +Similarly, some NgModules may before a static `forChild` that indicates the providers are intended to be added to components within your application hierarchy. + +```typescript +@Component({ + /* ... */ + providers: [ + CustomMenuModule.forChild(/* some config */), + ], +}) +export class UserProfile { /* ... */ } +``` -The root module is all you need in an application with few components. -As the application grows, you refactor the root module into [feature modules](guide/ngmodules/feature-modules) that represent collections of related functionality. -You then import these modules into the root module. +## Bootstrapping an application -## The basic NgModule +IMPORTANT: The Angular team recommends using [bootstrapApplication](api/platform-browser/bootstrapApplication) instead of `bootstrapModule` for all new code. Use this guide to understand existing applications bootstrapped with `@NgModule`. -The [Angular CLI](/tools/cli) generates the following basic `AppModule` when creating a new application with the `--no-standalone` option. +The `@NgModule` decorator accepts an optional `bootstrap` array that may contain one or more components. - -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; +You can use the [`bootstrapModule`](https://angular.dev/api/core/PlatformRef#bootstrapModule) method from either [`platformBrowser`](api/platform-browser/platformBrowser) or [`platformServer`](api/platform-server/platformServer) to start an Angular application. When run, this function locates any elements on the page with a CSS selector that matches the listed componet(s) and renders those components on the page. -import { AppComponent } from './app.component'; +```typescript +import {platformBrowser} from '@angular/platform-browser'; @NgModule({ - declarations: [AppComponent], - imports: [BrowserModule], - providers: [], - bootstrap: [AppComponent] + bootstrap: [MyApplication], }) -export class AppModule {} - +export class MyApplciationModule { } -At the top are the import statements. -The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). -For more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/ngmodules/bootstrapping). +platformBrowser().bootstrapModule(MyApplicationModule); +``` -## More on NgModules +Components listed in `bootstrap` are automatically included in the NgModule's declarations. - - - - - +When you bootstrap an application from an NgModule, the collected `providers` of this module and all of the `providers` of its `imports` are eagerly loaded and available to inject for the entire application. diff --git a/adev-ja/src/content/guide/ngmodules/providers.md b/adev-ja/src/content/guide/ngmodules/providers.md deleted file mode 100644 index b945027f6..000000000 --- a/adev-ja/src/content/guide/ngmodules/providers.md +++ /dev/null @@ -1,133 +0,0 @@ -# Providing dependencies in modules - -A provider is an instruction to the [Dependency Injection](guide/di) system on how to obtain a value for a dependency. -Most of the time, these dependencies are services that you create and provide. - -## Providing a service - -If you already have an application that was created with the [Angular CLI](/tools/cli), you can create a service using the `ng generate` CLI command in the root project directory. -Replace *User* with the name of your service. - - - -ng generate service User - - - -This command creates the following `UserService` skeleton: - - -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class UserService { -} - - -You can now inject `UserService` anywhere in your application. - -The service itself is a class that the CLI generated and that's decorated with `@Injectable()`. -By default, this decorator has a `providedIn` property, which creates a provider for the service. -In this case, `providedIn: 'root'` specifies that Angular should provide the service in the root injector. - -## Provider scope - -When you add a service provider to the root application injector, it's available throughout the application. -Additionally, these providers are also available to all the classes in the application as long they have the lookup token. - -You should always provide your service in the root injector unless there is a case where you want the service to be available only if the consumer imports a particular `@NgModule`. - -## Limiting provider scope by lazy loading modules - -In the basic CLI-generated app, modules are eagerly loaded which means that they are all loaded when the application launches. -Angular uses an injector system to make things available between modules. -In an eagerly loaded app, the root application injector makes all of the providers in all of the modules available throughout the application. - -This behavior necessarily changes when you use lazy loading. -Lazy loading is when you load modules only when you need them; for example, when routing. -They aren't loaded right away like with eagerly loaded modules. -This means that any services listed in their provider arrays aren't available because the root injector doesn't know about these modules. - - - - -When the Angular router lazy-loads a module, it creates a new injector. -This injector is a child of the root application injector. -Imagine a tree of injectors; there is a single root injector and then a child injector for each lazy loaded module. -This child injector gets populated with all the module-specific providers, if any. -Look up resolution for every provider follows the [rules of dependency injection hierarchy](guide/di/hierarchical-dependency-injection#resolution-rules). - -Any component created within a lazy loaded module's context, such as by router navigation, gets its own local instance of child provided services, not the instance in the root application injector. -Components in external modules continue to receive the instances created for the application root injector. - -Though you can provide services by lazy loading modules, not all services can be lazy loaded. -For instance, some modules only work in the root module, such as the Router. -The Router works with the global location object in the browser. - -As of Angular version 9, you can provide a new instance of a service with each lazy loaded module. -The following code adds this functionality to `UserService`. - - -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'any', -}) -export class UserService { -} - - -With `providedIn: 'any'`, all eagerly loaded modules share a singleton instance; however, lazy loaded modules each get their own unique instance, as shown in the following diagram. - -any-provider-scope - -## Limiting provider scope with components - -Another way to limit provider scope is by adding the service you want to limit to the component's `providers` array. -Component providers and NgModule providers are independent of each other. -This method is helpful when you want to eagerly load a module that needs a service all to itself. -Providing a service in the component limits the service only to that component and its descendants. -Other components in the same module can't access it. - - -@Component({ - // ... - providers: [UserService] -}) -export class AppComponent {} - - -## Providing services in modules vs. components - -Generally, provide services the whole application needs in the root module and scope services by providing them in lazy loaded modules. - -The router works at the root level so if you put providers in a component, even `AppComponent`, lazy loaded modules, which rely on the router, can't see them. - - -Register a provider with a component when you must limit a service instance to a component and its component tree, that is, its child components. -For example, a user editing component, `UserEditorComponent`, that needs a private copy of a caching `UserService` should register the `UserService` with the `UserEditorComponent`. -Then each new instance of the `UserEditorComponent` gets its own cached service instance. - -## Injector hierarchy and service instances - -Services are singletons within the scope of an injector, which means there is at most one instance of a service in a given injector. - -Angular DI has a [hierarchical injection system](guide/di/hierarchical-dependency-injection), which means that nested injectors can create their own service instances. -Whenever Angular creates a new instance of a component that has `providers` specified in `@Component()`, it also creates a new child injector for that instance. -Similarly, when a new NgModule is lazy-loaded at run time, Angular can create an injector for it with its own providers. - -Child modules and component injectors are independent of each other, and create their own separate instances of the provided services. -When Angular destroys an NgModule or component instance, it also destroys that injector and that injector's service instances. - -For more information, see [Hierarchical injectors](guide/di/hierarchical-dependency-injection). - -## More on NgModules - - - - - - - diff --git a/adev-ja/src/content/guide/ngmodules/sharing.md b/adev-ja/src/content/guide/ngmodules/sharing.md deleted file mode 100644 index 4da06dd0d..000000000 --- a/adev-ja/src/content/guide/ngmodules/sharing.md +++ /dev/null @@ -1,51 +0,0 @@ -# Sharing modules - -Creating shared modules allows you to organize and streamline your code. -You can put commonly used directives, pipes, and components into one module and then import just that module wherever you need it in other parts of your application. - -Consider the following module from an imaginary app: - - -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { CustomerComponent } from './customer.component'; -import { NewItemDirective } from './new-item.directive'; -import { OrdersPipe } from './orders.pipe'; - -@NgModule({ - imports: [CommonModule], - declarations: [ - CustomerComponent, - NewItemDirective, - OrdersPipe - ], - exports: [ - CustomerComponent, - NewItemDirective, - OrdersPipe, - CommonModule, - FormsModule - ], -}) -export class SharedModule { } - - - -Notice the following: - -* It imports the `CommonModule` because the module's component needs common directives -* It declares and exports the utility pipe, directive, and component classes -* It re-exports the `CommonModule` and `FormsModule` - -By re-exporting `CommonModule` and `FormsModule`, any other module that imports this `SharedModule`, gets access to directives like `NgIf` and `NgFor` from `CommonModule` and can bind to component properties with `[(ngModel)]`, a directive in the `FormsModule`. - -Even though the components declared by `SharedModule` might not bind with `[(ngModel)]` and there may be no need for `SharedModule` to import `FormsModule`, `SharedModule` can still export `FormsModule` without listing it among its `imports`. -This way, you can give other modules access to `FormsModule` without having to make it available for itself. - -## More on NgModules - - - - - diff --git a/adev-ja/src/content/guide/ngmodules/singleton-services.md b/adev-ja/src/content/guide/ngmodules/singleton-services.md deleted file mode 100644 index d14349018..000000000 --- a/adev-ja/src/content/guide/ngmodules/singleton-services.md +++ /dev/null @@ -1,185 +0,0 @@ -# Singleton services - -A singleton service is a service for which only one instance exists in an application. - -## Providing a singleton service - -There are two ways to make a service a singleton in Angular: - -* Set the `providedIn` property of the `@Injectable()` to `"root"` -* Include the service in the `AppModule` or in a module that is only imported by the `AppModule` - -### Using `providedIn` - -The preferred way to create a singleton service is to set `providedIn` to `root` on the service's `@Injectable()` decorator. -This tells Angular to provide the service in the application root. - - -import { Injectable } from '@angular/core'; - -@Injectable({ - providedIn: 'root', -}) -export class UserService { -} - - -### NgModule `providers` array - -In applications built with Angular versions prior to 6.0, services were commonly registered in the `@NgModule` `providers` field as followed: - - -@NgModule({ - // ... - providers: [UserService], -}) - - -If this NgModule were the root `AppModule`, the `UserService` would be a singleton and available throughout the application. -Though you may see it coded this way, using the `providedIn` property of the `@Injectable()` decorator on the service itself is preferable as of Angular 6.0 as it makes your services tree-shakable. - -## The `forRoot()` pattern - -Generally, you'll only need `providedIn` for providing services and `forRoot()`/`forChild()` for routing. -However, understanding how `forRoot()` works to make sure a service is a singleton will inform your development at a deeper level. - -If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. -This could result in multiple service instances and the service would no longer behave as a singleton. - -There are multiple ways to prevent this: - -* Use the [`providedIn` syntax](#using-providedin) instead of registering the service in the module. -* Separate your services into their own module that is imported once. -* Define `forRoot()` and `forChild()` methods in the module. - -For an introductory explanation see the [Lazy Loading Feature Modules](guide/ngmodules/lazy-loading) guide. - -Use `forRoot()` to separate providers from a module so you can import that module into the root module with `providers` and child modules without `providers`. - -1. Create a static method `forRoot()` on the module. -1. Place the providers into the `forRoot()` method. - - -@NgModule({...}) -export class GreetingModule { - static forRoot(config: UserServiceConfig): ModuleWithProviders { - return { - ngModule: GreetingModule, - providers: [ - {provide: UserServiceConfig, useValue: config } - ] - }; - } -} - - -### `forRoot()` and the `Router` - -`RouterModule` provides the `Router` service, as well as router directives, such as `RouterOutlet` and `routerLink`. -The root application module imports `RouterModule` so that the application has a `Router` and the root application components can access the router directives. -Any feature modules must also import `RouterModule` so that their components can place router directives into their templates. - -If the `RouterModule` didn't have `forRoot()` then each feature module would instantiate a new `Router` instance, which would break the application as there can only be one `Router`. -By using the `forRoot()` method, the root application module imports `RouterModule.forRoot(...)` and gets a `Router`, and all feature modules import `RouterModule.forChild(...)` which does not instantiate another `Router`. - -HELPFUL: If you have a module which has both providers and declarations, you *can* use this technique to separate them out and you may see this pattern in legacy applications. -However, since Angular 6.0, the best practice for providing services is with the `@Injectable()` `providedIn` property. - -### How `forRoot()` works - -`forRoot()` takes a service configuration object and returns a [ModuleWithProviders](api/core/ModuleWithProviders), which is a simple object with the following properties: - -| Properties | Details | -|:--- |:--- | -| `ngModule` | In this example, the `GreetingModule` class | -| `providers` | The configured providers | - -Specifically, Angular accumulates all imported providers before appending the items listed in `@NgModule.providers`. -This sequence ensures that whatever you add explicitly to the `AppModule` providers takes precedence over the providers of imported modules. - -The sample application imports `GreetingModule` and uses its `forRoot()` method one time, in `AppModule`. -Registering it once like this prevents multiple instances. - -In the following example, the `UserServiceConfig` is optionally injected in the `UserService`. -If a config exists, the service sets the user name based on the retrieved config. - - - constructor(@Optional() config?: UserServiceConfig) { - if (config) { - this._userName = config.userName; - } - } - - -Here's `forRoot()` that takes a `UserServiceConfig` object: - - -@NgModule({...}) -export class GreetingModule { - static forRoot(config: UserServiceConfig): ModuleWithProviders { - return { - ngModule: GreetingModule, - providers: [ - {provide: UserServiceConfig, useValue: config } - ] - }; - } -} - - -Lastly, call it within the `imports` list of the `AppModule`. -In the following snippet, other parts of the file are left out. - - -import { GreetingModule } from './greeting/greeting.module'; - -@NgModule({ - // ... - imports: [ - // ... - GreetingModule.forRoot({userName: 'Miss Marple'}), - ], -}) - - -The application will then display "Miss Marple" as the user. - -Remember to import `GreetingModule` as a JavaScript import, and don't add usages of `forRoot` to more than one `@NgModule` `imports` list. - -## Prevent reimport of the `GreetingModule` - -Only the root `AppModule` should import the `GreetingModule`. -If a lazy-loaded module imports it too, the application can generate [multiple instances](guide/ngmodules/faq#why-is-it-bad-if-a-shared-module-provides-a-service-to-a-lazy-loaded-module?) of a service. - -To guard against a lazy loaded module re-importing `GreetingModule`, add the following `GreetingModule` constructor. - - - constructor(@Optional() @SkipSelf() parentModule?: GreetingModule) { - if (parentModule) { - throw new Error( - 'GreetingModule is already loaded. Import it in the AppModule only'); - } - } - - -The constructor tells Angular to inject the `GreetingModule` into itself. -The injection would be circular if Angular looked for `GreetingModule` in the *current* injector, but the `@SkipSelf()` decorator means "look for `GreetingModule` in an ancestor injector, above me in the injector hierarchy". - -By default, the injector throws an error when it can't find a requested provider. -The `@Optional()` decorator means not finding the service is OK. -The injector returns `null`, the `parentModule` parameter is null, and the constructor concludes uneventfully. - -It's a different story if you improperly import `GreetingModule` into a lazy loaded module such as `CustomersModule`. - -Angular creates a lazy loaded module with its own injector, a child of the root injector. -`@SkipSelf()` causes Angular to look for a `GreetingModule` in the parent injector, which this time is the root injector. -Of course it finds the instance imported by the root `AppModule`. -Now `parentModule` exists and the constructor throws the error. - -## More on NgModules - - - - - - diff --git a/adev-ja/src/content/guide/ngmodules/vs-jsmodule.md b/adev-ja/src/content/guide/ngmodules/vs-jsmodule.md deleted file mode 100644 index 3936351ec..000000000 --- a/adev-ja/src/content/guide/ngmodules/vs-jsmodule.md +++ /dev/null @@ -1,86 +0,0 @@ -# JavaScript modules vs. NgModules - -JavaScript modules and NgModules can help you modularize your code, but they are very different. -Angular applications rely on both kinds of modules. - -## JavaScript modules: Files exposing code - -A [JavaScript module](https://javascript.info/modules "JavaScript.Info - Modules") is an individual file with JavaScript code, usually containing a class or a library of functions for a specific purpose within your application. -JavaScript modules let you spread your work across multiple files. - -HELPFUL: To learn more about JavaScript modules, see [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules). -For the module specification, see the [6th Edition of the ECMAScript standard](https://www.ecma-international.org/ecma-262/6.0/#sec-modules). - -To make the code in a JavaScript module available to other modules, use an `export` statement at the end of the relevant code in the module, such as the following: - - -export class AppComponent { … } - - -When you need that module's code in another module, use an `import` statement as follows: - - -import { AppComponent } from './app.component'; - - -Each module has its own top-level scope. -In other words, top-level variables and functions in a module are not seen in other scripts or modules. - -## NgModules: Classes with metadata for compiling - -An NgModule is a class marked by the `@NgModule` decorator with a metadata object that describes how that particular part of the application fits together with the other parts. -NgModules are specific to Angular. -While classes with an `@NgModule` decorator are by convention kept in their own files, they differ from JavaScript modules because they include this metadata. - -The `@NgModule` metadata plays an important role in guiding the Angular compilation process that converts the application code you write into highly performant JavaScript code. -The metadata describes how to compile a component's template and how to create an injector at runtime. -It identifies the NgModule's components, directives, and pipes"), -and makes some of them public through the `exports` property so that external components can use them. -You can also use an NgModule to add providers for services, so that the services are available elsewhere in your application. - -Rather than defining all member classes in one giant file as a JavaScript module, declare which components, directives, and pipes belong to the NgModule in the `@NgModule.declarations` list. -These classes are called declarables. -An NgModule can export only the declarable classes it owns or imports from other NgModules. -It doesn't declare or export any other kind of class. -Declarables are the only classes that matter to the Angular compilation process. - -For a complete description of the NgModule metadata properties, see [Using the NgModule metadata](guide/ngmodules/api "Using the NgModule metadata"). - -## An example that uses both - -The root NgModule `AppModule` generated by the [Angular CLI](/tools/cli) (when using the `--no-standalone` option) for a new application project demonstrates how you use both kinds of modules: - - -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; - -import { AppComponent } from './app.component'; - -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule {} - - -The root NgModule starts with `import` statements to import JavaScript modules. -It then configures the `@NgModule` with the following arrays: - -* `declarations`: The components, directives, and pipes that belong to the NgModule. - A new application project's root NgModule has only one component, called `AppComponent`. - -* `imports`: Other NgModules you are using, so that you can use their declarables. - The newly generated root NgModule imports [`BrowserModule`](api/platform-browser/BrowserModule "BrowserModule NgModule") in order to use browser-specific services such as [DOM](https://www.w3.org/TR/DOM-Level-2-Core/introduction.html "Definition of Document Object Model") rendering, sanitization, and location. - -* `providers`: Providers of services that components in other NgModules can use. - There are no providers in a newly generated root NgModule. - -* `bootstrap`: The component that Angular creates and inserts into the `index.html` host web page, thereby bootstrapping the application. - This component, `AppComponent`, appears in both the `declarations` and the `bootstrap` arrays. - -## Next steps - -* To learn more about the root NgModule, see [Launching an app with a root NgModule](guide/ngmodules/bootstrapping "Launching an app with a root NgModule"). -* To learn about frequently used Angular NgModules and how to import them into your app, see [Frequently-used modules](guide/ngmodules/frequent "Frequently-used modules"). diff --git a/adev-ja/src/content/guide/performance/overview.en.md b/adev-ja/src/content/guide/performance/overview.en.md index 8fa027efd..1c9488da3 100644 --- a/adev-ja/src/content/guide/performance/overview.en.md +++ b/adev-ja/src/content/guide/performance/overview.en.md @@ -1,15 +1,11 @@ - -Learn about different ways you can optimize the performance of your application. + +Learn about different ways you can optimize the performance of your application with different rendering strategies. -One of the top priorities of any developer is ensuring that their application is as performant as possible. These guides are here to help you follow best practices for building performant applications. - -That said, please note that these best practices will only take the performance of your application so far. At the end of the day, we encourage you to measure performance in order to best understand what custom optimizations are best for your application. +One of the top priorities of any developer is ensuring that their application is as performant as possible. These guides are here to help you follow best practices for building performant applications by taking advantage of different rendering strategies. | Guides Types | Description | | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------- | -| [Deferrable views](/guide/defer) | Defer loading of select dependencies within a template by wrapping corresponding parts in a `@defer` block. | -| [Image optimization](/guide/image-optimization) | Use the `NgOptimizedImage` directive to adopt best practices for loading images. | | [Server-side rendering](/guide/ssr) | Learn how to leverage rendering pages on the server to improve load times. | | [Build-time prerendering](/guide/prerendering) | Also known as static-side generation (SSG), is an alternate rendering method to improve load times. | | [Hydration](/guide/hydration) | A process to improve application performance by restoring its state after server-side rendering and reusing existing DOM structure as much as possible. | diff --git a/adev-ja/src/content/guide/performance/overview.md b/adev-ja/src/content/guide/performance/overview.md index c75720e87..2e9aaac46 100644 --- a/adev-ja/src/content/guide/performance/overview.md +++ b/adev-ja/src/content/guide/performance/overview.md @@ -1,15 +1,11 @@ - -アプリケーションのパフォーマンスを最適化するためのさまざまな方法について学びましょう。 + +さまざまなレンダリング戦略でアプリケーションのパフォーマンスを最適化する方法について学びましょう。 -開発者の最優先事項の1つは、アプリケーションのパフォーマンスを可能な限り向上させることです。これらのガイドは、パフォーマンスの高いアプリケーションを構築するためのベストプラクティスに従うのに役立ちます。 - -とはいえ、これらのベストプラクティスは、アプリケーションのパフォーマンスをある程度までしか向上させられません。最終的には、パフォーマンスを測定して、アプリケーションに最適なカスタム最適化を理解することをお勧めします。 +開発者にとって最優先事項のひとつは、アプリケーションのパフォーマンスを可能な限り向上させることです。これらのガイドでは、さまざまなレンダリング戦略を活用することで、パフォーマンスの高いアプリケーションを構築するためのベストプラクティスを紹介します。 | ガイドの種類 | 説明 | | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------- | -| [遅延可能なビュー](/guide/defer) | `@defer`ブロックで対応する部分をラップすることで、テンプレート内の選択された依存関係の読み込みを遅らせます。 | -| [画像の最適化](/guide/image-optimization) | `NgOptimizedImage`ディレクティブを使用して、画像の読み込みのベストプラクティスを採用します。 | | [サーバーサイドレンダリング](/guide/ssr) | ロード時間を短縮するために、サーバーでページをレンダリングする方法について学びます。 | | [ビルド時の事前レンダリング](/guide/prerendering) | 静的サイドジェネレーション(SSG)とも呼ばれ、ロード時間を短縮するための別のレンダリング方法です。 | | [ハイドレーション](/guide/hydration) | サーバーサイドレンダリング後にアプリケーションの状態を復元し、可能な限り既存のDOM構造を再利用することで、アプリケーションのパフォーマンスを向上させるプロセスです。 | diff --git a/adev-ja/src/content/guide/pipes/template.en.md b/adev-ja/src/content/guide/pipes/template.en.md index ae5f7d32d..21fe89dbe 100644 --- a/adev-ja/src/content/guide/pipes/template.en.md +++ b/adev-ja/src/content/guide/pipes/template.en.md @@ -14,7 +14,6 @@ import { Component } from '@angular/core'; import { DatePipe } from '@angular/common'; @Component({ - standalone: true, templateUrl: './app.component.html', imports: [DatePipe], }) diff --git a/adev-ja/src/content/guide/pipes/template.md b/adev-ja/src/content/guide/pipes/template.md index 48f4778be..a9886b7af 100644 --- a/adev-ja/src/content/guide/pipes/template.md +++ b/adev-ja/src/content/guide/pipes/template.md @@ -14,7 +14,6 @@ import { Component } from '@angular/core'; import { DatePipe } from '@angular/common'; @Component({ - standalone: true, templateUrl: './app.component.html', imports: [DatePipe], }) diff --git a/adev-ja/src/content/guide/pipes/transform-data.en.md b/adev-ja/src/content/guide/pipes/transform-data.en.md index 7cac3abd8..545275e43 100644 --- a/adev-ja/src/content/guide/pipes/transform-data.en.md +++ b/adev-ja/src/content/guide/pipes/transform-data.en.md @@ -19,7 +19,6 @@ import { Pipe } from '@angular/core'; @Pipe({ name: 'greet', - standalone: true, }) export class GreetPipe {} ``` @@ -35,7 +34,6 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'greet', - standalone: true, }) export class GreetPipe implements PipeTransform { transform(value: string, param1: boolean, param2: boolean): string { diff --git a/adev-ja/src/content/guide/pipes/transform-data.md b/adev-ja/src/content/guide/pipes/transform-data.md index bd834b9ec..4799dbc98 100644 --- a/adev-ja/src/content/guide/pipes/transform-data.md +++ b/adev-ja/src/content/guide/pipes/transform-data.md @@ -19,7 +19,6 @@ import { Pipe } from '@angular/core'; @Pipe({ name: 'greet', - standalone: true, }) export class GreetPipe {} ``` @@ -35,7 +34,6 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'greet', - standalone: true, }) export class GreetPipe implements PipeTransform { transform(value: string, param1: boolean, param2: boolean): string { diff --git a/adev-ja/src/content/guide/routing/common-router-tasks.en.md b/adev-ja/src/content/guide/routing/common-router-tasks.en.md index f12949eb3..a0b2b1f26 100644 --- a/adev-ja/src/content/guide/routing/common-router-tasks.en.md +++ b/adev-ja/src/content/guide/routing/common-router-tasks.en.md @@ -110,7 +110,6 @@ You also need to add the `RouterLink`, `RouterLinkActive`, and `RouterOutlet` to ```ts @Component({ selector: 'app-root', - standalone: true, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] diff --git a/adev-ja/src/content/guide/routing/common-router-tasks.md b/adev-ja/src/content/guide/routing/common-router-tasks.md index 0cc0bd74e..f91e79afb 100644 --- a/adev-ja/src/content/guide/routing/common-router-tasks.md +++ b/adev-ja/src/content/guide/routing/common-router-tasks.md @@ -110,7 +110,6 @@ const routes: Routes = [ ```ts @Component({ selector: 'app-root', - standalone: true, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] diff --git a/adev-ja/src/content/guide/signals/inputs.en.md b/adev-ja/src/content/guide/signals/inputs.en.md deleted file mode 100644 index eabd33bfd..000000000 --- a/adev-ja/src/content/guide/signals/inputs.en.md +++ /dev/null @@ -1,147 +0,0 @@ -# Signal inputs - -Signal inputs allow values to be bound from parent components. -Those values are exposed using a `Signal` and can change during the lifecycle of your component. - -HELPFUL: Signal inputs are currently in [developer preview](/reference/releases#developer-preview). - -Angular supports two variants of inputs: - -**Optional inputs** -Inputs are optional by default, unless you use `input.required`. -You can specify an explicit initial value, or Angular will use `undefined` implicitly. - -**Required inputs** -Required inputs always have a value of the given input type. -They are declared using the `input.required` function. - -```typescript -import {Component, input} from '@angular/core'; - -@Component({...}) -export class MyComp { - // optional - firstName = input(); // InputSignal - age = input(0); // InputSignal - - // required - lastName = input.required(); // InputSignal -} -``` - -An input is automatically recognized by Angular whenever you use the `input` or `input.required` functions as initializer of class members. - -## Aliasing an input - -Angular uses the class member name as the name of the input. -You can alias inputs to change their public name to be different. - -```typescript -class StudentDirective { - age = input(0, {alias: 'studentAge'}); -} -``` - -This allows users to bind to your input using `[studentAge]`, while inside your component you can access the input values using `this.age`. - -## Using in templates - -Signal inputs are read-only signals. -As with signals declared via `signal()`, you access the current value of the input by calling the input signal. - -```angular-html -

First name: {{firstName()}}

-

Last name: {{lastName()}}

-``` - -This access to the value is captured in reactive contexts and can notify active consumers, like Angular itself, whenever the input value changes. - -An input signal in practice is a trivial extension of signals that you know from [the signals guide](guide/signals). - -```typescript -export class InputSignal extends Signal { ... }`. -``` - -## Deriving values - -As with signals, you can derive values from inputs using `computed`. - -```typescript -import {Component, input, computed} from '@angular/core'; - -@Component({...}) -export class MyComp { - age = input(0); - - // age multiplied by two. - ageMultiplied = computed(() => this.age() * 2); -} -``` - -Computed signals memoize values. -See more details in the [dedicated section for computed](guide/signals#computed-signals). - -## Monitoring changes - -With signal inputs, users can leverage the `effect` function. -The function will execute whenever the input changes. - -Consider the following example. -The new value is printed to the console whenever the `firstName` input changes. - -```typescript -import {input, effect} from '@angular/core'; - -class MyComp { - firstName = input.required(); - - constructor() { - effect(() => { - console.log(this.firstName()); - }); - } -} -``` - -The `console.log` function is invoked every time the `firstName` input changes. -This will happen as soon as `firstName` is available, and for subsequent changes during the lifetime of `MyComp`. - -## Value transforms - -You may want to coerce or parse input values without changing the meaning of the input. -Transforms convert the raw value from parent templates to the expected input type. -Transforms should be [pure functions](https://en.wikipedia.org/wiki/Pure_function). - -```typescript -class MyComp { - disabled = input(false, { - transform: (value: boolean|string) => typeof value === 'string' ? value === '' : value, - }); -} -``` - -In the example above, you are declaring an input named `disabled` that is accepting values of type `boolean` and `string`. -This is captured by the explicit parameter type of `value` in the `transform` option. -These values are then parsed to a `boolean` with the transform, resulting in booleans. - -That way, you are only dealing with `boolean` inside your component when calling `this.disabled()`, while users of your component can pass an empty string as a shorthand to mark your component as disabled. - -```angular-html - -``` - -IMPORTANT: Do not use transforms if they change the meaning of the input, or if they are [impure](https://en.wikipedia.org/wiki/Pure_function#Impure_functions). -Instead, use `computed` for transformations with different meaning, or an `effect` for impure code that should run whenever the input changes. - -## Why should we use signal inputs and not `@Input()`? - -Signal inputs are a reactive alternative to decorator-based `@Input()`. - -In comparison to decorator-based `@Input`, signal inputs provide numerous benefits: - -1. Signal inputs are more **type safe**: -
• Required inputs do not require initial values, or tricks to tell TypeScript that an input _always_ has a value. -
• Transforms are automatically checked to match the accepted input values. -2. Signal inputs, when used in templates, will **automatically** mark `OnPush` components as dirty. -3. Values can be easily **derived** whenever an input changes using `computed`. -4. Easier and more local monitoring of inputs using `effect` instead of `ngOnChanges` or setters. diff --git a/adev-ja/src/content/guide/signals/inputs.md b/adev-ja/src/content/guide/signals/inputs.md deleted file mode 100644 index e6c6a5320..000000000 --- a/adev-ja/src/content/guide/signals/inputs.md +++ /dev/null @@ -1,147 +0,0 @@ -# シグナル入力 - -シグナル入力を使用すると、親コンポーネントから値をバインドできます。 -これらの値は `Signal` を使用して公開され、コンポーネントのライフサイクル中に変化する可能性があります。 - -HELPFUL: シグナル入力は現在、[開発者プレビュー](/reference/releases#developer-preview)にあります。 - -Angularは、2種類の入力をサポートしています。 - -**オプション入力** -`input.required` を使用しない限り、入力はデフォルトでオプショナルです。 -明示的な初期値を指定できます。指定しない場合、Angularは暗黙的に `undefined` を使用します。 - -**必須入力** -必須入力は常に、指定された入力タイプの値を持ちます。 -`input.required` 関数を使用して宣言されます。 - -```typescript -import {Component, input} from '@angular/core'; - -@Component({...}) -export class MyComp { - // オプション - firstName = input(); // InputSignal - age = input(0); // InputSignal - - // 必須 - lastName = input.required(); // InputSignal -} -``` - -クラスメンバーのイニシャライザーとして `input` または `input.required` 関数を使用すると、Angularは自動的に入力を認識します。 - -## 入力に別名をつける - -Angularは、クラスメンバー名を入力の名前として使用します。 -別名を使用すると、公開名を変更できます。 - -```typescript -class StudentDirective { - age = input(0, {alias: 'studentAge'}); -} -``` - -これにより、ユーザーは `[studentAge]` を使用して入力にバインドできます。一方、コンポーネント内では `this.age` を使用して入力値にアクセスできます。 - -## テンプレートでの使用 - -シグナル入力は、読み取り専用のシグナルです。 -`signal()` を使用して宣言されたシグナルと同様に、入力シグナルを呼び出すことで、入力の現在の値にアクセスできます。 - -```angular-html -

First name: {{firstName()}}

-

Last name: {{lastName()}}

-``` - -この値へのアクセスは、リアクティブなコンテキストでキャプチャされ、入力値が変更されるたびに、Angular自身などのアクティブなコンシューマーに通知できます。 - -実際には、入力シグナルは、[シグナルガイド](guide/signals)で知られているシグナルの単純な拡張です。 - -```typescript -export class InputSignal extends Signal { ... }`. -``` - -## 値の派生 - -シグナルと同様に、`computed` を使用して入力から値を派生できます。 - -```typescript -import {Component, input, computed} from '@angular/core'; - -@Component({...}) -export class MyComp { - age = input(0); - - // 年齢を 2 倍した値。 - ageMultiplied = computed(() => this.age() * 2); -} -``` - -算出シグナルは、値をメモ化します。 -詳細については、[計算されたシグナルに関するセクション](guide/signals#computed-signals)を参照してください。 - -## 変更の監視 - -シグナル入力を使用すると、ユーザーは `effect` 関数を利用できます。 -この関数は、入力が変更されるたびに実行されます。 - -次の例を考えてみましょう。 -`firstName` 入力が変更されるたびに、新しい値がコンソールに出力されます。 - -```typescript -import {input, effect} from '@angular/core'; - -class MyComp { - firstName = input.required(); - - constructor() { - effect(() => { - console.log(this.firstName()); - }); - } -} -``` - -`console.log` 関数は、`firstName` 入力が変更されるたびに呼び出されます。 -これは、`firstName` が使用可能になった直後と、`MyComp` のライフサイクル中の後続の変更に対して発生します。 - -## 値の変換 - -入力の値を、その意味を変更せずに、強制変換または解析したい場合があります。 -変換は、親テンプレートからの生の値を、期待される型に変換します。 -変換は、[純粋関数](https://en.wikipedia.org/wiki/Pure_function)である必要があります。 - -```typescript -class MyComp { - disabled = input(false, { - transform: (value: boolean|string) => typeof value === 'string' ? value === '' : value, - }); -} -``` - -上記の例では、`disabled` という名前の入力を宣言しています。この入力は、`boolean` 型と `string` 型の値を受け入れます。 -これは、`transform` オプションの `value` の明示的なパラメーター型によってキャプチャされます。 -これらの値は、変換によって `boolean` に解析され、`boolean` になります。 - -このように、`this.disabled()` を呼び出す際に、コンポーネント内では `boolean` のみを使用できます。一方、コンポーネントのユーザーは、空の文字列を省略記号として渡して、コンポーネントを無効にできます。 - -```angular-html - -``` - -IMPORTANT: 入力の意味を変更する場合、または[不純な](https://en.wikipedia.org/wiki/Pure_function#Impure_functions)関数の場合は、変換を使用しないでください。 -代わりに、意味が異なる変換には `computed` を、入力が変更されるたびに実行されるべき不純なコードには `effect` を使用してください。 - -## なぜシグナル入力を使用すべきなのか、`@Input()` を使用すべきではないのか - -シグナル入力は、デコレーターベースの `@Input()` のリアクティブな代替手段です。 - -デコレーターベースの `@Input` と比較して、シグナル入力は多くの利点があります。 - -1. シグナル入力は、より**型安全**です。 -
• 必須入力は、初期値や、入力に常に値があることをTypeScriptに伝えるためのトリックを必要としません。 -
• 変換は、受け入れられた入力値と一致するように自動的にチェックされます。 -2. テンプレートで使用されるシグナル入力は、`OnPush` コンポーネントを**自動的に**ダーティにします。 -3. 入力が変更されるたびに、`computed` を使用して簡単に値を**派生**できます。 -4. `ngOnChanges` やセッターの代わりに、`effect` を使用することで、入力の監視が簡単になり、より局所的になります。 diff --git a/adev-ja/src/content/guide/signals/linked-signal.md b/adev-ja/src/content/guide/signals/linked-signal.md new file mode 100644 index 000000000..97a806747 --- /dev/null +++ b/adev-ja/src/content/guide/signals/linked-signal.md @@ -0,0 +1,109 @@ +# `linkedSignal` + +IMPORTANT: `linkedSignal` is [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. + +You can use the `signal` function to hold some state in your Angular code. Sometimes, this state depends on some _other_ state. For example, imagine a component that lets the user select a shipping method for an order: + +```typescript +@Component({/* ... */}) +export class ShippingMethodPicker { + shippingOptions: Signal = getShippingOptions(); + + // Select the first shipping option by default. + selectedOption = signal(this.shippingOptions()[0]); + + changeShipping(newOptionIndex: number) { + this.selectedOption.set(this.shippingOptions()[newOptionIndex]); + } +} +``` + +In this example, the `selectedOption` defaults to the first option, but changes if the user selects another option. But `shippingOptions` is a signal— its value may change! If `shippingOptions` changes, `selectedOption` may contain a value that is no longer a valid option. + +**The `linkedSignal` function lets you create a signal to hold some state that is intrinsically _linked_ to some other state.** Revisiting the example above, `linkedSignal` can replace `signal`: + +```typescript +@Component({/* ... */}) +export class ShippingMethodPicker { + shippingOptions: Signal = getShippingOptions(); + + // Initialize selectedOption to the first shipping option. + selectedOption = linkedSignal(() => this.shippingOptions()[0]); + + changeShipping(index: number) { + this.selectedOption.set(this.shippingOptions()[index]); + } +} +``` + +`linkedSignal` works similarly to `signal` with one key difference— instead of passing a default value, you pass a _computation function_, just like `computed`. When the value of the computation changes, the value of the `linkedSignal` changes to the computation result. This helps ensure that the `linkedSignal` always has a valid value. + +The following example shows how the value of a `linkedSignal` can change based on its linked state: + +```typescript +const shippingOptions = signal(['Ground', 'Air', 'Sea']); +const selectedOption = linkedSignal(() => shippingOptions()[0]); +console.log(selectedOption()); // 'Ground' + +selectedOption.set(shippingOptions[2]); +console.log(selectedOption()); // 'Sea' + +shippingOptions.set(['Email', 'Will Call', 'Postal service']); +console.log(selectedOption()); // 'Email' +``` + +## Accounting for previous state + +In some cases, the computation for a `linkedSignal` needs to account for the previous value of the `linkedSignal`. + +In the example above, `selectedOption` always updates back to the first option when `shippingOptions` changes. You may, however, want to preserve the user's selection if their selected option is still somewhere in the list. To accomplish this, you can create a `linkedSignal` with a separate _source_ and _computation_: + +```typescript +@Component({/* ... */}) +export class ShippingMethodPicker { + shippingOptions: Signal = getShippingOptions(); + + selectedOption = linkedSignal({ + // `selectedOption` is set to the `computation` result whenever this `source` changes. + source: shippingOptions, + computation: (newOptions, previous) => { + // If the newOptions contain the previously selected option, preserve that selection. + // Otherwise, default to the first option. + return newOptions.find(opt => opt.id === previous?.value) ?? newOptions[0]; + } + }); + + changeShipping(newOptionIndex: number) { + this.selectedOption.set(this.shippingOptions()[newOptionIndex]); + } +} +``` + +When you create a `linkedSignal`, you can pass an object with separate `source` and `computation` properties instead of providing just a computation. + +The `source` can be any signal, such as a `computed` or component `input`. When the value of `source` changes, `linkedSignal` updates its value to the result of the provided `computation`. + +The `computation` is a function that receives the new value of `source` and a `previous` object. The `previous` object has two properties— `previous.source` is the previous value of `source`, and `previous.value` is the previous result of the `computation`. You can use these previous values to decide the new result of the computation. + +## Custom equality comparison + +`linkedSignal` updates to the result of the computation every time its linked state changes. By default, Angular uses referential equality to determine if the linked state has changed. You can alternatively provide a custom equality function. + +```typescript +const activeUser = signal({id: 123, name: 'Morgan'}); +const email = linkedSignal(() => `${activeUser().name}@example.com`, { + // Consider the user as the same if it's the same `id`. + equal: (a, b) => a.id === b.id, +}); + +// Or, if separating `source` and `computation` +const alternateEmail = linkedSignal({ + source: activeUser, + computation: user => `${user.name}@example.com`, + equal: (a, b) => a.id === b.id, +}); + +// This update to `activeUser` does not cause `email` or `alternateEmail` +// to update because the `id` is the same. +activeUser.set({id: 123, name: 'Morgan', isAdmin: false}); +``` diff --git a/adev-ja/src/content/guide/signals/model.en.md b/adev-ja/src/content/guide/signals/model.en.md deleted file mode 100644 index 142d53b08..000000000 --- a/adev-ja/src/content/guide/signals/model.en.md +++ /dev/null @@ -1,136 +0,0 @@ -# Model inputs - -**Model inputs** are a special type of input that enable a component to propagate new values -back to another component. - -HELPFUL: Model inputs are currently in [developer preview](/reference/releases#developer-preview). - -When creating a component, you can define a model input similarly to how you create a standard -input. - -```angular-ts -import {Component, model, input} from '@angular/core'; - -@Component({...}) -export class CustomCheckbox { - // This is a model input. - checked = model(false); - - // This is a standard input. - disabled = input(false); -} -``` - -Both types of input allow someone to bind a value into the property. However, **model inputs allow -the component author to write values into the property**. - -In other respects, you can use model inputs the same way you use standard inputs. You can read the -value by calling the signal function, including in reactive contexts like `computed` and `effect`. - -```angular-ts -import {Component, model, input} from '@angular/core'; - -@Component({ - selector: 'custom-checkbox', - template: '
...
', -}) -export class CustomCheckbox { - checked = model(false); - disabled = input(false); - - toggle() { - // While standard inputs are read-only, you can write directly to model inputs. - this.checked.set(!this.checked()); - } -} -``` - -When a component writes a new value into a model input, Angular can propagate the new value back -to the component that is binding a value into that input. This is called **two-way binding** because -values can flow in both directions. - -## Two-way binding with signals - -You can bind a writable signal to a model input. - -```angular-ts -@Component({ - ..., - // `checked` is a model input. - // The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding - template: '', -}) -export class UserProfile { - protected isAdmin = signal(false); -} -``` - -In the above example, the `CustomCheckbox` can write values into its `checked` model input, which -then propagates those values back to the `isAdmin` signal in `UserProfile`. This binding keeps that -values of `checked` and `isAdmin` in sync. Notice that the binding passes the `isAdmin` signal -itself, not the _value_ of the signal. - -## Two-way binding with plain properties - -You can bind a plain JavaScript property to a model input. - -```angular-ts -@Component({ - ..., - // `checked` is a model input. - // The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding - template: '', -}) -export class UserProfile { - protected isAdmin = false; -} -``` - -In the example above, the `CustomCheckbox` can write values into its `checked` model input, which -then propagates those values back to the `isAdmin` property in `UserProfile`. This binding keeps -that values of `checked` and `isAdmin` in sync. - -## Implicit `change` events - -When you declare a model input in a component or directive, Angular automatically creates a -corresponding [output](guide/components/outputs) for that model. The output's name is the model -input's name suffixed with "Change". - -```angular-ts -@Directive({...}) -export class CustomCheckbox { - // This automatically creates an output named "checkedChange". - // Can be subscribed to using `(checkedChange)="handler()"` in the template. - checked = model(false); -} -``` - -Angular emits this change event whenever you write a new value into the model input by calling -its `set` or `update` methods. - -## Customizing model inputs - -You can mark a model input as required or provide an alias in the same way as a -[standard input](guide/signals/inputs). - -Model inputs do not support input transforms. - -## Differences between `model()` and `input()` - -Both `input()` and `model()` functions are ways to define signal-based inputs in Angular, but they -differ in a few ways: -1. `model()` defines **both** an input and an output. The output's name is always the name of the -input suffixed with `Change` to support two-way bindings. It will be up to the consumer of your -directive to decide if they want to use just the input, just the output, or both. -2. `ModelSignal` is a `WritableSignal` which means that its value can be changed from anywhere -using the `set` and `update` methods. When a new value is assigned, the `ModelSignal` will emit -to its output. This is different from `InputSignal` which is read-only and can only be changed -through the template. -3. Model inputs do not support input transforms while signal inputs do. - -## When to use model inputs - -Use model inputs when you want a component to support two-way binding. This is typically -appropriate when a component exists to modify a value based on user interaction. Most commonly, -custom form controls such as a date picker or combobox should use model inputs for their primary -value. diff --git a/adev-ja/src/content/guide/signals/model.md b/adev-ja/src/content/guide/signals/model.md deleted file mode 100644 index ae6e6afb0..000000000 --- a/adev-ja/src/content/guide/signals/model.md +++ /dev/null @@ -1,136 +0,0 @@ -# モデル入力 - -**モデル入力** は、コンポーネントが新しい値を別のコンポーネントに伝播できるようにする、 -特殊な入力です。 - -HELPFUL: モデル入力は現在 [開発者プレビュー](/reference/releases#developer-preview) です。 - -コンポーネントを作成するときは、普通の入力を作成する方法と同様に、 -モデル入力を定義できます。 - -```angular-ts -import {Component, model, input} from '@angular/core'; - -@Component({...}) -export class CustomCheckbox { - // これはモデル入力です。 - checked = model(false); - - // これは普通の入力です。 - disabled = input(false); -} -``` - -2種類の入力はどちらも、値をプロパティにバインドすることを可能にします。 -ただし、**モデル入力を使用すると、コンポーネントの作者はプロパティに値を書き込むことができます**。 - -その他の点では、モデル入力を普通の入力と同じように使用できます。 -`computed` や `effect` などのリアクティブコンテキストを含め、シグナル関数を呼び出して値を読み取ることができます。 - -```angular-ts -import {Component, model, input} from '@angular/core'; - -@Component({ - selector: 'custom-checkbox', - template: '
...
', -}) -export class CustomCheckbox { - checked = model(false); - disabled = input(false); - - toggle() { - // 普通の入力は読み取り専用ですが、モデル入力には直接書き込むことができます。 - this.checked.set(!this.checked()); - } -} -``` - -コンポーネントがモデル入力に新しい値を書き込むと、 -Angularはその入力に値をバインドしているコンポーネントに新しい値を伝播できます。 -これは、値が双方向に流れるため、**双方向バインディング** と呼ばれます。 - -## シグナルによる双方向バインディング - -書き込み可能なシグナルをモデル入力にバインドできます。 - -```angular-ts -@Component({ - ..., - // `checked` はモデル入力です。 - // 括弧内角括弧構文(別名「バナナインボックス」)は、双方向バインディングを作成します。 - template: '', -}) -export class UserProfile { - protected isAdmin = signal(false); -} -``` - -上記の例では、`CustomCheckbox` は `checked` モデル入力に値を書き込むことができ、 -その値は `UserProfile` の `isAdmin` シグナルに伝播されます。 -このバインディングにより、`checked` と `isAdmin` の値が同期されます。 -バインディングは `isAdmin` シグナル自体を渡し、シグナルの _値_ は渡さないことに注意してください。 - -## プレーンなプロパティによる双方向バインディング - -プレーンなJavaScriptプロパティをモデル入力にバインドできます。 - -```angular-ts -@Component({ - ..., - // `checked` はモデル入力です。 - // 括弧内角括弧構文(別名「バナナインボックス」)は、双方向バインディングを作成します。 - template: '', -}) -export class UserProfile { - protected isAdmin = false; -} -``` - -上記の例では、`CustomCheckbox` は `checked` モデル入力に値を書き込むことができ、 -その値は `UserProfile` の `isAdmin` プロパティに伝播されます。 -このバインディングにより、`checked` と `isAdmin` の値が同期されます。 - -## 暗黙的な `change` イベント - -コンポーネントまたはディレクティブでモデル入力を宣言すると、 -Angularはそのモデルに対応する [出力](guide/components/outputs) を自動的に作成します。 -出力の名前は、モデル入力の名前の後に「Change」が付加されたものです。 - -```angular-ts -@Directive({...}) -export class CustomCheckbox { - // これは、自動的に「checkedChange」という名前の出力を作成します。 - // テンプレートで `(checkedChange)="handler()"` を使用して購読できます。 - checked = model(false); -} -``` - -`set` または `update` メソッドを呼び出してモデル入力に新しい値を書き込むたびに、 -Angularはこの変更イベントを発行します。 - -## モデル入力のカスタマイズ - -普通の入力と同様に、モデル入力を必須としてマークしたり、 -別名を提供したりできます。 - -モデル入力は、入力の変換をサポートしていません。 - -## `model()` と `input()` の違い - -`input()` と `model()` の両方の関数は、Angularで信号ベースの入力を定義する方法ですが、 -いくつかの違いがあります。 -1. `model()` は、**入力と出力の両方** を定義します。 -出力の名前は常に、双方向バインディングをサポートするために、入力名に `Change` が付加されたものです。 -ディレクティブの利用者は、入力のみ、出力のみ、または両方を使用するかを決定します。 -2. `ModelSignal` は `WritableSignal` であり、 -`set` メソッドと `update` メソッドを使用して、どこからでも値を変更できます。 -新しい値が割り当てられると、`ModelSignal` は出力にイベントを発行します。 -これは、読み取り専用で、テンプレートを通じてのみ変更できる `InputSignal` とは異なります。 -3. モデル入力は入力変換をサポートしませんが、信号入力はサポートします。 - -## いつモデル入力を使用すべきか - -コンポーネントが双方向バインディングをサポートしたい場合、モデル入力を使います。 -これは通常、コンポーネントがユーザーとの対話に基づいて値を変更するために存在する場合に適しています。 -最も一般的なのは、日付ピッカーやコンボボックスのようなカスタムフォームコントロールで、 -主要な値にはモデル入力を使うべきです。 diff --git a/adev-ja/src/content/guide/signals/overview.en.md b/adev-ja/src/content/guide/signals/overview.en.md index c3da40225..99b850b7c 100644 --- a/adev-ja/src/content/guide/signals/overview.en.md +++ b/adev-ja/src/content/guide/signals/overview.en.md @@ -2,7 +2,7 @@ Angular Signals is a system that granularly tracks how and where your state is used throughout an application, allowing the framework to optimize rendering updates.
-Tip: Check out Angular's [Essentials](essentials/managing-dynamic-data) before diving into this comprehensive guide. +Tip: Check out Angular's [Essentials](essentials/signals) before diving into this comprehensive guide. ## What are signals? @@ -119,8 +119,6 @@ Effects are rarely needed in most application code, but may be useful in specifi Avoid using effects for propagation of state changes. This can result in `ExpressionChangedAfterItHasBeenChecked` errors, infinite circular updates, or unnecessary change detection cycles. -Because of these risks, Angular by default prevents you from setting signals in effects. It can be enabled if absolutely necessary by setting the `allowSignalWrites` flag when you create an effect. - Instead, use `computed` signals to model state that depends on other state. @@ -154,7 +152,7 @@ export class EffectiveCounterComponent { } ``` -To create an effect outside of the constructor, you can pass an `Injector` to `effect` via its options: +To create an effect outside the constructor, you can pass an `Injector` to `effect` via its options: ```ts @Component({...}) @@ -249,3 +247,7 @@ effect((onCleanup) => { }); }); ``` + +## Using signals with RxJS + +See [RxJS interop with Angular signals](ecosystem/rxjs-interop) for details on interoperability between signals and RxJS. diff --git a/adev-ja/src/content/guide/signals/overview.md b/adev-ja/src/content/guide/signals/overview.md index ae3bf03d0..70f8a0307 100644 --- a/adev-ja/src/content/guide/signals/overview.md +++ b/adev-ja/src/content/guide/signals/overview.md @@ -2,7 +2,7 @@ Angularシグナルは、アプリケーション全体で状態がどのように使用されているかを細かく追跡するシステムであり、フレームワークがレンダリングの更新を最適化することを可能にします。
-Tip: この包括的なガイドを読む前に、Angularの[基本概念](essentials/managing-dynamic-data)をご覧ください。 +Tip: この包括的なガイドを読む前に、Angularの[基本概念](essentials/signals)をご覧ください。 ## シグナルとは何か? @@ -119,8 +119,6 @@ effect(() => { 状態変更の伝播にエフェクトを使用することは避けてください。これは、`ExpressionChangedAfterItHasBeenChecked`エラー、無限の循環更新、または不要な変更検知サイクルが発生する可能性があります。 -これらのリスクのため、Angularはデフォルトで、エフェクト内でシグナルを設定することを防ぎます。これは、エフェクトを作成する際に`allowSignalWrites`フラグを設定することで有効にできます。 - 代わりに、`computed` シグナルを使用して、他の状態に依存する状態をモデル化してください。 @@ -249,3 +247,7 @@ effect((onCleanup) => { }); }); ``` + +## RxJSとシグナルを併用する + +シグナルとRxJSの相互運用性の詳細については、[RxJSとAngularシグナルの相互運用](ecosystem/rxjs-interop) を参照してください。 diff --git a/adev-ja/src/content/guide/signals/queries.en.md b/adev-ja/src/content/guide/signals/queries.en.md deleted file mode 100644 index 583390cfa..000000000 --- a/adev-ja/src/content/guide/signals/queries.en.md +++ /dev/null @@ -1,191 +0,0 @@ -# Signal queries - -A component or directive can define queries that find child elements and read values from their injectors. - -Developers most commonly use queries to retrieve references to components, directives, DOM elements, and more. - -There are two categories of query: view queries and content queries. - -Signal queries supply query results as a reactive signal primitive. You can use query results in `computed` and `effect`, composing these results with other signals. - -IMPORTANT: Signal queries are in [developer preview](reference/releases#developer-preview). APIs may change based on feedback without going through Angular's deprecation cycle. - -If you're already familiar with Angular queries, you can jump straight to [Comparing signal-based queries to decorator-based queries](#comparing-signal-based-queries-to-decorator-based-queries) - -## View queries - -View queries retrieve results from the elements in the component's own template (view). - -### `viewChild` - -You can declare a query targeting a single result with the `viewChild` function. - -```angular-ts -@Component({ - template: ` -
- - ` -}) -export class TestComponent { - // query for a single result by a string predicate - divEl = viewChild('el') // Signal - // query for a single result by a type predicate - cmp = viewChild(MyComponent); // Signal -} -``` - -### `viewChildren` - -You can also query for multiple results with the `viewChildren` function. - -```angular-ts -@Component({ - template: ` -
- @if (show) { -
- } - ` -}) -export class TestComponent { - show = true; - - // query for multiple results - divEls = viewChildren('el'); // Signal> -} -``` - -### View query options - -The `viewChild` and the `viewChildren` query declaration functions have a similar signature accepting two arguments: - -* a **locator** to specify the query target - it can be either a `string` or any injectable token -* a set of **options** to adjust behavior of a given query. - -Signal-based view queries accept only one option: `read`. The `read` option indicates the type of result to inject from the matched nodes and return in the final results. - -```angular-ts -@Component({ - template: `` -}) -export class TestComponent { - // query for a single result with options - cmp = viewChild(MyComponent, {read: ElementRef}); // Signal -} -``` - -## Content queries - -Content queries retrieve results from the elements in the component's content — the elements nested inside the component tag in the template where it's used. - -### `contentChild` - -You can query for a single result with the `contentChild` function. - -```ts -@Component({...}) - export class TestComponent { - // query by a string predicate - headerEl = contentChild('h'); // Signal - - // query by a type predicate - header = contentChild(MyHeader); // Signal -} -``` - - ### `contentChildren` - -You can also query for multiple results with the `contentChildren` function. - -```ts -@Component({...}) -export class TestComponent { - // query for multiple results - divEls = contentChildren('h'); // Signal> -} -``` - -### Content query options - -The `contentChild` and the `contentChildren` query declaration functions have a similar signature accepting two arguments: - -* a **locator** to specify the query target - it can be either a `string` or any injectable token -* a set of **options** to adjust behavior of a given query. - -Content queries accept the following options: - -* `descendants` By default, content queries find only direct children of the component and do not traverse into descendants. If this option is changed to `true`, query results will include all descendants of the element. Even when `true`, however, queries _never_ descend into components. -* `read` indicates the type of result to retrieve from the matched nodes and return in the final results. - -### Required child queries - -If a child query (`viewChild` or `contentChild`) does not find a result, its value is `undefined`. This may occur if the target element is hidden by a control flow statement like`@if` or `@for`. - -Because of this, the child queries return a signal that potentially have the `undefined` value. Most of the time, and especially for the view child queries, developers author their code such that: -* there is at least one matching result; -* results are accessed when the template was processed and query results are available. - -For such cases, you can mark child queries as `required` to enforce presence of at least one matching result. This eliminates `undefined` from the result type signature. If a `required` query does not find any results, Angular throws an error. - -```angular-ts -@Component({ - selector: 'app-root', - standalone: true, - template: ` -
- `, -}) -export class App { - existingEl = viewChild.required('requiredEl'); // required and existing result - missingEl = viewChild.required('notInATemplate'); // required but NOT existing result - - ngAfterViewInit() { - console.log(this.existingEl()); // OK :-) - console.log(this.missingEl()); // Runtime error: result marked as required by not available! - } -} -``` - -## Results availability timing - -A signal query authoring functions will be executed as part of the directive instance construction. This happens before we could create a query instance and execute the template’s creation mode to collect any matches. As a consequence, there is a period of time where the signal instance was created (and can be read) but no query results could have been collected. By default Angular will return `undefined` (for child queries) or an empty array (for children queries) before results are available. Required queries will throw if accessed at this point. - -Angular computes signal-based query results lazily, on demand. This means that query results are not collected unless there is a code path that reads the signal. - -Query results can change over time due to the view manipulation - either through the Angular's control flow (`@if`, `@for` etc.) or by the direct calls to the `ViewContainerRef` API. When you read the value from the query result signal, you can receive different values over time. - -Note: to avoid returning incomplete query results while a template is rendered, Angular delays query resolution until it finishes rendering a given template. - -## Query declarations functions and the associated rules - -The `viewChild`, `contentChild`, `viewChildren` and `contentChildren` functions are special function recognized by the Angular compiler. You can use those functions to declare queries by initializing a component or a directive property. You can never call these functions outside of component and directive property initializers. - -```angular-ts -@Component({ - selector: 'app-root', - standalone: true, - template: ` -
- `, -}) -export class App { - el = viewChild('el'); // all good! - - constructor() { - const myConst = viewChild('el'); // NOT SUPPORTED - } -} -``` - -## Comparing signal-based queries to decorator-based queries - -Signal queries are an alternative approach to the queries declared using the `@ContentChild`, `@ContentChildren`, `@ViewChild` or `@ViewChildren` decorators. The new approach exposes query results as signals which means that query results can be composed with other signals (using `computed` or `effect`) and drive change detection. Additionally, the signal-based query system offers other benefits: - -* **More predictable timing.** You can access query results as soon as they're available. -* **Simpler API surface.** All queries return a signal, and queries with more than one result let you work with a standard array. -* **Improved type safety.** Fewer query use cases include `undefined` in the possible results. -* **More accurate type inference.** TypeScript can infer more accurate types when you use a type predicate or when you specify an explicit `read` option. -* **Lazier updates.** - Angular updates signal-based query results lazily; the framework does no work unless your code explicitly reads the query results. - -The underlying query mechanism doesn't change much - conceptually Angular still creates singular "child" or plural "children" queries that target elements in a template (view) or content. The difference is in type of results and the exact timing of the results availability. The authoring format for declaring signal-based queries changed as well: the `viewChild`, `viewChildren`, `contentChild` and `contentChildren` functions used as initializer of class members are automatically recognized by Angular. diff --git a/adev-ja/src/content/guide/signals/queries.md b/adev-ja/src/content/guide/signals/queries.md deleted file mode 100644 index cf4bf63b6..000000000 --- a/adev-ja/src/content/guide/signals/queries.md +++ /dev/null @@ -1,191 +0,0 @@ -# シグナルクエリ - -コンポーネントまたはディレクティブは、子要素を見つけ、インジェクターから値を読み取るクエリを定義できます。 - -開発者は、クエリを使って、コンポーネント、ディレクティブ、DOM要素などの参照を取得することがよくあります。 - -クエリには、ビュークエリとコンテンツクエリの2つのカテゴリーがあります。 - -シグナルクエリは、クエリ結果をリアクティブなシグナルプリミティブとして提供します。クエリ結果を `computed` や `effect` で使用し、これらの結果を他のシグナルと組み合わせることができます。 - -IMPORTANT: シグナルクエリは [開発者プレビュー](reference/releases#developer-preview) です。APIは、Angularの非推奨サイクルを経ることなく、フィードバックに基づいて変更される可能性があります。 - -Angularのクエリに既に詳しい場合は、[シグナルベースのクエリとデコレーターベースのクエリの比較](#comparing-signal-based-queries-to-decorator-based-queries) に直接進むことができます。 - -## ビュークエリ - -ビュークエリは、コンポーネント自身のテンプレート(ビュー)内の要素から結果を取得します。 - -### `viewChild` - -`viewChild` 関数を使って、単一の結果をターゲットとするクエリを宣言できます。 - -```angular-ts -@Component({ - template: ` -
- - ` -}) -export class TestComponent { - // query for a single result by a string predicate - divEl = viewChild('el') // Signal - // query for a single result by a type predicate - cmp = viewChild(MyComponent); // Signal -} -``` - -### `viewChildren` - -`viewChildren` 関数を使って、複数の結果をクエリできます。 - -```angular-ts -@Component({ - template: ` -
- @if (show) { -
- } - ` -}) -export class TestComponent { - show = true; - - // query for multiple results - divEls = viewChildren('el'); // Signal> -} -``` - -### ビュークエリオプション - -`viewChild` と `viewChildren` のクエリ宣言関数は、2つの引数を受け取る似たようなシグネチャを持っています。 - -* クエリターゲットを指定する**ロケーター** - これは、`string` または注入可能なトークンです。 -* 指定されたクエリの動作を調整する**オプション**のセット。 - -シグナルベースのビュークエリは、`read` という1つのオプションのみを受け付けます。`read` オプションは、一致したノードから注入して最終結果で返す結果の型を示します。 - -```angular-ts -@Component({ - template: `` -}) -export class TestComponent { - // query for a single result with options - cmp = viewChild(MyComponent, {read: ElementRef}); // Signal -} -``` - -## コンテンツクエリ - -コンテンツクエリは、コンポーネントのコンテンツ、つまりコンポーネントが使用されるテンプレート内のコンポーネントタグ内にネストされた要素から結果を取得します。 - -### `contentChild` - -`contentChild` 関数を使って、単一の結果をクエリできます。 - -```ts -@Component({...}) - export class TestComponent { - // query by a string predicate - headerEl = contentChild('h'); // Signal - - // query by a type predicate - header = contentChild(MyHeader); // Signal -} -``` - - ### `contentChildren` - -`contentChildren` 関数を使って、複数の結果をクエリできます。 - -```ts -@Component({...}) -export class TestComponent { - // query for multiple results - divEls = contentChildren('h'); // Signal> -} -``` - -### コンテンツクエリオプション - -`contentChild` と `contentChildren` のクエリ宣言関数は、2つの引数を受け取る似たようなシグネチャを持っています。 - -* クエリターゲットを指定する**ロケーター** - これは、`string` または注入可能なトークンです。 -* 指定されたクエリの動作を調整する**オプション**のセット。 - -コンテンツクエリは、次のオプションを受け付けます。 - -* `descendants` デフォルトでは、コンテンツクエリはコンポーネントの直接の子のみを見つけ、子孫にはトラバースしません。このオプションが `true` に変更された場合、クエリ結果は要素のすべての子孫を含みます。ただし、`true` でも、クエリは*決して*コンポーネント内に降りていきません。 -* `read` は、一致したノードから取得して最終結果で返す結果の型を示します。 - -### 必須の子クエリ - -子クエリ (`viewChild` または `contentChild`) が結果を見つけられない場合、その値は `undefined` になります。これは、`@if` や `@for` などの制御フローステートメントによってターゲット要素が非表示になっている場合に発生する可能性があります。 - -このため、子クエリは `undefined` の値を持つ可能性があるシグナルを返します。ほとんどの場合、特にビューの子クエリの場合、開発者はコードを次のように記述します。 -* 少なくとも1つのマッチする結果がある。 -* 結果は、テンプレートが処理され、クエリ結果が利用可能となったときにアクセスされる。 - -このような場合、子クエリを `required` とマークすることで、少なくとも1つのマッチする結果の存在を強制できます。これにより、結果型シグネチャから `undefined` が削除されます。`required` クエリが結果を見つけられない場合、Angularはエラーをスローします。 - -```angular-ts -@Component({ - selector: 'app-root', - standalone: true, - template: ` -
- `, -}) -export class App { - existingEl = viewChild.required('requiredEl'); // required and existing result - missingEl = viewChild.required('notInATemplate'); // required but NOT existing result - - ngAfterViewInit() { - console.log(this.existingEl()); // OK :-) - console.log(this.missingEl()); // Runtime error: result marked as required by not available! - } -} -``` - -## 結果の利用可能性タイミング - -シグナルクエリを作る関数は、ディレクティブインスタンスの構築の一部として実行されます。これは、クエリインスタンスを作成して、テンプレートの作成モードを実行して一致するものを収集する前に発生します。結果として、シグナルインスタンスが作成され(読み取ることが可能)、クエリ結果が収集できない期間があります。デフォルトでは、Angularは結果が利用可能となる前に `undefined`(子クエリの場合)または空の配列(子クエリの場合)を返します。必須クエリは、この時点でアクセスされるとスローします。 - -Angularは、シグナルベースのクエリ結果を必要に応じて遅延評価します。つまり、クエリ結果が収集されるのは、シグナルを読み取るコードパスがある場合のみです。 - -クエリ結果は、ビューの操作によって時間の経過とともに変化する可能性があります。これは、Angularの制御フロー(`@if`、`@for` など)または `ViewContainerRef` APIへの直接呼び出しのいずれかによって行われます。クエリ結果のシグナルから値を読み取ると、時間の経過とともに異なる値を受け取る可能性があります。 - -注: テンプレートがレンダリングされている間に不完全なクエリ結果を返さないよう、Angularは指定されたテンプレートのレンダリングが完了するまでクエリ解決を遅らせます。 - -## クエリ宣言関数と関連するルール - -`viewChild`、`contentChild`、`viewChildren`、`contentChildren` 関数は、Angularコンパイラによって認識される特別な関数です。これらの関数を使って、コンポーネントまたはディレクティブプロパティを初期化することでクエリを宣言できます。これらの関数をコンポーネントとディレクティブのプロパティイニシャライザー以外で呼び出すことはできません。 - -```angular-ts -@Component({ - selector: 'app-root', - standalone: true, - template: ` -
- `, -}) -export class App { - el = viewChild('el'); // 問題なし! - - constructor() { - const myConst = viewChild('el'); // サポートされていません - } -} -``` - -## シグナルベースのクエリとデコレーターベースのクエリの比較 - -シグナルクエリは、`@ContentChild`、`@ContentChildren`、`@ViewChild` または `@ViewChildren` デコレーターを使って宣言されたクエリに対する代替アプローチです。新しいアプローチでは、クエリ結果がシグナルとして公開されるため、クエリ結果を他のシグナル(`computed` または `effect` を使用して)と組み合わせ、変更検知を駆動できます。さらに、シグナルベースのクエリシステムは、次のような利点も提供します。 - -* **より予測可能なタイミング。** クエリ結果が利用可能になったらすぐにアクセスできます。 -* **よりシンプルなAPIサーフェス。** すべてのクエリがシグナルを返し、複数の結果を持つクエリでは標準の配列を操作できます。 -* **改善された型安全性。** より少ないクエリのユースケースで、`undefined` が可能な結果に含まれます。 -* **より正確な型推論。** TypeScriptは、型述語を使用する場合や、明示的な `read` オプションを指定する場合に、より正確な型を推論できます。 -* **より遅延した更新。** Angularは、シグナルベースのクエリ結果を遅延して更新します。フレームワークは、コードが明示的にクエリ結果を読み取らない限り、何も動作しません。 - -クエリのメカニズムは本質的にほとんど変わりません。概念的には、Angularは依然としてテンプレート(ビュー)またはコンテンツ内の要素をターゲットとする単一の「子」クエリまたは複数の「子」クエリを作成します。違いは、結果の型と結果の利用可能性のタイミングです。シグナルベースのクエリを宣言するための記述形式も変更されました。クラスメンバーのイニシャライザーとして使用される `viewChild`、`viewChildren`、`contentChild`、`contentChildren` 関数は、Angularによって自動的に認識されます。 diff --git a/adev-ja/src/content/guide/signals/resource.md b/adev-ja/src/content/guide/signals/resource.md new file mode 100644 index 000000000..3de38a2f1 --- /dev/null +++ b/adev-ja/src/content/guide/signals/resource.md @@ -0,0 +1,115 @@ +# Async reactivity with resources + +IMPORTANT: `resource` is [experimental](reference/releases#experimental). It's ready for you to try, but it might change before it is stable. + +Most signal APIs are synchronous— `signal`, `computed`, `input`, etc. However, applications often need to deal with data that is available asynchronously. A `Resource` gives you a way to incorporate async data into your application's signal-based code. + +You can use a `Resource` to perform any kind of async operation, but the most common use-case for `Resource` is fetching data from a server. The following creates a resource to fetch some user data. + +The easiest way to create a `Resource` is the `resource` function. + +```typescript +import {resource, Signal} from '@angular/core'; + +const userId: Signal = getUserId(); + +const userResource = resource({ + // Define a reactive request computation. + // The request value recomputes whenever any read signals change. + request: () => ({id: userId()}), + + // Define an async loader that retrieves data. + // The resource calls this function every time the `request` value changes. + loader: ({request}) => fetchUser(request), +}); + +// Created a computed based on the result of the resource's loader function. +const firstName = computed(() => userResource.value().firstName); +``` + +The `resource` function accepts a `ResourceOptions` object with two main properties: `request` and `loader`. + +The `request` property defines a reactive computation that produce a request value. Whenever signals read in this computation change, the resource produces a new request value, similar to `computed`. + +The `loader` property defines a `ResourceLoader`— an async function that retrieves some state. The resource calls the loader every time the `request` computation produces a new value, passing that value to the loader. See [Resource loaders](#resource-loaders) below for more details. + +`Resource` has a `value` signal that contains the results of the loader. + +## Resource loaders + +When creating a resource, you specify a `ResourceLoader`. This loader is an async function that accepts a single parameter— a `ResourceLoaderParams` object— and returns a value. + +The `ResourceLoaderParams` object contains three properties: `request`, `previous`, and `abortSignal`. + +| Property | Description | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `request` | The value of the resource's `request` computation. | +| `previous` | An object with a `status` property, containing the previous `ResourceStatus`. | +| `abortSignal` | An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). See [Aborting requests](#aborting-requests) below for details. | + +### Aborting requests + +A resource aborts an outstanding request if the `request` computation changes while the resource is loading. + +You can use the `abortSignal` in `ResourceLoaderParams` to respond to aborted requests. For example, the native `fetch` function accepts an `AbortSignal`: + +```typescript +const userId: Signal = getUserId(); + +const userResource = resource({ + request: () => ({id: userId()}), + loader: ({request, abortSignal}): Promise => { + // fetch cancels any outstanding HTTP requests when the given `AbortSignal` + // indicates that the request has been aborted. + return fetch(`users/${request.id}`, {signal: abortSignal}); + }, +}); +``` + +See [`AbortSignal` on MDN](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for more details on request cancellation with `AbortSignal`. + +### Reloading + +You can programmatically trigger a resource's `loader` by calling the `reload` method. + +```typescript +const userId: Signal = getUserId(); + +const userResource = resource({ + request: () => ({id: userId()}), + loader: ({request}) => fetchUser(request), +}); + +// ... + +userResource.reload(); +``` + +### `undefined` requests + +A request value of `undefined` prevents the resource from running its loader, and will put the resource into an `Idle` state. + +## Resource status + +The resource object has several signal properties for reading the status of the asynchronous loader. + +| Property | Description | +| ----------- | --------------------------------------------------------------------------------------------------------------- | +| `value` | The most recent value of the resource, or `undefined` if no value has been recieved. | +| `hasValue` | Whether the resource has a value. | +| `error` | The most recent error encountered while running the resource's loader, or `undefined` if no error has occurred. | +| `isLoading` | Whether the resource loader is currently running. | +| `status` | The resource's specific `ResourceStatus`, as described below. | + +The `status` signal provides a specific `ResourceStatus` that describes the state of the resource. + +| Status | `value()` | Description | +| ----------- | :---------------- | ---------------------------------------------------------------------------- | +| `Idle` | `undefined` | The resource has no valid request and the loader has not run. | +| `Error` | `undefined` | The loader has encountered an error. | +| `Loading` | `undefined` | The loader is running as a result of the `request` value changing. | +| `Reloading` | Previous value | The loader is running as a result calling of the resource's `reload` method. | +| `Resolved` | Resolved value | The loader has completed. | +| `Local` | Locally set value | The resource's value has been set locally via `.set()` or `.update()` | + +You can use this status information to conditionally display user interface elements, such loading indicators and error messages. diff --git a/adev-ja/src/content/guide/signals/rxjs-interop.md b/adev-ja/src/content/guide/signals/rxjs-interop.md deleted file mode 100644 index 996901d02..000000000 --- a/adev-ja/src/content/guide/signals/rxjs-interop.md +++ /dev/null @@ -1,131 +0,0 @@ -# RxJSとの相互運用 - -IMPORTANT: RxJS Interopパッケージは [開発者プレビュー](reference/releases#developer-preview) で利用可能です。お試しいただけますが、安定版になるまでは変更される可能性があります。 - -Angularの `@angular/core/rxjs-interop` パッケージは、[Angularシグナル](guide/signals) とRxJSのObservablesを統合するための便利なユーティリティを提供します。 - -## `toSignal` - -`toSignal` 関数を使用して、Observableの値を追跡するシグナルを作成します。これはテンプレート内の `async` パイプと似ていますが、より柔軟で、アプリケーション内のどこでも使用できます。 - -```ts -import { Component } from '@angular/core'; -import { AsyncPipe } from '@angular/common'; -import { interval } from 'rxjs'; -import { toSignal } from '@angular/core/rxjs-interop'; - -@Component({ - template: `{{ counter() }}`, -}) -export class Ticker { - counterObservable = interval(1000); - - // `counterObservable` の値を表す `Signal` を取得します。 - counter = toSignal(this.counterObservable, {initialValue: 0}); -} -``` - -`async` パイプと同様に、`toSignal` はObservableをすぐに購読します。これにより副作用が発生する可能性があります。`toSignal` によって作成された購読は、`toSignal` を呼び出すコンポーネントまたはサービスが破棄されると、指定されたObservableから自動的に購読解除されます。 - -IMPORTANT: `toSignal` は購読を作成します。同じObservableに対して繰り返し呼び出すことは避け、代わりに返されたシグナルを再利用してください。 - -### 注入コンテキスト - -`toSignal` は、コンポーネントまたはサービスの構築時など、[注入コンテキスト](guide/di/dependency-injection-context) で実行する必要があります。注入コンテキストが利用できない場合は、代わりに使用する `Injector` を手動で指定できます。 - -### 初期値 - -Observableは購読時に同期的に値を生成するとは限りませんが、シグナルは常に現在の値を必要とします。`toSignal` シグナルのこの「初期」値を扱う方法はいくつかあります。 - -#### `initialValue` オプション - -上記の例のように、Observableが初めて値を発行する前にシグナルが返す値を `initialValue` オプションで指定できます。 - -#### `undefined` 初期値 - -`initialValue` を指定しない場合、生成されたシグナルは、Observableが値を発行するまでは `undefined` を返します。これは、`async` パイプが `null` を返す動作に似ています。 - -#### `requireSync` オプション - -`BehaviorSubject` のように、同期的に値を発行することが保証されているObservableもあります。このような場合は、`requireSync: true` オプションを指定できます。 - -`requiredSync` が `true` の場合、`toSignal` はObservableが購読時に同期的に値を発行することを強制します。これにより、シグナルは常に値を持ち、`undefined` 型または初期値は不要になります。 - -### `manualCleanup` - -デフォルトでは、`toSignal` は、それを作成したコンポーネントまたはサービスが破棄されると、Observableから自動的に購読解除されます。 - -この動作をオーバーライドするには、`manualCleanup` オプションを渡すことができます。この設定は、自然に完了するObservableに使用できます。 - -### エラーと完了 - -`toSignal` で使用されるObservableがエラーを発生させた場合、そのエラーはシグナルが読み取られるときにスローされます。 - -`toSignal` で使用されるObservableが完了した場合、シグナルは完了前に発行された最後の値を返します。 - -## `toObservable` - -`toObservable` ユーティリティを使用して、シグナルの値を追跡する `Observable` を作成します。シグナルの値は、値が変更されるとObservableに値を発行する `effect` で監視されます。 - -```ts -import { Component, signal } from '@angular/core'; - -@Component(...) -export class SearchResults { - query: Signal = inject(QueryService).query; - query$ = toObservable(this.query); - - results$ = this.query$.pipe( - switchMap(query => this.http.get('/search?q=' + query )) - ); -} -``` - -`query` シグナルが変更されると、`query$` Observableは最新のクエリを発行し、新しいHTTPリクエストをトリガーします。 - -### 注入コンテキスト - -`toObservable` は、コンポーネントまたはサービスの構築時など、[注入コンテキスト](guide/di/dependency-injection-context) で実行する必要があります。注入コンテキストが利用できない場合は、代わりに使用する `Injector` を手動で指定できます。 - -### `toObservable` のタイミング - -`toObservable` は、`ReplaySubject` 内でシグナルの値を追跡するために `effect` を使用します。購読時に、最初の値(存在する場合)は同期的に発行される可能性があり、その後のすべての値は非同期になります。 - -Observableとは異なり、シグナルは同期的な変更通知を提供しません。シグナルの値を複数回更新しても、`toObservable` はシグナルが安定した後にのみ値を発行します。 - -```ts -const obs$ = toObservable(mySignal); -obs$.subscribe(value => console.log(value)); - -mySignal.set(1); -mySignal.set(2); -mySignal.set(3); -``` - -ここでは、最後の値 (3) のみがログに出力されます。 - -### `outputFromObservable` - -`outputFromObservable(...)` は、RxJSのObservableに基づいて値を発行するAngularの出力を宣言します。 - -```ts -class MyDir { - nameChange$ = new Observable(/* ... */); - nameChange = outputFromObservable(this.nameChange$); // OutputRef -} -``` - -[output() APIガイド](/guide/components/output-fn)で詳細を確認してください。 - -### `outputToObservable` - -`outputToObservable(...)` はAngularの出力をObservableに変換します。 -これにより、Angularの出力をRxJSストリームに簡単に統合できます。 - -```ts -outputToObservable(myComp.instance.onNameChange) - .pipe(...) - .subscribe(...) -``` - -[output() APIガイド](/guide/components/output-fn)で詳細を確認してください。 diff --git a/adev-ja/src/content/guide/ssr.en.md b/adev-ja/src/content/guide/ssr.en.md index 26c5181b0..c5c9c60a7 100644 --- a/adev-ja/src/content/guide/ssr.en.md +++ b/adev-ja/src/content/guide/ssr.en.md @@ -14,32 +14,28 @@ The main advantages of SSR as compared to client-side rendering (CSR) are: To create a **new** project with SSR, run: - - +```shell ng new --ssr - - +``` To add SSR to an **existing** project, use the Angular CLI `ng add` command. - - +```shell ng add @angular/ssr +``` - +Note: Interested in the latest SSR advancements in Angular? Take a look at the developer preview [hybrid rendering APIs](guide/hybrid-rendering). These commands create and update application code to enable SSR and adds extra files to the project structure. - - +``` my-app |-- server.ts # application server └── src |-- app | └── app.config.server.ts # server application configuration └── main.server.ts # main server application bootstrapping - - +``` To verify that the application is server-side rendered, run it locally with `ng serve`. The initial HTML request should contain application content. @@ -51,20 +47,10 @@ The `server.ts` file configures a Node.js Express server and Angular server-side -The `render` method of `CommonEngine` accepts an object with the following properties: - -| Properties | Details | Default Value | -| ------------------- | ---------------------------------------------------------------------------------------- | ------------- | -| `bootstrap` | A method which returns an `NgModule` or a promise which resolves to an `ApplicationRef`. | | -| `providers` | An array of platform level providers for the current request. | | -| `url` | The url of the page to render. | | -| `inlineCriticalCss` | Whether to reduce render blocking requests by inlining critical CSS. | `true` | -| `publicPath` | Base path for browser files and assets. | | -| `document` | The initial DOM to use for bootstrapping the server application. | | -| `documentFilePath` | File path of the initial DOM to use to bootstrap the server application. | | - Angular CLI will scaffold an initial server implementation focused on server-side rendering your Angular application. This server can be extended to support other features such as API routes, redirects, static assets, and more. See [Express documentation](https://expressjs.com/) for more details. +For more information on the APIs, refer to the [`CommonEngine` API reference](api/ssr/node/CommonEngineRenderOptions). + ## Hydration Hydration is the process that restores the server side rendered application on the client. This includes things like reusing the server rendered DOM structures, persisting the application state, transferring application data that was retrieved already by the server, and other processes. Hydration is enabled by default when you use SSR. You can find more info in [the hydration guide](guide/hydration). @@ -75,8 +61,7 @@ Hydration is the process that restores the server side rendered application on t By default, `HttpClient` caches all `HEAD` and `GET` requests which don't contain `Authorization` or `Proxy-Authorization` headers. You can override those settings by using [`withHttpTransferCacheOptions`](api/platform-browser/withHttpTransferCacheOptions) when providing hydration. - - +```typescript bootstrapApplication(AppComponent, { providers: [ provideClientHydration(withHttpTransferCacheOptions({ @@ -84,8 +69,7 @@ bootstrapApplication(AppComponent, { })) ] }); - - +``` ## Authoring server-compatible components @@ -93,8 +77,7 @@ Some common browser APIs and capabilities might not be available on the server. In general, code which relies on browser-specific symbols should only be executed in the browser, not on the server. This can be enforced through the [`afterRender`](api/core/afterRender) and [`afterNextRender`](api/core/afterNextRender) lifecycle hooks. These are only executed on the browser and skipped on the server. - - +```angular-ts import { Component, ViewChild, afterNextRender } from '@angular/core'; @Component({ @@ -111,8 +94,7 @@ export class MyComponent { }); } } - - +``` ## Using Angular Service Worker diff --git a/adev-ja/src/content/guide/ssr.md b/adev-ja/src/content/guide/ssr.md index 1c42a831a..cae1b46ec 100644 --- a/adev-ja/src/content/guide/ssr.md +++ b/adev-ja/src/content/guide/ssr.md @@ -14,32 +14,28 @@ SSRはクライアントサイドレンダリング (CSR) に比べて、主に SSRを使用して**新規**プロジェクトを作成するには、次のコマンドを実行します。 - - +```shell ng new --ssr - - +``` **既存**のプロジェクトにSSRを追加するには、Angular CLIの `ng add` コマンドを使用します。 - - +```shell ng add @angular/ssr +``` - +Note: Angularにおける最新のSSRの進化に興味がありますか?開発者プレビューの[ハイブリッドレンダリングAPI](guide/hybrid-rendering)を覗いてみてください。 これらのコマンドは、SSRを有効にするためのアプリケーションコードを作成および更新し、プロジェクト構造に余分なファイルを追加します。 - - +``` my-app |-- server.ts # アプリケーションサーバー └── src |-- app | └── app.config.server.ts # サーバーアプリケーション構成 └── main.server.ts # メインサーバーアプリケーションのブートストラップ - - +``` アプリケーションがサーバーサイドでレンダリングされていることを確認するには、`ng serve` でローカルに実行します。最初のHTMLリクエストには、アプリケーションのコンテンツが含まれている必要があります。 @@ -51,20 +47,10 @@ Note: In Angular v17 and later, `server.ts` is no longer used by `ng serve`. The -`CommonEngine` の `render` メソッドは、以下のプロパティを持つオブジェクトを受け取ります。 - -| プロパティ | 詳細 | デフォルト値 | -| ------------------- | ---------------------------------------------------------------------------------------- | ------------- | -| `bootstrap` | `NgModule` を返すメソッド、または `ApplicationRef` に解決されるPromise。 | | -| `providers` | 現在のリクエストに対するプラットフォームレベルのプロバイダーの配列。 | | -| `url` | レンダリングするページの URL。 | | -| `inlineCriticalCss` | レンダリングブロッキングリクエストを減らすために、重要な CSS をインライン化するかどうかのフラグ。 | `true` | -| `publicPath` | ブラウザファイルとアセットのベースパス。 | | -| `document` | サーバーアプリケーションのブートストラップに使用する最初の DOM。 | | -| `documentFilePath` | サーバーアプリケーションのブートストラップに使用する最初の DOM のファイルパス。 | | - Angular CLIは、Angularアプリケーションのサーバーサイドレンダリングに焦点を当てた、初期のサーバー実装をスキャフォールディングします。このサーバーは、APIルート、リダイレクト、静的アセットなど、他の機能をサポートするように拡張できます。詳細については、[Express ドキュメント](https://expressjs.com/) を参照してください。 +APIの詳細については、[`CommonEngine` APIリファレンス](api/ssr/node/CommonEngineRenderOptions)を参照してください。 + ## ハイドレーション ハイドレーションは、クライアント上でサーバーサイドレンダリングされたアプリケーションを復元するプロセスです。これには、サーバーでレンダリングされたDOM構造の再利用、アプリケーション状態の永続化、サーバーが既に取得したアプリケーションデータの転送、その他のプロセスが含まれます。ハイドレーションは、SSRを使用する場合、デフォルトで有効になります。ハイドレーションの詳細については、[ハイドレーションガイド](guide/hydration) を参照してください。 @@ -75,8 +61,7 @@ Angular CLIは、Angularアプリケーションのサーバーサイドレン デフォルトでは、`HttpClient` は、`Authorization` または `Proxy-Authorization` ヘッダーが含まれていないすべての `HEAD` および `GET` リクエストをキャッシュします。これらの設定は、ハイドレーションを提供するときに [`withHttpTransferCacheOptions`](api/platform-browser/withHttpTransferCacheOptions) を使用することでオーバーライドできます。 - - +```typescript bootstrapApplication(AppComponent, { providers: [ provideClientHydration(withHttpTransferCacheOptions({ @@ -84,8 +69,7 @@ bootstrapApplication(AppComponent, { })) ] }); - - +``` ## サーバー対応コンポーネントのオーサリング @@ -93,8 +77,7 @@ bootstrapApplication(AppComponent, { 一般的に、ブラウザ固有のシンボルに依存するコードは、サーバーではなく、ブラウザでのみ実行する必要があります。これは、[`afterRender`](api/core/afterRender) および [`afterNextRender`](api/core/afterNextRender) ライフサイクルフックを使用して強制できます。これらは、ブラウザでのみ実行され、サーバーではスキップされます。 - - +```angular-ts import { Component, ViewChild, afterNextRender } from '@angular/core'; @Component({ @@ -111,8 +94,7 @@ export class MyComponent { }); } } - - +``` ## Angular Service Worker の使用 diff --git a/adev-ja/src/content/guide/templates/defer.en.md b/adev-ja/src/content/guide/templates/defer.en.md index fbf3ddf1e..354199efd 100644 --- a/adev-ja/src/content/guide/templates/defer.en.md +++ b/adev-ja/src/content/guide/templates/defer.en.md @@ -200,7 +200,7 @@ Alternatively, you can specify a [template reference variable](/guide/templates/ #### `hover` -The `hover` trigger loads the deferred content when the mouse has hovered over the triggered area through the `mouseenter` and `focusin` events. +The `hover` trigger loads the deferred content when the mouse has hovered over the triggered area through the `mouseover` and `focusin` events. By default, the placeholder acts as the interaction element. Placeholders used this way must have a single root element. diff --git a/adev-ja/src/content/guide/templates/defer.md b/adev-ja/src/content/guide/templates/defer.md index c0c2845e6..1e99205ad 100644 --- a/adev-ja/src/content/guide/templates/defer.md +++ b/adev-ja/src/content/guide/templates/defer.md @@ -200,7 +200,7 @@ Angularは、読み込みが完了すると、プレースホルダーコンテ #### `hover` -`hover` トリガーは、マウスが `mouseenter` イベントと `focusin` イベントを通じてトリガーされた領域にホバーすると、遅延コンテンツを読み込みます。 +`hover` トリガーは、マウスが `mouseover` イベントと `focusin` イベントを通じてトリガーされた領域にホバーすると、遅延コンテンツを読み込みます。 デフォルトでは、プレースホルダーが対話要素として機能します。このように使用されるプレースホルダーは、単一のルート要素を持つ必要があります。 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 bcf269137..46508c98a 100644 --- a/adev-ja/src/content/guide/templates/ng-content.en.md +++ b/adev-ja/src/content/guide/templates/ng-content.en.md @@ -10,7 +10,6 @@ import { Component } from '@angular/core'; @Component({ selector: 'button[baseButton]', - standalone: true, template: ` `, @@ -25,7 +24,6 @@ import { BaseButton } from './base-button/base-button.component.ts'; @Component({ selector: 'app-root', - standalone: true, imports: [BaseButton], template: ` {{ count }} @@ -134,7 +131,6 @@ import { CounterComponent } from './counter/counter.component'; @Component({ selector: 'app-root', - standalone: true, imports: [CounterComponent], template: `
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 3bd08f94a..08f63645e 100644 --- a/adev-ja/src/content/guide/templates/two-way-binding.md +++ b/adev-ja/src/content/guide/templates/two-way-binding.md @@ -17,7 +17,6 @@ import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; @Component({ - standalone: true, imports: [FormsModule], template: `
@@ -54,7 +53,6 @@ import { CounterComponent } from './counter/counter.component'; @Component({ selector: 'app-root', - standalone: true, imports: [CounterComponent], template: `
@@ -74,7 +72,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'app-counter', - standalone: true, template: ` {{ count }} @@ -134,7 +131,6 @@ import { CounterComponent } from './counter/counter.component'; @Component({ selector: 'app-root', - standalone: true, imports: [CounterComponent], template: `
diff --git a/adev-ja/src/content/guide/templates/variables.en.md b/adev-ja/src/content/guide/templates/variables.en.md index bfd26f151..8e3d28628 100644 --- a/adev-ja/src/content/guide/templates/variables.en.md +++ b/adev-ja/src/content/guide/templates/variables.en.md @@ -6,8 +6,6 @@ Angular has two types of variable declarations in templates: local template vari Angular's `@let` syntax allows you to define a local variable and re-use it across a template, similar to the [JavaScript `let` syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let). -IMPORTANT: the `@let` syntax is currently in [Developer Preview](/reference/releases#developer-preview). - ### Using `@let` Use `@let` to declare a variable whose value is based on the result of a template expression. Angular automatically keeps the variable's value up-to-date with the given expression, similar to [bindings](./bindings). diff --git a/adev-ja/src/content/guide/templates/variables.md b/adev-ja/src/content/guide/templates/variables.md index 81ecc4ba3..40b816ac5 100644 --- a/adev-ja/src/content/guide/templates/variables.md +++ b/adev-ja/src/content/guide/templates/variables.md @@ -6,8 +6,6 @@ Angularには、テンプレート内で2種類の変数宣言があります。 Angularの `@let` 構文を使用すると、ローカル変数を定義し、テンプレート全体で再利用できます。これは、[JavaScriptの`let`構文](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let)に似ています。 -IMPORTANT: `@let`構文は現在、[Developer Preview](/reference/releases#developer-preview)にあります。 - ### `@let` の使用方法 `@let` を使用して、テンプレート式の結果に基づいた値を持つ変数を宣言します。 Angularは、[バインディング](./bindings)と同様に、指定された式を使用して変数の値を自動的に最新の状態に保ちます。 diff --git a/adev-ja/src/content/guide/zoneless.md b/adev-ja/src/content/guide/zoneless.md index 3d7e34193..5577b2339 100644 --- a/adev-ja/src/content/guide/zoneless.md +++ b/adev-ja/src/content/guide/zoneless.md @@ -81,15 +81,15 @@ Zoneless applications. In fact, removing these calls can lead to performance reg are used in applications that still rely on ZoneJS. -### `ExperimentalPendingTasks` for Server Side Rendering (SSR) +### `PendingTasks` for Server Side Rendering (SSR) If you are using SSR with Angular, you may know that it relies on ZoneJS to help determine when the application is "stable" and can be serialized. If there are asynchronous tasks that should prevent serialization, an application -not using ZoneJS will need to make Angular aware of these with the `ExperimentalPendingTasks` service. Serialization +not using ZoneJS will need to make Angular aware of these with the `PendingTasks` service. Serialization will wait for the first moment that all pending tasks have been removed. ```typescript -const taskService = inject(ExperimentalPendingTasks); +const taskService = inject(PendingTasks); const taskCleanup = taskService.add(); await doSomeWorkThatNeedsToBeRendered(); taskCleanup(); diff --git a/adev-ja/src/content/introduction/essentials/components.en.md b/adev-ja/src/content/introduction/essentials/components.en.md index 2607751ee..85ea7cc61 100644 --- a/adev-ja/src/content/introduction/essentials/components.en.md +++ b/adev-ja/src/content/introduction/essentials/components.en.md @@ -2,136 +2,122 @@ The fundamental building block for creating applications in Angular. -Components provide structure for organizing your project into easy-to-understand parts with clear responsibilities so that your code is maintainable and scalable. +Components are the main building blocks of Angular applications. Each component represents a part of a larger web page. Organizing an application into components helps provide structure to your project, clearly separating code into specific parts that are easy to maintain and grow over time. -Here is an example of how a Todo application could be broken down into a tree of components. +## Defining a component -```mermaid -flowchart TD - A[TodoApp]-->B - A-->C - B[TodoList]-->D - C[TodoMetrics] - D[TodoListItem] -``` - -In this guide, we'll take a look at how to create and use components in Angular. +Every component has a few main parts: -## Defining a Component +1. A `@Component`[decorator](https://www.typescriptlang.org/docs/handbook/decorators.html) that contains some configuration used by Angular. +2. An HTML template that controls what renders into the DOM. +3. A [CSS selector](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) that defines how the component is used in HTML. +4. A TypeScript class with behaviors, such as handling user input or making requests to a server. -Every component has the following core properties: - -1. A `@Component`[decorator](https://www.typescriptlang.org/docs/handbook/decorators.html) that contains some configuration -2. An HTML template that controls what renders into the DOM -3. A [CSS selector](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) that defines how the component is used in HTML -4. A TypeScript class with behaviors such as managing state, handling user input, or fetching data from a server. - -Here is a simplified example of a TodoListItem component. +Here is a simplified example of a `UserProfile` component. ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - selector: 'todo-list-item', + selector: 'user-profile', template: ` -
  • (TODO) Read Angular Essentials Guide
  • +

    User profile

    +

    This is the user profile page

    `, }) -export class TodoListItem { - /* Component behavior is defined in here */ -} +export class UserProfile { /* Your component code goes here */ } ``` -Other common metadata that you'll also see in components include: - -- `standalone: true` — The recommended approach of streamlining the authoring experience of components -- `styles` — A string or array of strings that contains any CSS styles you want applied to the component - -Knowing this, here is an updated version of our `TodoListItem` component. +The `@Component` decorator also optionally accepts a `styles` property for any CSS you want to apply to your template: ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - standalone: true, - selector: 'todo-list-item', + selector: 'user-profile', template: ` -
  • (TODO) Read Angular Essentials Guide
  • - `, - styles: ` - li { - color: red; - font-weight: 300; - } +

    User profile

    +

    This is the user profile page

    `, + styles: `h1 { font-size: 3em; } `, }) -export class TodoListItem { - /* Component behavior is defined in here */ -} +export class UserProfile { /* Your component code goes here */ } ``` ### Separating HTML and CSS into separate files -For teams that prefer managing their HTML and/or CSS in separate files, Angular provides two additional properties: `templateUrl` and `styleUrl`. - -Using the previous `TodoListItem` component, the alternative approach looks like: +You can define a component's HTML and CSS in separate files using `templateUrl` and `styleUrl`: ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - standalone: true, - selector: 'todo-list-item', - templateUrl: './todo-list-item.component.html', - styleUrl: './todo-list-item.component.css', + selector: 'user-profile', + templateUrl: 'user-profile.html', + styleUrl: 'user-profile.css', }) -export class TodoListItem { - /* Component behavior is defined in here */ +export class UserProfile { + // Component behavior is defined in here } ``` ```angular-html - -
  • (TODO) Read Angular Essentials Guide
  • + +

    Use profile

    +

    This is the user profile page

    ``` ```css -/* todo-list-item.component.css */ -li { - color: red; - font-weight: 300; +/* user-profile.css */ +h1 { + font-size: 3em; } ``` -## Using a Component +## Using components -One advantage of component architecture is that your application is modular. In other words, components can be used in other components. +You build an application by composing multiple components together. For example, if you are building a user profile page, you might break the page up into several components like this: + +```mermaid +flowchart TD + A[UserProfile]-->B + A-->C + B[UserBiography]-->D + C[ProfilePhoto] + D[UserAddress] +``` -To use a component, you need to: +Here, the `UserProfile` component uses several other components to produce the final page. -1. Import the component into the file -2. Add it to the component's `imports` array -3. Use the component's selector in the `template` +To import and use a component, you need to: +1. In your component's TypeScript file, add an `import` statement for the component you want to use. +2. In your `@Component` decorator, add an entry to the `imports` array for the component you want to use. +3. In your component's template, add an element that matches the selector of the component you want to use. -Here's an example of a `TodoList` component importing the `TodoListItem` component from before: +Here's an example of a `UserProfile` component importing a `ProfilePhoto` component: ```angular-ts -// todo-list.component.ts -import {TodoListItem} from './todo-list-item.component.ts'; +// user-profile.ts +import {ProfilePhoto} from 'profile-photo.ts'; @Component({ - standalone: true, - imports: [TodoListItem], + selector: 'user-profile', + imports: [ProfilePhoto], template: ` -
      - -
    +

    User profile

    + +

    This is the user profile page

    `, }) -export class TodoList {} +export class UserProfile { + // Component behavior is defined in here +} ``` +Tip: Want to know more about Angular components? See the [In-depth Components guide](guide/components) for the full details. + ## Next Step Now that you know how components work in Angular, it's time to learn how we add and manage dynamic data in our application. - + + diff --git a/adev-ja/src/content/introduction/essentials/components.md b/adev-ja/src/content/introduction/essentials/components.md index d8d437734..c19b87db3 100644 --- a/adev-ja/src/content/introduction/essentials/components.md +++ b/adev-ja/src/content/introduction/essentials/components.md @@ -1,137 +1,123 @@ -Angularでアプリケーションを作成するための基本的な構成要素。 +Angularでアプリケーションを作成するための基本的な構成要素 -コンポーネントは、プロジェクトを理解しやすい部品に分割し、明確な責任を持たせることで、コードの保守性とスケーラビリティを向上させます。 - -Todoアプリケーションをコンポーネントのツリーに分解する例を示します。 - -```mermaid -flowchart TD - A[TodoApp]-->B - A-->C - B[TodoList]-->D - C[TodoMetrics] - D[TodoListItem] -``` - -このガイドでは、Angularでコンポーネントを作成および使用する方法について説明します。 +コンポーネントは、Angularアプリケーションの主要な構成要素です。各コンポーネントは、より大きなウェブページの一部を表します。アプリケーションをコンポーネントに整理することで、プロジェクトに構造が与えられ、コードが特定の部分に明確に分割されるため、保守と拡張が容易になります。 ## コンポーネントの定義 -すべてのコンポーネントには、核となる次のプロパティがあります。 +すべてのコンポーネントには、いくつかの主要な部分があります。 -1. いくつかの設定を含む `@Component`[デコレーター](https://www.typescriptlang.org/docs/handbook/decorators.html) -2. DOMにレンダリングされる内容を制御するHTMLテンプレート -3. HTMLでコンポーネントがどのように使用されるかを定義する [CSSセレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) -4. 状態管理、ユーザー入力処理、サーバーからのデータフェッチなどの動作を持つTypeScriptクラス +1. Angularによって使用されるいくつかの設定を含む`@Component`[デコレーター](https://www.typescriptlang.org/docs/handbook/decorators.html)。 +2. DOMにレンダリングされる内容を制御するHTMLテンプレート。 +3. HTMLでコンポーネントがどのように使用されるかを定義する[CSSセレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors)。 +4. ユーザー入力の処理やサーバーへのリクエストの実行など、動作を定義するTypeScriptクラス。 -TodoListItemコンポーネントの簡略化された例を次に示します。 +`UserProfile`コンポーネントの簡略化された例を以下に示します。 ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - selector: 'todo-list-item', + selector: 'user-profile', template: ` -
  • (TODO) Read Angular Essentials Guide
  • +

    User profile

    +

    This is the user profile page

    `, }) -export class TodoListItem { - /* コンポーネントの動作はここで定義します。 */ -} +export class UserProfile { /* Your component code goes here */ } ``` -コンポーネントでよく見られるその他のメタデータには次のものがあります。 - -- `standalone: true` — コンポーネントの作成を簡素化する推奨アプローチ -- `styles` — コンポーネントに適用するCSSスタイルを含む文字列または文字列の配列 - -これを踏まえて、`TodoListItem` コンポーネントの更新バージョンを示します。 +`@Component`デコレーターは、テンプレートに適用するCSSを指定するために、オプションで`styles`プロパティも受け付けます。 ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - standalone: true, - selector: 'todo-list-item', + selector: 'user-profile', template: ` -
  • (TODO) Read Angular Essentials Guide
  • - `, - styles: ` - li { - color: red; - font-weight: 300; - } +

    User profile

    +

    This is the user profile page

    `, + styles: `h1 { font-size: 3em; } `, }) -export class TodoListItem { - /* コンポーネントの動作はここで定義します。 */ -} +export class UserProfile { /* Your component code goes here */ } ``` -### HTMLとCSSを別ファイルに分離する - -HTMLやCSSを別ファイルで管理することを好むチーム向けに、Angularは `templateUrl` と `styleUrl` の2つの追加プロパティを提供します。 +### HTMLとCSSを別々のファイルに分離する -前の `TodoListItem` コンポーネントを使用して、代替アプローチは次のようになります。 +`templateUrl`と`styleUrl`を使用して、コンポーネントのHTMLとCSSを別々のファイルで定義できます。 ```angular-ts -// todo-list-item.component.ts +// user-profile.ts @Component({ - standalone: true, - selector: 'todo-list-item', - templateUrl: './todo-list-item.component.html', - styleUrl: './todo-list-item.component.css', + selector: 'user-profile', + templateUrl: 'user-profile.html', + styleUrl: 'user-profile.css', }) -export class TodoListItem { - /* コンポーネントの動作はここで定義します。 */ +export class UserProfile { + // コンポーネントの動作はここに定義されます } ``` ```angular-html - -
  • (TODO) Read Angular Essentials Guide
  • + +

    Use profile

    +

    This is the user profile page

    ``` ```css -/* todo-list-item.component.css */ -li { - color: red; - font-weight: 300; +/* user-profile.css */ +h1 { + font-size: 3em; } ``` ## コンポーネントの使用 -コンポーネントアーキテクチャの利点の1つは、アプリケーションがモジュール化されることです。つまり、コンポーネントは他のコンポーネントの中で使用できます。 +複数のコンポーネントを組み合わせてアプリケーションを構築します。例えば、ユーザープロフィールページを構築する場合、ページを次のような複数のコンポーネントに分割できます。 + +```mermaid +flowchart TD + A[UserProfile]-->B + A-->C + B[UserBiography]-->D + C[ProfilePhoto] + D[UserAddress] +``` -コンポーネントを使用するには、次の手順を実行します。 +ここでは、`UserProfile`コンポーネントは他のいくつかのコンポーネントを使用して最終的なページを作成します。 -1. ファイルにコンポーネントをインポートする -2. コンポーネントの `imports` 配列に追加する -3. テンプレートでコンポーネントのセレクターを使用する +コンポーネントをインポートして使用するには、次の手順が必要です。 +1. コンポーネントのTypeScriptファイルで、使用するコンポーネントの`import`文を追加します。 +2. `@Component`デコレーターで、使用するコンポーネントの`imports`配列にエントリを追加します。 +3. コンポーネントのテンプレートで、使用するコンポーネントのセレクターと一致する要素を追加します。 -前の `TodoListItem` コンポーネントをインポートする `TodoList` コンポーネントの例を次に示します。 +`UserProfile`コンポーネントが`ProfilePhoto`コンポーネントをインポートする例を以下に示します。 ```angular-ts -// todo-list.component.ts -import {TodoListItem} from './todo-list-item.component.ts'; +// user-profile.ts +import {ProfilePhoto} from 'profile-photo.ts'; @Component({ - standalone: true, - imports: [TodoListItem], + selector: 'user-profile', + imports: [ProfilePhoto], template: ` -
      - -
    +

    User profile

    + +

    This is the user profile page

    `, }) -export class TodoList {} +export class UserProfile { + // コンポーネントの動作はここに定義されます +} ``` -## 次のステップ +Tip: Angularコンポーネントについてもっと知りたいですか? 詳細については、[詳細なコンポーネントガイド](guide/components)を参照してください。 + +## 次の手順 -Angularのコンポーネントの仕組みがわかったところで、アプリケーションに動的なデータを追加して管理する方法について学びましょう。 +Angularでのコンポーネントの動作が分かったところで、アプリケーションに動的なデータをどのように追加して管理するかを学ぶ時です。 - + + diff --git a/adev-ja/src/content/introduction/essentials/conditionals-and-loops.en.md b/adev-ja/src/content/introduction/essentials/conditionals-and-loops.en.md deleted file mode 100644 index 7ea0363c9..000000000 --- a/adev-ja/src/content/introduction/essentials/conditionals-and-loops.en.md +++ /dev/null @@ -1,112 +0,0 @@ - -Conditionally show and/or repeat content based on dynamic data. - - -One of the advantages of using a framework like Angular is that it provides built-in solutions for common problems that developers encounter. Examples of this include: displaying content based on a certain condition, rendering a list of items based on application data, etc. - -To solve this problem, Angular uses built-in control flow blocks, which tell the framework when and how your templates should be rendered. - -## Conditional rendering - -One of the most common scenarios that developers encounter is the desire to show or hide content in templates based on a condition. - -A common example of this is whether or not to display certain controls on the screen based on the user's permission level. - -### `@if` block - -Similar to JavaScript's `if` statement, Angular uses `@if` control flow blocks to conditionally hide and show part of a template and its contents. - -```angular-ts -// user-controls.component.ts -@Component({ - standalone: true, - selector: 'user-controls', - template: ` - @if (isAdmin) { - - } - `, -}) -export class UserControls { - isAdmin = true; -} -``` - -In this example, Angular only renders the ` - } @else { -

    You are not authorized.

    - } - `, -}) -export class UserControls { - isAdmin = true; -} -``` - -## Rendering a list - -Another common scenario developers encounter is the need to render a list of items. - -### `@for` block - -Similar to JavaScript’s `for...of` loops, Angular provides the `@for` block for rendering repeated elements. - -```angular-html - -
      - @for (ingredient of ingredientList; track ingredient.name) { -
    • {{ ingredient.quantity }} - {{ ingredient.name }}
    • - } -
    -``` - -```angular-ts -// ingredient-list.component.ts -@Component({ - standalone: true, - selector: 'ingredient-list', - templateUrl: './ingredient-list.component.html', -}) -export class IngredientList { - ingredientList = [ - {name: 'noodles', quantity: 1}, - {name: 'miso broth', quantity: 1}, - {name: 'egg', quantity: 2}, - ]; -} -``` - -However, unlike a standard `for...of` loop, you might've noticed that there's an additional `track` keyword. - -#### `track` property - -When Angular renders a list of elements with `@for`, those items can later change or move. Angular needs to track each element through any reordering, usually by treating a property of the item as a unique identifier or key. - -This ensures any updates to the list are reflected correctly in the UI and tracked properly within Angular, especially in the case of stateful elements or animations. - -To accomplish this, we can provide a unique key to Angular with the `track` keyword. - -## Next Step - -With the ability to determine when and how templates are rendered, it's time to learn how we handle an important aspect of most applications: handling user input. - - - - diff --git a/adev-ja/src/content/introduction/essentials/conditionals-and-loops.md b/adev-ja/src/content/introduction/essentials/conditionals-and-loops.md deleted file mode 100644 index 43fd704f5..000000000 --- a/adev-ja/src/content/introduction/essentials/conditionals-and-loops.md +++ /dev/null @@ -1,112 +0,0 @@ - -動的なデータに基づいてコンテンツの表示を切り替えたり、繰り返したりします。 - - -Angularのようなフレームワークを使用することの利点の1つは、開発者が遭遇する一般的な問題に対する組み込みの解決策を提供することです。これには、特定の条件に基づいてコンテンツを表示したり、アプリケーションデータに基づいてアイテムのリストをレンダリングしたりすることが含まれます。 - -この問題を解決するために、Angularは組み込みの制御フローブロックを使用します。これは、テンプレートがいつどのようにレンダリングされるかをフレームワークに伝えます。 - -## 条件付きレンダリング - -開発者が遭遇する最も一般的なシナリオの1つは、条件に基づいてテンプレート内のコンテンツを表示または非表示にすることです。 - -この一般的な例は、ユーザーの権限レベルに基づいて、画面に特定のコントロールを表示するかどうかです。 - -### `@if` ブロック - -JavaScriptの`if`ステートメントと同様に、Angularは`@if`制御フローブロックを使用して、テンプレートとそのコンテンツの一部を条件付きで非表示にし、表示します。 - -```angular-ts -// user-controls.component.ts -@Component({ - standalone: true, - selector: 'user-controls', - template: ` - @if (isAdmin) { - - } - `, -}) -export class UserControls { - isAdmin = true; -} -``` - -この例では、Angularは`isAdmin`プロパティがtrueの場合にのみ` - } @else { -

    権限がありません。

    - } - `, -}) -export class UserControls { - isAdmin = true; -} -``` - -## リストのレンダリング - -開発者が遭遇するもう1つの一般的なシナリオは、アイテムのリストをレンダリングする必要があることです。 - -### `@for` ブロック - -JavaScriptの`for...of`ループと同様に、Angularは繰り返される要素をレンダリングするための`@for`ブロックを提供します。 - -```angular-html - -
      - @for (ingredient of ingredientList; track ingredient.name) { -
    • {{ ingredient.quantity }} - {{ ingredient.name }}
    • - } -
    -``` - -```angular-ts -// ingredient-list.component.ts -@Component({ - standalone: true, - selector: 'ingredient-list', - templateUrl: './ingredient-list.component.html', -}) -export class IngredientList { - ingredientList = [ - {name: 'noodles', quantity: 1}, - {name: 'miso broth', quantity: 1}, - {name: 'egg', quantity: 2}, - ]; -} -``` - -ただし、標準の`for...of`ループとは異なり、`track`キーワードが追加されていることに気付いたかもしれません。 - -#### `track`プロパティ - -Angularが`@for`を使用して要素のリストをレンダリングすると、これらのアイテムは後で変更・移動される可能性があります。Angularは、通常、アイテムのプロパティを一意の識別子として扱うことで、再順序付けを通じて各アイテムを追跡する必要があります。 - -これにより、リストへの更新がUIに正しく反映され、特に状態のある要素やアニメーションの場合に、Angular内で適切に追跡されます。 - -これを達成するために、`track`キーワードを使用してAngularに一意のキーを提供できます。 - -## 次のステップ - -テンプレートがいつどのようにレンダリングされるかを決定する機能により、ほとんどのアプリケーションの重要な側面の1つであるユーザー入力の処理方法を学ぶことができます。 - - - - diff --git a/adev-ja/src/content/introduction/essentials/sharing-logic.en.md b/adev-ja/src/content/introduction/essentials/dependency-injection.en.md similarity index 73% rename from adev-ja/src/content/introduction/essentials/sharing-logic.en.md rename to adev-ja/src/content/introduction/essentials/dependency-injection.en.md index 5b66a1a18..60fef13cf 100644 --- a/adev-ja/src/content/introduction/essentials/sharing-logic.en.md +++ b/adev-ja/src/content/introduction/essentials/dependency-injection.en.md @@ -1,5 +1,5 @@ - -Dependency injection allows you to share code. + +Reuse code and control behaviors across your application and tests. When you need to share logic between components, Angular leverages the design pattern of [dependency injection](guide/di) that allows you to create a “service” which allows you to inject code into components while managing it from a single source of truth. @@ -18,10 +18,8 @@ Here is an example of a `Calculator` service. ```angular-ts import {Injectable} from '@angular/core'; -@Injectable({ - providedIn: 'root', -}) -export class CalculatorService { +@Injectable({providedIn: 'root'}) +export class Calculator { add(x: number, y: number) { return x + y; } @@ -39,7 +37,7 @@ Here’s what it might look like in the `Receipt` component: ```angular-ts import { Component, inject } from '@angular/core'; -import { CalculatorService } from './calculator.service'; +import { Calculator } from './calculator'; @Component({ selector: 'app-receipt', @@ -47,15 +45,16 @@ import { CalculatorService } from './calculator.service'; }) export class Receipt { - private calculatorService = inject(CalculatorService); - totalCost = this.calculatorService.add(50, 25); + private calculator = inject(Calculator); + totalCost = this.calculator.add(50, 25); } ``` -In this example, the `CalculatorService` is being used by calling the Angular function `inject` and passing in the service to it. +In this example, the `Calculator` is being used by calling the Angular function `inject` and passing in the service to it. ## Next Step + diff --git a/adev-ja/src/content/introduction/essentials/sharing-logic.md b/adev-ja/src/content/introduction/essentials/dependency-injection.md similarity index 73% rename from adev-ja/src/content/introduction/essentials/sharing-logic.md rename to adev-ja/src/content/introduction/essentials/dependency-injection.md index e34eec995..7acdf9c1a 100644 --- a/adev-ja/src/content/introduction/essentials/sharing-logic.md +++ b/adev-ja/src/content/introduction/essentials/dependency-injection.md @@ -1,10 +1,10 @@ - -依存性の注入を使用するとコードを共有できます。 + +コードを再利用し、アプリケーションとテストの振る舞いを制御します。 コンポーネント間でロジックを共有する必要がある場合、Angularは[依存性の注入](guide/di)の設計パターンを活用します。これにより、「サービス」を作成できます。サービスを使用すると、コードをコンポーネントに注入しながら、信頼できる唯一の情報源から管理できます。 -## サービスとは +## サービスとは? サービスは、注入できる再利用可能なコードの断片です。 @@ -18,10 +18,8 @@ ```angular-ts import {Injectable} from '@angular/core'; -@Injectable({ - providedIn: 'root', -}) -export class CalculatorService { +@Injectable({providedIn: 'root'}) +export class Calculator { add(x: number, y: number) { return x + y; } @@ -39,23 +37,24 @@ export class CalculatorService { ```angular-ts import { Component, inject } from '@angular/core'; -import { CalculatorService } from './calculator.service'; +import { Calculator } from './calculator'; @Component({ selector: 'app-receipt', - template: `

    合計金額は {{ totalCost }}

    `, + template: `

    The total is {{ totalCost }}

    `, }) export class Receipt { - private calculatorService = inject(CalculatorService); - totalCost = this.calculatorService.add(50, 25); + private calculator = inject(Calculator); + totalCost = this.calculator.add(50, 25); } ``` -この例では、`CalculatorService`は、Angular関数`inject`を呼び出してサービスを渡すことによって使用されています。 +この例では、`Calculator`は、Angularの関数`inject`を呼び出してサービスを渡すことによって使用されています。 ## 次のステップ - + + diff --git a/adev-ja/src/content/introduction/essentials/handling-user-interaction.en.md b/adev-ja/src/content/introduction/essentials/handling-user-interaction.en.md deleted file mode 100644 index 19df131aa..000000000 --- a/adev-ja/src/content/introduction/essentials/handling-user-interaction.en.md +++ /dev/null @@ -1,59 +0,0 @@ - -Handle user interaction in your application. - - -The ability to handle user interaction and then work with - it is one of the key aspects of building dynamic applications. In this guide, we'll take a look at simple user interaction - event handling. - -## Event Handling - -You can add an event handler to an element by: - -1. Adding an attribute with the events name inside of parentheses -2. Specify what JavaScript statement you want to run when it fires - -```angular-html - -``` - -For example, if we wanted to create a button that would run a `transformText` function when the `click` event is fired, it would look like the following: - -```angular-ts -// text-transformer.component.ts -@Component({ - standalone: true, - selector: 'text-transformer', - template: ` -

    {{ announcement }}

    - - `, -}) -export class TextTransformer { - announcement = 'Hello again Angular!'; - - transformText() { - this.announcement = this.announcement.toUpperCase(); - } -} -``` - -Other common examples of event listeners include: - -```angular-html - - -``` - - -### $event - -If you need to access the [event](https://developer.mozilla.org/docs/Web/API/Event) object, Angular provides an implicit `$event` variable that you can pass to a function: - -```angular-html - -``` - -## Next Step - - - - diff --git a/adev-ja/src/content/introduction/essentials/handling-user-interaction.md b/adev-ja/src/content/introduction/essentials/handling-user-interaction.md deleted file mode 100644 index 23d3e4bbc..000000000 --- a/adev-ja/src/content/introduction/essentials/handling-user-interaction.md +++ /dev/null @@ -1,58 +0,0 @@ - -アプリケーション中でユーザーインタラクションを処理します。 - - -ユーザーインタラクションを処理し、それを使って作業することは、動的なアプリケーションを構築する上で重要な側面の1つです。このガイドでは、単純なユーザーインタラクションであるイベント処理について説明します。 - -## イベント処理 - -要素にイベントハンドラーを追加するには、次の手順に従います。 - -1. イベント名を含む属性を括弧内に追加する。 -2. イベントが発生したときに実行するJavaScript文を指定する。 - -```angular-html - -``` - -たとえば、`click`イベントが発生したときに`transformText`関数を呼び出すボタンを作成する場合は、次のようになります。 - -```angular-ts -// text-transformer.component.ts -@Component({ - standalone: true, - selector: 'text-transformer', - template: ` -

    {{ announcement }}

    - - `, -}) -export class TextTransformer { - announcement = 'Hello again Angular!'; - - transformText() { - this.announcement = this.announcement.toUpperCase(); - } -} -``` - -その他の一般的なイベントリスナーの例を次に示します。 - -```angular-html - - -``` - -### $event - -[イベント](https://developer.mozilla.org/ja/docs/Web/API/Event)オブジェクトにアクセスする必要がある場合、Angularは関数に渡すことができる暗黙の`$event`変数を提供します。 - -```angular-html - -``` - -## 次のステップ - - - - diff --git a/adev-ja/src/content/introduction/essentials/managing-dynamic-data.en.md b/adev-ja/src/content/introduction/essentials/managing-dynamic-data.en.md deleted file mode 100644 index be4feb353..000000000 --- a/adev-ja/src/content/introduction/essentials/managing-dynamic-data.en.md +++ /dev/null @@ -1,56 +0,0 @@ - -Define component state and behavior to manage dynamic data. - - -Now that we have learned the basic structure for a component, let’s learn how you can define the component’s data (i.e., state) and behavior. - -## What is state? - -Components let you neatly encapsulate responsibility for discrete parts of your application. For example, a `SignUpForm` component might need to keep track of whether the form is valid or not before allowing users to take a specific action. As a result, the various properties that a component needs to track is often referred to as "state." - -## Defining state - -To define state, you use [class fields syntax](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes/Public_class_fields) inside of your component. - -For example, using the `TodoListItem` component, create two properties that you want to track: - -1. `taskTitle` — What the title of the task is -2. `isComplete` — Whether or not the task is complete - -```angular-ts -// todo-list-item.component.ts -@Component({ ... }) -export class TodoListItem { - taskTitle = ''; - isComplete = false; -} -``` - -## Updating state - -When you want to update state, this is typically accomplished by defining methods in the component class that can access the various class fields with the `this` keyword. - -```angular-ts -// todo-list-item.component.ts -@Component({ ... }) -export class TodoListItem { - taskTitle = ''; - isComplete = false; - - completeTask() { - this.isComplete = true; - } - - updateTitle(newTitle: string) { - this.taskTitle = newTitle; - } -} -``` - -## Next Step - -Now that you have learned how to declare and manage dynamic data, it's time to learn how to use that data inside of templates. - - - - diff --git a/adev-ja/src/content/introduction/essentials/managing-dynamic-data.md b/adev-ja/src/content/introduction/essentials/managing-dynamic-data.md deleted file mode 100644 index d786178ee..000000000 --- a/adev-ja/src/content/introduction/essentials/managing-dynamic-data.md +++ /dev/null @@ -1,56 +0,0 @@ - -コンポーネントの状態と動作を定義して、動的なデータを管理します。 - - -コンポーネントの基本構造を学んだので、コンポーネントのデータ(つまり状態)と動作を定義する方法を学びましょう。 - -## 状態とは? - -コンポーネントを使用すると、アプリケーションの個別部分の責任をきれいにカプセル化できます。たとえば、`SignUpForm`コンポーネントは、ユーザーが特定の操作をする前に、フォームが有効かどうかを追跡しなければならない場合があります。その結果、コンポーネントが追跡する必要のあるさまざまなプロパティは、しばしば「状態」と呼ばれます。 - -## 状態の定義 - -状態を定義するには、コンポーネント内で[クラスフィールド構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes/Public_class_fields)を使用します。 - -たとえば、`TodoListItem`コンポーネントを使用して、追跡する2つのプロパティを作成します。 - -1. `taskTitle` — タスクのタイトル -2. `isComplete` — タスクが完了しているかどうか - -```angular-ts -// todo-list-item.component.ts -@Component({ ... }) -export class TodoListItem { - taskTitle = ''; - isComplete = false; -} -``` - -## 状態の更新 - -状態を更新する場合は、通常、コンポーネントクラスでメソッドを定義して、`this`キーワードを使用してさまざまなクラスフィールドにアクセスします。 - -```angular-ts -// todo-list-item.component.ts -@Component({ ... }) -export class TodoListItem { - taskTitle = ''; - isComplete = false; - - completeTask() { - this.isComplete = true; - } - - updateTitle(newTitle: string) { - this.taskTitle = newTitle; - } -} -``` - -## 次のステップ - -動的なデータの宣言と管理方法を学んだので、テンプレート内でそのデータを使用する方法を学ぶ時です。 - - - - diff --git a/adev-ja/src/content/introduction/essentials/next-steps.en.md b/adev-ja/src/content/introduction/essentials/next-steps.en.md index 10a86f866..b6ab96a14 100644 --- a/adev-ja/src/content/introduction/essentials/next-steps.en.md +++ b/adev-ja/src/content/introduction/essentials/next-steps.en.md @@ -1,16 +1,20 @@ -Now that you have been introduced to core concepts of Angular - you're ready to put what you learned into practices with our interactive tutorials and learn more with our in-depth guides. +Now that you have been introduced to main concepts of Angular - you're ready to put what you learned into practices with our interactive tutorials and learn more with our in-depth guides. ## Playground +Try out Angular in an interactive code editor to further explore the concepts you've learned. + ## Tutorials +Put these main concepts into practice with our in-browser tutorial or build your first app locally with the Angular CLI. + diff --git a/adev-ja/src/content/introduction/essentials/next-steps.md b/adev-ja/src/content/introduction/essentials/next-steps.md index f71411d54..55db03914 100644 --- a/adev-ja/src/content/introduction/essentials/next-steps.md +++ b/adev-ja/src/content/introduction/essentials/next-steps.md @@ -1,16 +1,20 @@ -Angularの基本概念について学んだので、学んだことをインタラクティブなチュートリアルで実践し、詳細なガイドでさらに学びましょう。 +Angularの主要なコンセプトに入門できました。学んだことをインタラクティブなチュートリアルで実践し、詳細なガイドでさらに学びましょう。 ## プレイグラウンド +インタラクティブなコードエディターでAngularを試し、学んだコンセプトをさらに探求しましょう。 + ## チュートリアル +ブラウザ内のチュートリアルでこれらの主なコンセプトを実践、またはAngular CLIを使ってローカルで最初のアプリケーションを作成しましょう。 + diff --git a/adev-ja/src/content/introduction/essentials/overview.en.md b/adev-ja/src/content/introduction/essentials/overview.en.md index cdc37282c..71ae5543b 100644 --- a/adev-ja/src/content/introduction/essentials/overview.en.md +++ b/adev-ja/src/content/introduction/essentials/overview.en.md @@ -1,11 +1,18 @@ - + +A short introduction to some of Angular's main concepts. -## Before you start +## Interested in Angular? -Like most modern frameworks, Angular expects you to be familiar with HTML, CSS and JavaScript. If you are totally new to frontend development, it might not be the best idea to jump right into a framework as your first step. Grasp the basics and then come back! Prior experience with other frameworks helps, but is not required. +Welcome! This _Essentials_ guide explains some of Angular's main concepts, helping you understand what it's like to use the framework. This guide focuses on just a few building blocks as a short introduction. If you're looking for deep, comprehensive documentation, you can navigate to specific _In-depth Guides_ from the [documentation landing page](overview). -In addition, you should be familiar with the following concepts: +If you prefer to immediately start writing some code, you can [skip straight to the hands-on tutorial](tutorials/learn-angular). + +### Before you start + +This site expects that you're familiar with HTML, CSS and TypeScript. If you are totally new to web development, you should seek out some more introductory content before coming back here. + +In particular, you should be familiar with the following concepts: - [JavaScript Classes](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Classes) - [TypeScript fundamentals](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) diff --git a/adev-ja/src/content/introduction/essentials/overview.md b/adev-ja/src/content/introduction/essentials/overview.md index d968335c0..022540699 100644 --- a/adev-ja/src/content/introduction/essentials/overview.md +++ b/adev-ja/src/content/introduction/essentials/overview.md @@ -1,11 +1,18 @@ +Angularの主要なコンセプトを簡単に紹介します -## 始める前に +## Angularに興味がありますか? -ほとんどのモダンなフレームワークと同様に、AngularではHTML、CSS、JavaScriptについての知識が必要です。フロントエンド開発が完全に初めての場合は、最初のステップとしてフレームワークに飛び込むのはあまり良い考えではありません。基本を理解してから戻ってきてください!他のフレームワークの経験は役に立ちますが、必須ではありません。 +ようこそ!この_基本概念_ガイドでは、Angularの主要なコンセプトのいくつかを説明し、フレームワークの使い方を理解する手助けをします。このガイドは短い入門編として、いくつかのビルディングブロックに焦点を当てています。深く包括的なドキュメントをお探しの場合は、[ドキュメントランディングページ](概要)から特定の_詳細ガイド_に移動できます。 -さらに、次の概念についても理解しておく必要があります。 +すぐにコードを書き始めたい場合は、[ハンズオンチュートリアルまでスキップする](tutorials/learn-angular)ことができます。 + +### はじめに + +このサイトは、あなたがHTML、CSS、TypeScriptに精通していることを前提としています。ウェブ開発の全くの初心者の方は、ここに戻ってくる前に、もっと入門的なコンテンツを探してみてください。 + +特に、次の概念についても理解しておく必要があります。 - [JavaScript クラス](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes) - [TypeScript の基礎](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) diff --git a/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.en.md b/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.en.md deleted file mode 100644 index 85d71e004..000000000 --- a/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.en.md +++ /dev/null @@ -1,95 +0,0 @@ - -Use Angular's template syntax to create dynamic HTML. - - -What you've learned so far enables you to break an application up into components of HTML, but this limits you to static templates (i.e., content that doesn't change). The next step is to learn how to make use of Angular's template syntax to create dynamic HTML. - -## Rendering Dynamic Data - -When you need to display dynamic content in your template, Angular uses the double curly brace syntax in order to distinguish between static and dynamic content. - -Here is a simplified example from a `TodoListItem` component. - -```angular-ts -@Component({ - selector: 'todo-list-item', - template: ` -

    Title: {{ taskTitle }}

    - `, -}) -export class TodoListItem { - taskTitle = 'Read cup of coffee'; -} -``` - -When Angular renders the component you'll see the output: - -```angular-html -

    Title: Read cup of coffee

    -``` - -This syntax declares an **interpolation** between the dynamic data property inside of the HTML. As a result, whenever the data changes, Angular will automatically update the DOM reflecting the new value of the property. - -## Dynamic Properties - -When you need to dynamically set the value of standard DOM properties on an HTML element, the property is wrapped in square brackets to inform Angular that the declared value should be interpreted as a JavaScript-like statement ([with some Angular enhancements](guide/templates/binding#render-dynamic-text-with-text-interpolation)) instead of a plain string. - -For example, a common example of dynamically updating properties in your HTML is determining whether the form submit button should be disabled based on whether the form is valid or not. - -Wrap the desired property in square brackets to tell Angular that the assigned value is dynamic (i.e., not a static string). - -```angular-ts -@Component({ - selector: 'sign-up-form', - template: ` - - `, -}) -export class SignUpForm { - formIsInvalid = true; -} -``` - -In this example, because `formIsInvalid` is true, the rendered HTML would be: - -```angular-html - -``` - -## Dynamic Attributes - -In the event you want to dynamically bind custom HTML attributes (e.g., `aria-`, `data-`, etc.), you might be inclined to wrap the custom attributes with the same square brackets. - -```angular-ts -@Component({ - standalone: true, - template: ` - - `, -}) -export class AppBanner { - testId = 'main-cta'; -} -``` - -Unfortunately, this will not work because custom HTML attributes are not standard DOM properties. In order for this to work as intended, we need to prepend the custom HTML attribute with the `attr.` prefix. - -```angular-ts -@Component({ - standalone: true, - template: ` - - `, -}) -export class AppBanner { - testId = 'main-cta'; -} -``` - -## Next Step - -Now that you have dynamic data and templates in the application, it's time to learn how to enhance templates by conditionally hiding or showing certain elements, looping over elements, and more. - - - - diff --git a/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.md b/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.md deleted file mode 100644 index f72ab1ad9..000000000 --- a/adev-ja/src/content/introduction/essentials/rendering-dynamic-templates.md +++ /dev/null @@ -1,95 +0,0 @@ - -Angularのテンプレート構文を使用して、動的なHTMLを作成します。 - - -これまで学んできたことは、アプリケーションをHTMLのコンポーネントに分割することを可能にしますが、これらは静的なテンプレート(つまり、変化しないコンテンツ)に限定されます。次のステップは、Angularのテンプレート構文を使用して動的なHTMLを作成する方法を学ぶことです。 - -## 動的なデータのレンダリング - -テンプレートに動的なコンテンツを表示する必要がある場合、Angularは二重中括弧構文を使用して、静的なコンテンツと動的なコンテンツを区別します。 - -以下は、`TodoListItem`コンポーネントの簡略化された例です。 - -```angular-ts -@Component({ - selector: 'todo-list-item', - template: ` -

    Title: {{ taskTitle }}

    - `, -}) -export class TodoListItem { - taskTitle = 'Read cup of coffee'; -} -``` - -Angularがコンポーネントをレンダリングすると、次の出力が見られます。 - -```angular-html -

    Title: Read cup of coffee

    -``` - -この構文は、HTML内の動的データプロパティ間の**補間**を宣言します。その結果、データが変更されるたびに、Angularは自動的にDOMを更新してプロパティの新しい値を反映します。 - -## 動的なプロパティ - -HTML要素の標準DOMプロパティの値を動的に設定する必要がある場合、そのプロパティは角括弧で囲まれます。これにより、Angularに宣言された値が、プレーンな文字列ではなく、JavaScriptのようなステートメント([Angularの拡張機能付き](guide/templates/binding#render-dynamic-text-with-text-interpolation))として解釈されるべきであることを伝えます。 - -たとえば、HTMLでプロパティを動的に更新する一般的な例は、フォームが有効かどうかによって、フォーム送信ボタンを無効にするかどうかを決定することです。 - -目的のプロパティを角括弧で囲むことで、Angularに割り当てられた値が動的である(つまり、静的な文字列ではない)ことを伝えます。 - -```angular-ts -@Component({ - selector: 'sign-up-form', - template: ` - - `, -}) -export class SignUpForm { - formIsInvalid = true; -} -``` - -この例では、`formIsInvalid`がtrueであるため、レンダリングされたHTMLは次のようになります。 - -```angular-html - -``` - -## 動的な属性 - -カスタムHTML属性(例:`aria-`、`data-`など)を動的にバインドしたい場合、カスタム属性を同じ角括弧で囲むことを試みるかもしれません。 - -```angular-ts -@Component({ - standalone: true, - template: ` - - `, -}) -export class AppBanner { - testId = 'main-cta'; -} -``` - -残念ながら、これは機能しません。なぜなら、カスタムHTML属性は標準DOMプロパティではないからです。これを意図したとおりに機能させるには、カスタムHTML属性の前に`attr.`プレフィックスを追加する必要があります。 - -```angular-ts -@Component({ - standalone: true, - template: ` - - `, -}) -export class AppBanner { - testId = 'main-cta'; -} -``` - -## 次のステップ - -アプリケーションに動的なデータとテンプレートができたので、条件付きで特定の要素を非表示または表示したり、要素をループ処理したりするなど、テンプレートを強化する方法を学ぶ時がきました。 - - - - diff --git a/adev-ja/src/content/introduction/essentials/signals.en.md b/adev-ja/src/content/introduction/essentials/signals.en.md new file mode 100644 index 000000000..570d9da6c --- /dev/null +++ b/adev-ja/src/content/introduction/essentials/signals.en.md @@ -0,0 +1,80 @@ + +Create and manage dynamic data. + + +In Angular, you use *signals* to create and manage state. A signal is a lightweight wrapper around a value. + +Use the `signal` function to create a signal for holding local state: + +```typescript +import {signal} from '@angular/core'; + +// Create a signal with the `signal` function. +const firstName = signal('Morgan'); + +// Read a signal value by calling it— signals are functions. +console.log(firstName()); + +// Change the value of this signal by calling its `set` method with a new value. +firstName.set('Jaime'); + +// You can also use the `update` method to change the value +// based on the previous value. +firstName.update(name => name.toUpperCase()); +``` + +Angular tracks where signals are read and when they're updated. The framework uses this information to do additional work, such as updating the DOM with new state. This ability to respond to changing signal values over time is known as *reactivity*. + +## Computed expressions + +A `computed` is a signal that produces its value based on other signals. + +```typescript +import {signal, computed} from '@angular/core'; + +const firstName = signal('Morgan'); +const firstNameCapitalized = computed(() => firstName().toUpperCase()); + +console.log(firstNameCapitalized()); // MORGAN +``` + +A `computed` signal is read-only; it does not have a `set` or an `update` method. Instead, the value of the `computed` signal automatically changes when any of the signals it reads change: + +```typescript +import {signal, computed} from '@angular/core'; + +const firstName = signal('Morgan'); +const firstNameCapitalized = computed(() => firstName().toUpperCase()); +console.log(firstNameCapitalized()); // MORGAN + +firstName.set('Jaime'); +console.log(firstNameCapitalized()); // JAIME +``` + +## Using signals in components + +Use `signal` and `computed` inside your components to create and manage state: + +```typescript +@Component({/* ... */}) +export class UserProfile { + isTrial = signal(false); + isTrialExpired = signal(false); + showTrialDuration = computed(() => this.isTrial() && !this.isTrialExpired()); + + activateTrial() { + this.isTrial.set(true); + } +} +``` + +Tip: Want to know more about Angular Signals? See the [In-depth Signals guide](guide/signals) for the full details. + +## Next Step + +Now that you have learned how to declare and manage dynamic data, it's time to learn how to use that data inside of templates. + + + + + diff --git a/adev-ja/src/content/introduction/essentials/signals.md b/adev-ja/src/content/introduction/essentials/signals.md new file mode 100644 index 000000000..1e580727d --- /dev/null +++ b/adev-ja/src/content/introduction/essentials/signals.md @@ -0,0 +1,80 @@ + +動的なデータを生成して管理します。 + + +Angularでは、*シグナル*を使用して状態を作成および管理します。シグナルは、値をラップする軽量なラッパーです。 + +`signal`関数を用いて、ローカル状態を保持するためのシグナルを作成します。 + +```typescript +import {signal} from '@angular/core'; + +// `signal` 関数でシグナルを作成します。 +const firstName = signal('Morgan'); + +// シグナルの値を読み取るには、それを呼び出します。シグナルは関数です。 +console.log(firstName()); + +// 新しい値を指定して `set` メソッドを呼び出すことで、このシグナルの値を変更します。 +firstName.set('Jaime'); + +// `update` メソッドを使用して、 +// 前の値に基づいて値を変更することもできます。 +firstName.update(name => name.toUpperCase()); +``` + +Angularは、シグナルがどこで読み取られ、いつ更新されたかを追跡します。フレームワークはこの情報を使用して、新しい状態をDOMに更新するなど、追加の作業をします。時間の経過とともに変化するシグナル値に応答するこの機能は、*リアクティビティ*として知られています。 + +## 算出式 + +`computed`は、他のシグナルに基づいて値を生成するシグナルです。 + +```typescript +import {signal, computed} from '@angular/core'; + +const firstName = signal('Morgan'); +const firstNameCapitalized = computed(() => firstName().toUpperCase()); + +console.log(firstNameCapitalized()); // MORGAN +``` + +`computed`シグナルは読み取り専用です。`set`メソッドまたは`update`メソッドはありません。代わりに、`computed`シグナルの値は、読み取るシグナルのいずれかが変更されると自動的に変更されます。 + +```typescript +import {signal, computed} from '@angular/core'; + +const firstName = signal('Morgan'); +const firstNameCapitalized = computed(() => firstName().toUpperCase()); +console.log(firstNameCapitalized()); // MORGAN + +firstName.set('Jaime'); +console.log(firstNameCapitalized()); // JAIME +``` + +## コンポーネントでシグナルを使う + +`signal`と`computed`をコンポーネント内で使用して、状態を作成および管理します。 + +```typescript +@Component({/* ... */}) +export class UserProfile { + isTrial = signal(false); + isTrialExpired = signal(false); + showTrialDuration = computed(() => this.isTrial() && !this.isTrialExpired()); + + activateTrial() { + this.isTrial.set(true); + } +} +``` + +TIP: Angularのシグナルについてもっと知りたいですか?[詳細なシグナルガイド](guide/signals)を参照してください。 + +## 次の手順 + +動的なデータの宣言と管理方法を学習したので、テンプレート内でそのデータを使用する方法を学習する時間です。 + + + + + diff --git a/adev-ja/src/content/introduction/essentials/templates.en.md b/adev-ja/src/content/introduction/essentials/templates.en.md new file mode 100644 index 000000000..0a4336a3d --- /dev/null +++ b/adev-ja/src/content/introduction/essentials/templates.en.md @@ -0,0 +1,148 @@ + +Use Angular's template syntax to create dynamic user interfaces. + + +Component templates aren't just static HTML— they can use data from your component class and set up handlers for user interaction. + +## Showing dynamic text + +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. + +You can create a binding to show some dynamic text in a template by using double curly-braces: + +```angular-ts +@Component({ + selector: 'user-profile', + template: `

    Profile for {{userName()}}

    `, +}) +export class TodoListItem { + userName = signal('pro_programmer_123'); +} +``` + +When Angular renders the component, you see: + +```html +

    Profile file pro_programmer_123

    +``` + +Angular automatically keeps the binding up-to-date when the value of the signal changes. Building on +the example above, if we update the value of the `userName` signal: + +```typescript +this.userName.set('cool_coder_789'); +``` + +The rendered page updates to reflect the new value: + +```html +

    Profile file cool_coder_789

    +``` + +## Setting dynamic properties and attributes + +Angular supports binding dynamic values into DOM properties with square brackets: + +```angular-ts +@Component({ + /*...*/ + // Set the `disabled` property of the button based on the value of `isAccountDisabled`. + template: ``, +}) +export class UserProfile { + isValidUserId = signal(false); +} +``` + +You can also bind to HTML _attributes_ by prefixing the attribute name with `attr.`: + +```angular-html + +