diff --git a/adev-ja/src/app/core/layout/navigation/navigation.component.en.html b/adev-ja/src/app/core/layout/navigation/navigation.component.en.html index 6416329965..40fb718ee5 100644 --- a/adev-ja/src/app/core/layout/navigation/navigation.component.en.html +++ b/adev-ja/src/app/core/layout/navigation/navigation.component.en.html @@ -79,62 +79,69 @@ -
  • - +
  • +
  • - +
  • - +
  • - +
  • @@ -385,7 +396,13 @@
  • - +
  • - + - - + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 127.14 96.36" + width="20" + height="20" + fill="none" + > + +
  • @@ -434,13 +457,17 @@ (cdkMenuOpened)="openMenu('theme-picker')" > - @switch (theme()) { @case ('light') { - {{ 'light_mode' }} - } @case ('dark') { - {{ 'dark_mode' }} - } @case ('auto') { - {{ 'routine' }} - } } + @switch (theme()) { + @case ('light') { + {{ 'light_mode' }} + } + @case ('dark') { + {{ 'dark_mode' }} + } + @case ('auto') { + {{ 'routine' }} + } + } @@ -486,16 +513,17 @@ - @if (activeRouteItem() === DOCS_ROUTE || activeRouteItem() === REFERENCE_ROUTE) { -
    - -
    + @if (activeRouteItem() === PAGE_PREFIX.DOCS || activeRouteItem() === PAGE_PREFIX.REFERENCE) { +
    + +
    } diff --git a/adev-ja/src/app/core/layout/navigation/navigation.component.html b/adev-ja/src/app/core/layout/navigation/navigation.component.html index b64f55795c..d675467e1a 100644 --- a/adev-ja/src/app/core/layout/navigation/navigation.component.html +++ b/adev-ja/src/app/core/layout/navigation/navigation.component.html @@ -79,62 +79,69 @@ -
  • - +
  • +
  • - +
  • - +
  • - +
  • @@ -385,7 +393,13 @@
  • - +
  • - + - - + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 127.14 96.36" + width="20" + height="20" + fill="none" + > + +
  • @@ -434,13 +454,17 @@ (cdkMenuOpened)="openMenu('theme-picker')" > - @switch (theme()) { @case ('light') { - {{ 'light_mode' }} - } @case ('dark') { - {{ 'dark_mode' }} - } @case ('auto') { - {{ 'routine' }} - } } + @switch (theme()) { + @case ('light') { + {{ 'light_mode' }} + } + @case ('dark') { + {{ 'dark_mode' }} + } + @case ('auto') { + {{ 'routine' }} + } + } @@ -486,16 +510,17 @@ - @if (activeRouteItem() === DOCS_ROUTE || activeRouteItem() === REFERENCE_ROUTE) { -
    - -
    + @if (activeRouteItem() === PAGE_PREFIX.DOCS || activeRouteItem() === PAGE_PREFIX.REFERENCE) { +
    + +
    } diff --git a/adev-ja/src/app/features/update/update.component.en.html b/adev-ja/src/app/features/update/update.component.en.html index 30e38a1495..331255b1ec 100644 --- a/adev-ja/src/app/features/update/update.component.en.html +++ b/adev-ja/src/app/features/update/update.component.en.html @@ -140,12 +140,12 @@

    Package Manager

    beforeRecommendations.length > 0 || duringRecommendations.length > 0 || afterRecommendations.length > 0 ) {
    -

    {{title}}

    +

    {{title()}}

    Before you update

    @for (r of beforeRecommendations; track $index) { -
    - +
    +
    } @@ -163,8 +163,8 @@

    Update to the new version

    } @for (r of duringRecommendations; track $index) { -
    - +
    +
    } @@ -176,8 +176,8 @@

    Update to the new version

    After you update

    @for (r of afterRecommendations; track $index) { -
    - +
    +
    } 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 3dc8bc3e8b..859823f946 100644 --- a/adev-ja/src/app/features/update/update.component.en.ts +++ b/adev-ja/src/app/features/update/update.component.en.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ChangeDetectionStrategy, Component, HostListener, inject} from '@angular/core'; +import {ChangeDetectionStrategy, Component, HostListener, inject, signal} from '@angular/core'; import {Step, RECOMMENDATIONS} from './recommendations'; import {Clipboard} from '@angular/cdk/clipboard'; import {CdkMenuModule} from '@angular/cdk/menu'; @@ -18,6 +18,7 @@ import {MatButtonToggleModule} from '@angular/material/button-toggle'; import {IconComponent} from '@angular/docs'; import {ActivatedRoute, Router} from '@angular/router'; import {marked} from 'marked'; +import {MatSnackBar} from '@angular/material/snack-bar'; interface Option { id: keyof Step; @@ -40,8 +41,10 @@ interface Option { ], changeDetection: ChangeDetectionStrategy.OnPush, }) -export default class AppComponent { - protected title = ''; +export default class UpdateComponent { + private readonly snackBar = inject(MatSnackBar); + + protected title = signal(''); protected level = 1; protected options: Record = { @@ -128,8 +131,8 @@ export default class AppComponent { @HostListener('click', ['$event.target']) copyCode({tagName, textContent}: Element) { if (tagName === 'CODE') { - // TODO: add a toast notification this.clipboard.copy(textContent!); + this.snackBar.open('Copied to clipboard', '', {duration: 2000}); } } @@ -149,9 +152,9 @@ export default class AppComponent { const labelMedium = 'medium applications'; const labelAdvanced = 'advanced applications'; - this.title = `${labelTitle} v${this.from.name} -> v${this.to.name} + this.title.set(`${labelTitle} v${this.from.name} -> v${this.to.name} for - ${this.level < 2 ? labelBasic : this.level < 3 ? labelMedium : labelAdvanced}`; + ${this.level < 2 ? labelBasic : this.level < 3 ? labelMedium : labelAdvanced}`); // Find applicable steps and organize them into before, during, and after upgrade for (const step of this.steps) { diff --git a/adev-ja/src/app/features/update/update.component.html b/adev-ja/src/app/features/update/update.component.html index c654e7a788..3d6e6d95b6 100644 --- a/adev-ja/src/app/features/update/update.component.html +++ b/adev-ja/src/app/features/update/update.component.html @@ -140,12 +140,12 @@

    パッケージマネージャー

    beforeRecommendations.length > 0 || duringRecommendations.length > 0 || afterRecommendations.length > 0 ) {
    -

    {{title}}

    +

    {{title()}}

    アップデート前

    @for (r of beforeRecommendations; track $index) { -
    - +
    +
    } @@ -163,8 +163,8 @@

    新しいバージョンにアップデートする

    } @for (r of duringRecommendations; track $index) { -
    - +
    +
    } @@ -176,8 +176,8 @@

    新しいバージョンにアップデートする

    アップデート後

    @for (r of afterRecommendations; track $index) { -
    - +
    +
    } diff --git a/adev-ja/src/app/features/update/update.component.ts b/adev-ja/src/app/features/update/update.component.ts index 63958af72c..257aa8e9c7 100644 --- a/adev-ja/src/app/features/update/update.component.ts +++ b/adev-ja/src/app/features/update/update.component.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ChangeDetectionStrategy, Component, HostListener, inject} from '@angular/core'; +import {ChangeDetectionStrategy, Component, HostListener, inject, signal} from '@angular/core'; import {Step, RECOMMENDATIONS} from './recommendations'; import {Clipboard} from '@angular/cdk/clipboard'; import {CdkMenuModule} from '@angular/cdk/menu'; @@ -18,6 +18,7 @@ import {MatButtonToggleModule} from '@angular/material/button-toggle'; import {IconComponent} from '@angular/docs'; import {ActivatedRoute, Router} from '@angular/router'; import {marked} from 'marked'; +import {MatSnackBar} from '@angular/material/snack-bar'; interface Option { id: keyof Step; @@ -40,8 +41,10 @@ interface Option { ], changeDetection: ChangeDetectionStrategy.OnPush, }) -export default class AppComponent { - protected title = ''; +export default class UpdateComponent { + private readonly snackBar = inject(MatSnackBar); + + protected title = signal(''); protected level = 1; protected options: Record = { @@ -128,8 +131,8 @@ export default class AppComponent { @HostListener('click', ['$event.target']) copyCode({tagName, textContent}: Element) { if (tagName === 'CODE') { - // TODO: add a toast notification this.clipboard.copy(textContent!); + this.snackBar.open('Copied to clipboard', '', {duration: 2000}); } } @@ -149,9 +152,9 @@ export default class AppComponent { const labelMedium = '中級'; const labelAdvanced = '上級'; - this.title = `${labelTitle} v${this.from.name} -> v${this.to.name} + this.title.set(`${labelTitle} v${this.from.name} -> v${this.to.name} - (${this.level < 2 ? labelBasic : this.level < 3 ? labelMedium : labelAdvanced})`; + (${this.level < 2 ? labelBasic : this.level < 3 ? labelMedium : labelAdvanced})`); // Find applicable steps and organize them into before, during, and after upgrade for (const step of this.steps) { diff --git a/adev-ja/src/app/sub-navigation-data.en.ts b/adev-ja/src/app/sub-navigation-data.en.ts index 8124d3200d..0306e21208 100644 --- a/adev-ja/src/app/sub-navigation-data.en.ts +++ b/adev-ja/src/app/sub-navigation-data.en.ts @@ -6,17 +6,19 @@ * found in the LICENSE file at https://angular.dev/license */ +import {isDevMode} from '@angular/core'; 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 SIGNALS_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/signals/routes.json'; import ERRORS_NAV_DATA from '../../src/assets/content/reference/errors/routes.json'; import EXT_DIAGNOSTICS_NAV_DATA from '../../src/assets/content/reference/extended-diagnostics/routes.json'; -import {DefaultPage} from './core/enums/pages'; import {getApiNavigationItems} from './features/references/helpers/manifest.helper'; +import {DEFAULT_PAGES} from './core/constants/pages'; interface SubNavigationData { docs: NavigationItem[]; @@ -394,6 +396,12 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'guide/routing/rendering-strategies', status: 'new', }, + { + label: 'Customizing route behavior', + path: 'guide/routing/customizing-route-behavior', + contentPath: 'guide/routing/customizing-route-behavior', + status: 'new', + }, { label: 'Router reference', path: 'guide/routing/router-reference', @@ -646,17 +654,17 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ label: 'Animations', status: 'updated', children: [ - { - label: 'Animating your content', - path: 'guide/animations/css', - contentPath: 'guide/animations/css', - }, { label: 'Enter and Leave animations', - path: 'guide/animations/enter-and-leave', + path: 'guide/animations', contentPath: 'guide/animations/enter-and-leave', status: 'new', }, + { + label: 'Complex Animations with CSS', + path: 'guide/animations/css', + contentPath: 'guide/animations/css', + }, { label: 'Route transition animations', path: 'guide/routing/route-transition-animations', @@ -915,26 +923,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'guide/ngmodules/overview', }, { - label: 'Animations', + label: 'Legacy Animations', children: [ { label: 'Overview', - path: 'guide/animations', + path: 'guide/legacy-animations', contentPath: 'guide/animations/overview', }, { label: 'Transition and Triggers', - path: 'guide/animations/transition-and-triggers', + path: 'guide/legacy-animations/transition-and-triggers', contentPath: 'guide/animations/transition-and-triggers', }, { label: 'Complex Sequences', - path: 'guide/animations/complex-sequences', + path: 'guide/legacy-animations/complex-sequences', contentPath: 'guide/animations/complex-sequences', }, { label: 'Reusable Animations', - path: 'guide/animations/reusable-animations', + path: 'guide/legacy-animations/reusable-animations', contentPath: 'guide/animations/reusable-animations', }, { @@ -1046,14 +1054,29 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, + ...(isDevMode() + ? [ + { + label: 'Adev Dev Guide', + children: [ + { + label: 'Kitchen Sink', + path: 'kitchen-sink', + contentPath: 'kitchen-sink', + }, + ], + }, + ] + : []), ]; export const TUTORIALS_SUB_NAVIGATION_DATA: NavigationItem[] = [ FIRST_APP_TUTORIAL_NAV_DATA, LEARN_ANGULAR_TUTORIAL_NAV_DATA, DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA, + SIGNALS_TUTORIAL_NAV_DATA, { - path: DefaultPage.TUTORIALS, + path: DEFAULT_PAGES.TUTORIALS, contentPath: 'tutorials/home', label: 'Tutorials', }, diff --git a/adev-ja/src/app/sub-navigation-data.ts b/adev-ja/src/app/sub-navigation-data.ts index 6f0ff1a1a4..7749d01a14 100644 --- a/adev-ja/src/app/sub-navigation-data.ts +++ b/adev-ja/src/app/sub-navigation-data.ts @@ -6,17 +6,19 @@ * found in the LICENSE file at https://angular.dev/license */ +import {isDevMode} from '@angular/core'; 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 SIGNALS_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/signals/routes.json'; import ERRORS_NAV_DATA from '../../src/assets/content/reference/errors/routes.json'; import EXT_DIAGNOSTICS_NAV_DATA from '../../src/assets/content/reference/extended-diagnostics/routes.json'; -import {DefaultPage} from './core/enums/pages'; import {getApiNavigationItems} from './features/references/helpers/manifest.helper'; +import {DEFAULT_PAGES} from './core/constants/pages'; interface SubNavigationData { docs: NavigationItem[]; @@ -399,6 +401,12 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/routing/router-reference', contentPath: 'guide/routing/router-reference', }, + { + label: 'ルートの動作のカスタマイズ', + path: 'guide/routing/customizing-route-behavior', + contentPath: 'guide/routing/customizing-route-behavior', + status: 'new', + }, { label: 'ルート遷移アニメーション', path: 'guide/routing/route-transition-animations', @@ -646,17 +654,17 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ label: 'アニメーション', status: 'updated', children: [ - { - label: 'コンテンツのアニメーション', - path: 'guide/animations/css', - contentPath: 'guide/animations/css', - }, { label: 'Enter and Leave アニメーション', path: 'guide/animations/enter-and-leave', contentPath: 'guide/animations/enter-and-leave', status: 'new', }, + { + label: '複雑なCSSアニメーション', + path: 'guide/animations/css', + contentPath: 'guide/animations/css', + }, { label: 'ルート遷移アニメーション', path: 'guide/routing/route-transition-animations', @@ -915,26 +923,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'guide/ngmodules/overview', }, { - label: 'アニメーション', + label: 'レガシーアニメーション', children: [ { label: '概要', - path: 'guide/animations', + path: 'guide/legacy-animations', contentPath: 'guide/animations/overview', }, { label: 'トランジションとトリガー', - path: 'guide/animations/transition-and-triggers', + path: 'guide/legacy-animations/transition-and-triggers', contentPath: 'guide/animations/transition-and-triggers', }, { label: '複雑なシーケンス', - path: 'guide/animations/complex-sequences', + path: 'guide/legacy-animations/complex-sequences', contentPath: 'guide/animations/complex-sequences', }, { label: '再利用可能なアニメーション', - path: 'guide/animations/reusable-animations', + path: 'guide/legacy-animations/reusable-animations', contentPath: 'guide/animations/reusable-animations', }, { @@ -1046,14 +1054,29 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, + ...(isDevMode() + ? [ + { + label: 'Adev Dev Guide', + children: [ + { + label: 'Kitchen Sink', + path: 'kitchen-sink', + contentPath: 'kitchen-sink', + }, + ], + }, + ] + : []), ]; export const TUTORIALS_SUB_NAVIGATION_DATA: NavigationItem[] = [ FIRST_APP_TUTORIAL_NAV_DATA, LEARN_ANGULAR_TUTORIAL_NAV_DATA, DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA, + SIGNALS_TUTORIAL_NAV_DATA, { - path: DefaultPage.TUTORIALS, + path: DEFAULT_PAGES.TUTORIALS, contentPath: 'tutorials/home', label: 'チュートリアル', }, diff --git a/adev-ja/src/content/ai/mcp-server-setup.en.md b/adev-ja/src/content/ai/mcp-server-setup.en.md index 2f114ec4f7..504a28f7f2 100644 --- a/adev-ja/src/content/ai/mcp-server-setup.en.md +++ b/adev-ja/src/content/ai/mcp-server-setup.en.md @@ -136,4 +136,4 @@ The Angular CLI MCP server provides several tools to assist you in your developm ## Feedback and New Ideas -The Angular team welcomes your feedback on the existing MCP capabilities and any ideas you have for new tools or features. Please share your thoughts by opening an issue on the [angular/angular GitHub repository](https://github.com/angular/angular/issues). +The Angular team welcomes your feedback on the existing MCP capabilities and any ideas you have for new tools or features. Please share your thoughts by opening an issue on the [angular/angular GitHub repository](https://github.com/angular/angular/issues). \ No newline at end of file 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 917e01795c..63128d8535 100644 --- a/adev-ja/src/content/best-practices/style-guide.en.md +++ b/adev-ja/src/content/best-practices/style-guide.en.md @@ -189,7 +189,7 @@ export class UserProfile { } ``` -### Use `readonly` on properties that are initialized by Angular +### Use `readonly` for properties that shouldn't change Mark component and directive properties initialized by Angular as `readonly`. This includes properties initialized by `input`, `model`, `output`, and queries. The readonly access modifier @@ -200,6 +200,7 @@ ensures that the value set by Angular is not overwritten. export class UserProfile { readonly userId = input(); readonly userSaved = output(); + readonly userName = model(); } ``` diff --git a/adev-ja/src/content/best-practices/style-guide.md b/adev-ja/src/content/best-practices/style-guide.md index fb48bd25bb..ae71e9c141 100644 --- a/adev-ja/src/content/best-practices/style-guide.md +++ b/adev-ja/src/content/best-practices/style-guide.md @@ -189,7 +189,7 @@ export class UserProfile { } ``` -### Angularによって初期化されるプロパティには`readonly`を使用する {#use-readonly-on-properties-that-are-initialized-by-angular} +### 変更されるべきでないプロパティには`readonly`を使用する {#use-readonly-for-properties-that-shouldnt-change} Angularによって初期化されるコンポーネントとディレクティブのプロパティを`readonly`としてマークします。 これには、`input`、`model`、`output`、およびクエリによって初期化されるプロパティが含まれます。 @@ -200,6 +200,7 @@ readonlyアクセス修飾子は、Angularによって設定された値が上 export class UserProfile { readonly userId = input(); readonly userSaved = output(); + readonly userName = model(); } ``` diff --git a/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md index 79096063ae..ae8762e733 100644 --- a/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md +++ b/adev-ja/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -125,7 +125,7 @@ export class UserProfile { // The `stream` property expects a factory function that returns // a data stream as an RxJS Observable. - stream: ({userId}) => this.userData.load(userId), + stream: ({params}) => this.userData.load(params.userId), }); } ``` diff --git a/adev-ja/src/content/ecosystem/service-workers/getting-started.md b/adev-ja/src/content/ecosystem/service-workers/getting-started.md index c75039ad1c..aedaeb7f6e 100644 --- a/adev-ja/src/content/ecosystem/service-workers/getting-started.md +++ b/adev-ja/src/content/ecosystem/service-workers/getting-started.md @@ -159,6 +159,159 @@ Now look at how the browser and service worker handle the updated application. The service worker installed the updated version of your application _in the background_, and the next time the page is loaded or reloaded, the service worker switches to the latest version. +## Service worker configuration + +Angular service workers support comprehensive configuration options through the `SwRegistrationOptions` interface, providing fine-grained control over registration behavior, caching, and script execution. + +### Enabling and disabling service workers + +The `enabled` option controls whether the service worker will be registered and related services will attempt to communicate with it. + + + +import { ApplicationConfig, isDevMode } from '@angular/core'; +import { provideServiceWorker } from '@angular/service-worker'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), // Disable in development, enable in production + }), + ], +}; + + + + +### Cache control with updateViaCache + +The `updateViaCache` option controls how the browser consults the HTTP cache during service worker updates. This provides fine-grained control over when the browser fetches updated service worker scripts and imported modules. + + + +export const appConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + updateViaCache: 'imports', + }), + ], +}; + + + +The `updateViaCache` option accepts the following values: + +* **`'imports'`** - The HTTP cache is consulted for the service worker script's imported scripts, but not for the service worker script itself +* **`'all'`** - The HTTP cache is consulted for both the service worker script and its imported scripts +* **`'none'`** - The HTTP cache is not consulted for the service worker script or its imported scripts + +### ES Module support with type option + +The `type` option enables specifying the script type when registering service workers, providing support for ES module features in your service worker scripts. + + + +export const appConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + type: 'module', // Enable ES module features + }), + ], +}; + + + +The `type` option accepts the following values: + +* **`'classic'`** (default) - Traditional service worker script execution. ES module features such as `import` and `export` are NOT allowed in the script +* **`'module'`** - Registers the script as an ES module. Allows use of `import`/`export` syntax and module features + +### Registration scope control + +The `scope` option defines the service worker's registration scope, determining what range of URLs it can control. + + + +export const appConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + scope: '/app/', // Service worker will only control URLs under /app/ + }), + ], +}; + + + +* Controls which URLs the service worker can intercept and manage +* By default, the scope is the directory containing the service worker script +* Used when calling `ServiceWorkerContainer.register()` + +### Registration strategy configuration + +The `registrationStrategy` option defines when the service worker will be registered with the browser, providing control over the timing of registration. + + + +export const appConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: 'registerWhenStable:30000', + }), + ], +}; + + + +Available registration strategies: + +* **`'registerWhenStable:timeout'`** (default: `'registerWhenStable:30000'`) - Register as soon as the application stabilizes (no pending micro-/macro-tasks) but no later than the specified timeout in milliseconds +* **`'registerImmediately'`** - Register the service worker immediately +* **`'registerWithDelay:timeout'`** - Register with a delay of the specified timeout in milliseconds + + + +// Register immediately +export const immediateConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: 'registerImmediately', + }), + ], +}; + +// Register with a 5-second delay +export const delayedConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: 'registerWithDelay:5000', + }), + ], +}; + + + +You can also provide an Observable factory function for custom registration timing: + + +import { timer } from 'rxjs'; + +export const customConfig: ApplicationConfig = { + providers: [ + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: () => timer(10_000), // Register after 10 seconds + }), + ], +}; + + + ## More on Angular service workers You might also be interested in the following: diff --git a/adev-ja/src/content/ecosystem/service-workers/overview.md b/adev-ja/src/content/ecosystem/service-workers/overview.md index 3ce87dfbaa..6e0fe6e078 100644 --- a/adev-ja/src/content/ecosystem/service-workers/overview.md +++ b/adev-ja/src/content/ecosystem/service-workers/overview.md @@ -1,5 +1,7 @@ # Angular service worker overview +IMPORTANT: The Angular Service Worker is a basic caching utility for simple offline support with a limited featureset. We will not be accepting any new features other than security fixes. For more advanced caching and offline capabilities, we recommend exploring native browser APIs directly. + Service workers augment the traditional web deployment model and empower applications to deliver a user experience with the reliability and performance on par with code that is written to run on your operating system and hardware. Adding a service worker to an Angular application is one of the steps for turning an application into a [Progressive Web App](https://web.dev/progressive-web-apps/) (also known as a PWA). diff --git a/adev-ja/src/content/guide/animations/complex-sequences.md b/adev-ja/src/content/guide/animations/complex-sequences.md index 05502cc9a9..4eb1da6869 100644 --- a/adev-ja/src/content/guide/animations/complex-sequences.md +++ b/adev-ja/src/content/guide/animations/complex-sequences.md @@ -1,6 +1,6 @@ # Complex animation sequences -IMPORTANT: The Angular team recommends using native CSS for animations instead of the Animations package for all new code. Use this guide to understand existing code built with the Animations Package. See [Migrating away from Angular's Animations package](guide/animations/migration#complex-sequences) to learn how you can start using pure CSS animations in your apps. +IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations/enter-and-leave). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps. So far, we've learned simple animations of single HTML elements. Angular also lets you animate coordinated sequences, such as an entire grid or list of elements as they enter and leave a page. @@ -37,7 +37,7 @@ The first argument of `query()` is a [css selector](https://developer.mozilla.or Not all child elements are actually considered as entering/leaving; this can, at times, be counterintuitive and confusing. Please see the [query api docs](api/animations/query#entering-and-leaving-elements) for more information. -You can also see an illustration of this in the animations example \(introduced in the animations [introduction section](guide/animations#about-this-guide)\) under the Querying tab. +You can also see an illustration of this in the animations example \(introduced in the animations [introduction section](guide/legacy-animations#about-this-guide)\) under the Querying tab. @@ -137,8 +137,9 @@ The remaining functions, `stagger()`, [`group()`](api/animations/group), and `se You might also be interested in the following: - - - + + + + diff --git a/adev-ja/src/content/guide/animations/css.md b/adev-ja/src/content/guide/animations/css.md index 2d89ecb0e8..b91b3743d4 100644 --- a/adev-ja/src/content/guide/animations/css.md +++ b/adev-ja/src/content/guide/animations/css.md @@ -90,7 +90,7 @@ Animating an element when it leaves the view is similar to animating when enteri -For more information on `animate.enter` and `animate.leave`, see the [Enter and Leave animations guide](guide/animations/enter-and-leave). +For more information on `animate.enter` and `animate.leave`, see the [Enter and Leave animations guide](guide/animations). ### Animating increment and decrement @@ -178,3 +178,12 @@ Items in a `@for` loop will be removed and re-added, which will fire off animati ## Programmatic control of animations You can retrieve animations off an element directly using [`Element.getAnimations()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAnimations). This returns an array of every [`Animation`](https://developer.mozilla.org/en-US/docs/Web/API/Animation) on that element. You can use the `Animation` API to do much more than you could with what the `AnimationPlayer` from the animations package offered. From here you can `cancel()`, `play()`, `pause()`, `reverse()` and much more. This native API should provide everything you need to control your animations. + +## More on Angular animations + +You might also be interested in the following: + + + + + diff --git a/adev-ja/src/content/guide/animations/enter-and-leave.en.md b/adev-ja/src/content/guide/animations/enter-and-leave.en.md index 61ec5a158e..5059af9c29 100644 --- a/adev-ja/src/content/guide/animations/enter-and-leave.en.md +++ b/adev-ja/src/content/guide/animations/enter-and-leave.en.md @@ -11,7 +11,7 @@ Angular provides `animate.enter` and `animate.leave` to animate your application ## `animate.enter` -You can use `animate.enter` to animate elements as they _enter_ the DOM. You can define enter animations using CSS classes with either transforms or keyframe animations. +You can use `animate.enter` to animate elements as they _enter_ the DOM. You can define enter animations using CSS classes with either transitions or keyframe animations. @@ -25,6 +25,8 @@ NOTE: When using multiple keyframe animations or transition properties on an ele You can use `animate.enter` with any other Angular features, such as control flow or dynamic expressions. `animate.enter` accepts both a single class string (with multiple classes separated by spaces), or an array of class strings. +A quick note about using CSS transitions: If you choose to use transitions instead of keyframe animations, the classes added to the element with `animate.enter` represent the state that the transition will animate _to_. Your base element CSS is what the element will look like when no animations run, which is likely similar to the end state of the CSS transition. So you would still need to pair it with `@starting-style` to have an appropriate _from_ state for your transition to work. + @@ -86,3 +88,12 @@ If you want to test that the animations are animating in a browser test, for exa This will configure animations in your test environment to behave normally. NOTE: Some test environments do not emit animation events like `animationstart`, `animationend` and their transition event equivalents. + +## More on Angular animations + +You might also be interested in the following: + + + + + diff --git a/adev-ja/src/content/guide/animations/enter-and-leave.md b/adev-ja/src/content/guide/animations/enter-and-leave.md index ae5fb69756..a7432f3c05 100644 --- a/adev-ja/src/content/guide/animations/enter-and-leave.md +++ b/adev-ja/src/content/guide/animations/enter-and-leave.md @@ -11,7 +11,7 @@ Angularは、アプリケーションの要素をアニメーション化する ## `animate.enter` -`animate.enter`はDOMに_入る_要素をアニメーション化するために使用できます。CSSクラスと、transformまたはキーフレームアニメーションのいずれかを使用して、enterアニメーションを定義できます。 +`animate.enter`はDOMに_入る_要素をアニメーション化するために使用できます。CSSクラスと、transitionまたはキーフレームアニメーションのいずれかを使用して、enterアニメーションを定義できます。 @@ -25,6 +25,8 @@ NOTE: 要素で複数のキーフレームアニメーションまたはtransiti `animate.enter`は、制御フローや動的式など、他のAngular機能と組み合わせて使用できます。`animate.enter`は、単一のクラス文字列(複数のクラスがスペースで区切られているもの)またはクラス文字列の配列を受け入れます。 +CSSトランジションの使用についての簡単な注意:キーフレームアニメーションの代わりにトランジションを使用することを選択した場合、`animate.enter`で要素に追加されるクラスは、トランジションがアニメーション_する先の_状態を表します。基本要素のCSSは、アニメーションが実行されないときの要素の外観であり、CSSトランジションの終了状態に似ている可能性があります。そのため、トランジションが機能するために適切な_from_状態を持つために、`@starting-style`と組み合わせる必要があります。 + @@ -86,3 +88,12 @@ TestBedは、テスト環境でアニメーションを有効または無効に これにより、テスト環境でのアニメーションが通常通りに動作するように設定されます。 NOTE: 一部のテスト環境では、`animationstart`、`animationend`のようなアニメーションイベントや、それらに相当するトランジションイベントを発行しません。 + +## Angularアニメーションについてさらに詳しく + +以下の項目にも興味があるかもしれません: + + + + + diff --git a/adev-ja/src/content/guide/animations/migration.md b/adev-ja/src/content/guide/animations/migration.md index 22862b1166..8ca357a02c 100644 --- a/adev-ja/src/content/guide/animations/migration.md +++ b/adev-ja/src/content/guide/animations/migration.md @@ -135,7 +135,7 @@ Use `animate.leave` to animate elements as they leave the view, which will apply -For more information on `animate.enter` and `animate.leave`, see the [Enter and Leave animations guide](guide/animations/enter-and-leave). +For more information on `animate.enter` and `animate.leave`, see the [Enter and Leave animations guide](guide/animations). ### Animating increment and decrement diff --git a/adev-ja/src/content/guide/animations/overview.md b/adev-ja/src/content/guide/animations/overview.md index 733f84b474..bb6991efb8 100644 --- a/adev-ja/src/content/guide/animations/overview.md +++ b/adev-ja/src/content/guide/animations/overview.md @@ -1,6 +1,6 @@ # Introduction to Angular animations -IMPORTANT: The Angular team recommends using native CSS for animations instead of the Animations package for all new code. Use this guide to understand existing code built with the Animations Package. See [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start using pure CSS animations in your apps. +IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations/enter-and-leave). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps. Animation provides the illusion of motion: HTML elements change styling over time. Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic. @@ -56,7 +56,7 @@ If you plan to use specific animation functions in component files, import those -See all [available animation functions](guide/animations#animations-api-summary) at the end of this guide. +See all [available animation functions](guide/legacy-animations#animations-api-summary) at the end of this guide. @@ -263,7 +263,7 @@ Here are the code files discussed in the transition example. You learned to add animation to a transition between two states, using `style()` and [`state()`](api/animations/state) along with `animate()` for the timing. -Learn about more advanced features in Angular animations under the Animation section, beginning with advanced techniques in [transition and triggers](guide/animations/transition-and-triggers). +Learn about more advanced features in Angular animations under the Animation section, beginning with advanced techniques in [transition and triggers](guide/legacy-animations/transition-and-triggers). ## Animations API summary @@ -295,8 +295,9 @@ HELPFUL: Check out this [presentation](https://www.youtube.com/watch?v=rnTK9meY5 You might also be interested in the following: - - - + + + + diff --git a/adev-ja/src/content/guide/animations/reusable-animations.md b/adev-ja/src/content/guide/animations/reusable-animations.md index 1fc7361c98..d8d024fab7 100644 --- a/adev-ja/src/content/guide/animations/reusable-animations.md +++ b/adev-ja/src/content/guide/animations/reusable-animations.md @@ -1,6 +1,6 @@ # Reusable animations -IMPORTANT: The Angular team recommends using native CSS for animations instead of the Animations package for all new code. Use this guide to understand existing code built with the Animations Package. See [Migrating away from Angular's Animations package](guide/animations/migration#creating-reusable-animations) to learn how you can start using pure CSS animations in your apps. +IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations/enter-and-leave). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps. This topic provides some examples of how to create reusable animations. @@ -30,8 +30,9 @@ For example, the following code snippet imports the `transitionAnimation` variab You might also be interested in the following: - - - + + + + diff --git a/adev-ja/src/content/guide/animations/transition-and-triggers.md b/adev-ja/src/content/guide/animations/transition-and-triggers.md index fcb6d2c211..cc35138022 100644 --- a/adev-ja/src/content/guide/animations/transition-and-triggers.md +++ b/adev-ja/src/content/guide/animations/transition-and-triggers.md @@ -1,6 +1,6 @@ # Animation transitions and triggers -IMPORTANT: The Angular team recommends using native CSS for animations instead of the Animations package for all new code. Use this guide to understand existing code built with the Animations Package. See [Migrating away from Angular's Animations package](guide/animations/migration#transition-and-triggers) to learn how you can start using pure CSS animations in your apps. +IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations/enter-and-leave). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps. This guide goes into depth on special transition states such as the `*` wildcard and `void`. It shows how these special states are used for elements entering and leaving a view. This section also explores multiple animation triggers, animation callbacks, and sequence-based animation using keyframes. @@ -57,7 +57,7 @@ Wildcard is a fallback value that's used if the state being animated isn't decla ### Void state Use the `void` state to configure transitions for an element that is entering or leaving a page. -See [Animating entering and leaving a view](guide/animations/transition-and-triggers#aliases-enter-and-leave). +See [Animating entering and leaving a view](guide/legacy-animations/transition-and-triggers#aliases-enter-and-leave). ### Combine wildcard and void states @@ -119,7 +119,7 @@ The `transition()` function takes other selector values, `:increment` and `:decr Use these to kick off a transition when a numeric value has increased or decreased in value. HELPFUL: The following example uses `query()` and `stagger()` methods. -For more information on these methods, see the [complex sequences](guide/animations/complex-sequences) page. +For more information on these methods, see the [complex sequences](guide/legacy-animations/complex-sequences) page. @@ -296,8 +296,9 @@ The `keyframes()` function in Angular allows you to specify multiple interim sty You might also be interested in the following: - - - + + + + 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 666748698b..7a5ae1a6f1 100644 --- a/adev-ja/src/content/guide/components/content-projection.en.md +++ b/adev-ja/src/content/guide/components/content-projection.en.md @@ -68,7 +68,7 @@ placeholder that tells Angular where to render content. Angular's compiler proce all `` elements at build-time. You cannot insert, remove, or modify `` at run time. You cannot add directives, styles, or arbitrary attributes to ``. -You should not conditionally include `` with `@if`, `@for`, or `@switch`. Angular always +IMPORTANT: You should not conditionally include `` with `@if`, `@for`, or `@switch`. Angular always instantiates and creates DOM nodes for content rendered to a `` placeholder, even if that `` placeholder is hidden. For conditional rendering of component content, see [Template fragments](api/core/ng-template). @@ -79,21 +79,48 @@ Angular supports projecting multiple different elements into different `card-title`, +}) +export class CardTitle {} + +@Component({ + selector: 'card-body', + template: `card-body`, +}) +export class CardBody {} +``` + +```angular-ts -
    - -
    - -
    +Component({ + selector: 'custom-card', + template: ` +
    + +
    + +
    + `, +}) +export class CustomCard {} ``` -```angular-html +```angular-ts - - Hello - Welcome to the example - +@Component({ + selector: 'app-root', + imports: [CustomCard, CardTitle, CardBody], + template: ` + + Hello + Welcome to the example + +`, +}) +export class App {} ``` ```angular-html diff --git a/adev-ja/src/content/guide/components/content-projection.md b/adev-ja/src/content/guide/components/content-projection.md index 8ba7dd36fb..b9b4b9b6e2 100644 --- a/adev-ja/src/content/guide/components/content-projection.md +++ b/adev-ja/src/content/guide/components/content-projection.md @@ -68,7 +68,7 @@ Angularは、このように渡されるコンポーネントの子要素を、 Angularのコンパイラは、ビルド時にすべての``要素を処理します。 実行時に``の挿入や削除、変更はできません。ディレクティブやスタイル、任意の属性も``には追加できません。 -``を`@if`、`@for`、または`@switch`によって条件付きで含めるべきではありません。 +IMPORTANT: ``を`@if`、`@for`、または`@switch`によって条件付きで含めるべきではありません。 Angularは常にレンダリングされたコンテンツのDOMノードをインスタンス化して作成します。 その``プレースホルダが非表示であってもです。コンポーネントコンテンツの条件付きレンダリングについては [テンプレートフラグメント](api/core/ng-template)を参照してください。 @@ -79,21 +79,48 @@ Angularは、CSSセレクターに基づいて、複数の異なる要素を異 上記のカードの例を拡張して、`select`属性を使用して、 カードのタイトルと本文の2つのプレースホルダーを作成できます。 -```angular-html +```angular-ts +@Component({ + selector: 'card-title', + template: `card-title`, +}) +export class CardTitle {} + +@Component({ + selector: 'card-body', + template: `card-body`, +}) +export class CardBody {} +``` + +```angular-ts -
    - -
    - -
    +Component({ + selector: 'custom-card', + template: ` +
    + +
    + +
    + `, +}) +export class CustomCard {} ``` -```angular-html +```angular-ts - - こんにちは - 例へようこそ - +@Component({ + selector: 'app-root', + imports: [CustomCard, CardTitle, CardBody], + template: ` + + こんにちは + 例へようこそ + +`, +}) +export class App {} ``` ```angular-html diff --git a/adev-ja/src/content/guide/components/selectors.en.md b/adev-ja/src/content/guide/components/selectors.en.md index eb77b16a09..acbb953e6f 100644 --- a/adev-ja/src/content/guide/components/selectors.en.md +++ b/adev-ja/src/content/guide/components/selectors.en.md @@ -118,8 +118,8 @@ prefix your components with `yt-`, with components like `yt-menu`, `yt-player`, your selectors like this makes it immediately clear where a particular component comes from. By default, the Angular CLI uses `app-`. -Angular uses the `ng` selector prefix for its own framework APIs. Never use `ng` as a selector -prefix for your own custom components. +IMPORTANT: Angular uses the `ng` selector prefix for its own framework APIs. Never use `ng` as a selector prefix for your own custom components. + ### When to use an attribute selector diff --git a/adev-ja/src/content/guide/components/selectors.md b/adev-ja/src/content/guide/components/selectors.md index a581dc9204..44d03622cc 100644 --- a/adev-ja/src/content/guide/components/selectors.md +++ b/adev-ja/src/content/guide/components/selectors.md @@ -118,8 +118,8 @@ Angularチームは、プロジェクト内で定義されているすべての このようにセレクターに名前空間を付けると、特定のコンポーネントの出所がすぐにわかります。 デフォルトでは、Angular CLIは `app-` を使用します。 -Angularは、独自のフレームワークAPIに対して `ng` セレクタープレフィックスを使用します。 -独自の カスタム コンポーネントのセレクター プレフィックスとして `ng` を使用しないでください。 +IMPORTANT: Angularは、独自のフレームワークAPIに対して `ng` セレクタープレフィックスを使用します。独自のカスタムコンポーネントのセレクタープレフィックスとして `ng` を使用しないでください。 + ### 属性セレクターを使用する場合 diff --git a/adev-ja/src/content/guide/drag-drop.md b/adev-ja/src/content/guide/drag-drop.md index 0e97557678..50f89e3365 100644 --- a/adev-ja/src/content/guide/drag-drop.md +++ b/adev-ja/src/content/guide/drag-drop.md @@ -312,6 +312,20 @@ There are cases where draggable elements can be dragged out of one `cdkDropList` Alternatively, you can modify the `CDK_DRAG_CONFIG` injection token to update sortingDisabled within the config. For more information see the [dependency injection guide](https://angular.dev/guide/di), [drag config injection token API](api/cdk/drag-drop/CDK_DRAG_CONFIG), and the [drag drop config API](api/cdk/drag-drop/DragDropConfig). +### Copying items between lists + +By default, when an item is dragged from one list to another, it is moved out of its original list. However, you can configure the directives to copy the item, leaving the original item in the source list. + +To enable copying, you can set the `cdkDropListHasAnchor` input. This tells the `cdkDropList` to create an "anchor" element that stays in the original container and doesn't move with the item. If the user moves the item back into the original container, the anchor is removed automatically. The anchor element can be styled by targeting the `.cdk-drag-anchor` CSS class. + +Combining `cdkDropListHasAnchor` with `cdkDropListSortingDisabled` makes it possible to construct a list from which a user can copy items without being able to reorder the source list (e.g. a product list and a shopping cart). + + + + + + + ## Customize animations Drag and drop supports animations for both: @@ -341,3 +355,12 @@ Both `cdkDrag` and `cdkDropList` directives only apply essential styles needed f | .cdk-drop-list-dragging | Selector for `cdkDropList` container element that has a draggable element currently being dragged. | | .cdk-drop-list-disabled | Selector for `cdkDropList` container elements that are disabled. | | .cdk-drop-list-receiving | Selector for `cdkDropList` container element that has a draggable element it can receive from a connected drop list that is currently being dragged. | +| .cdk-drag-anchor | Selector for the anchor element that is created when `cdkDropListHasAnchor` is enabled. This element indicates the position from which the dragged item started. | + +## Dragging in a scrollable container + +If your draggable items are inside a scrollable container (e.g., a `div` with `overflow: auto`), automatic scrolling will not work unless the scrollable container has the `cdkScrollable` directive. Without it, the CDK cannot detect or control the scroll behavior of the container during drag operations. + +## Integrations with other components + +The CDK's drag-and-drop functionality can be integrated with different components. Common use cases include sortable `MatTable` components and sortable `MatTabGroup` components. diff --git a/adev-ja/src/content/guide/i18n/overview.md b/adev-ja/src/content/guide/i18n/overview.md index 0ad407604c..d504e1aadf 100644 --- a/adev-ja/src/content/guide/i18n/overview.md +++ b/adev-ja/src/content/guide/i18n/overview.md @@ -1,4 +1,4 @@ -# Angular Internationalization +# Angular Internationalization (i18n) *Internationalization*, sometimes referenced as i18n, is the process of designing and preparing your project for use in different locales around the world. *Localization* is the process of building versions of your project for different locales. diff --git a/adev-ja/src/content/guide/routing/customizing-route-behavior.md b/adev-ja/src/content/guide/routing/customizing-route-behavior.md new file mode 100644 index 0000000000..6ae475fc59 --- /dev/null +++ b/adev-ja/src/content/guide/routing/customizing-route-behavior.md @@ -0,0 +1,473 @@ +# Customizing route behavior + +Angular Router provides powerful extension points that allow you to customize how routes behave in your application. While the default routing behavior works well for most applications, specific requirements often demand custom implementations for performance optimization, specialized URL handling, or complex routing logic. + +Route customization can become valuable when your application needs: + +- **Component state preservation** across navigations to avoid re-fetching data +- **Strategic lazy module loading** based on user behavior or network conditions +- **External URL integration** or handling Angular routes alongside legacy systems +- **Dynamic route matching** based on runtime conditions beyond simple path + patterns + +NOTE: Before implementing custom strategies, ensure the default router behavior doesn't meet your needs. Angular's default routing is optimized for common use cases and provides the best balance of performance and simplicity. Customizing route strategies can create additional code complexity and have performance implications on memory usage if not carefully managed. + +Angular Router exposes four main areas for customization: + + + + + + + + +## Route reuse strategy + +Route reuse strategy controls whether Angular destroys and recreates components during navigation or preserves them for reuse. By default, Angular destroys component instances when navigating away from a route and creates new instances when navigating back. + +### When to implement route reuse + +Custom route reuse strategies benefit applications that need: + +- **Form state preservation** - Keep partially completed forms when users navigate away and return +- **Expensive data retention** - Avoid re-fetching large datasets or complex calculations +- **Scroll position maintenance** - Preserve scroll positions in long lists or infinite scroll implementations +- **Tab-like interfaces** - Maintain component state when switching between tabs + +### Creating a custom route reuse strategy + +Angular's `RouteReuseStrategy` class allows you to customize navigation behavior through the concept of "detached route handles." + +"Detached route handles" are Angular's way of storing component instances and their entire view hierarchy. When a route is detached, Angular preserves the component instance, its child components, and all associated state in memory. This preserved state can later be reattached when navigating back to the route. + +The `RouteReuseStrategy` class provides five methods that control the lifecycle of route components: + +| Method | Description | +| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| [`shouldDetach`](api/router/RouteReuseStrategy#shouldDetach) | Determines if a route should be stored for later reuse when navigating away | +| [`store`](api/router/RouteReuseStrategy#store) | Stores the detached route handle when `shouldDetach` returns true | +| [`shouldAttach`](api/router/RouteReuseStrategy#shouldAttach) | Determines if a stored route should be reattached when navigating to it | +| [`retrieve`](api/router/RouteReuseStrategy#retrieve) | Returns the previously stored route handle for reattachment | +| [`shouldReuseRoute`](api/router/RouteReuseStrategy#shouldReuseRoute) | Determines if the router should reuse the current route instance instead of destroying it during navigation | + +The following example demonstrates a custom route reuse strategy that selectively preserves component state based on route metadata: + +```ts +import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router'; +import { Injectable } from '@angular/core'; + +@Injectable() +export class CustomRouteReuseStrategy implements RouteReuseStrategy { + private handlers = new Map(); + + shouldDetach(route: ActivatedRouteSnapshot): boolean { + // Determines if a route should be stored for later reuse + return route.data['reuse'] === true; + } + + store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void { + // Stores the detached route handle when shouldDetach returns true + if (handle && route.data['reuse'] === true) { + const key = this.getRouteKey(route); + this.handlers.set(key, handle); + } + } + + shouldAttach(route: ActivatedRouteSnapshot): boolean { + // Checks if a stored route should be reattached + const key = this.getRouteKey(route); + return route.data['reuse'] === true && this.handlers.has(key); + } + + retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { + // Returns the stored route handle for reattachment + const key = this.getRouteKey(route); + return route.data['reuse'] === true ? this.handlers.get(key) ?? null : null; + } + + shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { + // Determines if the router should reuse the current route instance + return future.routeConfig === curr.routeConfig; + } + + private getRouteKey(route: ActivatedRouteSnapshot): string { + return route.routeConfig ?? ''; + } +} +``` + +### Configuring a route to use a custom route reuse strategy + +Routes can opt into reuse behavior through route configuration metadata. This approach keeps the reuse logic separate from component code, making it easy to adjust behavior without modifying components: + +```ts +export const routes: Routes = [ + { + path: 'products', + component: ProductListComponent, + data: { reuse: true } // Component state persists across navigations + }, + { + path: 'products/:id', + component: ProductDetailComponent, + // No reuse flag - component recreates on each navigation + }, + { + path: 'search', + component: SearchComponent, + data: { reuse: true } // Preserves search results and filter state + } +]; +``` + +You can also configure a custom route reuse strategy at the application level through Angular's dependency injection system. In this case, Angular creates a single instance of the strategy that manages all route reuse decisions throughout the application: + +```ts +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + { provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy } + ] +}; +``` + +## Preloading strategy + +Preloading strategies determine when Angular loads lazy-loaded route modules in the background. While lazy loading improves initial load time by deferring module downloads, users still experience a delay when first navigating to a lazy route. Preloading strategies eliminate this delay by loading modules before users request them. + +### Built-in preloading strategies + +Angular provides two preloading strategies out of the box: + +| Strategy | Description | +| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| [`NoPreloading`](api/router/NoPreloading) | The default strategy that disables all preloading. In other words, modules only load when users navigate to them | +| [`PreloadAllModules`](api/router/PreloadAllModules) | Loads all lazy-loaded modules immediately after the initial navigation | + +The `PreloadAllModules` strategy can be configured as follows: + +```ts +import { ApplicationConfig } from '@angular/core'; +import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router'; +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter( + routes, + withPreloading(PreloadAllModules) + ) + ] +}; +``` + +The `PreloadAllModules` strategy works well for small to medium applications where downloading all modules doesn't significantly impact performance. However, larger applications with many feature modules might benefit from more selective preloading. + +### Creating a custom preloading strategy + +Custom preloading strategies implement the `PreloadingStrategy` interface, which requires a single `preload` method. This method receives the route configuration and a function that triggers the actual module load. The strategy returns an Observable that emits when preloading completes or an empty Observable to skip preloading: + +```ts +import { Injectable } from '@angular/core'; +import { PreloadingStrategy, Route } from '@angular/router'; +import { Observable, of, timer } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +@Injectable() +export class SelectivePreloadingStrategy implements PreloadingStrategy { + preload(route: Route, load: () => Observable): Observable { + // Only preload routes marked with data: { preload: true } + if (route.data?.['preload']) { + return load(); + } + return of(null); + } +} +``` + +This selective strategy checks route metadata to determine preloading behavior. Routes can opt into preloading through their configuration: + +```ts +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + { + path: 'dashboard', + loadChildren: () => import('./dashboard/dashboard.routes'), + data: { preload: true } // Preload immediately after initial navigation + }, + { + path: 'reports', + loadChildren: () => import('./reports/reports.routes'), + data: { preload: false } // Only load when user navigates to reports + }, + { + path: 'admin', + loadChildren: () => import('./admin/admin.routes') + // No preload flag - won't be preloaded + } +]; +``` + +### Performance considerations for preloading + +Preloading impacts both network usage and memory consumption. Each preloaded module consumes bandwidth and increases the application's memory footprint. Mobile users on metered connections might prefer minimal preloading, while desktop users on fast networks can handle aggressive preloading strategies. + +The timing of preloading also matters. Immediate preloading after initial load might compete with other critical resources like images or API calls. Strategies should consider the application's post-load behavior and coordinate with other background tasks to avoid performance degradation. + +Browser resource limits also affect preloading behavior. Browsers limit concurrent HTTP connections, so aggressive preloading might queue behind other requests. Service workers can help by providing fine-grained control over caching and network requests, complementing the preloading strategy. + +## URL handling strategy + +URL handling strategies determine which URLs the Angular router processes versus which ones it ignores. By default, Angular attempts to handle all navigation events within the application, but real-world applications often need to coexist with other systems, handle external links, or integrate with legacy applications that manage their own routes. + +The `UrlHandlingStrategy` class gives you control over this boundary between Angular-managed routes and external URLs. This becomes essential when migrating applications to Angular incrementally or when Angular applications need to share URL space with other frameworks. + +### Implementing a custom URL handling strategy + +Custom URL handling strategies extend the `UrlHandlingStrategy` class and implement three methods. The `shouldProcessUrl` method determines whether Angular should handle a given URL, `extract` returns the portion of the URL that Angular should process, and `merge` combines the URL fragment with the rest of the URL: + +```ts +import { Injectable } from '@angular/core'; +import { UrlHandlingStrategy, UrlTree } from '@angular/router'; + +@Injectable() +export class CustomUrlHandlingStrategy implements UrlHandlingStrategy { + shouldProcessUrl(url: UrlTree): boolean { + // Only handle URLs that start with /app or /admin + return url.toString().startsWith('/app') || + url.toString().startsWith('/admin'); + } + + extract(url: UrlTree): UrlTree { + // Return the URL unchanged if we should process it + return url; + } + + merge(newUrlPart: UrlTree, rawUrl: UrlTree): UrlTree { + // Combine the URL fragment with the rest of the URL + return newUrlPart; + } +} +``` + +This strategy creates clear boundaries in the URL space. Angular handles `/app` and `/admin` paths while ignoring everything else. This pattern works well when migrating legacy applications where Angular controls specific sections while the legacy system maintains others. + +### Configuring a custom URL handling strategy + +You can register a custom strategy through Angular's dependency injection system: + +```ts +import { ApplicationConfig } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { UrlHandlingStrategy } from '@angular/router'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideRouter(routes), + { provide: UrlHandlingStrategy, useClass: CustomUrlHandlingStrategy } + ] +}; +``` + +## Custom route matchers + +By default, Angular's router iterates through routes in the order they're defined, attempting to match the URL path against each route's path pattern. It supports static segments, parameterized segments (`:id`), and wildcards (`**`). The first route that matches wins, and the router stops searching. + +When applications require more sophisticated matching logic based on runtime conditions, complex URL patterns, or other custom rules, custom matchers provide this flexibility without compromising the simplicity of standard routes. + +The router evaluates custom matchers during the route matching phase, before path matching occurs. When a matcher returns a successful match, it can also extract parameters from the URL, making them available to the activated component just like standard route parameters. + +### Creating a custom matcher + +A custom matcher is a function that receives URL segments and returns either a match result with consumed segments and parameters, or null to indicate no match. The matcher function runs before Angular evaluates the route's path property: + +```ts +import { Route, UrlSegment, UrlSegmentGroup, UrlMatchResult } from '@angular/router'; + +export function customMatcher( + segments: UrlSegment[], + group: UrlSegmentGroup, + route: Route +): UrlMatchResult | null { + // Matching logic here + if (matchSuccessful) { + return { + consumed: segments, + posParams: { + paramName: new UrlSegment('paramValue', {}) + } + }; + } + return null; +} +``` + +### Implementing version-based routing + +Consider an API documentation site that needs to route based on version numbers in the URL. Different versions might have different component structures or feature sets: + +```ts +import { Routes, UrlSegment, UrlMatchResult } from '@angular/router'; + +export function versionMatcher(segments: UrlSegment[]): UrlMatchResult | null { + // Match patterns like /v1/docs, /v2.1/docs, /v3.0.1/docs + if (segments.length >= 2 && segments[0].path.match(/^v\d+(\.\d+)*$/)) { + return { + consumed: segments.slice(0, 2), // Consume version and 'docs' + posParams: { + version: segments[0], // Make version available as a parameter + section: segments[1] // Make section available too + } + }; + } + return null; +} + +// Route configuration +export const routes: Routes = [ + { + matcher: versionMatcher, + component: DocumentationComponent + }, + { + path: 'latest/docs', + redirectTo: 'v3/docs' + } +]; +``` + +The component receives the extracted parameters through route inputs: + +```ts +import { Component, input, inject } from '@angular/core'; +import { resource } from '@angular/core'; + +@Component({ + selector: 'app-documentation', + template: ` + @if (documentation.isLoading()) { +
    Loading documentation...
    + } @else if (documentation.error()) { +
    Error loading documentation
    + } @else if (documentation.value(); as docs) { +
    {{ docs.content }}
    + } + ` +}) +export class DocumentationComponent { + // Route parameters are automatically bound to signal inputs + version = input.required(); // Receives the version parameter + section = input.required(); // Receives the section parameter + + private docsService = inject(DocumentationService); + + // Resource automatically loads documentation when version or section changes + documentation = resource({ + params: () => { + if (!this.version() || !this.section()) return; + + return { + version: this.version(), + section: this.section() + } + }, + loader: ({ params }) => { + return this.docsService.loadDocumentation(params.version, params.section); + } + }) +} +``` + +### Locale-aware routing + +International applications often encode locale information in URLs. A custom matcher can extract locale codes and route to appropriate components while making the locale available as a parameter: + +```ts +// Supported locales +const locales = ['en', 'es', 'fr', 'de', 'ja', 'zh']; + +export function localeMatcher(segments: UrlSegment[]): UrlMatchResult | null { + if (segments.length > 0) { + const potentialLocale = segments[0].path; + + if (locales.includes(potentialLocale)) { + // This is a locale prefix, consume it and continue matching + return { + consumed: [segments[0]], + posParams: { + locale: segments[0] + } + }; + } else { + // No locale prefix, use default locale + return { + consumed: [], // Don't consume any segments + posParams: { + locale: new UrlSegment('en', {}) + } + }; + } + } + + return null; +} +``` + +### Complex business logic matching + +Custom matchers excel at implementing business rules that would be awkward to express in path patterns. Consider an e-commerce site where product URLs follow different patterns based on product type: + +```ts +export function productMatcher(segments: UrlSegment[]): UrlMatchResult | null { + if (segments.length === 0) return null; + + const firstSegment = segments[0].path; + + // Books: /isbn-1234567890 + if (firstSegment.startsWith('isbn-')) { + return { + consumed: [segments[0]], + posParams: { + productType: new UrlSegment('book', {}), + identifier: new UrlSegment(firstSegment.substring(5), {}) + } + }; + } + + // Electronics: /sku/ABC123 + if (firstSegment === 'sku' && segments.length > 1) { + return { + consumed: segments.slice(0, 2), + posParams: { + productType: new UrlSegment('electronics', {}), + identifier: segments[1] + } + }; + } + + // Clothing: /style/BRAND/ITEM + if (firstSegment === 'style' && segments.length > 2) { + return { + consumed: segments.slice(0, 3), + posParams: { + productType: new UrlSegment('clothing', {}), + brand: segments[1], + identifier: segments[2] + } + }; + } + + return null; +} +``` + +### Performance considerations for custom matchers + +Custom matchers run for every navigation attempt until a match is found. As a result, complex matching logic can impact navigation performance, especially in applications with many routes. Keep matchers focused and efficient: + +- Return early when a match is impossible +- Avoid expensive operations like API calls or complex regular expressions +- Consider caching results for repeated URL patterns + +While custom matchers solve complex routing requirements elegantly, overuse can make route configuration harder to understand and maintain. Reserve custom matchers for scenarios where standard path matching genuinely falls short. diff --git a/adev-ja/src/content/guide/routing/overview.en.md b/adev-ja/src/content/guide/routing/overview.en.md index ae53fb3965..e219535ded 100644 --- a/adev-ja/src/content/guide/routing/overview.en.md +++ b/adev-ja/src/content/guide/routing/overview.en.md @@ -4,24 +4,6 @@ Routing helps you change what the user sees in a single-page app. Angular Router (`@angular/router`) is the official library for managing navigation in Angular applications and a core part of the framework. It is included by default in all projects created by Angular CLI. -## Installation - -Angular Router is included by default in all Angular projects setup with the Angular CLI `ng new` command. - -### Prerequisite - -- Angular CLI - -### Add to an existing project - -If your project does not include Angular Router, you can install it manually with the following command: - -```bash -ng add @angular/router -``` - -The Angular CLI will then install all the necessary dependencies. - ## Why is routing necessary in a SPA? When you navigate to a URL in your web browser, the browser normally makes a network request to a web server and displays the returned HTML page. When you navigate to a different URL, such as clicking a link, the browser makes another network request and replaces the entire page with a new one. diff --git a/adev-ja/src/content/guide/routing/overview.md b/adev-ja/src/content/guide/routing/overview.md index 613d333891..9e69082cad 100644 --- a/adev-ja/src/content/guide/routing/overview.md +++ b/adev-ja/src/content/guide/routing/overview.md @@ -4,24 +4,6 @@ Angular Router (`@angular/router`) は、Angularアプリケーションでナビゲーションを管理するための公式ライブラリであり、フレームワークの核となる部分です。Angular CLIによって作成されたすべてのプロジェクトにデフォルトで含まれています。 -## インストール {#installation} - -Angular Routerは、Angular CLIの`ng new`コマンドによりセットアップされたすべてのAngularプロジェクトにデフォルトで含まれています。 - -### 前提条件 {#prerequisite} - -- Angular CLI - -### 既存のプロジェクトに追加 {#add-to-an-existing-project} - -プロジェクトにAngular Routerが含まれていない場合、以下のコマンドで手動インストールできます: - -```bash -ng add @angular/router -``` - -Angular CLIが、必要なすべての依存関係をインストールします。 - ## SPAでルーティングが必要な理由 {#why-is-routing-necessary-in-a-spa} WebブラウザでURLに移動すると、ブラウザは通常、Webサーバーにネットワークリクエストを行い、返されたHTMLページを表示します。リンクをクリックするなどの別のURLに移動すると、ブラウザは別のネットワークリクエストを行い、ページ全体を新しいものに置き換えます。 diff --git a/adev-ja/src/content/guide/routing/route-transition-animations.en.md b/adev-ja/src/content/guide/routing/route-transition-animations.en.md index 0df19464a9..a9aa290a1c 100644 --- a/adev-ja/src/content/guide/routing/route-transition-animations.en.md +++ b/adev-ja/src/content/guide/routing/route-transition-animations.en.md @@ -201,13 +201,3 @@ Control view transitions programmatically using JavaScript APIs for complex anim - [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#animating-with-javascript) - [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-cklnkm) - -## Alternative: Angular Animations - -If you need broader browser support or more granular control over animations, you can use the [`@angular/animations`](/guide/animations) package instead of native view transitions. Angular's animation system works with router state changes and provides: - -- **Universal browser support** - Works across all browsers that support Angular -- **Fine-grained control** - Define complex animation sequences and timing -- **Router integration** - Create animations based on route changes, URL patterns, or [`ActivatedRoute`](/api/router/ActivatedRoute) data - -Learn more about creating route-based animations with [animation triggers and transitions](/guide/animations/transition-and-triggers). diff --git a/adev-ja/src/content/guide/routing/route-transition-animations.md b/adev-ja/src/content/guide/routing/route-transition-animations.md index ed554e84da..910a72b8b9 100644 --- a/adev-ja/src/content/guide/routing/route-transition-animations.md +++ b/adev-ja/src/content/guide/routing/route-transition-animations.md @@ -202,12 +202,3 @@ NOTE: Angular Routerはビュー遷移を遅延させる方法を提供してい - [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#animating-with-javascript) - [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-cklnkm) -## 代替案: Angularアニメーション {#alternative-angular-animations} - -より広範なブラウザサポートやアニメーションに対するよりきめ細かな制御が必要な場合は、ネイティブのビュー遷移の代わりに[`@angular/animations`](/guide/animations)パッケージを使用できます。Angularのアニメーションシステムはルーターの状態変更と連携し、以下を提供します。 - -- **ユニバーサルブラウザサポート** - Angularをサポートするすべてのブラウザで動作します -- **きめ細かな制御** - 複雑なアニメーションシーケンスとタイミングを定義します -- **ルーター統合** - ルート変更、URLパターン、または[`ActivatedRoute`](/api/router/ActivatedRoute)データに基づいてアニメーションを作成します - -ルートベースのアニメーションの作成については、[アニメーションのトリガーと遷移](/guide/animations/transition-and-triggers)で詳細を確認してください。 diff --git a/adev-ja/src/content/guide/signals/resource.en.md b/adev-ja/src/content/guide/signals/resource.en.md index 53e8db74eb..44c5efbc45 100644 --- a/adev-ja/src/content/guide/signals/resource.en.md +++ b/adev-ja/src/content/guide/signals/resource.en.md @@ -115,7 +115,7 @@ The `status` signal provides a specific `ResourceStatus` that describes the stat | ------------- | :---------------- | ---------------------------------------------------------------------------- | | `'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. | +| `'loading'` | `undefined` | The loader is running as a result of the `params` 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()` | diff --git a/adev-ja/src/content/guide/ssr.en.md b/adev-ja/src/content/guide/ssr.en.md index 1d03b5b85e..aad88700d5 100644 --- a/adev-ja/src/content/guide/ssr.en.md +++ b/adev-ja/src/content/guide/ssr.en.md @@ -4,7 +4,7 @@ Angular ships all applications as client-side rendered (CSR) by default. While t ## What is hybrid rendering? -Hybrid rendering allows developers to leverage 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 gives you fine-grained control over how your different parts of your app is rendered to give your users the best experience possible. +Hybrid rendering allows developers to leverage 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 gives you fine-grained control over how the different parts of your app are rendered to give your users the best experience possible. ## Setting up hybrid rendering @@ -98,7 +98,7 @@ The server routing configuration lets you specify how each route in your applica 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 (CSR) 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. @@ -110,7 +110,7 @@ When client-side rendering, the server does not need to do any work to render a 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 (SSR) 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. diff --git a/adev-ja/src/content/guide/ssr.md b/adev-ja/src/content/guide/ssr.md index 510d913547..bfd2706392 100644 --- a/adev-ja/src/content/guide/ssr.md +++ b/adev-ja/src/content/guide/ssr.md @@ -98,7 +98,7 @@ const serverConfig: ApplicationConfig = { 各レンダリングモードには異なる利点と欠点があります。アプリケーションの特定のニーズに基づいてレンダリングモードを選択できます。 -##### クライアントサイドレンダリング {#client-side-rendering} +##### クライアントサイドレンダリング (CSR) {#client-side-rendering-csr} クライアントサイドレンダリングは、常にWebブラウザで実行されることを前提としたコードを記述できるため、最もシンプルな開発モデルです。これにより、ブラウザで実行されることを前提とした幅広いクライアントサイドライブラリを使用できます。 @@ -110,7 +110,7 @@ const serverConfig: ApplicationConfig = { Service Workerによるインストール可能でオフラインのユーザー体験をサポートするアプリケーションは、サーバーと通信することなくクライアントサイドレンダリングに依存できます。 -##### サーバーサイドレンダリング {#server-side-rendering} +##### サーバーサイドレンダリング (SSR) {#server-side-rendering-ssr} サーバーサイドレンダリングは、クライアントサイドレンダリングよりも高速なページロードを提供します。JavaScriptのダウンロードと実行を待つ代わりに、サーバーはブラウザからのリクエストを受信すると、直接HTMLドキュメントをレンダリングします。ユーザーは、サーバーがデータを取得し、要求されたページをレンダリングするために必要な遅延のみを経験します。このモードでは、コードがサーバーでのレンダリング中にデータを取得できるため、ブラウザからの追加のネットワークリクエストも不要になります。 diff --git a/adev-ja/src/content/guide/tailwind.md b/adev-ja/src/content/guide/tailwind.md index feeea02d67..b5b8094f79 100644 --- a/adev-ja/src/content/guide/tailwind.md +++ b/adev-ja/src/content/guide/tailwind.md @@ -23,7 +23,7 @@ npm install tailwindcss @tailwindcss/postcss postcss ### 3. Configure PostCSS Plugins -Next, add a `.postcssrc.json` file in the file root of the project. +Next, add a `.postcssrc.json` file in the file root of the project. Add the `@tailwindcss/postcss` plugin into your PostCSS configuration. @@ -42,9 +42,9 @@ Add an `@import` to `./src/styles.css` that imports Tailwind CSS. @import "tailwindcss"; -If you're using SCSS, add `@use` to `./src/styles.scss`. +If you're using SCSS, add `@use` to `./src/styles.scss`. - + @use "tailwindcss"; diff --git a/adev-ja/src/content/guide/templates/expression-syntax.en.md b/adev-ja/src/content/guide/templates/expression-syntax.en.md index cf6ad5a872..15f0084491 100644 --- a/adev-ja/src/content/guide/templates/expression-syntax.en.md +++ b/adev-ja/src/content/guide/templates/expression-syntax.en.md @@ -114,7 +114,7 @@ Generally speaking, declarations are not supported in Angular expressions. This # Event listener statements -Event handlers are **statements** rather than expressions. While they support all of the same syntax as Angular expressions, the are two key differences: +Event handlers are **statements** rather than expressions. While they support all of the same syntax as Angular expressions, there are two key differences: 1. Statements **do support** assignment operators (but not destructing assignments) 1. Statements **do not support** pipes diff --git a/adev-ja/src/content/guide/templates/ng-template.en.md b/adev-ja/src/content/guide/templates/ng-template.en.md index 527cda4bcb..b3118b5054 100644 --- a/adev-ja/src/content/guide/templates/ng-template.en.md +++ b/adev-ja/src/content/guide/templates/ng-template.en.md @@ -60,7 +60,7 @@ You can then reference this fragment anywhere else in the template via the `myFr You can get a reference to a template fragment using any [component or directive query API](/guide/components/queries). -For example, if your template has exactly one template fragment, you can query directly for the `TemplateRef` object with a `@ViewChild` query: +You can query the `TemplateRef` object directly using a `viewChild` query. ```angular-ts @Component({ @@ -74,7 +74,7 @@ For example, if your template has exactly one template fragment, you can query d `, }) export class ComponentWithFragment { - @ViewChild(TemplateRef) myFragment: TemplateRef | undefined; + templateRef = viewChild>(TemplateRef); } ``` @@ -98,10 +98,8 @@ If a template contains multiple fragments, you can assign a name to each fragmen `, }) export class ComponentWithFragment { - // When querying by name, you can use the `read` option to specify that you want to get the - // TemplateRef object associated with the element. - @ViewChild('fragmentOne', {read: TemplateRef}) fragmentOne: TemplateRef | undefined; - @ViewChild('fragmentTwo', {read: TemplateRef}) fragmentTwo: TemplateRef | undefined; + fragmentOne = viewChild>('fragmentOne'); + fragmentTwo = viewChild>('fragmentTwo'); } ``` @@ -137,6 +135,7 @@ Once you have a reference to a template fragment's `TemplateRef` object, you can The `NgTemplateOutlet` directive from `@angular/common` accepts a `TemplateRef` and renders the fragment as a **sibling** to the element with the outlet. You should generally use `NgTemplateOutlet` on an [`` element](/guide/templates/ng-container). First, import `NgTemplateOutlet`: + ```typescript import { NgTemplateOutlet } from '@angular/common'; ``` diff --git a/adev-ja/src/content/guide/templates/ng-template.md b/adev-ja/src/content/guide/templates/ng-template.md index cf0bedc387..2fd16ce5f3 100644 --- a/adev-ja/src/content/guide/templates/ng-template.md +++ b/adev-ja/src/content/guide/templates/ng-template.md @@ -60,7 +60,7 @@ export class ItemCounter { [コンポーネントまたはディレクティブクエリAPI](/guide/components/queries) を使用して、テンプレートフラグメントへの参照を取得できます。 -たとえば、テンプレートにテンプレートフラグメントが1つだけ含まれている場合は、`@ViewChild` クエリを使用して `TemplateRef` オブジェクトを直接クエリできます。 +`viewChild` クエリを使用して `TemplateRef` オブジェクトを直接クエリできます。 ```angular-ts @Component({ @@ -74,7 +74,7 @@ export class ItemCounter { `, }) export class ComponentWithFragment { - @ViewChild(TemplateRef) myFragment: TemplateRef | undefined; + templateRef = viewChild>(TemplateRef); } ``` @@ -98,10 +98,8 @@ export class ComponentWithFragment { `, }) export class ComponentWithFragment { - // 名前でクエリを行う場合、`read` オプションを使用して、 - // 要素に関連付けられている `TemplateRef` オブジェクトを取得したいことを指定できます。 - @ViewChild('fragmentOne', {read: TemplateRef}) fragmentOne: TemplateRef | undefined; - @ViewChild('fragmentTwo', {read: TemplateRef}) fragmentTwo: TemplateRef | undefined; + fragmentOne = viewChild>('fragmentOne'); + fragmentTwo = viewChild>('fragmentTwo'); } ``` @@ -137,6 +135,7 @@ export class MyDirective { `@angular/common` の `NgTemplateOutlet` ディレクティブは、`TemplateRef` を受け取り、アウトレットに要素を持つ要素の**兄弟**としてフラグメントをレンダリングします。通常、`NgTemplateOutlet` は [`` 要素](/guide/templates/ng-container) で使用する必要があります。 まず、`NgTemplateOutlet` をインポートします: + ```typescript import { NgTemplateOutlet } from '@angular/common'; ``` diff --git a/adev-ja/src/content/guide/testing/experimental-unit-test.en.md b/adev-ja/src/content/guide/testing/experimental-unit-test.en.md index 3a2e322ebc..ccca5dc305 100644 --- a/adev-ja/src/content/guide/testing/experimental-unit-test.en.md +++ b/adev-ja/src/content/guide/testing/experimental-unit-test.en.md @@ -5,6 +5,24 @@ The Angular CLI provides an experimental unit test system that can use [Vitest]( IMPORTANT: This experimental unit testing system requires the use of the `application` build system. The `application` build system is the default for all newly created projects. +## Installing dependencies + +Some packages are required for the new builder to work. In order to install the new packages, run the following command : + + + +npm install vitest jsdom --save-dev + + + +If no other projects in your workspace use Karma, run the following command to uninstall the corresponding packages : + + + +npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter --save-dev + + + ## Set up testing The Angular CLI includes the test system within a new project but must be configured before it can be used. diff --git a/adev-ja/src/content/guide/testing/experimental-unit-test.md b/adev-ja/src/content/guide/testing/experimental-unit-test.md index 44f42f4166..ffb6da5868 100644 --- a/adev-ja/src/content/guide/testing/experimental-unit-test.md +++ b/adev-ja/src/content/guide/testing/experimental-unit-test.md @@ -5,6 +5,24 @@ Angular CLIは、テストランナーとして[Vitest](https://vitest.dev/)を IMPORTANT: この実験的なユニットテストシステムは、`application`ビルドシステムの使用を必要とします。 `application`ビルドシステムは、新しく作成されるすべてのプロジェクトのデフォルトです。 +## 依存関係のインストール {#installing-dependencies} + +新しいビルダーが動作するために、いくつかのパッケージが必要です。新しいパッケージをインストールするには、次のコマンドを実行します。 + + + +npm install vitest jsdom --save-dev + + + +ワークスペース内の他のプロジェクトがKarmaを使用していない場合は、次のコマンドを実行して対応するパッケージをアンインストールします。 + + + +npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter --save-dev + + + ## テストのセットアップ {#set-up-testing} Angular CLIは新しいプロジェクト内にテストシステムを含んでいますが、使用する前に設定が必要です。 diff --git a/adev-ja/src/content/guide/testing/using-component-harnesses.en.md b/adev-ja/src/content/guide/testing/using-component-harnesses.en.md index 5873913d5f..03cbe63ca8 100644 --- a/adev-ja/src/content/guide/testing/using-component-harnesses.en.md +++ b/adev-ja/src/content/guide/testing/using-component-harnesses.en.md @@ -71,6 +71,12 @@ const myComponentHarness = await loader.getHarness(MyComponent); const myComponentHarnesses = await loader.getHarnesses(MyComponent); +In addition to `getHarness` and `getAllHarnesses`, `HarnessLoader` has several other useful methods for querying for harnesses: + +- `getHarnessAtIndex(...)`: Gets the harness for a component that matches the given criteria at a specific index. +- `countHarnesses(...)`: Counts the number of component instances that match the given criteria. +- `hasHarness(...)`: Checks if at least one component instance matches the given criteria. + As an example, consider a reusable dialog-button component that opens a dialog on click. It contains the following components, each with a corresponding harness: - `MyDialogButton` (composes the `MyButton` and `MyDialog` with a convenient API) diff --git a/adev-ja/src/content/guide/testing/using-component-harnesses.md b/adev-ja/src/content/guide/testing/using-component-harnesses.md index ce620d47b7..05121036f0 100644 --- a/adev-ja/src/content/guide/testing/using-component-harnesses.md +++ b/adev-ja/src/content/guide/testing/using-component-harnesses.md @@ -71,6 +71,12 @@ const myComponentHarness = await loader.getHarness(MyComponent); const myComponentHarnesses = await loader.getHarnesses(MyComponent);
    +`getHarness`と`getAllHarnesses`に加えて、`HarnessLoader`にはハーネスを検索するためのその他の有用なメソッドがいくつかあります。 + +- `getHarnessAtIndex(...)`: 特定のインデックスで指定された条件に一致するコンポーネントのハーネスを取得します。 +- `countHarnesses(...)`: 指定された条件に一致するコンポーネントインスタンスの数をカウントします。 +- `hasHarness(...)`: 指定された条件に一致するコンポーネントインスタンスが少なくとも1つ存在するかどうかを確認します。 + 例として、クリック時にダイアログを開く再利用可能なダイアログボタンコンポーネントを考えます。これには、それぞれに対応するハーネスを持つ以下のコンポーネントが含まれています。 - `MyDialogButton` (`MyButton`と`MyDialog`を便利なAPIで構成) diff --git a/adev-ja/src/content/kitchen-sink.md b/adev-ja/src/content/kitchen-sink.md index 7f518966a6..fb2f194eb9 100644 --- a/adev-ja/src/content/kitchen-sink.md +++ b/adev-ja/src/content/kitchen-sink.md @@ -1,4 +1,4 @@ - + This is a visual list of all custom components and styles for Angular.dev. @@ -146,14 +146,13 @@ export class ComponentOverviewComponent {} Here's a code example fully styled: + visibleLines="[3,10]"> We also have styling for the terminal, just set the language as `shell`: @@ -176,34 +175,36 @@ We also have styling for the terminal, just set the language as `shell`: | `visibleLines` | `string of number[]` | range of lines for collapse mode | | `visibleRegion` | `string` | **DEPRECATED** FOR `visibleLines` | | `preview` | `boolean` | (False) display preview | +| `hideCode` | `boolean` | (False) Whether to collapse code example by default. | ### Multifile examples You can create multifile examples by wrapping the examples inside a ``. + path="adev/src/content/examples/hello-world/src/app/app.component.ts" + diff="adev/src/content/examples/hello-world/src/app/app.component-old.ts" + linenums + visibleLines="[3, 11]"/> + path="adev/src/content/examples/hello-world/src/app/app.component.css" /> #### `` Attributes -| Attributes | Type | Details | -|:--- |:--- |:--- | -| body contents | `string` | nested tabs of `docs-code` examples | -| `path` | `string` | Path to code example for preview and external link | -| `preview` | `boolean` | (False) display preview | +| Attributes | Type | Details | +|:--- |:--- |:--- | +| body contents | `string` | nested tabs of `docs-code` examples | +| `path` | `string` | Path to code example for preview and external link | +| `preview` | `boolean` | (False) display preview | +| `hideCode` | `boolean` | (False) Whether to collapse code example by default. | ### Adding `preview` to your code example @@ -211,35 +212,6 @@ Adding the `preview` flag builds a running example of the code below the code sn NOTE: `preview` only works with standalone. -#### built-in-template-functions - - - - - - -#### user-input - - - - - - - - ## Workflow Style numbered steps using ``. Numbering is created using CSS (handy!). @@ -316,12 +288,12 @@ Steps must start on a new line, and can contain `docs-code`s and other nested el You can add images using the semantic Markdown image: -![Rhubarb the cat](./images/kitchen-sink/rhubarb.jpg "Optional title") +![Rhubarb the cat](assets/images/kitchen-sink/rhubarb.jpg "Optional title") ### Add `#small` and `#medium` to change the image size -![Rhubarb the small cat](./images/kitchen-sink/rhubarb.jpg#small) -![Rhubarb the medium cat](./images/kitchen-sink/rhubarb.jpg#medium) +![Rhubarb the small cat](assets/images/kitchen-sink/rhubarb.jpg#small) +![Rhubarb the medium cat](assets/images/kitchen-sink/rhubarb.jpg#medium) Embedded videos are created with `docs-video` and just need a `src` and `alt`: diff --git a/adev-ja/src/content/reference/configs/npm-packages.md b/adev-ja/src/content/reference/configs/npm-packages.md index 7046bda0fd..8e40d95186 100644 --- a/adev-ja/src/content/reference/configs/npm-packages.md +++ b/adev-ja/src/content/reference/configs/npm-packages.md @@ -27,7 +27,7 @@ For a complete list of Angular packages, see the [API reference](api). | Package name | Details | |:--- |:--- | -| [`@angular/animations`](api#animations) | Angular's animations library makes it easy to define and apply animation effects such as page and list transitions. For more information, see the [Animations guide](guide/animations). | +| [`@angular/animations`](api#animations) | Angular's legacy animations library makes it easy to define and apply animation effects such as page and list transitions. For more information, see the [Legacy Animations guide](guide/legacy-animations). | | [`@angular/common`](api#common) | The commonly-needed services, pipes, and directives provided by the Angular team. | | `@angular/compiler` | Angular's template compiler. It understands Angular templates and can convert them to code that makes the application run. | | `@angular/compiler-cli` | Angular's compiler which is invoked by the Angular CLI's `ng build` and `ng serve` commands. It processes Angular templates with `@angular/compiler` inside a standard TypeScript compilation. | @@ -37,7 +37,7 @@ For a complete list of Angular packages, see the [API reference](api). | [`@angular/platform-browser-dynamic`](api#platform-browser-dynamic) | Includes [providers](api/core/Provider) and methods to compile and run the application on the client using the [JIT compiler](tools/cli/aot-compiler#choosing-a-compiler). | | [`@angular/router`](api#router) | The router module navigates among your application pages when the browser URL changes. For more information, see [Routing and Navigation](guide/routing). | | [`@angular/cli`](https://github.com/angular/angular-cli) | Contains the Angular CLI binary for running `ng` commands. | -| [`@angular-devkit/build-angular`](https://github.com/angular/angular-cli) | Contains default CLI builders for bundling, testing, and serving Angular applications and libraries. | -| `rxjs` | A library for reactive programming using `Observables`. | +| [`@angular-devkit/build-angular`](https://www.npmjs.com/package/@angular-devkit/build-angular) | Contains default CLI builders for bundling, testing, and serving Angular applications and libraries. | +| [`rxjs`](https://www.npmjs.com/package/rxjs) | A library for reactive programming using `Observables`. | | [`zone.js`](https://github.com/angular/zone.js) | Angular relies on `zone.js`` to run Angular's change detection processes when native JavaScript operations raise events. | | [`typescript`](https://www.npmjs.com/package/typescript) | The TypeScript compiler, language server, and built-in type definitions. | diff --git a/adev-ja/src/content/reference/errors/NG05104.en.md b/adev-ja/src/content/reference/errors/NG05104.en.md index c67ab03375..a651faac85 100644 --- a/adev-ja/src/content/reference/errors/NG05104.en.md +++ b/adev-ja/src/content/reference/errors/NG05104.en.md @@ -13,7 +13,7 @@ This issue occurs when the selector mismatches the tag selector: 'my-app', ... }) -class AppComponent {} +export class AppComponent {} ``` ```angular-html diff --git a/adev-ja/src/content/reference/errors/NG05104.md b/adev-ja/src/content/reference/errors/NG05104.md index 5e7b89a287..21f858fe5a 100644 --- a/adev-ja/src/content/reference/errors/NG05104.md +++ b/adev-ja/src/content/reference/errors/NG05104.md @@ -13,7 +13,7 @@ selector: 'my-app', ... }) -class AppComponent {} +export class AppComponent {} ``` ```angular-html diff --git a/adev-ja/src/content/tools/cli/build.en.md b/adev-ja/src/content/tools/cli/build.en.md index 0c7c792ece..8f7367e5fb 100644 --- a/adev-ja/src/content/tools/cli/build.en.md +++ b/adev-ja/src/content/tools/cli/build.en.md @@ -144,6 +144,6 @@ HELPFUL: Use [browsersl.ist](https://browsersl.ist) to display compatible browse ## Configuring Tailwind -Angular supports [Tailwind](https://tailwindcss.com/), a utility-first CSS framework. +Angular supports [Tailwind CSS](https://tailwindcss.com/), a utility-first CSS framework. -Follow the [Tailwind documentation](https://tailwindcss.com/docs/installation/framework-guides/angular) for integrating with Angular CLI. +To integrate Tailwind CSS with Angular CLI, see [Using Tailwind CSS with Angular](guide/tailwind) \ No newline at end of file diff --git a/adev-ja/src/content/tools/cli/build.md b/adev-ja/src/content/tools/cli/build.md index 5373c9f9d4..6bdc6dd9aa 100644 --- a/adev-ja/src/content/tools/cli/build.md +++ b/adev-ja/src/content/tools/cli/build.md @@ -144,6 +144,6 @@ HELPFUL: [browsersl.ist](https://browsersl.ist)を使用して、`browserslist` ## Tailwindの設定 {#configuring-tailwind} -Angularは[Tailwind](https://tailwindcss.com/)、ユーティリティファーストのCSSフレームワークをサポートしています。 +Angularは[Tailwind CSS](https://tailwindcss.com/)、ユーティリティファーストのCSSフレームワークをサポートしています。 -Angular CLIとの統合については、[Tailwindのドキュメント](https://tailwindcss.com/docs/installation/framework-guides/angular)を参照してください。 +Tailwind CSSをAngular CLIと統合するには、[AngularでのTailwind CSSの使用](guide/tailwind)を参照してください。 diff --git a/adev-ja/src/content/tools/language-service.md b/adev-ja/src/content/tools/language-service.md index 9cc309ca47..ea2b16e56f 100644 --- a/adev-ja/src/content/tools/language-service.md +++ b/adev-ja/src/content/tools/language-service.md @@ -56,7 +56,7 @@ You can then click "Go to definition" or press F12 to go directly to the definit ## Angular Language Service in your editor -Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com), [WebStorm](https://www.jetbrains.com/webstorm), [Sublime Text](https://www.sublimetext.com) and [Eclipse IDE](https://www.eclipse.org/eclipseide). +Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com), [WebStorm](https://www.jetbrains.com/webstorm), [Sublime Text](https://www.sublimetext.com), [Zed](https://zed.dev), [Neovim](https://neovim.io), and [Eclipse IDE](https://www.eclipse.org/eclipseide). ### Visual Studio Code @@ -174,7 +174,7 @@ Angular Language Service can be used with Neovim by using the [nvim-lspconfig](h ### Zed -In [Zed](https://zed.dev), install the extension from [Extensions: Marketplace](https://zed.dev/extensions?query=angular). +In [Zed](https://zed.dev), install the extension from [Extensions: Marketplace](https://zed.dev/extensions/angular). ## How the Language Service works diff --git a/adev-ja/src/content/tutorials/first-app/steps/01-hello-world/README.md b/adev-ja/src/content/tutorials/first-app/steps/01-hello-world/README.md index 69e6f2a67e..a1a8c61bdc 100644 --- a/adev-ja/src/content/tutorials/first-app/steps/01-hello-world/README.md +++ b/adev-ja/src/content/tutorials/first-app/steps/01-hello-world/README.md @@ -103,7 +103,7 @@ In your IDE: Then, save the changes you made to `app.ts`. 1. If you stopped the `ng serve` command from step 1, in the **Terminal** window of your IDE, run `ng serve` again. -1. Open your browser and navigate to `localhost:4200` and confirm that the app builds without error and displays *Hello world* in the title and body of your app: +1. Open your browser and navigate to `localhost:4200` and confirm that the app builds without error and displays *Homes* in the title and *Hello world* in the body of your app: browser frame of page displaying the text 'Hello World' diff --git a/adev-ja/src/content/tutorials/home.en.md b/adev-ja/src/content/tutorials/home.en.md index 0b4c2b2501..b6b745858d 100644 --- a/adev-ja/src/content/tutorials/home.en.md +++ b/adev-ja/src/content/tutorials/home.en.md @@ -9,6 +9,9 @@ Welcome to the Angular tutorials! These tutorials will guide you through the cor via npm + + via the Playground + via the Playground diff --git a/adev-ja/src/content/tutorials/home.md b/adev-ja/src/content/tutorials/home.md index 3649e04ede..bc2d6627f9 100644 --- a/adev-ja/src/content/tutorials/home.md +++ b/adev-ja/src/content/tutorials/home.md @@ -9,6 +9,9 @@ Angularチュートリアルへようこそ!これらのチュートリアル npm を使用 + + Playground を使用 + Playground を使用 diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.en.md b/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.en.md index 08d243af67..29b1e78c78 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.en.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.en.md @@ -18,7 +18,7 @@ import {UpperCasePipe} from '@angular/common'; template: `{{ loudMessage | uppercase }}`, imports: [UpperCasePipe], }) -class App { +export class App { loudMessage = 'we think you are doing great!' }
    diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.md b/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.md index be981c9219..adce81869e 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/22-pipes/README.md @@ -18,7 +18,7 @@ import {UpperCasePipe} from '@angular/common'; template: `{{ loudMessage | uppercase }}`, imports: [UpperCasePipe], }) -class App { +export class App { loudMessage = 'we think you are doing great!' }
    diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.en.md b/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.en.md index 4aa993494b..4d668d26af 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.en.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.en.md @@ -23,7 +23,7 @@ Here's an example of how to use the `@if` syntax in a component: } `, }) -class App { +export class App { isLoggedIn = true; } ``` diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.md b/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.md index 02699e224f..841e2b652f 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.md @@ -23,7 +23,7 @@ NOTE: [エッセンシャルガイドの制御フロー](/essentials/templates#c } `, }) -class App { +export class App { isLoggedIn = true; } ``` diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/7-event-handling/README.en.md b/adev-ja/src/content/tutorials/learn-angular/steps/7-event-handling/README.en.md index 679a4b2696..a1ba9dfd2c 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/7-event-handling/README.en.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/7-event-handling/README.en.md @@ -15,7 +15,7 @@ In Angular you bind to events with the parentheses syntax `()`. On a given eleme ... template: ` + +``` + + + + +Add a `toggleStatus()` method that switches between online and offline using the `update()` method. + +```ts +toggleStatus() { + this.userStatus.update(current => current === 'online' ? 'offline' : 'online'); +} +``` + +The `update()` method takes a function that receives the current value and returns the new value. This is useful when you need to modify the existing value based on its current state. + + + + +The toggle button is already in the template. Connect it to your `toggleStatus()` method: + +```html + +``` + + + + + +Congratulations! You've created your first signal and learned how to update it using both `set()` and `update()` methods. The `signal()` function creates a reactive value that Angular tracks, and when you update it, your UI automatically reflects the changes. + +Next, you'll learn [how to derive state from signals using computed](/tutorials/signals/2-deriving-state-with-computed-signals)! + + + +You might notice `ChangeDetectionStrategy.OnPush` in the component decorator throughout this tutorial. This is a performance optimization for Angular components that use signals. For now, you can safely ignore it—just know it helps your app run faster when using signals! You can learn more in the [change detection strategies API docs](/api/core/ChangeDetectionStrategy). + + diff --git a/adev-ja/src/content/tutorials/signals/steps/1-creating-your-first-signal/config.json b/adev-ja/src/content/tutorials/signals/steps/1-creating-your-first-signal/config.json new file mode 100644 index 0000000000..638a23f2a5 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/1-creating-your-first-signal/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/app.css"], + "type": "editor", + "title": "Creating and updating your first signal" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/README.md b/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/README.md new file mode 100644 index 0000000000..780cdbdecf --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/README.md @@ -0,0 +1,110 @@ +# Reacting to signal changes with effect + +Now that you've learned [querying child elements with signal queries](/tutorials/signals/9-query-child-elements-with-signal-queries), let's explore how to react to signal changes with effects. Effects are functions that run automatically when their dependencies change, making them perfect for side effects like logging, DOM manipulation, or API calls. + +**Important: Effects should be the last API you reach for.** Always prefer `computed()` for derived values and `linkedSignal()` for values that can be both derived and manually set. If you find yourself copying data from one signal to another with an effect, it's a sign you should move your source-of-truth higher up and use `computed()` or `linkedSignal()` instead. Effects are best for syncing signal state to imperative, non-signal APIs. + +In this activity, you'll learn how to use the `effect()` function appropriately for legitimate side effects that respond to signal changes. + +
    + +You have a theme manager app with signals already set up. Now you'll add effects to automatically react to signal changes. + + + + +Add `effect` to your existing imports. + +```ts +// Add effect to existing imports +import {Component, signal, computed, effect, ChangeDetectionStrategy} from '@angular/core'; +``` + +The `effect` function creates a reactive side effect that runs automatically when any signals it reads change. + + + +Add an effect that automatically saves the theme to local storage when it changes. + +```ts +constructor() { + // Save theme to localStorage whenever it changes + effect(() => { + localStorage.setItem('theme', this.theme()); + console.log('Theme saved to localStorage:', this.theme()); + }); +} +``` + +This effect runs whenever the theme signal changes, automatically persisting the user's preference. + + + +Add an effect that logs when the user logs in or out. + +```ts +constructor() { + // ... previous effect + + // Log user activity changes + effect(() => { + const status = this.isLoggedIn() ? 'logged in' : 'logged out'; + const user = this.username(); + console.log(`User ${user} is ${status}`); + }); +} +``` + +This effect demonstrates how effects can read multiple signals and react to changes in any of them. + + + +Add an effect that sets up a timer and cleans up when the component is destroyed. + +```ts +constructor() { + // ... previous effects + + // Timer effect with cleanup + effect((onCleanup) => { + const interval = setInterval(() => { + console.log('Timer tick - Current theme:', this.theme()); + }, 5000); + + // Clean up the interval when the effect is destroyed + onCleanup(() => { + clearInterval(interval); + console.log('Timer cleaned up'); + }); + }); +} +``` + +This effect demonstrates how to clean up resources when effects are destroyed or re-run. + + + +Open the browser console and interact with the app: + +- **Toggle Theme** - See localStorage saves and timer logs +- **Login/Logout** - See user activity logging +- **Watch Timer** - See periodic theme logging every 5 seconds + +The effects run automatically whenever their tracked signals change! + + + + +Excellent! You've now learned how to use effects with signals. Key concepts to remember: + +- **Effects are reactive**: They automatically run when any signal they read changes +- **Side effects only**: Perfect for logging, DOM manipulation, API calls, and syncing to imperative APIs +- **Cleanup**: Use the `onCleanup` callback to clean up resources like timers or subscriptions +- **Automatic tracking**: Effects automatically track which signals they read and re-run when those signals change + +**Remember: Use effects sparingly!** The examples in this lesson (localStorage sync, logging, timers) are appropriate uses. Avoid effects for: +- Deriving values from other signals - use `computed()` instead +- Creating writable derived state - use `linkedSignal()` instead +- Copying data between signals - restructure to use a shared source of truth + +Effects are powerful but should be your last resort when `computed()` and `linkedSignal()` can't solve your use case. diff --git a/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/config.json b/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/config.json new file mode 100644 index 0000000000..3cc4f403f8 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/10-reacting-to-signal-changes-with-effect/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/app.css"], + "type": "editor", + "title": "Reacting to signal changes with effects" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/11-next-steps/README.md b/adev-ja/src/content/tutorials/signals/steps/11-next-steps/README.md new file mode 100644 index 0000000000..55de1ae4c7 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/11-next-steps/README.md @@ -0,0 +1,12 @@ +# Want to dive deeper into signals? + +You can also learn more about signals with the following resources: + + + + The official guide for Angular Signals. + + + Read about Angular's open source roadmap including current, future and accomplished projects. + + diff --git a/adev-ja/src/content/tutorials/signals/steps/11-next-steps/config.json b/adev-ja/src/content/tutorials/signals/steps/11-next-steps/config.json new file mode 100644 index 0000000000..e579515e03 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/11-next-steps/config.json @@ -0,0 +1,3 @@ +{ + "title": "Next steps" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md b/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md new file mode 100644 index 0000000000..e0a5bc5cf0 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/README.md @@ -0,0 +1,109 @@ +# Deriving state with computed signals + +Now that you've learned [how to create and update signals](/tutorials/signals/1-creating-and-updating-your-first-signal), let's learn about computed signals. Computed signals are derived values that automatically update when their dependencies change. They're perfect for creating reactive calculations based on other signals. + +In this activity, you'll learn how to use the `computed()` function to create derived state that updates automatically when the underlying signals change. + +Let's enhance our user status system by adding computed values that derive information from our user status signal. The starter code now includes three status options: `'online'`, `'away'`, and `'offline'`. + +
    + + + + +Add `computed` to your existing imports. + +```ts +// Add computed to existing imports +import {Component, signal, computed, ChangeDetectionStrategy} from '@angular/core'; +``` + + + + +Add a computed signal that determines if notifications should be enabled based on user status. + +```ts +notificationsEnabled = computed(() => this.userStatus() === 'online'); +``` + +This computed signal will automatically recalculate whenever the `userStatus` signal changes. Notice how we call `this.userStatus()` inside the computed function to read the signal's value. + + + +Add a computed signal that creates a descriptive message based on the user status. + +```ts +statusMessage = computed(() => { + const status = this.userStatus(); + switch (status) { + case 'online': return 'Available for meetings and messages'; + case 'away': return 'Temporarily away, will respond soon'; + case 'offline': return 'Not available, check back later'; + default: return 'Status unknown'; + } +}); +``` + +This shows how computed signals can handle more complex logic with switch statements and string transformations. + + + +Add a computed signal that calculates if the user is within their working hours. + +```ts +isWithinWorkingHours = computed(() => { + const now = new Date(); + const hour = now.getHours(); + const isWeekday = now.getDay() > 0 && now.getDay() < 6; + return isWeekday && hour >= 9 && hour < 17 && this.userStatus() !== 'offline'; +}); +``` + +This demonstrates how computed signals can perform calculations and combine multiple data sources. The value updates automatically when the `userStatus` changes. + + + +The template already has placeholders showing "Loading...". Replace them with your computed signals: + +1. For notifications, replace `Loading...` with an @if block: + +```html +@if (notificationsEnabled()) { + Enabled +} @else { + Disabled +} +``` + +2. For the message, replace `Loading...` with: + +```html +{{ statusMessage() }} +``` + +3. For working hours, replace `Loading...` with an @if block: + +```html +@if (isWithinWorkingHours()) { + Yes +} @else { + No +} +``` + +Notice how computed signals are called just like regular signals - with parentheses! + + + + +Excellent! You've now learned how to create computed signals. + +Here are some key points to remember: + +- **Computed signals are reactive**: They automatically update when their dependencies change +- **They're read-only**: You can't directly set computed values, they're derived from other signals +- **They can contain complex logic**: Use them for calculations, transformations, and derived state +- **They provide a way to make performant computations based on dynamic state**: Angular only recalculates them when their dependencies actually change + +In the next lesson, you'll learn about [a different way to derive state with linkedSignals](/tutorials/signals/3-deriving-state-with-linked-signals)! diff --git a/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/config.json b/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/config.json new file mode 100644 index 0000000000..79e1243300 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/2-deriving-state-with-computed-signals/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/app.css"], + "type": "editor", + "title": "Deriving state with computed signals" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md b/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md new file mode 100644 index 0000000000..5623d00ed3 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/README.md @@ -0,0 +1,95 @@ +# Deriving state with linked signals + +Now that you've learned [how to derive state with computed signals](/tutorials/signals/2-deriving-state-with-computed-signals), you created a computed signal for `notificationsEnabled` that automatically followed your user status. But what if users want to manually disable notifications even when they're online? That's where linked signals come in. + +Linked signals are writable signals that maintain a reactive connection to their source signals. They're perfect for creating state that normally follows a computation but can be overridden when needed. + +In this activity, you'll learn how `linkedSignal()` differs from `computed()` by enhancing the previous user status system's computed `notificationsEnabled` to a writable linked signal. + +
    + + + + +Add `linkedSignal` to your existing imports. + +```ts +// Add linkedSignal to existing imports +import {Component, signal, computed, linkedSignal, ChangeDetectionStrategy} from '@angular/core'; +``` + + + + +Replace the computed `notificationsEnabled` with a linkedSignal using the exact same expression: + +```ts +// Previously (from lesson 2): +// notificationsEnabled = computed(() => this.userStatus() === 'online'); + +// Now with linkedSignal - same expression, but writable: +notificationsEnabled = linkedSignal(() => this.userStatus() === 'online'); +``` + +The expression is identical, but linkedSignal creates a writable signal. It will still automatically update when `userStatus` changes, but you can also set it manually. + + + +Add a method to demonstrate that linked signals can be written to directly: + +```ts +toggleNotifications() { + // This works with linkedSignal but would error with computed! + this.notificationsEnabled.set(!this.notificationsEnabled()); +} +``` + +This is the key difference: computed signals are read-only, but linked signals can be updated manually while still maintaining their reactive connection. + + + +Update your template to add a toggle button for notifications: + +```html +
    +
    + Notifications: + @if (notificationsEnabled()) { + Enabled + } @else { + Disabled + } + +
    + +
    +``` + +
    + + +Now test the behavior: + +1. Change the user status - notice how `notificationsEnabled` updates automatically +2. Manually toggle notifications - it overrides the computed value +3. Change status again - the linked signal re-syncs with its computation + +This demonstrates that linked signals maintain their reactive connection even after being manually set! + + +
    + +Excellent! You've learned the key differences between computed and linked signals: + +- **Computed signals**: Read-only, always derived from other signals +- **Linked signals**: Writable, can be both derived AND manually updated +- **Use computed when**: The value should always be calculated +- **Use linkedSignal when**: You need a default computation that can be overridden + +In the next lesson, you'll learn [how to manage async data with signals](/tutorials/signals/4-managing-async-data-with-signals)! diff --git a/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/config.json b/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/config.json new file mode 100644 index 0000000000..a8332116ec --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/3-deriving-state-with-linked-signals/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/app.css"], + "type": "editor", + "title": "Deriving state with linked signals" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md b/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md new file mode 100644 index 0000000000..93a79e4e66 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/README.md @@ -0,0 +1,110 @@ +# Managing async data with signals using the Resources API + +Now that you've learned [how to derive state with linked signals](/tutorials/signals/3-deriving-state-with-linked-signals), let's explore how to handle asynchronous data with the Resource API. The Resource API provides a powerful way to manage async operations using signals, with built-in loading states, error handling, and request management. + +In this activity, you'll learn how to use the `resource()` function to load data asynchronously and how to handle different states of async operations by building a user profile loader that demonstrates the Resource API in action. + +
    + + + + +Add `resource` to your existing imports and import the fake API function. + +```ts +// Add resource to existing imports +import {Component, signal, computed, resource, ChangeDetectionStrategy} from '@angular/core'; +// Import mock API function +import {loadUser} from './user-api'; +``` + + + + +Add a property in the component class that creates a resource to load user data based on a user ID signal. + +```ts +userId = signal(1); + +userResource = resource({ + params: () => ({ id: this.userId() }), + loader: (params) => loadUser(params.params.id) +}); +``` + + + + +Add methods to change the user ID and reload the resource. + +```ts +loadUser(id: number) { + this.userId.set(id); +} + +reloadUser() { + this.userResource.reload(); +} +``` + +Changing the params signal automatically triggers a reload, or you can manually reload with `reload()`. + + + +Add computed signals to access different states of the resource. + +```ts +isLoading = computed(() => this.userResource.status() === 'loading'); +hasError = computed(() => this.userResource.status() === 'error'); +``` + +Resources provide a `status()` signal that can be 'loading', 'success', or 'error', a `value()` signal for the loaded data, and a `hasValue()` method that safely checks if data is available. + + + +The template structure is already provided. Now connect everything: + +Part 1. **Add click handlers to the buttons:** + +```html + + + + +``` + +Part 2. **Replace the placeholder with resource state handling:** + +```html +@if (isLoading()) { +

    Loading user...

    +} @else if (hasError()) { +

    Error: {{ userResource.error()?.message }}

    +} @else if (userResource.hasValue()) { + +} +``` + +The resource provides different methods to check its state: + +- `isLoading()` - true when fetching data +- `hasError()` - true when an error occurred +- `userResource.hasValue()` - true when data is available +- `userResource.value()` - access the loaded data +- `userResource.error()` - access error information + +
    + +
    + +Excellent! You've now learned how to use the Resource API with signals. Key concepts to remember: + +- **Resources are reactive**: They automatically reload when params change +- **Built-in state management**: Resources provide `status()`, `value()`, and `error()` signals +- **Automatic cleanup**: Resources handle request cancellation and cleanup automatically +- **Manual control**: You can manually reload or abort requests when needed + +In the next lesson, you'll learn [how to pass data to components with input signals](/tutorials/signals/5-component-communication-with-signals)! diff --git a/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/config.json b/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/config.json new file mode 100644 index 0000000000..48bac02bb9 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/4-managing-async-data-with-signals/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/user-api.ts", "src/app/app.css"], + "type": "editor", + "title": "Managing async data with signals using the Resources API" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md b/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md new file mode 100644 index 0000000000..528af3b1b3 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/README.md @@ -0,0 +1,105 @@ +# Passing data to components with input signals + +Now that you've learned [managing async data with signals](/tutorials/signals/4-managing-async-data-with-signals), let's explore Angular's signal-based `input()` API for passing data from parent to child components, making component data flow more reactive and efficient. If you're familiar with component props from other frameworks, inputs are the same idea. + +In this activity, you'll add signal inputs to a product card component and see how parent data flows down reactively. + +
    + + + + +Add signal `input()` functions to receive data in the `product-card` component. + +```ts +// Add imports for signal inputs +import {Component, input} from '@angular/core'; + +// Add these signal inputs +name = input.required(); +price = input.required(); +available = input(true); +``` + +Notice how `input.required()` creates an input that must be provided, while `input()` with a default value is optional. + + + +Update the template in `product-card` to display the signal input values. + +```html +
    +

    {{ name() }}

    +

    \${{ price() }}

    +

    Status: + @if (available()) { + Available + } @else { + Out of Stock + } +

    +
    +``` + +Input signals work just like regular signals in templates - call them as functions to access their values. +
    + + +Update the `product-card` usage in `app.ts` to pass dynamic signal values instead of static ones. + +```html + + + + + +``` + +The square brackets `[]` create property bindings that pass the current signal values to the child. + + + +Add methods in `app.ts` to update the parent signals and see how the child component reacts automatically. + +```ts +updateProduct() { + this.productName.set('Updated Product'); + this.productPrice.set(149); +} + +toggleAvailability() { + this.productAvailable.set(!this.productAvailable()); +} +``` + +```html + +
    + + +
    +``` + +When parent signals change, the child component automatically receives and displays the new values! +
    + +
    + +Excellent! You've learned how signal inputs work: + +- **Signal inputs** - Use `input()` and `input.required()` to receive data from parent components +- **Reactive updates** - Child components automatically update when parent signal values change +- **Type safety** - Signal inputs provide full TypeScript type checking +- **Default values** - Optional inputs can have default values while required inputs must be provided + +Signal inputs make component communication more reactive and eliminate the need for `OnChanges` lifecycle hooks in many cases. + +In the next lesson, you'll learn about [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals)! diff --git a/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/config.json b/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/config.json new file mode 100644 index 0000000000..dffea145ef --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/5-component-communication-with-signals/config.json @@ -0,0 +1,10 @@ +{ + "openFiles": [ + "src/app/app.ts", + "src/app/product-card.ts", + "src/app/quantity-selector.ts", + "src/app/app.css" + ], + "type": "editor", + "title": "Passing data to components with input signals" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md b/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md new file mode 100644 index 0000000000..d68cdee85f --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/README.md @@ -0,0 +1,128 @@ +# Two-way binding with model signals + +Now that you've learned [passing data to components with input signals](/tutorials/signals/5-component-communication-with-signals), let's explore Angular's `model()` API for two-way binding. Model signals are perfect for UI components like checkboxes, sliders, or custom form controls where the component needs to both receive a value AND update it. + +In this activity, you'll create a custom checkbox component that manages its own state while keeping the parent synchronized. + +
    + + + + +Create a model signal in the `custom-checkbox` component that can both receive and update the parent's value. + +```ts +// Add imports for model signals +import {Component, model, input} from '@angular/core'; + +// Model signal for two-way binding +checked = model.required(); + +// Optional input for label +label = input(''); +``` + +Unlike `input()` signals which are read-only, `model()` signals can be both read and written to. + + + +Build the checkbox template that responds to clicks and updates its own model. + +```html + +``` + +The component reads from its model signal and has a method to update it. + + + +Implement the toggle method that updates the model signal when the checkbox is clicked. + +```ts +toggle() { + // This updates BOTH the component's state AND the parent's model! + this.checked.set(!this.checked()); +} +``` + +When the child component calls `this.checked.set()`, it automatically propagates the change back to the parent. This is the key difference from `input()` signals. + + + +First, uncomment the model signal properties and methods in `app.ts`: + +```ts +// Parent signal models +agreedToTerms = model(false); +enableNotifications = model(true); + +// Methods to test two-way binding +toggleTermsFromParent() { + this.agreedToTerms.set(!this.agreedToTerms()); +} + +resetAll() { + this.agreedToTerms.set(false); + this.enableNotifications.set(false); +} +``` + +Then update the template: + +Part 1. **Uncomment the checkboxes and add two-way binding:** + +- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="agreedToTerms"` for the first checkbox +- Replace `___ADD_TWO_WAY_BINDING___` with `[(checked)]="enableNotifications"` for the second + +Part 2. **Replace the `???` placeholders with @if blocks:** + +```html +@if (agreedToTerms()) { + Yes +} @else { + No +} +``` + +Part 3. **Add click handlers to the buttons:** + +```html + + +``` + +The `[(checked)]` syntax creates two-way binding - data flows down to the component AND changes flow back up to the parent by emitting an event that references the signal itself and does _not_ call the signal getter directly. + + + +Interact with your app to see two-way binding in action: + +1. **Click checkboxes** - Component updates its own state and notifies parent +2. **Click "Toggle Terms from Parent"** - Parent updates propagate down to component +3. **Click "Reset All"** - Parent resets both models and components update automatically + +Both the parent and child can update the shared state, and both stay in sync automatically! + + + + +Perfect! You've learned how model signals enable two-way binding: + +- **Model signals** - Use `model()` and `model.required()` for values that can be both read and written +- **Two-way binding** - Use `[(property)]` syntax to bind parent signals to child models +- **Perfect for UI components** - Checkboxes, form controls, and widgets that need to manage their own state +- **Automatic synchronization** - Parent and child stay in sync without manual event handling + +**When to use `model()` vs `input()`:** + +- Use `input()` for data that only flows down (display data, configuration) +- Use `model()` for UI components that need to update their own value (form controls, toggles) + +In the next lesson, you'll learn about [using signals with services](/tutorials/signals/7-using-signals-with-services)! diff --git a/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/config.json b/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/config.json new file mode 100644 index 0000000000..37e7c462da --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/6-two-way-binding-with-model-signals/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/custom-checkbox.ts", "src/app/app.css"], + "type": "editor", + "title": "Two-way binding with model signals" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md b/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md new file mode 100644 index 0000000000..6d6c554126 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/README.md @@ -0,0 +1,103 @@ +# Using signals with services + +Now that you've learned [two-way binding with model signals](/tutorials/signals/6-two-way-binding-with-model-signals), let's explore how to use signals with Angular services. Services are perfect for sharing reactive state across multiple components, and signals make this even more powerful by providing automatic change detection and clean reactive patterns. + +In this activity, you'll learn how to create a cart store with signals that allow the cart display component to react to state changes automatically. + +
    + + + + +Add readonly and computed signals to make the cart state reactive in `cart-store.ts`. + +```ts +// Add the computed import +import {Injectable, signal, computed} from '@angular/core'; + +// Then add these signals to the class: + +// Readonly signals +readonly cartItems = this.items.asReadonly(); + +// Computed signals +readonly totalQuantity = computed(() => { + return this.items().reduce((sum, item) => sum + item.quantity, 0); +}); + +readonly totalPrice = computed(() => { + return this.items().reduce((sum, item) => sum + item.price * item.quantity, 0); +}); +``` + +These signals allow components to reactively access cart data and computed totals. The `asReadonly()` method prevents external code from modifying the cart items directly, while `computed()` creates derived state that automatically updates when the source signal changes. + + + +The cart display component in `cart-display.ts` already uses the cart store signals in its template. Complete the quantity update methods to modify cart items: + +```ts +increaseQuantity(id: string) { + const items = this.cartStore.cartItems(); + const currentItem = items.find((item) => item.id === id); + if (currentItem) { + this.cartStore.updateQuantity(id, currentItem.quantity + 1); + } +} + +decreaseQuantity(id: string) { + const items = this.cartStore.cartItems(); + const currentItem = items.find((item) => item.id === id); + if (currentItem && currentItem.quantity > 1) { + this.cartStore.updateQuantity(id, currentItem.quantity - 1); + } +} +``` + +These methods read the current cart state using `cartItems()` and update quantities through the store's methods. The UI automatically updates when the signals change! + + + +Update the main app component in `app.ts` to use the cart service and display the cart component. + +```ts +import {Component, inject} from '@angular/core'; +import {CartStore} from './cart-store'; +import {CartDisplay} from './cart-display'; + +@Component({ + selector: 'app-root', + imports: [CartDisplay], + template: ` +
    +
    +

    Signals with Services Demo

    +
    + Cart: {{ cartStore.totalQuantity() }} items (\${{ cartStore.totalPrice() }}) +
    +
    + +
    + +
    +
    + `, + styleUrl: './app.css', +}) +export class App { + cartStore = inject(CartStore); +} +``` + +
    + +
    + +Excellent! You've now learned how to use signals with services. Key concepts to remember: + +- **Service-level signals**: Services can use signals to manage reactive state +- **Dependency injection**: Use `inject()` to access services with signals in components +- **Computed signals in services**: Create derived state that updates automatically +- **Readonly signals**: Expose read-only versions of signals to prevent external mutations + +In the next lesson, you'll learn about [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives)! diff --git a/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/config.json b/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/config.json new file mode 100644 index 0000000000..845081b9c4 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/7-using-signals-with-services/config.json @@ -0,0 +1,11 @@ +{ + "openFiles": [ + "src/app/app.ts", + "src/app/cart-store.ts", + "src/app/cart-display.ts", + "src/app/cart-types.ts", + "src/app/app.css" + ], + "type": "editor", + "title": "Using signals with services" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md b/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md new file mode 100644 index 0000000000..aae2a5d690 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/README.md @@ -0,0 +1,110 @@ +# Using signals with directives + +Now that you've learned [using signals with services](/tutorials/signals/7-using-signals-with-services), let's explore how directives use signals. **The great news: signals work exactly the same in directives as they do in components!** The main difference is that since directives don't have templates, you'll primarily use signals in host bindings to reactively update the host element. + +In this activity, you'll build a highlight directive that demonstrates how signals create reactive behavior in directives. + +
    + + + + +Import the signal functions and create your reactive state. This works exactly the same as in components: + +```ts +import {Directive, input, signal, computed} from '@angular/core'; + +@Directive({ + selector: '[highlight]', +}) +export class HighlightDirective { + // Signal inputs - same as components! + color = input('yellow'); + intensity = input(0.3); + + // Internal state - same as components! + private isHovered = signal(false); + + // Computed signals - same as components! + backgroundStyle = computed(() => { + const baseColor = this.color(); + const alpha = this.isHovered() ? this.intensity() : this.intensity() * 0.5; + + const colorMap: Record = { + 'yellow': `rgba(255, 255, 0, ${alpha})`, + 'blue': `rgba(0, 100, 255, ${alpha})`, + 'green': `rgba(0, 200, 0, ${alpha})`, + 'red': `rgba(255, 0, 0, ${alpha})`, + }; + + return colorMap[baseColor] || colorMap['yellow']; + }); +} +``` + +Notice how this is identical to component patterns - the only difference is we're in a `@Directive` instead of `@Component`. + + + +Since directives don't have templates, you'll use signals in **host bindings** to reactively update the host element. Add the `host` configuration and event handlers: + +```ts +@Directive({ + selector: '[highlight]', + host: { + '[style.backgroundColor]': 'backgroundStyle()', + '(mouseenter)': 'onMouseEnter()', + '(mouseleave)': 'onMouseLeave()', + }, +}) +export class HighlightDirective { + // ... signals from previous step ... + + onMouseEnter() { + this.isHovered.set(true); + } + + onMouseLeave() { + this.isHovered.set(false); + } +} +``` + +The host bindings automatically re-evaluate when the signals change - just like template bindings in components! When `isHovered` changes, the `backgroundStyle` computed signal recalculates, and the host binding updates the element's style. + + + +Update the app template to demonstrate the reactive directive: + +```ts +template: ` +
    +

    Directive with Signals

    + +
    + Hover me - Yellow highlight +
    + +
    + Hover me - Blue highlight +
    + +
    + Hover me - Green highlight +
    +
    +`, +``` + +The directive automatically applies reactive highlighting based on the signal inputs! +
    + +
    + +Perfect! You've now seen how signals work with directives. Some key takeaways from this lesson are: + +- **Signals are universal** - All signal APIs (`input()`, `signal()`, `computed()`, `effect()`) work the same in both directives and components +- **Host bindings are the primary use case** - Since directives don't have templates, you use signals in host bindings to reactively modify the host element +- **Same reactive patterns** - Signal updates trigger automatic re-evaluation of computed signals and host bindings, just like in component templates + +In the next lesson, you'll [learn how to query child elements with signal queries](/tutorials/signals/9-query-child-elements-with-signal-queries)! diff --git a/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/config.json b/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/config.json new file mode 100644 index 0000000000..5df1706f90 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/8-using-signals-with-directives/config.json @@ -0,0 +1,5 @@ +{ + "openFiles": ["src/app/app.ts", "src/app/highlight-directive.ts", "src/app/app.css"], + "type": "editor", + "title": "Using signals with directives" +} diff --git a/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md b/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md new file mode 100644 index 0000000000..8354746f5d --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/README.md @@ -0,0 +1,66 @@ +# Query child elements with signal queries + +Now that you've learned [how to use signals with directives](/tutorials/signals/8-using-signals-with-directives), let's explore signal-based query APIs. These provide a reactive way to access and interact with child components and directives. Both components and directives can perform queries while also being queried themselves. Unlike the traditional ViewChild, signal queries automatically update and provide type-safe access to child components and directives. + +In this activity, you'll add viewChild queries to interact with child components programmatically. + +
    + + + + +First, add the `viewChild` import to access child components in `app.ts`. + +```ts +import {Component, signal, computed, viewChild, ChangeDetectionStrategy} from '@angular/core'; +``` + + + + +Add viewChild queries to the App component to access child components. + +```ts +// Query APIs to access child components +firstProduct = viewChild(ProductCard); +cartSummary = viewChild(CartSummary); +``` + +These queries create signals that reference child component instances. + + + +Use the viewChild queries to call methods on child components in `app.ts`: + +```ts +showFirstProductDetails() { + const product = this.firstProduct(); + if (product) { + product.highlight(); + } +} + +initiateCheckout() { + const summary = this.cartSummary(); + if (summary) { + summary.initiateCheckout(); + } +} +``` + + + + +The control buttons should now work: + +- **"Show First Product Details"** - Calls `highlight()` on the ProductCard +- **"Initiate Checkout"** - Calls `initiateCheckout()` on the CartSummary + +Click the buttons to see how viewChild queries enable parent components to control child behavior. + + + + +Perfect! You've learned how to use signal-based query APIs for child component interaction: + +In the next lesson, you'll learn about [how to react to signal changes with effect](/tutorials/signals/10-reacting-to-signal-changes-with-effect)! diff --git a/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/config.json b/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/config.json new file mode 100644 index 0000000000..148894a291 --- /dev/null +++ b/adev-ja/src/content/tutorials/signals/steps/9-query-child-elements-with-signal-queries/config.json @@ -0,0 +1,10 @@ +{ + "openFiles": [ + "src/app/app.ts", + "src/app/product-card.ts", + "src/app/cart-summary.ts", + "src/app/app.css" + ], + "type": "editor", + "title": "Query child elements with signal queries" +} diff --git a/origin b/origin index 65c5a734e2..739b0914dd 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit 65c5a734e2c11fe5d2fc4a1ef6dcf61d6ad1859a +Subproject commit 739b0914dd07bb41aac8b4b5cddc3725c9446df7 diff --git a/tools/adev-patches/translate-tutorial-label.patch b/tools/adev-patches/translate-tutorial-label.patch index b6344c4ae2..9398c5a06f 100644 --- a/tools/adev-patches/translate-tutorial-label.patch +++ b/tools/adev-patches/translate-tutorial-label.patch @@ -2,9 +2,10 @@ diff --git a/adev/src/app/features/tutorial/tutorial.component.ts b/adev/src/app index 5ed9051ae2..bb24ca4242 100644 --- a/adev/src/app/features/tutorial/tutorial.component.ts +++ b/adev/src/app/features/tutorial/tutorial.component.ts -@@ -46,7 +46,7 @@ import { +@@ -49,7 +49,7 @@ import { } from '../../editor/index'; import {SplitResizerHandler} from './split-resizer-handler.service'; + import {PAGE_PREFIX} from '../../core/constants/pages'; -const INTRODUCTION_LABEL = 'Introduction'; +const INTRODUCTION_LABEL = 'イントロダクション';