From fa83b760afcf9b2433823e708db215871db8cfa3 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sun, 14 Jul 2024 00:46:04 +0900 Subject: [PATCH 001/253] chore: add initial workflow for adev build (#927) --- .github/workflows/adev-preview-build.yml | 58 +++++++++++++ .github/workflows/adev-preview-deploy.yml | 63 ++++++++++++++ .github/workflows/adev-production-deploy.yml | 45 ++++++++++ .github/workflows/build-and-test.yml | 67 +++++++++------ origin | 2 +- tools/adev-patches/change-analytics-id.patch | 11 +++ .../adev-patches/change-document-title.patch | 79 ++++++++++++++++++ tools/adev-patches/localize-home.patch | 82 +++++++++++++++++++ tools/build.mjs | 40 +++++---- tools/lib/common.mjs | 16 ++-- 10 files changed, 412 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/adev-preview-build.yml create mode 100644 .github/workflows/adev-preview-deploy.yml create mode 100644 .github/workflows/adev-production-deploy.yml create mode 100644 tools/adev-patches/change-analytics-id.patch create mode 100644 tools/adev-patches/change-document-title.patch create mode 100644 tools/adev-patches/localize-home.patch diff --git a/.github/workflows/adev-preview-build.yml b/.github/workflows/adev-preview-build.yml new file mode 100644 index 0000000000..ae63c6ca70 --- /dev/null +++ b/.github/workflows/adev-preview-build.yml @@ -0,0 +1,58 @@ +# This workflow builds the previews for pull requests when a certain label is applied. +# The actual deployment happens as part of a dedicated second workflow to avoid security +# issues where the building would otherwise occur in an authorized context where secrets +# could be leaked. More details can be found here: + +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. + +name: Build adev for preview deployment + +on: + pull_request: + types: [synchronize, labeled] + +permissions: read-all + +jobs: + adev-build: + runs-on: ubuntu-latest + if: | + (github.event.action == 'labeled' && github.event.label.name == 'adev: preview') || + (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview')) + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-node@v4 + with: + node-version-file: '.node-version' + cache: yarn + - uses: bazel-contrib/setup-bazel@0.8.5 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + bazelrc: | + # Print all the options that apply to the build. + # This helps us diagnose which options override others + # (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) + build --announce_rc + + # More details on failures + build --verbose_failures=true + + # CI supports colors but Bazel does not detect it. + common --color=yes + - run: yarn install + - run: yarn build + - run: chmod 755 build/dist/bin/adev/build/browser + - name: Inject pull request number + run: echo "${{ github.event.pull_request.number }}" >> __metadata__pull_number.txt + working-directory: build/dist/bin/adev/build/browser + - name: Inject commit hash + run: echo "${{ github.sha }}" >> __metadata__commit_hash.txt + working-directory: build/dist/bin/adev/build/browser + - uses: actions/upload-artifact@v4 + with: + name: adev-preview + path: build/dist/bin/adev/build/browser diff --git a/.github/workflows/adev-preview-deploy.yml b/.github/workflows/adev-preview-deploy.yml new file mode 100644 index 0000000000..2b42b8fed4 --- /dev/null +++ b/.github/workflows/adev-preview-deploy.yml @@ -0,0 +1,63 @@ +# This workflow runs whenever the ADEV build workflow has completed. Deployment happens +# as part of a dedicated second workflow to avoid security issues where the building would +# otherwise occur in an authorized context where secrets could be leaked. +# +# More details can be found here: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/. + +name: Deploying adev preview + +on: + workflow_run: + workflows: ['Build adev for preview deployment'] + types: [completed] + +permissions: + # Needed in order to be able to comment on the pull request. + pull-requests: write + # Needed in order to checkout the repository + contents: read + # Needed in order to retrieve the artifacts from the previous job + actions: read + +env: + +jobs: + deploy: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/download-artifact@v4 + with: + github-token: '${{secrets.GITHUB_TOKEN}}' + name: adev-preview + - run: ls -R + - name: Extract pull request number + run: | + PR_NUMBER=$(cat build/dist/bin/adev/build/browser/__metadata__pull_number.txt) + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV + working-directory: build/dist/bin/adev/build/browser + - name: Extract commit hash + run: | + COMMIT_HASH=$(cat build/dist/bin/adev/build/browser/__metadata__commit_hash.txt) + echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV + working-directory: build/dist/bin/adev/build/browser + - run: echo $PR_NUMBER $COMMIT_HASH + - name: Deploy to cloudflare pages + run: npx wrangler publish ./ --project-name $CLOUDFLARE_PAGES_PROJECT --branch pr-$PR_NUMBER --commit-hash $COMMIT_HASH + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_PAGES_PROJECT: ${{ var.CLOUDFLARE_PAGES_PROJECT }} + - name: Comment on pull request + uses: actions/github-script@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = process.env.PR_NUMBER; + github.issues.createComment({ + issue_number: Number(prNumber), + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Preview deployed to https://pr-${prNumber}.dev-angular-jp.pages.dev' + }) + diff --git a/.github/workflows/adev-production-deploy.yml b/.github/workflows/adev-production-deploy.yml new file mode 100644 index 0000000000..f91b3f0d6f --- /dev/null +++ b/.github/workflows/adev-production-deploy.yml @@ -0,0 +1,45 @@ +name: Build adev and deploy to production + +on: + push: + branches: + - main + +env: + BAZEL_REPO_CACHE_PATH: '~/.cache/bazel_repo_cache' + +jobs: + adev-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-node@v4 + with: + node-version-file: '.node-version' + cache: yarn + - uses: bazel-contrib/setup-bazel@0.8.5 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + bazelrc: | + # Print all the options that apply to the build. + # This helps us diagnose which options override others + # (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) + build --announce_rc + + # More details on failures + build --verbose_failures=true + + # CI supports colors but Bazel does not detect it. + common --color=yes + - run: yarn install + - run: yarn build + - name: Deploy to cloudflare pages + run: npx wrangler pages deploy $OUTPUT_DIR --project-name $CLOUDFLARE_PAGES_PROJECT + env: + OUTPUT_DIR: build/dist/bin/adev/build/browser + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_PAGES_PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT }} diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index edfb85cb8b..cd87ce3c99 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,19 +8,22 @@ on: [pull_request] permissions: contents: read +env: + BAZEL_REPO_CACHE_PATH: '~/.cache/bazel_repo_cache' + jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - uses: actions/setup-node@v4 - with: - node-version-file: '.node-version' - cache: yarn - - run: yarn install - - run: yarn test + # test: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # with: + # submodules: true + # - uses: actions/setup-node@v4 + # with: + # node-version-file: '.node-version' + # cache: yarn + # - run: yarn install + # - run: yarn test build-ubuntu: runs-on: ubuntu-latest steps: @@ -31,6 +34,22 @@ jobs: with: node-version-file: '.node-version' cache: yarn + - uses: bazel-contrib/setup-bazel@0.8.5 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + bazelrc: | + # Print all the options that apply to the build. + # This helps us diagnose which options override others + # (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) + build --announce_rc + + # More details on failures + build --verbose_failures=true + + # CI supports colors but Bazel does not detect it. + common --color=yes - run: yarn install - run: yarn build # build-windows: @@ -46,15 +65,15 @@ jobs: # - run: yarn install # - run: yarn build # shell: pwsh - build-macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - uses: actions/setup-node@v4 - with: - node-version-file: '.node-version' - cache: yarn - - run: yarn install - - run: yarn build + # build-macos: + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v4 + # with: + # submodules: true + # - uses: actions/setup-node@v4 + # with: + # node-version-file: '.node-version' + # cache: yarn + # - run: yarn install + # - run: yarn build diff --git a/origin b/origin index 27e6936912..07a0b87a4c 160000 --- a/origin +++ b/origin @@ -1 +1 @@ -Subproject commit 27e693691235f1971f8a4a631631fbca25834fed +Subproject commit 07a0b87a4c1e032c92d5596ce7a17b74a87e04e3 diff --git a/tools/adev-patches/change-analytics-id.patch b/tools/adev-patches/change-analytics-id.patch new file mode 100644 index 0000000000..104086fd26 --- /dev/null +++ b/tools/adev-patches/change-analytics-id.patch @@ -0,0 +1,11 @@ +diff --git a/adev/src/app/environment.ts b/adev/src/app/environment.ts +index 30f0d78db3..c6c18b5183 100644 +--- a/adev/src/app/environment.ts ++++ b/adev/src/app/environment.ts +@@ -15,5 +15,5 @@ export default { + apiKey: 'dfca7ed184db27927a512e5c6668b968', + indexName: 'angular_v17', + }, +- googleAnalyticsId: 'G-XB6NEVW32B', ++ googleAnalyticsId: 'G-ZE76R447BW', + }; diff --git a/tools/adev-patches/change-document-title.patch b/tools/adev-patches/change-document-title.patch new file mode 100644 index 0000000000..989e081d7b --- /dev/null +++ b/tools/adev-patches/change-document-title.patch @@ -0,0 +1,79 @@ +diff --git a/adev/src/app/core/services/a-dev-title-strategy.ts b/adev/src/app/core/services/a-dev-title-strategy.ts +index 75a1daa0e9..6acf3ec62f 100644 +--- a/adev/src/app/core/services/a-dev-title-strategy.ts ++++ b/adev/src/app/core/services/a-dev-title-strategy.ts +@@ -13,7 +13,7 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot, TitleStrategy} from '@angul + + export const ROUTE_TITLE_PROPERTY = 'label'; + export const ROUTE_PARENT_PROPERTY = 'parent'; +-export const TITLE_SUFFIX = 'Angular'; ++export const TITLE_SUFFIX = 'Angular 日本語版'; + export const TITLE_SEPARATOR = ' • '; + export const DEFAULT_PAGE_TITLE = 'Overview'; + +diff --git a/adev/src/index.html b/adev/src/index.html +index f6d4c0eb48..292608d443 100644 +--- a/adev/src/index.html ++++ b/adev/src/index.html +@@ -1,6 +1,6 @@ + + +- ++ + + + + + + + +``` + +After those steps, if you add event listeners for the `scroll` event, the listeners will be `passive`. + +Note that the above case applies only to applications using zone.js. + +## What's next + + + + + + + diff --git a/adev-ja/src/content/guide/templates/interpolation.md b/adev-ja/src/content/guide/templates/interpolation.md new file mode 100644 index 0000000000..7e990a4cf2 --- /dev/null +++ b/adev-ja/src/content/guide/templates/interpolation.md @@ -0,0 +1,24 @@ +# Displaying values with interpolation + +Interpolation refers to embedding expressions into marked up text. By default, interpolation uses the double curly braces `{{` and `}}` as delimiters. + +To illustrate how interpolation works, consider an Angular component that contains a `currentCustomer` variable: + + + +Use interpolation to display the value of this variable in the corresponding component template: + + + +Angular replaces `currentCustomer` with the string value of the corresponding component property. In this case, the value is `Maria`. + +In the following example, Angular evaluates the `title` and `itemImageUrl` properties to display some title text and an image. + + + +## What's Next + + + + + diff --git a/adev-ja/src/content/guide/templates/let-template-variables.md b/adev-ja/src/content/guide/templates/let-template-variables.md new file mode 100644 index 0000000000..60480e83f9 --- /dev/null +++ b/adev-ja/src/content/guide/templates/let-template-variables.md @@ -0,0 +1,104 @@ +# Local template variables + +Angular's `@let` syntax allows you to define a local variable and re-use it across the template. + +IMPORTANT: the `@let` syntax is currently in [Developer Preview](/reference/releases#developer-preview). + +## Syntax + +`@let` declarations are similar to [JavaScript's `let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let) and +their values can be any valid Angular expression. The expressions will be re-evaluated any time the +template is executed. + +```html +@let name = user.name; +@let greeting = 'Hello, ' + name; +@let data = data$ | async; +@let pi = 3.1459; +@let coordinates = {x: 50, y: 100}; +@let longExpression = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit ' + + 'sed do eiusmod tempor incididunt ut labore et dolore magna ' + + 'Ut enim ad minim veniam...'; +``` + +### Referencing the value of `@let` + +Once you've declared the `@let`, you can reuse it anywhere in the template: + +```html +@let user = user$ | async; + +@if (user) { +

Hello, {{user.name}}

+ + +
    + @for (snack of user.favoriteSnacks; track snack.id) { +
  • {{snack.name}}
  • + } +
+ + +} +``` + +## Assignability + +A key difference between `@let` and JavaScript's `let` is that `@let` cannot be re-assigned +within the template, however its value will be recomputed when Angular runs change detection. + +```html +@let value = 1; + + + +``` + +## Scope + +`@let` declarations are scoped to the current view and its descendants. Since they are not +hoisted, they **cannot** be accessed by parent views or siblings: + +```html +@let topLevel = value; + +
+ @let insideDiv = value; +
+ +{{topLevel}} +{{insideDiv}} + +@if (condition) { + {{topLevel + insideDiv}} + + @let nested = value; + + @if (condition) { + {{topLevel + insideDiv + nested}} + } +} + +
+ {{topLevel + insideDiv}} + + @let nestedNgIf = value; + +
+ {{topLevel + insideDiv + nestedNgIf}} +
+
+ +{{nested}} +{{nestedNgIf}} +``` + +## Syntax definition + +The `@let` syntax is formally defined as: +* The `@let` keyword. +* Followed by one or more whitespaces, not including new lines. +* Followed by a valid JavaScript name and zero or more whitespaces. +* Followed by the = symbol and zero or more whitespaces. +* Followed by an Angular expression which can be multi-line. +* Terminated by the `;` symbol. diff --git a/adev-ja/src/content/guide/templates/overview.md b/adev-ja/src/content/guide/templates/overview.md new file mode 100644 index 0000000000..427d51c883 --- /dev/null +++ b/adev-ja/src/content/guide/templates/overview.md @@ -0,0 +1,50 @@ + +In Angular, a *template* is a chunk of HTML. +Use special syntax within a template to build on many of Angular's features. + + +Tip: Check out Angular's [Essentials](essentials/rendering-dynamic-templates) before diving into this comprehensive guide. + + + + +Each Angular template in your application is a section of HTML to include as a part of the page that the browser displays. +An Angular HTML template renders a view, or user interface, in the browser, just like regular HTML, but with a lot more functionality. + +When you generate an Angular application with the Angular CLI, the `app.component.html` file is the default template containing placeholder HTML. + +The template syntax guides show you how to control the UX/UI by coordinating data between the class and the template. + +## Empower your HTML + +Extend the HTML vocabulary of your applications with special Angular syntax in your templates. +For example, Angular helps you get and set DOM \(Document Object Model\) values dynamically with features such as built-in template functions, variables, event listening, and data binding. + +Almost all HTML syntax is valid template syntax. +However, because an Angular template is part of an overall webpage, and not the entire page, you don't need to include elements such as ``, ``, or ``, and can focus exclusively on the part of the page you are developing. + +IMPORTANT: To eliminate the risk of script injection attacks, Angular does not support the ` + + + + +
diff --git a/adev-ja/src/content/tools/cli/aot-compiler.md b/adev-ja/src/content/tools/cli/aot-compiler.md new file mode 100644 index 0000000000..cda07153b8 --- /dev/null +++ b/adev-ja/src/content/tools/cli/aot-compiler.md @@ -0,0 +1,513 @@ +# Ahead-of-time (AOT) compilation + +An Angular application consists mainly of components and their HTML templates. +Because the components and templates provided by Angular cannot be understood by the browser directly, Angular applications require a compilation process before they can run in a browser. + +The Angular ahead-of-time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase *before* the browser downloads and runs that code. +Compiling your application during the build process provides a faster rendering in the browser. + +This guide explains how to specify metadata and apply available compiler options to compile your applications efficiently using the AOT compiler. + +HELPFUL: [Watch Alex Rickabaugh explain the Angular compiler](https://www.youtube.com/watch?v=anphffaCZrQ) at AngularConnect 2019. + +Here are some reasons you might want to use AOT. + +| Reasons | Details | +|:--- |:--- | +| Faster rendering | With AOT, the browser downloads a pre-compiled version of the application. The browser loads executable code so it can render the application immediately, without waiting to compile the application first. | +| Fewer asynchronous requests | The compiler *inlines* external HTML templates and CSS style sheets within the application JavaScript, eliminating separate ajax requests for those source files. | +| Smaller Angular framework download size | There's no need to download the Angular compiler if the application is already compiled. The compiler is roughly half of Angular itself, so omitting it dramatically reduces the application payload. | +| Detect template errors earlier | The AOT compiler detects and reports template binding errors during the build step before users can see them. | +| Better security | AOT compiles HTML templates and components into JavaScript files long before they are served to the client. With no templates to read and no risky client-side HTML or JavaScript evaluation, there are fewer opportunities for injection attacks. | + +## Choosing a compiler + +Angular offers two ways to compile your application: + +| Angular compile | Details | +|:--- |:--- | +| Just-in-Time \(JIT\) | Compiles your application in the browser at runtime. This was the default until Angular 8. | +| Ahead-of-Time \(AOT\) | Compiles your application and libraries at build time. This is the default starting in Angular 9. | + +When you run the [`ng build`](cli/build) \(build only\) or [`ng serve`](cli/serve) \(build and serve locally\) CLI commands, the type of compilation \(JIT or AOT\) depends on the value of the `aot` property in your build configuration specified in `angular.json`. +By default, `aot` is set to `true` for new CLI applications. + +See the [CLI command reference](cli) and [Building and serving Angular apps](tools/cli/build) for more information. + +## How AOT works + +The Angular AOT compiler extracts **metadata** to interpret the parts of the application that Angular is supposed to manage. +You can specify the metadata explicitly in **decorators** such as `@Component()` and `@Input()`, or implicitly in the constructor declarations of the decorated classes. +The metadata tells Angular how to construct instances of your application classes and interact with them at runtime. + +In the following example, the `@Component()` metadata object and the class constructor tell Angular how to create and display an instance of `TypicalComponent`. + + + +@Component({ + selector: 'app-typical', + template: '
A typical component for {{data.name}}
' +}) +export class TypicalComponent { + @Input() data: TypicalData; + constructor(private someService: SomeService) { … } +} + +
+ +The Angular compiler extracts the metadata *once* and generates a *factory* for `TypicalComponent`. +When it needs to create a `TypicalComponent` instance, Angular calls the factory, which produces a new visual element, bound to a new instance of the component class with its injected dependency. + +### Compilation phases + +There are three phases of AOT compilation. + +| | Phase | Details | +|:--- |:--- |:--- | +| 1 | code analysis | In this phase, the TypeScript compiler and *AOT collector* create a representation of the source. The collector does not attempt to interpret the metadata it collects. It represents the metadata as best it can and records errors when it detects a metadata syntax violation. | +| 2 | code generation | In this phase, the compiler's `StaticReflector` interprets the metadata collected in phase 1, performs additional validation of the metadata, and throws an error if it detects a metadata restriction violation. | +| 3 | template type checking | In this optional phase, the Angular *template compiler* uses the TypeScript compiler to validate the binding expressions in templates. You can enable this phase explicitly by setting the `strictTemplates` configuration option; see [Angular compiler options](reference/configs/angular-compiler-options). | + +### Metadata restrictions + +You write metadata in a *subset* of TypeScript that must conform to the following general constraints: + +* Limit [expression syntax](#expression-syntax-limitations) to the supported subset of JavaScript +* Only reference exported symbols after [code folding](#code-folding) +* Only call [functions supported](#supported-classes-and-functions) by the compiler +* Input/Outputs and data-bound class members must be public or protected.For additional guidelines and instructions on preparing an application for AOT compilation, see [Angular: Writing AOT-friendly applications](https://medium.com/sparkles-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f). + +HELPFUL: Errors in AOT compilation commonly occur because of metadata that does not conform to the compiler's requirements \(as described more fully below\). +For help in understanding and resolving these problems, see [AOT Metadata Errors](tools/cli/aot-metadata-errors). + +### Configuring AOT compilation + +You can provide options in the [TypeScript configuration file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) that controls the compilation process. +See [Angular compiler options](reference/configs/angular-compiler-options) for a complete list of available options. + +## Phase 1: Code analysis + +The TypeScript compiler does some of the analytic work of the first phase. +It emits the `.d.ts` *type definition files* with type information that the AOT compiler needs to generate application code. +At the same time, the AOT **collector** analyzes the metadata recorded in the Angular decorators and outputs metadata information in **`.metadata.json`** files, one per `.d.ts` file. + +You can think of `.metadata.json` as a diagram of the overall structure of a decorator's metadata, represented as an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree). + +HELPFUL: Angular's [schema.ts](https://github.com/angular/angular/blob/main/packages/compiler-cli/src/metadata/schema.ts) describes the JSON format as a collection of TypeScript interfaces. + +### Expression syntax limitations + +The AOT collector only understands a subset of JavaScript. +Define metadata objects with the following limited syntax: + +| Syntax | Example | +|:--- |:--- | +| Literal object | `{cherry: true, apple: true, mincemeat: false}` | +| Literal array | `['cherries', 'flour', 'sugar']` | +| Spread in literal array | `['apples', 'flour', …]` | +| Calls | `bake(ingredients)` | +| New | `new Oven()` | +| Property access | `pie.slice` | +| Array index | `ingredients[0]` | +| Identity reference | `Component` | +| A template string | `pie is ${multiplier} times better than cake` | +| Literal string | `'pi'` | +| Literal number | `3.14153265` | +| Literal boolean | `true` | +| Literal null | `null` | +| Supported prefix operator | `!cake` | +| Supported binary operator | `a+b` | +| Conditional operator | `a ? b : c` | +| Parentheses | `(a+b)` | + +If an expression uses unsupported syntax, the collector writes an error node to the `.metadata.json` file. +The compiler later reports the error if it needs that piece of metadata to generate the application code. + +HELPFUL: If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in the TypeScript configuration file. + + + +"angularCompilerOptions": { + … + "strictMetadataEmit" : true +} + + + +Angular libraries have this option to ensure that all Angular `.metadata.json` files are clean and it is a best practice to do the same when building your own libraries. + +### No arrow functions + +The AOT compiler does not support [function expressions](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/function) +and [arrow functions](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions), also called *lambda* functions. + +Consider the following component decorator: + + + +@Component({ + … + providers: [{provide: server, useFactory: () => new Server()}] +}) + + + +The AOT collector does not support the arrow function, `() => new Server()`, in a metadata expression. +It generates an error node in place of the function. +When the compiler later interprets this node, it reports an error that invites you to turn the arrow function into an *exported function*. + +You can fix the error by converting to this: + + + +export function serverFactory() { + return new Server(); +} + +@Component({ + … + providers: [{provide: server, useFactory: serverFactory}] +}) + + + +In version 5 and later, the compiler automatically performs this rewriting while emitting the `.js` file. + +### Code folding + +The compiler can only resolve references to ***exported*** symbols. +The collector, however, can evaluate an expression during collection and record the result in the `.metadata.json`, rather than the original expression. +This allows you to make limited use of non-exported symbols within expressions. + +For example, the collector can evaluate the expression `1 + 2 + 3 + 4` and replace it with the result, `10`. +This process is called *folding*. +An expression that can be reduced in this manner is *foldable*. + +The collector can evaluate references to module-local `const` declarations and initialized `var` and `let` declarations, effectively removing them from the `.metadata.json` file. + +Consider the following component definition: + + + +const template = '
{{hero.name}}
'; + +@Component({ + selector: 'app-hero', + template: template +}) +export class HeroComponent { + @Input() hero: Hero; +} + +
+ +The compiler could not refer to the `template` constant because it isn't exported. +The collector, however, can fold the `template` constant into the metadata definition by in-lining its contents. +The effect is the same as if you had written: + + + +@Component({ + selector: 'app-hero', + template: '
{{hero.name}}
' +}) +export class HeroComponent { + @Input() hero: Hero; +} + +
+ +There is no longer a reference to `template` and, therefore, nothing to trouble the compiler when it later interprets the *collector's* output in `.metadata.json`. + +You can take this example a step further by including the `template` constant in another expression: + + + +const template = '
{{hero.name}}
'; + +@Component({ + selector: 'app-hero', + template: template + '
{{hero.title}}
' +}) +export class HeroComponent { + @Input() hero: Hero; +} + +
+ +The collector reduces this expression to its equivalent *folded* string: + + + +'
{{hero.name}}
{{hero.title}}
' + +
+ +#### Foldable syntax + +The following table describes which expressions the collector can and cannot fold: + +| Syntax | Foldable | +|:--- |:--- | +| Literal object | yes | +| Literal array | yes | +| Spread in literal array | no | +| Calls | no | +| New | no | +| Property access | yes, if target is foldable | +| Array index | yes, if target and index are foldable | +| Identity reference | yes, if it is a reference to a local | +| A template with no substitutions | yes | +| A template with substitutions | yes, if the substitutions are foldable | +| Literal string | yes | +| Literal number | yes | +| Literal boolean | yes | +| Literal null | yes | +| Supported prefix operator | yes, if operand is foldable | +| Supported binary operator | yes, if both left and right are foldable | +| Conditional operator | yes, if condition is foldable | +| Parentheses | yes, if the expression is foldable | + +If an expression is not foldable, the collector writes it to `.metadata.json` as an [AST](https://en.wikipedia.org/wiki/Abstract*syntax*tree) for the compiler to resolve. + +## Phase 2: code generation + +The collector makes no attempt to understand the metadata that it collects and outputs to `.metadata.json`. +It represents the metadata as best it can and records errors when it detects a metadata syntax violation. +It's the compiler's job to interpret the `.metadata.json` in the code generation phase. + +The compiler understands all syntax forms that the collector supports, but it may reject *syntactically* correct metadata if the *semantics* violate compiler rules. + +### Public or protected symbols + +The compiler can only reference *exported symbols*. + +* Decorated component class members must be public or protected. + You cannot make an `@Input()` property private. + +* Data bound properties must also be public or protected + +### Supported classes and functions + +The collector can represent a function call or object creation with `new` as long as the syntax is valid. +The compiler, however, can later refuse to generate a call to a *particular* function or creation of a *particular* object. + +The compiler can only create instances of certain classes, supports only core decorators, and only supports calls to macros \(functions or static methods\) that return expressions. + +| Compiler action | Details | +|:--- |:--- | +| New instances | The compiler only allows metadata that create instances of the class `InjectionToken` from `@angular/core`. | +| Supported decorators | The compiler only supports metadata for the [Angular decorators in the `@angular/core` module](api/core#decorators). | +| Function calls | Factory functions must be exported, named functions. The AOT compiler does not support lambda expressions \("arrow functions"\) for factory functions. | + +### Functions and static method calls + +The collector accepts any function or static method that contains a single `return` statement. +The compiler, however, only supports macros in the form of functions or static methods that return an *expression*. + +For example, consider the following function: + + + +export function wrapInArray(value: T): T[] { + return [value]; +} + + + +You can call the `wrapInArray` in a metadata definition because it returns the value of an expression that conforms to the compiler's restrictive JavaScript subset. + +You might use `wrapInArray()` like this: + + + +@NgModule({ + declarations: wrapInArray(TypicalComponent) +}) +export class TypicalModule {} + + + +The compiler treats this usage as if you had written: + + + +@NgModule({ + declarations: [TypicalComponent] +}) +export class TypicalModule {} + + + +The Angular [`RouterModule`](api/router/RouterModule) exports two macro static methods, `forRoot` and `forChild`, to help declare root and child routes. +Review the [source code](https://github.com/angular/angular/blob/main/packages/router/src/router_module.ts#L139 "RouterModule.forRoot source code") +for these methods to see how macros can simplify configuration of complex [NgModules](guide/ngmodules). + +### Metadata rewriting + +The compiler treats object literals containing the fields `useClass`, `useValue`, `useFactory`, and `data` specially, converting the expression initializing one of these fields into an exported variable that replaces the expression. +This process of rewriting these expressions removes all the restrictions on what can be in them because +the compiler doesn't need to know the expression's value — it just needs to be able to generate a reference to the value. + +You might write something like: + + + +class TypicalServer { + +} + +@NgModule({ + providers: [{provide: SERVER, useFactory: () => TypicalServer}] +}) +export class TypicalModule {} + + + +Without rewriting, this would be invalid because lambdas are not supported and `TypicalServer` is not exported. +To allow this, the compiler automatically rewrites this to something like: + + + +class TypicalServer { + +} + +export const θ0 = () => new TypicalServer(); + +@NgModule({ + providers: [{provide: SERVER, useFactory: θ0}] +}) +export class TypicalModule {} + + + +This allows the compiler to generate a reference to `θ0` in the factory without having to know what the value of `θ0` contains. + +The compiler does the rewriting during the emit of the `.js` file. +It does not, however, rewrite the `.d.ts` file, so TypeScript doesn't recognize it as being an export. +And it does not interfere with the ES module's exported API. + +## Phase 3: Template type checking + +One of the Angular compiler's most helpful features is the ability to type-check expressions within templates, and catch any errors before they cause crashes at runtime. +In the template type-checking phase, the Angular template compiler uses the TypeScript compiler to validate the binding expressions in templates. + +Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's TypeScript configuration file +(see [Angular Compiler Options](reference/configs/angular-compiler-options)). + +Template validation produces error messages when a type error is detected in a template binding +expression, similar to how type errors are reported by the TypeScript compiler against code in a `.ts` +file. + +For example, consider the following component: + + + +@Component({ + selector: 'my-component', + template: '{{person.addresss.street}}' +}) +class MyComponent { + person?: Person; +} + + + +This produces the following error: + + + +my.component.ts.MyComponent.html(1,1): : Property 'addresss' does not exist on type 'Person'. Did you mean 'address'? + + + +The file name reported in the error message, `my.component.ts.MyComponent.html`, is a synthetic file +generated by the template compiler that holds contents of the `MyComponent` class template. +The compiler never writes this file to disk. +The line and column numbers are relative to the template string in the `@Component` annotation of the class, `MyComponent` in this case. +If a component uses `templateUrl` instead of `template`, the errors are reported in the HTML file referenced by the `templateUrl` instead of a synthetic file. + +The error location is the beginning of the text node that contains the interpolation expression with the error. +If the error is in an attribute binding such as `[value]="person.address.street"`, the error +location is the location of the attribute that contains the error. + +The validation uses the TypeScript type checker and the options supplied to the TypeScript compiler to control how detailed the type validation is. +For example, if the `strictTypeChecks` is specified, the error + + + +my.component.ts.MyComponent.html(1,1): : Object is possibly 'undefined' + + + +is reported as well as the above error message. + +### Type narrowing + +The expression used in an `ngIf` directive is used to narrow type unions in the Angular +template compiler, the same way the `if` expression does in TypeScript. +For example, to avoid `Object is possibly 'undefined'` error in the template above, modify it to only emit the interpolation if the value of `person` is initialized as shown below: + + + +@Component({ + selector: 'my-component', + template: ' {{person.address.street}} ' +}) +class MyComponent { + person?: Person; +} + + + +Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in the binding expression will never be `undefined`. + +For more information about input type narrowing, see [Improving template type checking for custom directives](guide/directives/structural-directives#directive-type-checks). + +### Non-null type assertion operator + +Use the non-null type assertion operator to suppress the `Object is possibly 'undefined'` error when it is inconvenient to use `*ngIf` or when some constraint in the component ensures that the expression is always non-null when the binding expression is interpolated. + +In the following example, the `person` and `address` properties are always set together, implying that `address` is always non-null if `person` is non-null. +There is no convenient way to describe this constraint to TypeScript and the template compiler, but the error is suppressed in the example by using `address!.street`. + + + +@Component({ + selector: 'my-component', + template: ' {{person.name}} lives on {{address!.street}} ' +}) +class MyComponent { + person?: Person; + address?: Address; + + setData(person: Person, address: Address) { + this.person = person; + this.address = address; + } +} + + + +The non-null assertion operator should be used sparingly as refactoring of the component might break this constraint. + +In this example it is recommended to include the checking of `address` in the `*ngIf` as shown below: + + + +@Component({ + selector: 'my-component', + template: ' {{person.name}} lives on {{address.street}} ' +}) +class MyComponent { + person?: Person; + address?: Address; + + setData(person: Person, address: Address) { + this.person = person; + this.address = address; + } +} + + diff --git a/adev-ja/src/content/tools/cli/aot-metadata-errors.md b/adev-ja/src/content/tools/cli/aot-metadata-errors.md new file mode 100644 index 0000000000..3d9e31a96d --- /dev/null +++ b/adev-ja/src/content/tools/cli/aot-metadata-errors.md @@ -0,0 +1,481 @@ +# AOT metadata errors + +The following are metadata errors you may encounter, with explanations and suggested corrections. + +## Expression form not supported + +HELPFUL: The compiler encountered an expression it didn't understand while evaluating Angular metadata. + +Language features outside of the compiler's [restricted expression syntax](tools/cli/aot-compiler#expression-syntax) +can produce this error, as seen in the following example: + + +// ERROR +export class Fooish { … } +… +const prop = typeof Fooish; // typeof is not valid in metadata + … + // bracket notation is not valid in metadata + { provide: 'token', useValue: { [prop]: 'value' } }; + … + + +You can use `typeof` and bracket notation in normal application code. +You just can't use those features within expressions that define Angular metadata. + +Avoid this error by sticking to the compiler's [restricted expression syntax](tools/cli/aot-compiler#expression-syntax) +when writing Angular metadata +and be wary of new or unusual TypeScript features. + +## Reference to a local (non-exported) symbol + +HELPFUL: Reference to a local \(non-exported\) symbol 'symbol name'. Consider exporting the symbol. + +The compiler encountered a reference to a locally defined symbol that either wasn't exported or wasn't initialized. + +Here's a `provider` example of the problem. + + + +// ERROR +let foo: number; // neither exported nor initialized + +@Component({ + selector: 'my-component', + template: … , + providers: [ + { provide: Foo, useValue: foo } + ] +}) +export class MyComponent {} + + + +The compiler generates the component factory, which includes the `useValue` provider code, in a separate module. *That* factory module can't reach back to *this* source module to access the local \(non-exported\) `foo` variable. + +You could fix the problem by initializing `foo`. + + +let foo = 42; // initialized + + +The compiler will [fold](tools/cli/aot-compiler#code-folding) the expression into the provider as if you had written this. + + +providers: [ + { provide: Foo, useValue: 42 } +] + + +Alternatively, you can fix it by exporting `foo` with the expectation that `foo` will be assigned at runtime when you actually know its value. + + +// CORRECTED +export let foo: number; // exported + +@Component({ + selector: 'my-component', + template: … , + providers: [ + { provide: Foo, useValue: foo } + ] +}) +export class MyComponent {} + + +Adding `export` often works for variables referenced in metadata such as `providers` and `animations` because the compiler can generate *references* to the exported variables in these expressions. It doesn't need the *values* of those variables. + +Adding `export` doesn't work when the compiler needs the *actual value* +in order to generate code. +For example, it doesn't work for the `template` property. + + + +// ERROR +export let someTemplate: string; // exported but not initialized + +@Component({ + selector: 'my-component', + template: someTemplate +}) +export class MyComponent {} + + + +The compiler needs the value of the `template` property *right now* to generate the component factory. +The variable reference alone is insufficient. +Prefixing the declaration with `export` merely produces a new error, "[`Only initialized variables and constants can be referenced`](#only-initialized-variables)". + +## Only initialized variables and constants + +HELPFUL: *Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler.* + +The compiler found a reference to an exported variable or static field that wasn't initialized. +It needs the value of that variable to generate code. + +The following example tries to set the component's `template` property to the value of the exported `someTemplate` variable which is declared but *unassigned*. + + + +// ERROR +export let someTemplate: string; + +@Component({ + selector: 'my-component', + template: someTemplate +}) +export class MyComponent {} + + + +You'd also get this error if you imported `someTemplate` from some other module and neglected to initialize it there. + + + +// ERROR - not initialized there either +import { someTemplate } from './config'; + +@Component({ + selector: 'my-component', + template: someTemplate +}) +export class MyComponent {} + + + +The compiler cannot wait until runtime to get the template information. +It must statically derive the value of the `someTemplate` variable from the source code so that it can generate the component factory, which includes instructions for building the element based on the template. + +To correct this error, provide the initial value of the variable in an initializer clause *on the same line*. + + + +// CORRECTED +export let someTemplate = '

Greetings from Angular

'; + +@Component({ + selector: 'my-component', + template: someTemplate +}) +export class MyComponent {} + +
+ +## Reference to a non-exported class + +HELPFUL: *Reference to a non-exported class ``.* +*Consider exporting the class.* + +Metadata referenced a class that wasn't exported. + +For example, you may have defined a class and used it as an injection token in a providers array but neglected to export that class. + + + +// ERROR +abstract class MyStrategy { } + + … + providers: [ + { provide: MyStrategy, useValue: … } + ] + … + + + +Angular generates a class factory in a separate module and that factory [can only access exported classes](tools/cli/aot-compiler#exported-symbols). +To correct this error, export the referenced class. + + + +// CORRECTED +export abstract class MyStrategy { } + + … + providers: [ + { provide: MyStrategy, useValue: … } + ] + … + + + +## Reference to a non-exported function + +HELPFUL: *Metadata referenced a function that wasn't exported.* + +For example, you may have set a providers `useFactory` property to a locally defined function that you neglected to export. + + + +// ERROR +function myStrategy() { … } + + … + providers: [ + { provide: MyStrategy, useFactory: myStrategy } + ] + … + + + +Angular generates a class factory in a separate module and that factory [can only access exported functions](tools/cli/aot-compiler#exported-symbols). +To correct this error, export the function. + + + +// CORRECTED +export function myStrategy() { … } + + … + providers: [ + { provide: MyStrategy, useFactory: myStrategy } + ] + … + + + +## Function calls are not supported + +HELPFUL: *Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function.* + +The compiler does not currently support [function expressions or lambda functions](tools/cli/aot-compiler#function-expression). +For example, you cannot set a provider's `useFactory` to an anonymous function or arrow function like this. + + + +// ERROR + … + providers: [ + { provide: MyStrategy, useFactory: function() { … } }, + { provide: OtherStrategy, useFactory: () => { … } } + ] + … + + + +You also get this error if you call a function or method in a provider's `useValue`. + + + +// ERROR +import { calculateValue } from './utilities'; + + … + providers: [ + { provide: SomeValue, useValue: calculateValue() } + ] + … + + + +To correct this error, export a function from the module and refer to the function in a `useFactory` provider instead. + + + +// CORRECTED +import { calculateValue } from './utilities'; + +export function myStrategy() { … } +export function otherStrategy() { … } +export function someValueFactory() { + return calculateValue(); +} + … + providers: [ + { provide: MyStrategy, useFactory: myStrategy }, + { provide: OtherStrategy, useFactory: otherStrategy }, + { provide: SomeValue, useFactory: someValueFactory } + ] + … + + + +## Destructured variable or constant not supported + +HELPFUL: *Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring.* + +The compiler does not support references to variables assigned by [destructuring](https://www.typescriptlang.org/docs/handbook/variable-declarations.html#destructuring). + +For example, you cannot write something like this: + + + +// ERROR +import { configuration } from './configuration'; + +// destructured assignment to foo and bar +const {foo, bar} = configuration; + … + providers: [ + {provide: Foo, useValue: foo}, + {provide: Bar, useValue: bar}, + ] + … + + + +To correct this error, refer to non-destructured values. + + + +// CORRECTED +import { configuration } from './configuration'; + … + providers: [ + {provide: Foo, useValue: configuration.foo}, + {provide: Bar, useValue: configuration.bar}, + ] + … + + + +## Could not resolve type + +HELPFUL: *The compiler encountered a type and can't determine which module exports that type.* + +This can happen if you refer to an ambient type. +For example, the `Window` type is an ambient type declared in the global `.d.ts` file. + +You'll get an error if you reference it in the component constructor, which the compiler must statically analyze. + + + +// ERROR +@Component({ }) +export class MyComponent { + constructor (private win: Window) { … } +} + + + +TypeScript understands ambient types so you don't import them. +The Angular compiler does not understand a type that you neglect to export or import. + +In this case, the compiler doesn't understand how to inject something with the `Window` token. + +Do not refer to ambient types in metadata expressions. + +If you must inject an instance of an ambient type, +you can finesse the problem in four steps: + +1. Create an injection token for an instance of the ambient type. +1. Create a factory function that returns that instance. +1. Add a `useFactory` provider with that factory function. +1. Use `@Inject` to inject the instance. + +Here's an illustrative example. + + + +// CORRECTED +import { Inject } from '@angular/core'; + +export const WINDOW = new InjectionToken('Window'); +export function _window() { return window; } + +@Component({ + … + providers: [ + { provide: WINDOW, useFactory: _window } + ] +}) +export class MyComponent { + constructor (@Inject(WINDOW) private win: Window) { … } +} + + + +The `Window` type in the constructor is no longer a problem for the compiler because it +uses the `@Inject(WINDOW)` to generate the injection code. + +Angular does something similar with the `DOCUMENT` token so you can inject the browser's `document` object \(or an abstraction of it, depending upon the platform in which the application runs\). + + + +import { Inject } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; + +@Component({ … }) +export class MyComponent { + constructor (@Inject(DOCUMENT) private doc: Document) { … } +} + + + +## Name expected + +HELPFUL: *The compiler expected a name in an expression it was evaluating.* + +This can happen if you use a number as a property name as in the following example. + + + +// ERROR +provider: [{ provide: Foo, useValue: { 0: 'test' } }] + + + +Change the name of the property to something non-numeric. + + + +// CORRECTED +provider: [{ provide: Foo, useValue: { '0': 'test' } }] + + + +## Unsupported enum member name + +HELPFUL: *Angular couldn't determine the value of the [enum member](https://www.typescriptlang.org/docs/handbook/enums.html) that you referenced in metadata.* + +The compiler can understand simple enum values but not complex values such as those derived from computed properties. + + + +// ERROR +enum Colors { + Red = 1, + White, + Blue = "Blue".length // computed +} + + … + providers: [ + { provide: BaseColor, useValue: Colors.White } // ok + { provide: DangerColor, useValue: Colors.Red } // ok + { provide: StrongColor, useValue: Colors.Blue } // bad + ] + … + + + +Avoid referring to enums with complicated initializers or computed properties. + +## Tagged template expressions are not supported + +HELPFUL: *Tagged template expressions are not supported in metadata.* + +The compiler encountered a JavaScript ES2015 [tagged template expression](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) such as the following. + + + +// ERROR +const expression = 'funky'; +const raw = String.raw`A tagged template ${expression} string`; + … + template: '
' + raw + '
' + … + +
+ +[`String.raw()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/raw) is a *tag function* native to JavaScript ES2015. + +The AOT compiler does not support tagged template expressions; avoid them in metadata expressions. + +## Symbol reference expected + +HELPFUL: *The compiler expected a reference to a symbol at the location specified in the error message.* + +This error can occur if you use an expression in the `extends` clause of a class. + + \ No newline at end of file diff --git a/adev-ja/src/content/tools/cli/build-system-migration.md b/adev-ja/src/content/tools/cli/build-system-migration.md new file mode 100644 index 0000000000..a823f8edd6 --- /dev/null +++ b/adev-ja/src/content/tools/cli/build-system-migration.md @@ -0,0 +1,261 @@ +# Migrating to the new build system + +In v17 and higher, the new build system provides an improved way to build Angular applications. This new build system includes: + +- A modern output format using ESM, with dynamic import expressions to support lazy module loading. +- Faster build-time performance for both initial builds and incremental rebuilds. +- Newer JavaScript ecosystem tools such as [esbuild](https://esbuild.github.io/) and [Vite](https://vitejs.dev/). +- Integrated SSR and prerendering capabilities. + +This new build system is stable and fully supported for use with Angular applications. +You can migrate to the new build system with applications that use the `browser` builder. +If using a custom builder, please refer to the documentation for that builder on possible migration options. + +IMPORTANT: The existing Webpack-based build system is still considered stable and fully supported. +Applications can continue to use the `browser` builder and will not be automatically migrated when updating. + +## For new applications + +New applications will use this new build system by default via the `application` builder. + +## For existing applications + +Both automated and manual procedures are available dependening on the requirements of the project. +Starting with v18, the update process will ask if you would like to migrate existing applications to use the new build system via the automated migration. + +HELPFUL: Remember to remove any CommonJS assumptions in the application server code if using SSR such as `require`, `__filename`, `__dirname`, or other constructs from the [CommonJS module scope](https://nodejs.org/api/modules.html#the-module-scope). All application code should be ESM compatible. This does not apply to third-party dependencies. + +### Automated migration (Recommended) + +The automated migration will adjust both the application configuration within `angular.json` as well as code and stylesheets to remove previous Webpack-specific feature usage. +While many changes can be automated and most applications will not require any further changes, each application is unique and there may be some manual changes required. +After the migration, please attempt a build of the application as there could be new errors that will require adjustments within the code. +The errors will attempt to provide solutions to the problem when possible and the later sections of this guide describe some of the more common situations that you may encounter. +When updating to Angular v18 via `ng update`, you will be asked to execute the migration. +This migration is entirely optional for v18 and can also be run manually at anytime after an update via the following command: + + + +ng update @angular/cli --name use-application-builder + + + +The migration does the following: + +* Converts existing `browser` or `browser-esbuild` target to `application` +* Removes any previous SSR builders (because `application` does that now). +* Updates configuration accordingly. +* Merges `tsconfig.server.json` with `tsconfig.app.json` and adds the TypeScript option `"esModuleInterop": true` to ensure `express` imports are [ESM compliant](#esm-default-imports-vs-namespace-imports). +* Updates application server code to use new bootstrapping and output directory structure. +* Removes any Webpack-specific builder stylesheet usage such as the tilde or caret in `@import`/`url()` and updates the configuration to provide equivalent behavior +* Converts to use the new lower dependency `@angular/build` Node.js package if no other `@angular-devkit/build-angular` usage is found. + +### Manual migration + +Additionally for existing projects, you can manually opt-in to use the new builder on a per-application basis with two different options. +Both options are considered stable and fully supported by the Angular team. +The choice of which option to use is a factor of how many changes you will need to make to migrate and what new features you would like to use in the project. + +- The `browser-esbuild` builder builds only the client-side bundle of an application designed to be compatible with the existing `browser` builder that provides the preexisting build system. It serves as a drop-in replacement for existing `browser` applications. +- The `application` builder covers an entire application, such as the client-side bundle, as well as optionally building a server for server-side rendering and performing build-time prerendering of static pages. + +The `application` builder is generally preferred as it improves server-side rendered (SSR) builds, and makes it easier for client-side rendered projects to adopt SSR in the future. +However it requires a little more migration effort, particularly for existing SSR applications if performed manually. +If the `application` builder is difficult for your project to adopt, `browser-esbuild` can be an easier solution which gives most of the build performance benefits with fewer breaking changes. + +#### Manual migration to the compatibility builder + +A builder named `browser-esbuild` is available within the `@angular-devkit/build-angular` package that is present in an Angular CLI generated application. +You can try out the new build system for applications that use the `browser` builder. +If using a custom builder, please refer to the documentation for that builder on possible migration options. + +The compatibility option was implemented to minimize the amount of changes necessary to initially migrate your applications. +This is provided via an alternate builder (`browser-esbuild`). +You can update the `build` target for any application target to migrate to the new build system. + +The following is what you would typically find in `angular.json` for an application: + + +... +"architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", +... + + +Changing the `builder` field is the only change you will need to make. + + +... +"architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser-esbuild", +... + + +#### Manual migration to the new `application` builder + +A builder named `application` is also available within the `@angular-devkit/build-angular` package that is present in an Angular CLI generated application. +This builder is the default for all new applications created via `ng new`. + +The following is what you would typically find in `angular.json` for an application: + + +... +"architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", +... + + +Changing the `builder` field is the first change you will need to make. + + +... +"architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", +... + + +Once the builder name has been changed, options within the `build` target will need to be updated. +The following list discusses all the `browser` builder options that will need to be adjusted. + +- `main` should be renamed to `browser`. +- `polyfills` should be an array, rather than a single file. +- `buildOptimizer` should be removed, as this is covered by the `optimization` option. +- `resourcesOutputPath` should be removed, this is now always `media`. +- `vendorChunk` should be removed, as this was a performance optimization which is no longer needed. +- `commonChunk` should be removed, as this was a performance optimization which is no longer needed. +- `deployUrl` should be removed and is not supported. Prefer [``](guide/routing/common-router-tasks) instead. See [deployment documentation](tools/cli/deployment#--deploy-url) for more information. +- `ngswConfigPath` should be renamed to `serviceWorker`. + +If the application is not using SSR currently, this should be the final step to allow `ng build` to function. +After executing `ng build` for the first time, there may be new warnings or errors based on behavioral differences or application usage of Webpack-specific features. +Many of the warnings will provide suggestions on how to remedy that problem. +If it appears that a warning is incorrect or the solution is not apparent, please open an issue on [GitHub](https://github.com/angular/angular-cli/issues). +Also, the later sections of this guide provide additional information on several specific cases as well as current known issues. + +For applications new to SSR, the [Angular SSR Guide](guide/ssr) provides additional information regarding the setup process for adding SSR to an application. + +For applications that are already using SSR, additional adjustments will be needed to update the application server to support the new integrated SSR capabilities. +The `application` builder now provides the integrated functionality for all of the following preexisting builders: + +- `app-shell` +- `prerender` +- `server` +- `ssr-dev-server` + +The `ng update` process will automatically remove usages of the `@nguniversal` scope packages where some of these builders were previously located. +The new `@angular/ssr` package will also be automatically added and used with configuration and code being adjusted during the update. +The `@angular/ssr` package supports the `browser` builder as well as the `application` builder. + +## Executing a build + +Once you have updated the application configuration, builds can be performed using `ng build` as was previously done. +Depending on the choice of builder migration, some of the command line options may be different. +If the build command is contained in any `npm` or other scripts, ensure they are reviewed and updated. +For applications that have migrated to the `application` builder and that use SSR and/or prererending, you also may be able to remove extra `ng run` commands from scripts now that `ng build` has integrated SSR support. + + + +ng build + + + +## Starting the development server + +The development server will automatically detect the new build system and use it to build the application. +To start the development server no changes are necessary to the `dev-server` builder configuration or command line. + + + +ng serve + + + +You can continue to use the [command line options](/cli/serve) you have used in the past with the development server. + +## Hot module replacement + +JavaScript-based hot module replacement (HMR) is currently not supported. +However, global stylesheet (`styles` build option) HMR is available and enabled by default. +Angular focused HMR capabilities are currently planned and will be introduced in a future version. + +## Unimplemented options and behavior + +Several build options are not yet implemented but will be added in the future as the build system moves towards a stable status. If your application uses these options, you can still try out the build system without removing them. Warnings will be issued for any unimplemented options but they will otherwise be ignored. However, if your application relies on any of these options to function, you may want to wait to try. + +- [WASM imports](https://github.com/angular/angular-cli/issues/25102) -- WASM can still be loaded manually via [standard web APIs](https://developer.mozilla.org/docs/WebAssembly/Loading_and_running). + +## ESM default imports vs. namespace imports + +TypeScript by default allows default exports to be imported as namespace imports and then used in call expressions. +This is unfortunately a divergence from the ECMAScript specification. +The underlying bundler (`esbuild`) within the new build system expects ESM code that conforms to the specification. +The build system will now generate a warning if your application uses an incorrect type of import of a package. +However, to allow TypeScript to accept the correct usage, a TypeScript option must be enabled within the application's `tsconfig` file. +When enabled, the [`esModuleInterop`](https://www.typescriptlang.org/tsconfig#esModuleInterop) option provides better alignment with the ECMAScript specification and is also recommended by the TypeScript team. +Once enabled, you can update package imports where applicable to an ECMAScript conformant form. + +Using the [`moment`](https://npmjs.com/package/moment) package as an example, the following application code will cause runtime errors: + +```ts +import * as moment from 'moment'; + +console.log(moment().format()); +``` + +The build will generate a warning to notify you that there is a potential problem. The warning will be similar to: + + +▲ [WARNING] Calling "moment" will crash at run-time because it's an import namespace object, not a function [call-import-namespace] + + src/main.ts:2:12: + 2 │ console.log(moment().format()); + ╵ ~~~~~~ + +Consider changing "moment" to a default import instead: + + src/main.ts:1:7: + 1 │ import * as moment from 'moment'; + │ ~~~~~~~~~~~ + ╵ moment + + + +However, you can avoid the runtime errors and the warning by enabling the `esModuleInterop` TypeScript option for the application and changing the import to the following: + +```ts +import moment from 'moment'; + +console.log(moment().format()); +``` + +## Vite as a development server + +The usage of Vite in the Angular CLI is currently only within a _development server capacity only_. Even without using the underlying Vite build system, Vite provides a full-featured development server with client side support that has been bundled into a low dependency npm package. This makes it an ideal candidate to provide comprehensive development server functionality. The current development server process uses the new build system to generate a development build of the application in memory and passes the results to Vite to serve the application. The usage of Vite, much like the Webpack-based development server, is encapsulated within the Angular CLI `dev-server` builder and currently cannot be directly configured. + +## Known Issues + +There are currently several known issues that you may encounter when trying the new build system. This list will be updated to stay current. If any of these issues are currently blocking you from trying out the new build system, please check back in the future as it may have been solved. + +### Type-checking of Web Worker code and processing of nested Web Workers + +Web Workers can be used within application code using the same syntax (`new Worker(new URL('', import.meta.url))`) that is supported with the `browser` builder. +However, the code within the Worker will not currently be type-checked by the TypeScript compiler. TypeScript code is supported just not type-checked. +Additionally, any nested workers will not be processed by the build system. A nested worker is a Worker instantiation within another Worker file. + +### Order-dependent side-effectful imports in lazy modules + +Import statements that are dependent on a specific ordering and are also used in multiple lazy modules can cause top-level statements to be executed out of order. +This is not common as it depends on the usage of side-effectful modules and does not apply to the `polyfills` option. +This is caused by a [defect](https://github.com/evanw/esbuild/issues/399) in the underlying bundler but will be addressed in a future update. + +IMPORTANT: Avoiding the use of modules with non-local side effects (outside of polyfills) is recommended whenever possible regardless of the build system being used and avoids this particular issue. Modules with non-local side effects can have a negative effect on both application size and runtime performance as well. + +## Bug reports + +Report issues and feature requests on [GitHub](https://github.com/angular/angular-cli/issues). + +Please provide a minimal reproduction where possible to aid the team in addressing issues. diff --git a/adev-ja/src/content/tools/cli/build.md b/adev-ja/src/content/tools/cli/build.md new file mode 100644 index 0000000000..f3a75945fe --- /dev/null +++ b/adev-ja/src/content/tools/cli/build.md @@ -0,0 +1,154 @@ +# Building Angular apps + +You can build your Angular CLI application or library with the `ng build` command. +This will compile your TypeScript code to JavaScript, as well as optimize, bundle, and minify the output as appropriate. + +`ng build` only executes the builder for the `build` target in the default project as specified in `angular.json`. +Angular CLI includes four builders typically used as `build` targets: + +| Builder | Purpose | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `@angular-devkit/build-angular:browser` | Bundles a client-side application for use in a browser with [Webpack](https://webpack.js.org/). | +| `@angular-devkit/build-angular:browser-esbuild` | Bundles a client-side application for use in a browser with [esbuild](https://esbuild.github.io/). See [`browser-esbuild` documentation](tools/cli/build-system-migration#manual-migration-to-the-compatibility-builder) for more information. | +| `@angular-devkit/build-angular:application` | Builds an application with a client-side bundle, a Node server, and build-time prerendered routes with [esbuild](https://esbuild.github.io/). | +| `@angular-devkit/build-angular:ng-packagr` | Builds an Angular library adhering to [Angular Package Format](tools/libraries/angular-package-format). | + +Applications generated by `ng new` use `@angular-devkit/build-angular:application` by default. +Libraries generated by `ng generate library` use `@angular-devkit/build-angular:ng-packagr` by default. + +You can determine which builder is being used for a particular project by looking up the `build` target for that project. + + + +{ + "projects": { + "my-app": { + "architect": { + // `ng build` invokes the Architect target named `build`. + "build": { + "builder": "@angular-devkit/build-angular:application", + … + }, + "serve": { … } + "test": { … } + … + } + } + } +} + + + +This page discusses usage and options of `@angular-devkit/build-angular:application`. + +## Output directory + +The result of this build process is output to a directory (`dist/${PROJECT_NAME}` by default). + +## Configuring size budgets + +As applications grow in functionality, they also grow in size. +The CLI lets you set size thresholds in your configuration to ensure that parts of your application stay within size boundaries that you define. + +Define your size boundaries in the CLI configuration file, `angular.json`, in a `budgets` section for each [configured environment](tools/cli/environments). + + + +{ + … + "configurations": { + "production": { + … + "budgets": [ + { + "type": "initial", + "maximumWarning": "250kb", + "maximumError": "500kb" + }, + ] + } + } +} + + + +You can specify size budgets for the entire app, and for particular parts. +Each budget entry configures a budget of a given type. +Specify size values in the following formats: + +| Size value | Details | +| :-------------- | :-------------------------------------------------------------------------- | +| `123` or `123b` | Size in bytes. | +| `123kb` | Size in kilobytes. | +| `123mb` | Size in megabytes. | +| `12%` | Percentage of size relative to baseline. \(Not valid for baseline values.\) | + +When you configure a budget, the builder warns or reports an error when a given part of the application reaches or exceeds a boundary size that you set. + +Each budget entry is a JSON object with the following properties: + +| Property | Value | +| :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| type | The type of budget. One of:
Value Details
bundle The size of a specific bundle.
initial The size of JavaScript needed for bootstrapping the application. Defaults to warning at 500kb and erroring at 1mb.
allScript The size of all scripts.
all The size of the entire application.
anyComponentStyle This size of any one component stylesheet. Defaults to warning at 2kb and erroring at 4kb.
anyScript The size of any one script.
any The size of any file.
| +| name | The name of the bundle (for `type=bundle`). | +| baseline | The baseline size for comparison. | +| maximumWarning | The maximum threshold for warning relative to the baseline. | +| maximumError | The maximum threshold for error relative to the baseline. | +| minimumWarning | The minimum threshold for warning relative to the baseline. | +| minimumError | The minimum threshold for error relative to the baseline. | +| warning | The threshold for warning relative to the baseline (min & max). | +| error | The threshold for error relative to the baseline (min & max). | + +## Configuring CommonJS dependencies + +Always prefer native [ECMAScript modules](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import) (ESM) throughout your application and its dependencies. +ESM is a fully specified web standard and JavaScript language feature with strong static analysis support. This makes bundle optimizations more powerful than other module formats. + +Angular CLI also supports importing [CommonJS](https://nodejs.org/api/modules.html) dependencies into your project and will bundle these dependencies automatically. +However, CommonJS modules can prevent bundlers and minifiers from optimizing those modules effectively, which results in larger bundle sizes. +For more information, see [How CommonJS is making your bundles larger](https://web.dev/commonjs-larger-bundles). + +Angular CLI outputs warnings if it detects that your browser application depends on CommonJS modules. +When you encounter a CommonJS dependency, consider asking the maintainer to support ECMAScript modules, contributing that support yourself, or using an alternative dependency which meets your needs. +If the best option is to use a CommonJS dependency, you can disable these warnings by adding the CommonJS module name to `allowedCommonJsDependencies` option in the `build` options located in `angular.json`. + + + +"build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "allowedCommonJsDependencies": [ + "lodash" + ] + … + } + … +}, + + + +## Configuring browser compatibility + +The Angular CLI uses [Browserslist](https://github.com/browserslist/browserslist) to ensure compatibility with different browser versions. +Depending on supported browsers, Angular will automatically transform certain JavaScript and CSS features to ensure the built application does not use a feature which has not been implemented by a supported browser. However, the Angular CLI will not automatically add polyfills to supplement missing Web APIs. Use the `polyfills` option in `angular.json` to add polyfills. + +Internally, the Angular CLI uses the below default `browserslist` configuration which matches the [browsers that are supported](reference/versions#browser-support) by Angular. + + + +last 2 Chrome versions +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR + + + +To override the internal configuration, run [`ng generate config browserslist`](cli/generate#config-command), which generates a `.browserslistrc` configuration file in the project directory. + +See the [browserslist repository](https://github.com/browserslist/browserslist) for more examples of how to target specific browsers and versions. +Avoid expanding this list to more browsers. Even if your application code more broadly compatible, Angular itself might not be. +You should only ever _reduce_ the set of browsers or versions in this list. + +HELPFUL: Use [browsersl.ist](https://browsersl.ist) to display compatible browsers for a `browserslist` query. diff --git a/adev-ja/src/content/tools/cli/cli-builder.md b/adev-ja/src/content/tools/cli/cli-builder.md new file mode 100644 index 0000000000..33e18ef021 --- /dev/null +++ b/adev-ja/src/content/tools/cli/cli-builder.md @@ -0,0 +1,407 @@ +# Angular CLI builders + +A number of Angular CLI commands run a complex process on your code, such as building, testing, or serving your application. +The commands use an internal tool called Architect to run *CLI builders*, which invoke another tool (bundler, test runner, server) to accomplish the desired task. +Custom builders can perform an entirely new task, or to change which third-party tool is used by an existing command. + +This document explains how CLI builders integrate with the workspace configuration file, and shows how you can create your own builder. + +HELPFUL: Find the code from the examples used here in this [GitHub repository](https://github.com/mgechev/cli-builders-demo). + +## CLI builders + +The internal Architect tool delegates work to handler functions called *builders*. +A builder handler function receives two arguments: + +| Argument | Type | +|:--- |:--- | +| `options` | `JSONObject` | +| `context` | `BuilderContext` | + +The separation of concerns here is the same as with [schematics](tools/cli/schematics-authoring), which are used for other CLI commands that touch your code (such as `ng generate`). + +* The `options` object is provided by the CLI user's options and configuration, while the `context` object is provided by the CLI Builder API automatically. +* In addition to the contextual information, the `context` object also provides access to a scheduling method, `context.scheduleTarget()`. + The scheduler executes the builder handler function with a given target configuration. + +The builder handler function can be synchronous (return a value), asynchronous (return a `Promise`), or watch and return multiple values (return an `Observable`). +The return values must always be of type `BuilderOutput`. +This object contains a Boolean `success` field and an optional `error` field that can contain an error message. + +Angular provides some builders that are used by the CLI for commands such as `ng build` and `ng test`. +Default target configurations for these and other built-in CLI builders can be found and configured in the "architect" section of the [workspace configuration file](reference/configs/workspace-config), `angular.json`. +Also, extend and customize Angular by creating your own builders, which you can run directly using the [`ng run` CLI command](cli/run). + +### Builder project structure + +A builder resides in a "project" folder that is similar in structure to an Angular workspace, with global configuration files at the top level, and more specific configuration in a source folder with the code files that define the behavior. +For example, your `myBuilder` folder could contain the following files. + +| Files | Purpose | +|:--- | :--- | +| `src/my-builder.ts` | Main source file for the builder definition. | +| `src/my-builder.spec.ts` | Source file for tests. | +| `src/schema.json` | Definition of builder input options. | +| `builders.json` | Builders definition. | +| `package.json` | Dependencies. See [https://docs.npmjs.com/files/package.json](https://docs.npmjs.com/files/package.json). | +| `tsconfig.json` | [TypeScript configuration](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html). | + +Builders can be published to `npm`, see [Publishing your Library](tools/libraries/creating-libraries). + +## Creating a builder + +As an example, create a builder that copies a file to a new location. +To create a builder, use the `createBuilder()` CLI Builder function, and return a `Promise` object. + + + +Now let's add some logic to it. +The following code retrieves the source and destination file paths from user options and copies the file from the source to the destination \(using the [Promise version of the built-in NodeJS `copyFile()` function](https://nodejs.org/api/fs.html#fs_fspromises_copyfile_src_dest_mode)\). +If the copy operation fails, it returns an error with a message about the underlying problem. + + + +### Handling output + +By default, `copyFile()` does not print anything to the process standard output or error. +If an error occurs, it might be difficult to understand exactly what the builder was trying to do when the problem occurred. +Add some additional context by logging additional information using the `Logger` API. +This also lets the builder itself be executed in a separate process, even if the standard output and error are deactivated. + +You can retrieve a `Logger` instance from the context. + + + +### Progress and status reporting + +The CLI Builder API includes progress and status reporting tools, which can provide hints for certain functions and interfaces. + +To report progress, use the `context.reportProgress()` method, which takes a current value, optional total, and status string as arguments. +The total can be any number. For example, if you know how many files you have to process, the total could be the number of files, and current should be the number processed so far. +The status string is unmodified unless you pass in a new string value. + +In our example, the copy operation either finishes or is still executing, so there's no need for a progress report, but you can report status so that a parent builder that called our builder would know what's going on. +Use the `context.reportStatus()` method to generate a status string of any length. + +HELPFUL: There's no guarantee that a long string will be shown entirely; it could be cut to fit the UI that displays it. + +Pass an empty string to remove the status. + + + +## Builder input + +You can invoke a builder indirectly through a CLI command such as `ng build`, or directly with the Angular CLI `ng run` command. +In either case, you must provide required inputs, but can let other inputs default to values that are pre-configured for a specific *target*, specified by a [configuration](tools/cli/environments), or set on the command line. + +### Input validation + +You define builder inputs in a JSON schema associated with that builder. +Similar to schematics, the Architect tool collects the resolved input values into an `options` object, and validates their types against the schema before passing them to the builder function. + +For our example builder, `options` should be a `JsonObject` with two keys: +a `source` and a `destination`, each of which are a string. + +You can provide the following schema for type validation of these values. + + + +{ + "$schema": "http://json-schema.org/schema", + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "destination": { + "type": "string" + } + } +} + + + +HELPFUL: This is a minimal example, but the use of a schema for validation can be very powerful. +For more information, see the [JSON schemas website](http://json-schema.org). + +To link our builder implementation with its schema and name, you need to create a *builder definition* file, which you can point to in `package.json`. + +Create a file named `builders.json` that looks like this: + + + +{ + "builders": { + "copy": { + "implementation": "./dist/my-builder.js", + "schema": "./src/schema.json", + "description": "Copies a file." + } + } +} + + + +In the `package.json` file, add a `builders` key that tells the Architect tool where to find our builder definition file. + + + +{ + "name": "@example/copy-file", + "version": "1.0.0", + "description": "Builder for copying files", + "builders": "builders.json", + "dependencies": { + "@angular-devkit/architect": "~0.1200.0", + "@angular-devkit/core": "^12.0.0" + } +} + + + +The official name of our builder is now `@example/copy-file:copy`. +The first part of this is the package name and the second part is the builder name as specified in the `builders.json` file. + +These values are accessed on `options.source` and `options.destination`. + + + +### Target configuration + +A builder must have a defined target that associates it with a specific input configuration and project. + +Targets are defined in the `angular.json` [CLI configuration file](reference/configs/workspace-config). +A target specifies the builder to use, its default options configuration, and named alternative configurations. +Architect in the Angular CLI uses the target definition to resolve input options for a given run. + +The `angular.json` file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'serve'. +By default, for example, the `ng build` command runs the builder `@angular-devkit/build-angular:browser` to perform the build task, and passes in default option values as specified for the `build` target in `angular.json`. + + + +… + +"myApp": { + … + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/myApp", + "index": "src/index.html", + … + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + … + } + } + }, + … + } +} + +… + + + +The command passes the builder the set of default options specified in the "options" section. +If you pass the `--configuration=production` flag, it uses the override values specified in the `production` configuration. +Specify further option overrides individually on the command line. + +#### Target strings + +The generic `ng run` CLI command takes as its first argument a target string of the following form. + + + +project:target[:configuration] + + + +| | Details | +|:--- |:--- | +| project | The name of the Angular CLI project that the target is associated with. | +| target | A named builder configuration from the `architect` section of the `angular.json` file. | +| configuration | (optional) The name of a specific configuration override for the given target, as defined in the `angular.json` file. | + +If your builder calls another builder, it might need to read a passed target string. +Parse this string into an object by using the `targetFromTargetString()` utility function from `@angular-devkit/architect`. + +## Schedule and run + +Architect runs builders asynchronously. +To invoke a builder, you schedule a task to be run when all configuration resolution is complete. + +The builder function is not executed until the scheduler returns a `BuilderRun` control object. +The CLI typically schedules tasks by calling the `context.scheduleTarget()` function, and then resolves input options using the target definition in the `angular.json` file. + +Architect resolves input options for a given target by taking the default options object, then overwriting values from the configuration, then further overwriting values from the overrides object passed to `context.scheduleTarget()`. +For the Angular CLI, the overrides object is built from command line arguments. + +Architect validates the resulting options values against the schema of the builder. +If inputs are valid, Architect creates the context and executes the builder. + +For more information see [Workspace Configuration](reference/configs/workspace-config). + +HELPFUL: You can also invoke a builder directly from another builder or test by calling `context.scheduleBuilder()`. +You pass an `options` object directly to the method, and those option values are validated against the schema of the builder without further adjustment. + +Only the `context.scheduleTarget()` method resolves the configuration and overrides through the `angular.json` file. + +### Default architect configuration + +Let's create a simple `angular.json` file that puts target configurations into context. + +You can publish the builder to npm (see [Publishing your Library](tools/libraries/creating-libraries#publishing-your-library)), and install it using the following command: + + + +npm install @example/copy-file + + + +If you create a new project with `ng new builder-test`, the generated `angular.json` file looks something like this, with only default builder configurations. + + + +{ + "projects": { + "builder-test": { + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + // more options... + "outputPath": "dist/builder-test", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json" + }, + "configurations": { + "production": { + // more options... + "optimization": true, + "aot": true, + "buildOptimizer": true + } + } + } + } + } + } +} + + + +### Adding a target + +Add a new target that will run our builder to copy a file. +This target tells the builder to copy the `package.json` file. + +* We will add a new target section to the `architect` object for our project +* The target named `copy-package` uses our builder, which you published to `@example/copy-file`. +* The options object provides default values for the two inputs that you defined. + * `source` - The existing file you are copying. + * `destination` - The path you want to copy to. + + + +{ + "projects": { + "builder-test": { + "architect": { + "copy-package": { + "builder": "@example/copy-file:copy", + "options": { + "source": "package.json", + "destination": "package-copy.json" + } + }, + + // Existing targets... + } + } + } +} + + + +### Running the builder + +To run our builder with the new target's default configuration, use the following CLI command. + + + +ng run builder-test:copy-package + + + +This copies the `package.json` file to `package-copy.json`. + +Use command-line arguments to override the configured defaults. +For example, to run with a different `destination` value, use the following CLI command. + + + +ng run builder-test:copy-package --destination=package-other.json + + + +This copies the file to `package-other.json` instead of `package-copy.json`. +Because you did not override the *source* option, it will still copy from the default `package.json` file. + +## Testing a builder + +Use integration testing for your builder, so that you can use the Architect scheduler to create a context, as in this [example](https://github.com/mgechev/cli-builders-demo). +In the builder source directory, create a new test file `my-builder.spec.ts`. The test creates new instances of `JsonSchemaRegistry` (for schema validation), `TestingArchitectHost` (an in-memory implementation of `ArchitectHost`), and `Architect`. + +Here's an example of a test that runs the copy file builder. +The test uses the builder to copy the `package.json` file and validates that the copied file's contents are the same as the source. + + + +HELPFUL: When running this test in your repo, you need the [`ts-node`](https://github.com/TypeStrong/ts-node) package. +You can avoid this by renaming `my-builder.spec.ts` to `my-builder.spec.js`. + +### Watch mode + +Most builders to run once and return. However, this behavior is not entirely compatible with a builder that watches for changes (like a devserver, for example). +Architect can support watch mode, but there are some things to look out for. + +* To be used with watch mode, a builder handler function should return an `Observable`. + Architect subscribes to the `Observable` until it completes and might reuse it if the builder is scheduled again with the same arguments. + +* The builder should always emit a `BuilderOutput` object after each execution. + Once it's been executed, it can enter a watch mode, to be triggered by an external event. + If an event triggers it to restart, the builder should execute the `context.reportRunning()` function to tell Architect that it is running again. + This prevents Architect from stopping the builder if another run is scheduled. + +When your builder calls `BuilderRun.stop()` to exit watch mode, Architect unsubscribes from the builder's `Observable` and calls the builder's teardown logic to clean up. +This behavior also allows for long-running builds to be stopped and cleaned up. + +In general, if your builder is watching an external event, you should separate your run into three phases. + +| Phases | Details | +|:--- |:--- | +| Running | The task being performed, such as invoking a compiler. This ends when the compiler finishes and your builder emits a `BuilderOutput` object. | +| Watching | Between two runs, watch an external event stream. For example, watch the file system for any changes. This ends when the compiler restarts, and `context.reportRunning()` is called. | +| Completion | Either the task is fully completed, such as a compiler which needs to run a number of times, or the builder run was stopped (using `BuilderRun.stop()`). Architect executes teardown logic and unsubscribes from your builder's `Observable`. | + +## Summary + +The CLI Builder API provides a means of changing the behavior of the Angular CLI by using builders to execute custom logic. + +* Builders can be synchronous or asynchronous, execute once or watch for external events, and can schedule other builders or targets. +* Builders have option defaults specified in the `angular.json` configuration file, which can be overwritten by an alternate configuration for the target, and further overwritten by command line flags +* The Angular team recommends that you use integration tests to test Architect builders. Use unit tests to validate the logic that the builder executes. +* If your builder returns an `Observable`, it should clean up the builder in the teardown logic of that `Observable`. diff --git a/adev-ja/src/content/tools/cli/deployment.md b/adev-ja/src/content/tools/cli/deployment.md new file mode 100644 index 0000000000..d69b3e1b8e --- /dev/null +++ b/adev-ja/src/content/tools/cli/deployment.md @@ -0,0 +1,134 @@ +# Deployment + +When you are ready to deploy your Angular application to a remote server, you have various options. + +## Automatic deployment with the CLI + +The Angular CLI command `ng deploy` executes the `deploy` [CLI builder](tools/cli/cli-builder) associated with your project. +A number of third-party builders implement deployment capabilities to different platforms. +You can add any of them to your project with `ng add`. + +When you add a package with deployment capability, it will automatically update your workspace configuration (`angular.json` file) with a `deploy` section for the selected project. +You can then use the `ng deploy` command to deploy that project. + +For example, the following command automatically deploys a project to [Firebase](https://firebase.google.com/). + + + +ng add @angular/fire +ng deploy + + + +The command is interactive. +In this case, you must have or create a Firebase account and authenticate using it. +The command prompts you to select a Firebase project for deployment before building your application and uploading the production assets to Firebase. + +The table below lists tools which implement deployment functionality to different platforms. +The `deploy` command for each package may require different command line options. +You can read more by following the links associated with the package names below: + +| Deployment to | Setup Command | +|:--- |:--- | +| [Firebase hosting](https://firebase.google.com/docs/hosting) | [`ng add @angular/fire`](https://npmjs.org/package/@angular/fire) | +| [Vercel](https://vercel.com/solutions/angular) | [`vercel init angular`](https://github.com/vercel/vercel/tree/main/examples/angular) | +| [Netlify](https://www.netlify.com) | [`ng add @netlify-builder/deploy`](https://npmjs.org/package/@netlify-builder/deploy) | +| [GitHub pages](https://pages.github.com) | [`ng add angular-cli-ghpages`](https://npmjs.org/package/angular-cli-ghpages) | +| [Amazon Cloud S3](https://aws.amazon.com/s3/?nc2=h_ql_prod_st_s3) | [`ng add @jefiozie/ngx-aws-deploy`](https://www.npmjs.com/package/@jefiozie/ngx-aws-deploy) | + +If you're deploying to a self-managed server or there's no builder for your favorite cloud platform, you can either [create a builder](tools/cli/cli-builder) that allows you to use the `ng deploy` command, or read through this guide to learn how to manually deploy your application. + +## Manual deployment to a remote server + +To manually deploy your application, create a production build and copy the output directory to a web server or content delivery network (CDN). +By default, `ng build` uses the `production` configuration. +If you have customized your build configurations, you may want to confirm [production optimizations](tools/cli/deployment#production-optimizations) are being applied before deploying. + +`ng build` outputs the built artifacts to `dist/my-app/` by default, however this path can be configured with the `outputPath` option in the `@angular-devkit/build-angular:browser` builder. +Copy this directory to the server and configure it to serve the directory. + +While this is a minimal deployment solution, there are a few requirements for the server to serve your Angular application correctly. + +## Server configuration + +This section covers changes you may need to configure on the server to run your Angular application. + +### Routed apps must fall back to `index.html` + +Client-side rendered Angular applications are perfect candidates for serving with a static HTML server because all the content is static and generated at build time. + +If the application uses the Angular router, you must configure the server to return the application's host page (`index.html`) when asked for a file that it does not have. + +A routed application should support "deep links". +A *deep link* is a URL that specifies a path to a component inside the application. +For example, `http://my-app.test/users/42` is a *deep link* to the user detail page that displays the user with `id` 42. + +There is no issue when the user initially loads the index page and then navigates to that URL from within a running client. +The Angular router performs the navigation *client-side* and does not request a new HTML page. + +But clicking a deep link in an email, entering it in the browser address bar, or even refreshing the browser while already on the deep linked page will all be handled by the browser itself, *outside* the running application. +The browser makes a direct request to the server for `/users/42`, bypassing Angular's router. + +A static server routinely returns `index.html` when it receives a request for `http://my-app.test/`. +But most servers by default will reject `http://my-app.test/users/42` and returns a `404 - Not Found` error *unless* it is configured to return `index.html` instead. +Configure the fallback route or 404 page to `index.html` for your server, so Angular is served for deep links and can display the correct route. +Some servers call this fallback behavior "Single-Page Application" (SPA) mode. + +Once the browser loads the application, Angular router will read the URL to determine which page it is on and display `/users/42` correctly. + +For "real" 404 pages such as `http://my-app.test/does-not-exist`, the server does not require any additional configuration. +[404 pages implemented in the Angular router](guide/routing/common-router-tasks#displaying-a-404-page) will be displayed correctly. + +### Requesting data from a different server (CORS) + +Web developers may encounter a [*cross-origin resource sharing*](https://developer.mozilla.org/docs/Web/HTTP/CORS "Cross-origin resource sharing") error when making a network request to a server other than the application's own host server. +Browsers forbid such requests unless the server explicitly permits them. + +There isn't anything Angular or the client application can do about these errors. +The _server_ must be configured to accept the application's requests. +Read about how to enable CORS for specific servers at [enable-cors.org](https://enable-cors.org/server.html "Enabling CORS server"). + +## Production optimizations + +`ng build` uses the `production` configuration unless configured otherwise. This configuration enables the following build optimization features. + +| Features | Details | +|:--- |:--- | +| [Ahead-of-Time (AOT) Compilation](tools/cli/aot-compiler) | Pre-compiles Angular component templates. | +| [Production mode](tools/cli/deployment#development-only-features) | Optimizes the application for the best runtime performance | +| Bundling | Concatenates your many application and library files into a minimum number of deployed files. | +| Minification | Removes excess whitespace, comments, and optional tokens. | +| Mangling | Renames functions, classes, and variables to use shorter, arbitrary identifiers. | +| Dead code elimination | Removes unreferenced modules and unused code. | + +See [`ng build`](cli/build) for more about CLI build options and their effects. + +### Development-only features + +When you run an application locally using `ng serve`, Angular uses the development configuration +at runtime which enables: + +* Extra safety checks such as [`expression-changed-after-checked`](errors/NG0100) detection. +* More detailed error messages. +* Additional debugging utilities such as the global `ng` variable with [debugging functions](api#core-global) and [Angular DevTools](tools/devtools) support. + +These features are helpful during development, but they require extra code in the app, which is +undesirable in production. To ensure these features do not negatively impact bundle size for end users, Angular CLI +removes development-only code from the bundle when building for production. + +Building your application with `ng build` by default uses the `production` configuration which removes these features from the output for optimal bundle size. + +## `--deploy-url` + +`--deploy-url` is a command line option used to specify the base path for resolving relative URLs for assets such as images, scripts, and style sheets at *compile* time. + + + +ng build --deploy-url /my/assets + + + +The effect and purpose of `--deploy-url` overlaps with [``](guide/routing/common-router-tasks). Both can be used for initial scripts, stylesheets, lazy scripts, and css resources. + +Unlike `` which can be defined in a single place at runtime, the `--deploy-url` needs to be hard-coded into an application at build time. +Prefer `` where possible. diff --git a/adev-ja/src/content/tools/cli/end-to-end.md b/adev-ja/src/content/tools/cli/end-to-end.md new file mode 100644 index 0000000000..20015084cf --- /dev/null +++ b/adev-ja/src/content/tools/cli/end-to-end.md @@ -0,0 +1,58 @@ +# End to End Testing + +End-to-end or (E2E) testing is a form of testing used to assert your entire application works as expected from start to finish or _"end-to-end"_. E2E testing differs from unit testing in that it is completely decoupled from the underlying implementation details of your code. It is typically used to validate an application in a way that mimics the way a user would interact with it. This page serves as a guide to getting started with end-to-end testing in Angular using the Angular CLI. + +## Setup E2E Testing + +The Angular CLI downloads and installs everything you need to run end-to-end tests for your Angular application. + + + +ng e2e + + + +The `ng e2e` command will first check your project for the "e2e" target. If it can't locate it, the CLI will then prompt you which e2e package you would like to use and walk you through the setup. + + + +Cannot find "e2e" target for the specified project. +You can add a package that implements these capabilities. + +For example: +Cypress: ng add @cypress/schematic +Nightwatch: ng add @nightwatch/schematics +WebdriverIO: ng add @wdio/schematics +Puppeteer: ng add @puppeteer/ng-schematics + +Would you like to add a package with "e2e" capabilities now? +No +❯ Cypress +Nightwatch +WebdriverIO +Puppeteer + + + +If you don't find the test runner you would like you use from the list above, you can add manually add a package using `ng add`. + +## Running E2E Tests + +Now that your application is configured for end-to-end testing we can now run the same command to execute your tests. + + + +ng e2e + + + +Note, their isn't anything "special" about running your tests with any of the integrated e2e packages. The `ng e2e` command is really just running the `e2e` builder under the hood. You can always [create your own custom builder](tools/cli/cli-builder#creating-a-builder) named `e2e` and run it using `ng e2e`. + +## More information on end-to-end testing tools + +| Testing Tool | Details | +| :----------- | :------------------------------------------------------------------------------------------------------------------- | +| Cypress | [Getting started with Cypress](https://docs.cypress.io/guides/end-to-end-testing/writing-your-first-end-to-end-test) | +| Nightwatch | [Getting started with Nightwatch](https://nightwatchjs.org/guide/writing-tests/introduction.html) | +| WebdriverIO | [Getting started with Webdriver.io](https://webdriver.io/docs/gettingstarted) | +| Puppeteer | [Getting started with Puppeteer](https://pptr.dev) | diff --git a/adev-ja/src/content/tools/cli/environments.md b/adev-ja/src/content/tools/cli/environments.md new file mode 100644 index 0000000000..8f50ba372c --- /dev/null +++ b/adev-ja/src/content/tools/cli/environments.md @@ -0,0 +1,217 @@ +# Configuring application environments + +You can define different named build configurations for your project, such as `development` and `staging`, with different defaults. + +Each named configuration can have defaults for any of the options that apply to the various builder targets, such as `build`, `serve`, and `test`. +The [Angular CLI](tools/cli) `build`, `serve`, and `test` commands can then replace files with appropriate versions for your intended target environment. + +## Angular CLI configurations + +Angular CLI builders support a `configurations` object, which allows overwriting specific options for a builder based on the configuration provided on the command line. + + + +{ + "projects": { + "my-app": { + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + // By default, disable source map generation. + "sourceMap": false + }, + "configurations": { + // For the `debug` configuration, enable source maps. + "debug": { + "sourceMap": true + } + } + }, + … + } + } + } +} + + + +You can choose which configuration to use with the `--configuration` option. + + + +ng build --configuration debug + + + +Configurations can be applied to any Angular CLI builder. Multiple configurations can be specified with a comma separator. The configurations are applied in order, with conflicting options using the value from the last configuration. + + + +ng build --configuration debug,production,customer-facing + + + +## Configure environment-specific defaults + +`@angular-devkit/build-angular:browser` supports file replacements, an option for substituting source files before executing a build. +Using this in combination with `--configuration` provides a mechanism for configuring environment-specific data in your application. + +Start by [generating environments](cli/generate#environments-command) to create the `src/environments/` directory and configure the project to use file replacements. + + + +ng generate environments + + + +The project's `src/environments/` directory contains the base configuration file, `environment.ts`, which provides the default configuration for production. +You can override default values for additional environments, such as `development` and `staging`, in target-specific configuration files. + +For example: + + + +my-app/src/environments +├── environment.development.ts +├── environment.staging.ts +└── environment.ts + + + +The base file `environment.ts`, contains the default environment settings. +For example: + + + +export const environment = { + production: true +}; + + + +The `build` command uses this as the build target when no environment is specified. +You can add further variables, either as additional properties on the environment object, or as separate objects. +For example, the following adds a default for a variable to the default environment: + + + +export const environment = { + production: true, + apiUrl: 'http://my-prod-url' +}; + + + +You can add target-specific configuration files, such as `environment.development.ts`. +The following content sets default values for the development build target: + + + +export const environment = { + production: false, + apiUrl: 'http://my-dev-url' +}; + + + +## Using environment-specific variables in your app + +To use the environment configurations you have defined, your components must import the original environments file: + + + +import { environment } from './environments/environment'; + + + +This ensures that the build and serve commands can find the configurations for specific build targets. + +The following code in the component file (`app.component.ts`) uses an environment variable defined in the configuration files. + + + +import { environment } from './../environments/environment'; + +// Fetches from `http://my-prod-url` in production, `http://my-dev-url` in development. +fetch(environment.apiUrl); + + + +The main CLI configuration file, `angular.json`, contains a `fileReplacements` section in the configuration for each build target, which lets you replace any file in the TypeScript program with a target-specific version of that file. +This is useful for including target-specific code or variables in a build that targets a specific environment, such as production or staging. + +By default no files are replaced, however `ng generate environments` sets up this configuration automatically. +You can change or add file replacements for specific build targets by editing the `angular.json` configuration directly. + + + + "configurations": { + "development": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ], + … + + + +This means that when you build your development configuration with `ng build --configuration development`, the `src/environments/environment.ts` file is replaced with the target-specific version of the file, `src/environments/environment.development.ts`. + +To add a staging environment, create a copy of `src/environments/environment.ts` called `src/environments/environment.staging.ts`, then add a `staging` configuration to `angular.json`: + + + + "configurations": { + "development": { … }, + "production": { … }, + "staging": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.staging.ts" + } + ] + } + } + + + +You can add more configuration options to this target environment as well. +Any option that your build supports can be overridden in a build target configuration. + +To build using the staging configuration, run the following command: + + + +ng build --configuration staging + + + +By default, the `build` target includes `production` and `development` configurations and `ng serve` uses the development build of the application. +You can also configure `ng serve` to use the targeted build configuration if you set the `buildTarget` option: + + + + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { … }, + "configurations": { + "development": { + // Use the `development` configuration of the `build` target. + "buildTarget": "my-app:build:development" + }, + "production": { + // Use the `production` configuration of the `build` target. + "buildTarget": "my-app:build:production" + } + }, + "defaultConfiguration": "development" + }, + + + +The `defaultConfiguration` option specifies which configuration is used by default. +When `defaultConfiguration` is not set, `options` are used directly without modification. diff --git a/adev-ja/src/content/tools/cli/overview.md b/adev-ja/src/content/tools/cli/overview.md new file mode 100644 index 0000000000..47b2fdfc8b --- /dev/null +++ b/adev-ja/src/content/tools/cli/overview.md @@ -0,0 +1,46 @@ +# The Angular CLI + +The Angular CLI is a command-line interface tool which allows you to scaffold, develop, test, deploy, and maintain Angular applications directly from a command shell. + +Angular CLI is published on npm as the `@angular/cli` package and includes a binary named `ng`. Commands invoking `ng` are using the Angular CLI. + + + +If you are new to Angular, you might want to start with [Try it now!](tutorials/learn-angular), which introduces the essentials of Angular in the context of a ready-made basic online store app for you to examine and modify. +This standalone tutorial takes advantage of the interactive [StackBlitz](https://stackblitz.com) environment for online development. +You don't need to set up your local environment until you're ready. + + + + + + Install Angular CLI to create and build your first app. + + + Discover CLI commands to make you more productive with Angular. + + + Create and run schematics to generate and modify source files in your application automatically. + + + Create and run builders to perform complex transformations from your source code to generated build outputs. + + + +## CLI command-language syntax + +Angular CLI roughly follows Unix/POSIX conventions for option syntax. + +### Boolean options + +Boolean options have two forms: `--this-option` sets the flag to `true`, `--no-this-option` sets it to `false`. +You can also use `--this-option=false` or `--this-option=true`. +If neither option is supplied, the flag remains in its default state, as listed in the reference documentation. + +### Array options + +Array options can be provided in two forms: `--option value1 value2` or `--option value1 --option value2`. + +### Relative paths + +Options that specify files can be given as absolute paths, or as paths relative to the current working directory, which is generally either the workspace or project root. diff --git a/adev-ja/src/content/tools/cli/schematics-authoring.md b/adev-ja/src/content/tools/cli/schematics-authoring.md new file mode 100644 index 0000000000..06f0295fc3 --- /dev/null +++ b/adev-ja/src/content/tools/cli/schematics-authoring.md @@ -0,0 +1,399 @@ +# Authoring schematics + +You can create your own schematics to operate on Angular projects. +Library developers typically package schematics with their libraries to integrate them with the Angular CLI. +You can also create stand-alone schematics to manipulate the files and constructs in Angular applications as a way of customizing them for your development environment and making them conform to your standards and constraints. +Schematics can be chained, running other schematics to perform complex operations. + +Manipulating the code in an application has the potential to be both very powerful and correspondingly dangerous. +For example, creating a file that already exists would be an error, and if it was applied immediately, it would discard all the other changes applied so far. +The Angular Schematics tooling guards against side effects and errors by creating a virtual file system. +A schematic describes a pipeline of transformations that can be applied to the virtual file system. +When a schematic runs, the transformations are recorded in memory, and only applied in the real file system once they're confirmed to be valid. + +## Schematics concepts + +The public API for schematics defines classes that represent the basic concepts. + +* The virtual file system is represented by a `Tree`. + The `Tree` data structure contains a *base* \(a set of files that already exists\) and a *staging area* \(a list of changes to be applied to the base\). + When making modifications, you don't actually change the base, but add those modifications to the staging area. + +* A `Rule` object defines a function that takes a `Tree`, applies transformations, and returns a new `Tree`. + The main file for a schematic, `index.ts`, defines a set of rules that implement the schematic's logic. + +* A transformation is represented by an `Action`. + There are four action types: `Create`, `Rename`, `Overwrite`, and `Delete`. + +* Each schematic runs in a context, represented by a `SchematicContext` object. + +The context object passed into a rule provides access to utility functions and metadata that the schematic might need to work with, including a logging API to help with debugging. +The context also defines a *merge strategy* that determines how changes are merged from the staged tree into the base tree. +A change can be accepted or ignored, or throw an exception. + +### Defining rules and actions + +When you create a new blank schematic with the [Schematics CLI](#schematics-cli), the generated entry function is a *rule factory*. +A `RuleFactory` object defines a higher-order function that creates a `Rule`. + + + +import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; + +// You don't have to export the function as default. +// You can also have more than one rule factory per file. +export function helloWorld(_options: any): Rule { + return (tree: Tree,_context: SchematicContext) => { + return tree; + }; +} + + + +Your rules can make changes to your projects by calling external tools and implementing logic. +You need a rule, for example, to define how a template in the schematic is to be merged into the hosting project. + +Rules can make use of utilities provided with the `@schematics/angular` package. +Look for helper functions for working with modules, dependencies, TypeScript, AST, JSON, Angular CLI workspaces and projects, and more. + + + +import { + JsonAstObject, + JsonObject, + JsonValue, + Path, + normalize, + parseJsonAst, + strings, +} from '@angular-devkit/core'; + + + +### Defining input options with a schema and interfaces + +Rules can collect option values from the caller and inject them into templates. +The options available to your rules, with their allowed values and defaults, are defined in the schematic's JSON schema file, `/schema.json`. +Define variable or enumerated data types for the schema using TypeScript interfaces. + +The schema defines the types and default values of variables used in the schematic. +For example, the hypothetical "Hello World" schematic might have the following schema. + + + +{ + "properties": { + "name": { + "type": "string", + "minLength": 1, + "default": "world" + }, + "useColor": { + "type": "boolean" + } + } +} + + +See examples of schema files for the Angular CLI command schematics in [`@schematics/angular`](https://github.com/angular/angular-cli/blob/main/packages/schematics/angular/application/schema.json). + +### Schematic prompts + +Schematic *prompts* introduce user interaction into schematic execution. +Configure schematic options to display a customizable question to the user. +The prompts are displayed before the execution of the schematic, which then uses the response as the value for the option. +This lets users direct the operation of the schematic without requiring in-depth knowledge of the full spectrum of available options. + +The "Hello World" schematic might, for example, ask the user for their name, and display that name in place of the default name "world". +To define such a prompt, add an `x-prompt` property to the schema for the `name` variable. + +Similarly, you can add a prompt to let the user decide whether the schematic uses color when executing its hello action. +The schema with both prompts would be as follows. + + + +{ + "properties": { + "name": { + "type": "string", + "minLength": 1, + "default": "world", + "x-prompt": "What is your name?" + }, + "useColor": { + "type": "boolean", + "x-prompt": "Would you like the response in color?" + } + } +} + + +#### Prompt short-form syntax + +These examples use a shorthand form of the prompt syntax, supplying only the text of the question. +In most cases, this is all that is required. +Notice however, that the two prompts expect different types of input. +When using the shorthand form, the most appropriate type is automatically selected based on the property's schema. +In the example, the `name` prompt uses the `input` type because it is a string property. +The `useColor` prompt uses a `confirmation` type because it is a Boolean property. +In this case, "yes" corresponds to `true` and "no" corresponds to `false`. + +There are three supported input types. + +| Input type | Details | +|:--- |:---- | +| confirmation | A yes or no question; ideal for Boolean options. | +| input | Textual input; ideal for string or number options. | +| list | A predefined set of allowed values. | + +In the short form, the type is inferred from the property's type and constraints. + +| Property schema | Prompt type | +|:--- |:--- | +| "type": "boolean" | confirmation \("yes"=`true`, "no"=`false`\) | +| "type": "string" | input | +| "type": "number" | input \(only valid numbers accepted\) | +| "type": "integer" | input \(only valid numbers accepted\) | +| "enum": […] | list \(enum members become list selections\) | + +In the following example, the property takes an enumerated value, so the schematic automatically chooses the list type, and creates a menu from the possible values. + + + +"style": { + "description": "The file extension or preprocessor to use for style files.", + "type": "string", + "default": "css", + "enum": [ + "css", + "scss", + "sass", + "less", + "styl" + ], + "x-prompt": "Which stylesheet format would you like to use?" +} + + + +The prompt runtime automatically validates the provided response against the constraints provided in the JSON schema. +If the value is not acceptable, the user is prompted for a new value. +This ensures that any values passed to the schematic meet the expectations of the schematic's implementation, so that you do not need to add additional checks within the schematic's code. + +#### Prompt long-form syntax + +The `x-prompt` field syntax supports a long form for cases where you require additional customization and control over the prompt. +In this form, the `x-prompt` field value is a JSON object with subfields that customize the behavior of the prompt. + +| Field | Data value | +|:--- |:--- | +| type | `confirmation`, `input`, or `list` \(selected automatically in short form\) | +| message | string \(required\) | +| items | string and/or label/value object pair \(only valid with type `list`\) | + +The following example of the long form is from the JSON schema for the schematic that the CLI uses to [generate applications](https://github.com/angular/angular-cli/blob/ba8a6ea59983bb52a6f1e66d105c5a77517f062e/packages/schematics/angular/application/schema.json#L56). +It defines the prompt that lets users choose which style preprocessor they want to use for the application being created. +By using the long form, the schematic can provide more explicit formatting of the menu choices. + + + +"style": { + "description": "The file extension or preprocessor to use for style files.", + "type": "string", + "default": "css", + "enum": [ + "css", + "scss", + "sass", + "less" + ], + "x-prompt": { + "message": "Which stylesheet format would you like to use?", + "type": "list", + "items": [ + { "value": "css", "label": "CSS" }, + { "value": "scss", "label": "SCSS [ https://sass-lang.com/documentation/syntax#scss ]" }, + { "value": "sass", "label": "Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]" }, + { "value": "less", "label": "Less [ https://lesscss.org/ ]" } + ] + }, +}, + + + +#### x-prompt schema + +The JSON schema that defines a schematic's options supports extensions to allow the declarative definition of prompts and their respective behavior. +No additional logic or changes are required to the code of a schematic to support the prompts. +The following JSON schema is a complete description of the long-form syntax for the `x-prompt` field. + + + +{ + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "type": { "type": "string" }, + "message": { "type": "string" }, + "items": { + "type": "array", + "items": { + "oneOf": [ + { "type": "string" }, + { + "type": "object", + "properties": { + "label": { "type": "string" }, + "value": { } + }, + "required": [ "value" ] + } + ] + } + } + }, + "required": [ "message" ] + } + ] +} + + + +## Schematics CLI + +Schematics come with their own command-line tool. +Using Node 6.9 or later, install the Schematics command line tool globally: + + + +npm install -g @angular-devkit/schematics-cli + + + +This installs the `schematics` executable, which you can use to create a new schematics collection in its own project folder, add a new schematic to an existing collection, or extend an existing schematic. + +In the following sections, you will create a new schematics collection using the CLI to introduce the files and file structure, and some of the basic concepts. + +The most common use of schematics, however, is to integrate an Angular library with the Angular CLI. +Do this by creating the schematic files directly within the library project in an Angular workspace, without using the Schematics CLI. +See [Schematics for Libraries](tools/cli/schematics-for-libraries). + +### Creating a schematics collection + +The following command creates a new schematic named `hello-world` in a new project folder of the same name. + + + +schematics blank --name=hello-world + + + +The `blank` schematic is provided by the Schematics CLI. +The command creates a new project folder \(the root folder for the collection\) and an initial named schematic in the collection. + +Go to the collection folder, install your npm dependencies, and open your new collection in your favorite editor to see the generated files. +For example, if you are using VS Code: + + + +cd hello-world +npm install +npm run build +code . + + + +The initial schematic gets the same name as the project folder, and is generated in `src/hello-world`. +Add related schematics to this collection, and modify the generated skeleton code to define your schematic's functionality. +Each schematic name must be unique within the collection. + +### Running a schematic + +Use the `schematics` command to run a named schematic. +Provide the path to the project folder, the schematic name, and any mandatory options, in the following format. + + + +schematics : --= + + + +The path can be absolute or relative to the current working directory where the command is executed. +For example, to run the schematic you just generated \(which has no required options\), use the following command. + + + +schematics .:hello-world + + + +### Adding a schematic to a collection + +To add a schematic to an existing collection, use the same command you use to start a new schematics project, but run the command inside the project folder. + + + +cd hello-world +schematics blank --name=goodbye-world + + + +The command generates the new named schematic inside your collection, with a main `index.ts` file and its associated test spec. +It also adds the name, description, and factory function for the new schematic to the collection's schema in the `collection.json` file. + +## Collection contents + +The top level of the root project folder for a collection contains configuration files, a `node_modules` folder, and a `src/` folder. +The `src/` folder contains subfolders for named schematics in the collection, and a schema, `collection.json`, which describes the collected schematics. +Each schematic is created with a name, description, and factory function. + + + +{ + "$schema": + "../node_modules/@angular-devkit/schematics/collection-schema.json", + "schematics": { + "hello-world": { + "description": "A blank schematic.", + "factory": "./hello-world/index#helloWorld" + } + } +} + + + +* The `$schema` property specifies the schema that the CLI uses for validation. +* The `schematics` property lists named schematics that belong to this collection. + Each schematic has a plain-text description, and points to the generated entry function in the main file. + +* The `factory` property points to the generated entry function. + In this example, you invoke the `hello-world` schematic by calling the `helloWorld()` factory function. + +* The optional `schema` property points to a JSON schema file that defines the command-line options available to the schematic. +* The optional `aliases` array specifies one or more strings that can be used to invoke the schematic. + For example, the schematic for the Angular CLI "generate" command has an alias "g", that lets you use the command `ng g`. + +### Named schematics + +When you use the Schematics CLI to create a blank schematics project, the new blank schematic is the first member of the collection, and has the same name as the collection. +When you add a new named schematic to this collection, it is automatically added to the `collection.json` schema. + +In addition to the name and description, each schematic has a `factory` property that identifies the schematic's entry point. +In the example, you invoke the schematic's defined functionality by calling the `helloWorld()` function in the main file, `hello-world/index.ts`. + +overview + +Each named schematic in the collection has the following main parts. + +| Parts | Details | +|:--- |:--- | +| `index.ts` | Code that defines the transformation logic for a named schematic. | +| `schema.json` | Schematic variable definition. | +| `schema.d.ts` | Schematic variables. | +| `files/` | Optional component/template files to replicate. | + +It is possible for a schematic to provide all of its logic in the `index.ts` file, without additional templates. +You can create dynamic schematics for Angular, however, by providing components and templates in the `files` folder, like those in standalone Angular projects. +The logic in the index file configures these templates by defining rules that inject data and modify variables. diff --git a/adev-ja/src/content/tools/cli/schematics-for-libraries.md b/adev-ja/src/content/tools/cli/schematics-for-libraries.md new file mode 100644 index 0000000000..79fac14781 --- /dev/null +++ b/adev-ja/src/content/tools/cli/schematics-for-libraries.md @@ -0,0 +1,311 @@ +# Schematics for libraries + +When you create an Angular library, you can provide and package it with schematics that integrate it with the Angular CLI. +With your schematics, your users can use `ng add` to install an initial version of your library, +`ng generate` to create artifacts defined in your library, and `ng update` to adjust their project for a new version of your library that introduces breaking changes. + +All three types of schematics can be part of a collection that you package with your library. + +## Creating a schematics collection + +To start a collection, you need to create the schematic files. +The following steps show you how to add initial support without modifying any project files. + +1. In your library's root folder, create a `schematics` folder. +1. In the `schematics/` folder, create an `ng-add` folder for your first schematic. +1. At the root level of the `schematics` folder, create a `collection.json` file. +1. Edit the `collection.json` file to define the initial schema for your collection. + + + + * The `$schema` path is relative to the Angular Devkit collection schema. + * The `schematics` object describes the named schematics that are part of this collection. + * The first entry is for a schematic named `ng-add`. + It contains the description, and points to the factory function that is called when your schematic is executed. + +1. In your library project's `package.json` file, add a "schematics" entry with the path to your schema file. + The Angular CLI uses this entry to find named schematics in your collection when it runs commands. + + + +The initial schema that you have created tells the CLI where to find the schematic that supports the `ng add` command. +Now you are ready to create that schematic. + +## Providing installation support + +A schematic for the `ng add` command can enhance the initial installation process for your users. +The following steps define this type of schematic. + +1. Go to the `/schematics/ng-add` folder. +1. Create the main file, `index.ts`. +1. Open `index.ts` and add the source code for your schematic factory function. + + + +The Angular CLI will install the latest version of the library automatically, and this example is taking it a step further by adding the `MyLibModule` to the root of the application. The `addRootImport` function accepts a callback that needs to return a code block. You can write any code inside of the string tagged with the `code` function and any external symbol have to be wrapped with the `external` function to ensure that the appropriate import statements are generated. + +### Define dependency type + +Use the `save` option of `ng-add` to configure if the library should be added to the `dependencies`, the `devDependencies`, or not saved at all in the project's `package.json` configuration file. + + + +Possible values are: + +| Values | Details | +|:--- |:--- | +| `false` | Don't add the package to `package.json` | +| `true` | Add the package to the dependencies | +| `"dependencies"` | Add the package to the dependencies | +| `"devDependencies"` | Add the package to the devDependencies | + +## Building your schematics + +To bundle your schematics together with your library, you must configure the library to build the schematics separately, then add them to the bundle. +You must build your schematics *after* you build your library, so they are placed in the correct directory. + +* Your library needs a custom Typescript configuration file with instructions on how to compile your schematics into your distributed library +* To add the schematics to the library bundle, add scripts to the library's `package.json` file + +Assume you have a library project `my-lib` in your Angular workspace. +To tell the library how to build the schematics, add a `tsconfig.schematics.json` file next to the generated `tsconfig.lib.json` file that configures the library build. + +1. Edit the `tsconfig.schematics.json` file to add the following content. + + + + | Options | Details | + |:--- |:--- | + | `rootDir` | Specifies that your `schematics` folder contains the input files to be compiled. | + | `outDir` | Maps to the library's output folder. By default, this is the `dist/my-lib` folder at the root of your workspace. | + +1. To make sure your schematics source files get compiled into the library bundle, add the following scripts to the `package.json` file in your library project's root folder \(`projects/my-lib`\). + + + + * The `build` script compiles your schematic using the custom `tsconfig.schematics.json` file + * The `postbuild` script copies the schematic files after the `build` script completes + * Both the `build` and the `postbuild` scripts require the `copyfiles` and `typescript` dependencies. + To install the dependencies, navigate to the path defined in `devDependencies` and run `npm install` before you run the scripts. + +## Providing generation support + +You can add a named schematic to your collection that lets your users use the `ng generate` command to create an artifact that is defined in your library. + +We'll assume that your library defines a service, `my-service`, that requires some setup. +You want your users to be able to generate it using the following CLI command. + + + +ng generate my-lib:my-service + + + +To begin, create a new subfolder, `my-service`, in the `schematics` folder. + +### Configure the new schematic + +When you add a schematic to the collection, you have to point to it in the collection's schema, and provide configuration files to define options that a user can pass to the command. + +1. Edit the `schematics/collection.json` file to point to the new schematic subfolder, and include a pointer to a schema file that specifies inputs for the new schematic. + + + +1. Go to the `/schematics/my-service` folder. +1. Create a `schema.json` file and define the available options for the schematic. + + + + * *id*: A unique ID for the schema in the collection. + * *title*: A human-readable description of the schema. + * *type*: A descriptor for the type provided by the properties. + * *properties*: An object that defines the available options for the schematic. + + Each option associates key with a type, description, and optional alias. + The type defines the shape of the value you expect, and the description is displayed when the user requests usage help for your schematic. + + See the workspace schema for additional customizations for schematic options. + +1. Create a `schema.ts` file and define an interface that stores the values of the options defined in the `schema.json` file. + + + + | Options | Details | + |:--- |:--- | + | name | The name you want to provide for the created service. | + | path | Overrides the path provided to the schematic. The default path value is based on the current working directory. | + | project | Provides a specific project to run the schematic on. In the schematic, you can provide a default if the option is not provided by the user. | + +### Add template files + +To add artifacts to a project, your schematic needs its own template files. +Schematic templates support special syntax to execute code and variable substitution. + +1. Create a `files/` folder inside the `schematics/my-service/` folder. +1. Create a file named `__name@dasherize__.service.ts.template` that defines a template to use for generating files. + This template will generate a service that already has Angular's `HttpClient` injected into its constructor. + + + + import { Injectable } from '@angular/core'; + import { HttpClient } from '@angular/common/http'; + + @Injectable({ + providedIn: 'root' + }) + export class <%= classify(name) %>Service { + constructor(private http: HttpClient) { } + } + + + + * The `classify` and `dasherize` methods are utility functions that your schematic uses to transform your source template and filename. + + * The `name` is provided as a property from your factory function. + It is the same `name` you defined in the schema. + +### Add the factory function + +Now that you have the infrastructure in place, you can define the main function that performs the modifications you need in the user's project. + +The Schematics framework provides a file templating system, which supports both path and content templates. +The system operates on placeholders defined inside files or paths that loaded in the input `Tree`. +It fills these in using values passed into the `Rule`. + +For details of these data structures and syntax, see the [Schematics README](https://github.com/angular/angular-cli/blob/main/packages/angular_devkit/schematics/README.md). + +1. Create the main file `index.ts` and add the source code for your schematic factory function. +1. First, import the schematics definitions you will need. + The Schematics framework offers many utility functions to create and use rules when running a schematic. + + + +1. Import the defined schema interface that provides the type information for your schematic's options. + + + +1. To build up the generation schematic, start with an empty rule factory. + + + +This rule factory returns the tree without modification. +The options are the option values passed through from the `ng generate` command. + +## Define a generation rule + +You now have the framework in place for creating the code that actually modifies the user's application to set it up for the service defined in your library. + +The Angular workspace where the user installed your library contains multiple projects \(applications and libraries\). +The user can specify the project on the command line, or let it default. +In either case, your code needs to identify the specific project to which this schematic is being applied, so that you can retrieve information from the project configuration. + +Do this using the `Tree` object that is passed in to the factory function. +The `Tree` methods give you access to the complete file tree in your workspace, letting you read and write files during the execution of the schematic. + +### Get the project configuration + +1. To determine the destination project, use the `workspaces.readWorkspace` method to read the contents of the workspace configuration file, `angular.json`. + To use `workspaces.readWorkspace` you need to create a `workspaces.WorkspaceHost` from the `Tree`. + Add the following code to your factory function. + + + + Be sure to check that the context exists and throw the appropriate error. + +1. Now that you have the project name, use it to retrieve the project-specific configuration information. + + + + The `workspace.projects` object contains all the project-specific configuration information. + +1. The `options.path` determines where the schematic template files are moved to once the schematic is applied. + + The `path` option in the schematic's schema is substituted by default with the current working directory. + If the `path` is not defined, use the `sourceRoot` from the project configuration along with the `projectType`. + + + +### Define the rule + +A `Rule` can use external template files, transform them, and return another `Rule` object with the transformed template. +Use the templating to generate any custom files required for your schematic. + +1. Add the following code to your factory function. + + + + | Methods | Details | + |:--- |:--- | + | `apply()` | Applies multiple rules to a source and returns the transformed source. It takes 2 arguments, a source and an array of rules. | + | `url()` | Reads source files from your filesystem, relative to the schematic. | + | `applyTemplates()` | Receives an argument of methods and properties you want make available to the schematic template and the schematic filenames. It returns a `Rule`. This is where you define the `classify()` and `dasherize()` methods, and the `name` property. | + | `classify()` | Takes a value and returns the value in title case. For example, if the provided name is `my service`, it is returned as `MyService`. | + | `dasherize()` | Takes a value and returns the value in dashed and lowercase. For example, if the provided name is MyService, it is returned as `my-service`. | + | `move()` | Moves the provided source files to their destination when the schematic is applied. | + +1. Finally, the rule factory must return a rule. + + + + The `chain()` method lets you combine multiple rules into a single rule, so that you can perform multiple operations in a single schematic. + Here you are only merging the template rules with any code executed by the schematic. + +See a complete example of the following schematic rule function. + + + +For more information about rules and utility methods, see [Provided Rules](https://github.com/angular/angular-cli/tree/main/packages/angular_devkit/schematics#provided-rules). + +## Running your library schematic + +After you build your library and schematics, you can install the schematics collection to run against your project. +The following steps show you how to generate a service using the schematic you created earlier. + +### Build your library and schematics + +From the root of your workspace, run the `ng build` command for your library. + + + +ng build my-lib + + + +Then, you change into your library directory to build the schematic + + + +cd projects/my-lib +npm run build + + + +### Link the library + +Your library and schematics are packaged and placed in the `dist/my-lib` folder at the root of your workspace. +For running the schematic, you need to link the library into your `node_modules` folder. +From the root of your workspace, run the `npm link` command with the path to your distributable library. + + + +npm link dist/my-lib + + + +### Run the schematic + +Now that your library is installed, run the schematic using the `ng generate` command. + + + +ng generate my-lib:my-service --name my-data + + + +In the console, you see that the schematic was run and the `my-data.service.ts` file was created in your application folder. + + + +CREATE src/app/my-data.service.ts (208 bytes) + + diff --git a/adev-ja/src/content/tools/cli/schematics.md b/adev-ja/src/content/tools/cli/schematics.md new file mode 100644 index 0000000000..33778b0df5 --- /dev/null +++ b/adev-ja/src/content/tools/cli/schematics.md @@ -0,0 +1,129 @@ +# Generating code using schematics + +A schematic is a template-based code generator that supports complex logic. +It is a set of instructions for transforming a software project by generating or modifying code. +Schematics are packaged into collections and installed with npm. + +The schematic collection can be a powerful tool for creating, modifying, and maintaining any software project, but is particularly useful for customizing Angular projects to suit the particular needs of your own organization. +You might use schematics, for example, to generate commonly-used UI patterns or specific components, using predefined templates or layouts. +Use schematics to enforce architectural rules and conventions, making your projects consistent and interoperative. + +## Schematics for the Angular CLI + +Schematics are part of the Angular ecosystem. +The Angular CLI uses schematics to apply transforms to a web-app project. +You can modify these schematics, and define new ones to do things like update your code to fix breaking changes in a dependency, for example, or to add a new configuration option or framework to an existing project. + +Schematics that are included in the `@schematics/angular` collection are run by default by the commands `ng generate` and `ng add`. +The package contains named schematics that configure the options that are available to the CLI for `ng generate` sub-commands, such as `ng generate component` and `ng generate service`. +The sub-commands for `ng generate` are shorthand for the corresponding schematic. +To specify and generate a particular schematic, or a collection of schematics, using the long form: + + + +ng generate my-schematic-collection:my-schematic-name + + + +or + + + +ng generate my-schematic-name --collection collection-name + + + +### Configuring CLI schematics + +A JSON schema associated with a schematic tells the Angular CLI what options are available to commands and sub-commands, and determines the defaults. +These defaults can be overridden by providing a different value for an option on the command line. +See [Workspace Configuration](reference/configs/workspace-config) for information about how to change the generation option defaults for your workspace. + +The JSON schemas for the default schematics used by the CLI to generate projects and parts of projects are collected in the package [`@schematics/angular`](https://github.com/angular/angular-cli/tree/main/packages/schematics/angular). +The schema describes the options available to the CLI for each of the `ng generate` sub-commands, as shown in the `--help` output. + +## Developing schematics for libraries + +As a library developer, you can create your own collections of custom schematics to integrate your library with the Angular CLI. + +* An *add schematic* lets developers install your library in an Angular workspace using `ng add` +* *Generation schematics* can tell the `ng generate` sub-commands how to modify projects, add configurations and scripts, and scaffold artifacts that are defined in your library +* An *update schematic* can tell the `ng update` command how to update your library's dependencies and adjust for breaking changes when you release a new version + +For more details of what these look like and how to create them, see: + + + + + + +### Add schematics + +An *add schematic* is typically supplied with a library, so that the library can be added to an existing project with `ng add`. +The `add` command uses your package manager to download new dependencies, and invokes an installation script that is implemented as a schematic. + +For example, the [`@angular/material`](https://material.angular.io/guide/schematics) schematic tells the `add` command to install and set up Angular Material and theming, and register new starter components that can be created with `ng generate`. +Look at this one as an example and model for your own add schematic. + +Partner and third party libraries also support the Angular CLI with add schematics. +For example, `@ng-bootstrap/schematics` adds [ng-bootstrap](https://ng-bootstrap.github.io) to an app, and `@clr/angular` installs and sets up [Clarity from VMWare](https://clarity.design/documentation/get-started). + +An *add schematic* can also update a project with configuration changes, add additional dependencies \(such as polyfills\), or scaffold package-specific initialization code. +For example, the `@angular/pwa` schematic turns your application into a PWA by adding an application manifest and service worker. + +### Generation schematics + +Generation schematics are instructions for the `ng generate` command. +The documented sub-commands use the default Angular generation schematics, but you can specify a different schematic \(in place of a sub-command\) to generate an artifact defined in your library. + +Angular Material, for example, supplies generation schematics for the UI components that it defines. +The following command uses one of these schematics to render an Angular Material `` that is pre-configured with a datasource for sorting and pagination. + + + +ng generate @angular/material:table + + + +### Update schematics + + The `ng update` command can be used to update your workspace's library dependencies. + If you supply no options or use the help option, the command examines your workspace and suggests libraries to update. + + + +ng update +We analyzed your package.json, there are some packages to update: + + Name Version Command to update + ‐------------------------------------------------------------------------------- + @angular/cdk 7.2.2 -> 7.3.1 ng update @angular/cdk + @angular/cli 7.2.3 -> 7.3.0 ng update @angular/cli + @angular/core 7.2.2 -> 7.2.3 ng update @angular/core + @angular/material 7.2.2 -> 7.3.1 ng update @angular/material + rxjs 6.3.3 -> 6.4.0 ng update rxjs + + There might be additional packages that are outdated. + Run "ng update --all" to try to update all at the same time. + + + +If you pass the command a set of libraries to update \(or the `--all` flag\), it updates those libraries, their peer dependencies, and the peer dependencies that depend on them. + +HELPFUL: If there are inconsistencies \(for example, if peer dependencies cannot be matched by a simple [semver](https://semver.io) range\), the command generates an error and does not change anything in the workspace. + +We recommend that you do not force an update of all dependencies by default. +Try updating specific dependencies first. + +For more about how the `ng update` command works, see [Update Command](https://github.com/angular/angular-cli/blob/main/docs/specifications/update.md). + +If you create a new version of your library that introduces potential breaking changes, you can provide an *update schematic* to enable the `ng update` command to automatically resolve any such changes in the project being updated. + +For example, suppose you want to update the Angular Material library. + + +ng update @angular/material + + +This command updates both `@angular/material` and its dependency `@angular/cdk` in your workspace's `package.json`. +If either package contains an update schematic that covers migration from the existing version to a new version, the command runs that schematic on your workspace. diff --git a/adev-ja/src/content/tools/cli/serve.md b/adev-ja/src/content/tools/cli/serve.md new file mode 100644 index 0000000000..c288f008cc --- /dev/null +++ b/adev-ja/src/content/tools/cli/serve.md @@ -0,0 +1,90 @@ +# Serving Angular apps for development + +You can serve your Angular CLI application with the `ng serve` command. +This will compile your application, skip unnecessary optimizations, start a devserver, and automatically rebuild and live reload any subsequent changes. +You can stop the server by pressing `Ctrl+C`. + +`ng serve` only executes the builder for the `serve` target in the default project as specified in `angular.json`. +While any builder can be used here, the most common (and default) builder is `@angular-devkit/build-angular:dev-server`. + +You can determine which builder is being used for a particular project by looking up the `serve` target for that project. + + + +{ + "projects": { + "my-app": { + "architect": { + // `ng serve` invokes the Architect target named `serve`. + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + // ... + }, + "build": { /* ... */ } + "test": { /* ... */ } + } + } + } +} + + + +This page discusses usage and options of `@angular-devkit/build-angular:dev-server`. + +## Proxying to a backend server + +Use [proxying support](https://webpack.js.org/configuration/dev-server/#devserverproxy) to divert certain URLs to a backend server, by passing a file to the `--proxy-config` build option. +For example, to divert all calls for `http://localhost:4200/api` to a server running on `http://localhost:3000/api`, take the following steps. + +1. Create a file `proxy.conf.json` in your project's `src/` folder. +1. Add the following content to the new proxy file: + + + + { + "/api": { + "target": "http://localhost:3000", + "secure": false + } + } + + + +1. In the CLI configuration file, `angular.json`, add the `proxyConfig` option to the `serve` target: + + + + { + "projects": { + "my-app": { + "architect": { + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "proxyConfig": "src/proxy.conf.json" + } + } + } + } + } + } + + + +1. To run the development server with this proxy configuration, call `ng serve`. + +Edit the proxy configuration file to add configuration options; following are some examples. +For a description of all options, see [webpack DevServer documentation](https://webpack.js.org/configuration/dev-server/#devserverproxy). + +NOTE: If you edit the proxy configuration file, you must relaunch the `ng serve` process to make your changes effective. + +## `localhost` resolution + +As of Node version 17, Node will _not_ always resolve `http://localhost:` to `http://127.0.0.1:` +depending on each machine's configuration. + +If you get an `ECONNREFUSED` error using a proxy targeting a `localhost` URL, +you can fix this issue by updating the target from `http://localhost:` to `http://127.0.0.1:`. + +See [the `http-proxy-middleware` documentation](https://github.com/chimurai/http-proxy-middleware#nodejs-17-econnrefused-issue-with-ipv6-and-localhost-705) +for more information. diff --git a/adev-ja/src/content/tools/cli/setup-local.md b/adev-ja/src/content/tools/cli/setup-local.md new file mode 100644 index 0000000000..f540d12280 --- /dev/null +++ b/adev-ja/src/content/tools/cli/setup-local.md @@ -0,0 +1,135 @@ +# Setting up the local environment and workspace + +This guide explains how to set up your environment for Angular development using the [Angular CLI](cli "CLI command reference"). +It includes information about installing the CLI, creating an initial workspace and starter app, and running that app locally to verify your setup. + + + +If you are new to Angular, you might want to start with [Try it now!](tutorials/learn-angular), which introduces the essentials of Angular in your browser. +This standalone tutorial takes advantage of the interactive [StackBlitz](https://stackblitz.com) environment for online development. +You don't need to set up your local environment until you're ready. + + + +## Before you start + +To use Angular CLI, you should be familiar with the following: + + + + + + + +You should also be familiar with usage of command line interface (CLI) tools and have a general understanding of command shells. +Knowledge of [TypeScript](https://www.typescriptlang.org) is helpful, but not required. + +## Dependencies + +To install Angular CLI on your local system, you need to install [Node.js](https://nodejs.org/). +Angular CLI uses Node and its associated package manager, npm, to install and run JavaScript tools outside the browser. + +[Download and install Node.js](https://nodejs.org/en/download), which will include the `npm` CLI as well. +Angular requires an [active LTS or maintenance LTS](https://nodejs.org/en/about/previous-releases) version of Node.js. +See [Angular's version compatibility](reference/versions) guide for more information. + +## Install the Angular CLI + +To install the Angular CLI, open a terminal window and run the following command: + + + +npm install -g @angular/cli + + + +### Powershell execution policy + +On Windows client computers, the execution of PowerShell scripts is disabled by default, so the above command may fail with an error. +To allow the execution of PowerShell scripts, which is needed for npm global binaries, you must set the following execution policy: + + + +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned + + + +Carefully read the message displayed after executing the command and follow the instructions. Make sure you understand the implications of setting an execution policy. + +### Unix permissions + +On some Unix-like setups, global npm scripts may be owned by the root user, so to the above command may fail with a permission error. +Run with `sudo` to execute the command as the root user and enter your password when prompted: + + + +sudo npm install -g @angular/cli + + + +Make sure you understand the implications of running commands as root. + +## Create a workspace and initial application + +You develop apps in the context of an Angular **workspace**. + +To create a new workspace and initial starter app, run the CLI command `ng new` and provide the name `my-app`, as shown here, then answer prompts about features to include: + + + +ng new my-app + + + +The Angular CLI installs the necessary Angular npm packages and other dependencies. +This can take a few minutes. + +The CLI creates a new workspace and a small welcome app in a new directory with the same name as the workspace, ready to run. +Navigate to the new directory so subsequent commands use this workspace. + + + +cd my-app + + + +## Run the application + +The Angular CLI includes a development server, for you to build and serve your app locally. Run the following command: + + + +ng serve --open + + + +The `ng serve` command launches the server, watches your files, as well as rebuilds the app and reloads the browser as you make changes to those files. + +The `--open` (or just `-o`) option automatically opens your browser to `http://localhost:4200/` to view the generated application. + +## Workspaces and project files + +The [`ng new`](cli/new) command creates an [Angular workspace](reference/configs/workspace-config) folder and generates a new application inside it. +A workspace can contain multiple applications and libraries. +The initial application created by the [`ng new`](cli/new) command is at the root directory of the workspace. +When you generate an additional application or library in an existing workspace, it goes into a `projects/` subfolder by default. + +A newly generated application contains the source files for a root component and template. +Each application has a `src` folder that contains its components, data, and assets. + +You can edit the generated files directly, or add to and modify them using CLI commands. +Use the [`ng generate`](cli/generate) command to add new files for additional components, directives, pipes, services, and more. +Commands such as [`ng add`](cli/add) and [`ng generate`](cli/generate), which create or operate on applications and libraries, must be executed +from within a workspace. By contrast, commands such as `ng new` must be executed *outside* a workspace because they will create a new one. + +## Next steps + +* Learn more about the [file structure](reference/configs/file-structure) and [configuration](reference/configs/workspace-config) of the generated workspace. + +* Test your new application with [`ng test`](cli/test). + +* Generate boilerplate like components, directives, and pipes with [`ng generate`](cli/generate). + +* Deploy your new application and make it available to real users with [`ng deploy`](cli/deploy). + +* Set up and run end-to-end tests of your application with [`ng e2e`](cli/e2e). diff --git a/adev-ja/src/content/tools/cli/template-typecheck.md b/adev-ja/src/content/tools/cli/template-typecheck.md new file mode 100644 index 0000000000..f2476811a0 --- /dev/null +++ b/adev-ja/src/content/tools/cli/template-typecheck.md @@ -0,0 +1,341 @@ +# Template type checking + +## Overview of template type checking + +Just as TypeScript catches type errors in your code, Angular checks the expressions and bindings within the templates of your application and can report any type errors it finds. +Angular currently has three modes of doing this, depending on the value of the `fullTemplateTypeCheck` and `strictTemplates` flags in [Angular's compiler options](reference/configs/angular-compiler-options). + +### Basic mode + +In the most basic type-checking mode, with the `fullTemplateTypeCheck` flag set to `false`, Angular validates only top-level expressions in a template. + +If you write ``, the compiler verifies the following: + +* `user` is a property on the component class +* `user` is an object with an address property +* `user.address` is an object with a city property + +The compiler does not verify that the value of `user.address.city` is assignable to the city input of the `` component. + +The compiler also has some major limitations in this mode: + +* Importantly, it doesn't check embedded views, such as `*ngIf`, `*ngFor`, other `` embedded view. +* It doesn't figure out the types of `#refs`, the results of pipes, or the type of `$event` in event bindings. + +In many cases, these things end up as type `any`, which can cause subsequent parts of the expression to go unchecked. + +### Full mode + +If the `fullTemplateTypeCheck` flag is set to `true`, Angular is more aggressive in its type-checking within templates. +In particular: + +* Embedded views \(such as those within an `*ngIf` or `*ngFor`\) are checked +* Pipes have the correct return type +* Local references to directives and pipes have the correct type \(except for any generic parameters, which will be `any`\) + +The following still have type `any`. + +* Local references to DOM elements +* The `$event` object +* Safe navigation expressions + +IMPORTANT: The `fullTemplateTypeCheck` flag has been deprecated in Angular 13. +The `strictTemplates` family of compiler options should be used instead. + +### Strict mode + +Angular maintains the behavior of the `fullTemplateTypeCheck` flag, and introduces a third "strict mode". +Strict mode is a superset of full mode, and is accessed by setting the `strictTemplates` flag to true. +This flag supersedes the `fullTemplateTypeCheck` flag. + +In addition to the full mode behavior, Angular does the following: + +* Verifies that component/directive bindings are assignable to their `@Input()`s +* Obeys TypeScript's `strictNullChecks` flag when validating the preceding mode +* Infers the correct type of components/directives, including generics +* Infers template context types where configured \(for example, allowing correct type-checking of `NgFor`\) +* Infers the correct type of `$event` in component/directive, DOM, and animation event bindings +* Infers the correct type of local references to DOM elements, based on the tag name \(for example, the type that `document.createElement` would return for that tag\) + +## Checking of `*ngFor` + +The three modes of type-checking treat embedded views differently. +Consider the following example. + + + +interface User { + name: string; + address: { + city: string; + state: string; + } +} + + + + + +
+

{{config.title}}

+ City: {{user.address.city}} +
+ +
+ +The `

` and the `` are in the `*ngFor` embedded view. +In basic mode, Angular doesn't check either of them. +However, in full mode, Angular checks that `config` and `user` exist and assumes a type of `any`. +In strict mode, Angular knows that the `user` in the `` has a type of `User`, and that `address` is an object with a `city` property of type `string`. + +## Troubleshooting template errors + +With strict mode, you might encounter template errors that didn't arise in either of the previous modes. +These errors often represent genuine type mismatches in the templates that were not caught by the previous tooling. +If this is the case, the error message should make it clear where in the template the problem occurs. + +There can also be false positives when the typings of an Angular library are either incomplete or incorrect, or when the typings don't quite line up with expectations as in the following cases. + +* When a library's typings are wrong or incomplete \(for example, missing `null | undefined` if the library was not written with `strictNullChecks` in mind\) +* When a library's input types are too narrow and the library hasn't added appropriate metadata for Angular to figure this out. + This usually occurs with disabled or other common Boolean inputs used as attributes, for example, ``. + +* When using `$event.target` for DOM events \(because of the possibility of event bubbling, `$event.target` in the DOM typings doesn't have the type you might expect\) + +In case of a false positive like these, there are a few options: + +* Use the `$any()` type-cast function in certain contexts to opt out of type-checking for a part of the expression +* Disable strict checks entirely by setting `strictTemplates: false` in the application's TypeScript configuration file, `tsconfig.json` +* Disable certain type-checking operations individually, while maintaining strictness in other aspects, by setting a *strictness flag* to `false` +* If you want to use `strictTemplates` and `strictNullChecks` together, opt out of strict null type checking specifically for input bindings using `strictNullInputTypes` + +Unless otherwise commented, each following option is set to the value for `strictTemplates` \(`true` when `strictTemplates` is `true` and conversely, the other way around\). + +| Strictness flag | Effect | +|:--- |:--- | +| `strictInputTypes` | Whether the assignability of a binding expression to the `@Input()` field is checked. Also affects the inference of directive generic types. | +| `strictInputAccessModifiers` | Whether access modifiers such as `private`/`protected`/`readonly` are honored when assigning a binding expression to an `@Input()`. If disabled, the access modifiers of the `@Input` are ignored; only the type is checked. This option is `false` by default, even with `strictTemplates` set to `true`. | +| `strictNullInputTypes` | Whether `strictNullChecks` is honored when checking `@Input()` bindings \(per `strictInputTypes`\). Turning this off can be useful when using a library that was not built with `strictNullChecks` in mind. | +| `strictAttributeTypes` | Whether to check `@Input()` bindings that are made using text attributes. For example, \(setting the `disabled` property to the string `'true'`\) vs \(setting the `disabled` property to the boolean `true`\). | +| `strictSafeNavigationTypes` | Whether the return type of safe navigation operations \(for example, `user?.name` will be correctly inferred based on the type of `user`\). If disabled, `user?.name` will be of type `any`. | +| `strictDomLocalRefTypes` | Whether local references to DOM elements will have the correct type. If disabled `ref` will be of type `any` for ``. | +| `strictOutputEventTypes` | Whether `$event` will have the correct type for event bindings to component/directive an `@Output()`, or to animation events. If disabled, it will be `any`. | +| `strictDomEventTypes` | Whether `$event` will have the correct type for event bindings to DOM events. If disabled, it will be `any`. | +| `strictContextGenerics` | Whether the type parameters of generic components will be inferred correctly \(including any generic bounds\). If disabled, any type parameters will be `any`. | +| `strictLiteralTypes` | Whether object and array literals declared in the template will have their type inferred. If disabled, the type of such literals will be `any`. This flag is `true` when *either* `fullTemplateTypeCheck` or `strictTemplates` is set to `true`. | + +If you still have issues after troubleshooting with these flags, fall back to full mode by disabling `strictTemplates`. + +If that doesn't work, an option of last resort is to turn off full mode entirely with `fullTemplateTypeCheck: false`. + +A type-checking error that you cannot resolve with any of the recommended methods can be the result of a bug in the template type-checker itself. +If you get errors that require falling back to basic mode, it is likely to be such a bug. +If this happens, [file an issue](https://github.com/angular/angular/issues) so the team can address it. + +## Inputs and type-checking + +The template type checker checks whether a binding expression's type is compatible with that of the corresponding directive input. +As an example, consider the following component: + + + +export interface User { + name: string; +} + +@Component({ + selector: 'user-detail', + template: '{{ user.name }}', +}) +export class UserDetailComponent { + @Input() user: User; +} + + + +The `AppComponent` template uses this component as follows: + + + +@Component({ + selector: 'app-root', + template: '', +}) +export class AppComponent { + selectedUser: User | null = null; +} + + + +Here, during type checking of the template for `AppComponent`, the `[user]="selectedUser"` binding corresponds with the `UserDetailComponent.user` input. +Therefore, Angular assigns the `selectedUser` property to `UserDetailComponent.user`, which would result in an error if their types were incompatible. +TypeScript checks the assignment according to its type system, obeying flags such as `strictNullChecks` as they are configured in the application. + +Avoid run-time type errors by providing more specific in-template type requirements to the template type checker. +Make the input type requirements for your own directives as specific as possible by providing template-guard functions in the directive definition. +See [Improving template type checking for custom directives](guide/directives/structural-directives#directive-type-checks) in this guide. + +### Strict null checks + +When you enable `strictTemplates` and the TypeScript flag `strictNullChecks`, typecheck errors might occur for certain situations that might not easily be avoided. +For example: + +* A nullable value that is bound to a directive from a library which did not have `strictNullChecks` enabled. + + For a library compiled without `strictNullChecks`, its declaration files will not indicate whether a field can be `null` or not. + For situations where the library handles `null` correctly, this is problematic, as the compiler will check a nullable value against the declaration files which omit the `null` type. + As such, the compiler produces a type-check error because it adheres to `strictNullChecks`. + +* Using the `async` pipe with an Observable which you know will emit synchronously. + + The `async` pipe currently assumes that the Observable it subscribes to can be asynchronous, which means that it's possible that there is no value available yet. + In that case, it still has to return something —which is `null`. + In other words, the return type of the `async` pipe includes `null`, which might result in errors in situations where the Observable is known to emit a non-nullable value synchronously. + +There are two potential workarounds to the preceding issues: + +* In the template, include the non-null assertion operator `!` at the end of a nullable expression, such as + + + + + + + + In this example, the compiler disregards type incompatibilities in nullability, just as in TypeScript code. + In the case of the `async` pipe, notice that the expression needs to be wrapped in parentheses, as in + + + + + + + +* Disable strict null checks in Angular templates completely. + + When `strictTemplates` is enabled, it is still possible to disable certain aspects of type checking. + Setting the option `strictNullInputTypes` to `false` disables strict null checks within Angular templates. + This flag applies for all components that are part of the application. + +### Advice for library authors + +As a library author, you can take several measures to provide an optimal experience for your users. +First, enabling `strictNullChecks` and including `null` in an input's type, as appropriate, communicates to your consumers whether they can provide a nullable value or not. +Additionally, it is possible to provide type hints that are specific to the template type checker. +See [Improving template type checking for custom directives](guide/directives/structural-directives#directive-type-checks), and [Input setter coercion](#input-setter-coercion). + +## Input setter coercion + +Occasionally it is desirable for the `@Input()` of a directive or component to alter the value bound to it, typically using a getter/setter pair for the input. +As an example, consider this custom button component: + +Consider the following directive: + + + +@Component({ + selector: 'submit-button', + template: ` +
+ +
+ `, +}) +class SubmitButton { + private _disabled: boolean; + + @Input() + get disabled(): boolean { + return this._disabled; + } + + set disabled(value: boolean) { + this._disabled = value; + } +} + +
+ +Here, the `disabled` input of the component is being passed on to the ``, + ..., +}) +export class UserProfile { } + + +テンプレートで他のコンポーネントを参照して使用する方法は、[コンポーネントのインポートと使用](guide/components/importing)を参照してください。 + +Angular は、遭遇したすべてのマッチする HTML 要素に対して、コンポーネントのインスタンスを作成します。コンポーネントのセレクターと一致する DOM 要素は、そのコンポーネントの**ホスト要素**と呼ばれます。コンポーネントのテンプレートの内容は、そのホスト要素内にレンダリングされます。 + +コンポーネントによってレンダリングされた DOM(コンポーネントのテンプレートに対応)は、そのコンポーネントの**ビュー**と呼ばれます。 + +このようにコンポーネントを組み合わせることで、**Angular アプリケーションはコンポーネントのツリーとして考えることができます**。 + +```mermaid +flowchart TD + A[AccountSettings]-->B + A-->C + B[UserProfile]-->D + B-->E + C[PaymentInfo] + D[ProfilePic] + E[UserBio] +``` + + +このツリー構造は、[依存性の注入](guide/di)や[子クエリ](guide/components/queries)など、他のいくつかの Angular の概念を理解する上で重要です。 diff --git a/tools/translate.ts b/tools/translate.ts index e5b1bfa59a..ca81acf3ce 100644 --- a/tools/translate.ts +++ b/tools/translate.ts @@ -38,9 +38,12 @@ async function main() { // Execute translation const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash', - systemInstruction: `あなたはWebフロントエンド技術に関するドキュメントの翻訳者です。英語を含むテキストファイルを受け取り、日本語に翻訳します。 - 翻訳を行う際は元のテキストの構造を維持してください。レスポンスは翻訳後のファイルだけを出力してください。 - `, + systemInstruction: ` +あなたはWebフロントエンドに関する技術文書の翻訳アシスタントです。 +翻訳を行う際は元のテキストの形式や構造を維持してください。初心者にもわかりやすく平易な日本語に翻訳してください。 +入力: 英語を含むテキストファイル +出力: 翻訳後のテキスト + `.trim(), }); const result = await model.generateContentStream([ { @@ -75,10 +78,7 @@ async function main() { const outFilePath = file.replace(/\.en\.([^.]+)$/, '.$1'); const save = await consola.prompt( `翻訳結果を保存しますか?\n保存先: ${outFilePath}`, - { - type: 'confirm', - initial: false, - } + { type: 'confirm', initial: false } ); if (!save) { return; From 3ad395f47b15df524528e18d1f072c7f8e710fd8 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Tue, 16 Jul 2024 22:45:25 +0900 Subject: [PATCH 028/253] chore: remove accidentially added file --- .../guide/components/anatomy-of-components.md | 101 ------------------ 1 file changed, 101 deletions(-) delete mode 100644 adev-ja/src/content/guide/components/anatomy-of-components.md diff --git a/adev-ja/src/content/guide/components/anatomy-of-components.md b/adev-ja/src/content/guide/components/anatomy-of-components.md deleted file mode 100644 index a0eb4d37e0..0000000000 --- a/adev-ja/src/content/guide/components/anatomy-of-components.md +++ /dev/null @@ -1,101 +0,0 @@ - - - -ヒント: このガイドでは、すでに[基本ガイド](essentials)を読んでいることを前提としています。Angular を初めて使用する場合は、まずそちらを読んでください。 - -すべてのコンポーネントには、次のものが必要です。 - -* ユーザー入力の処理やサーバーからのデータの取得など、_動作_ を持つ TypeScript クラス -* DOM にレンダリングされる内容を制御する HTML テンプレート -* HTML でコンポーネントがどのように使用されるかを定義する[CSS セレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) - -TypeScript クラスの上に `@Component` [デコレーター](https://www.typescriptlang.org/docs/handbook/decorators.html) を追加することで、コンポーネントに Angular 固有の情報を提供します。 - - -@Component({ - selector: 'profile-photo', - template: `Your profile photo`, -}) -export class ProfilePhoto { } - - -Angular テンプレートの書き方については、[テンプレートガイド](guide/templates)を参照してください。 - -`@Component` デコレーターに渡されるオブジェクトは、コンポーネントの**メタデータ**と呼ばれます。これには、`selector`、`template`、およびこのガイド全体で説明されているその他のプロパティが含まれます。 - -コンポーネントには、そのコンポーネントの DOM に適用される CSS スタイルのリストをオプションで含めることができます。 - - -@Component({ - selector: 'profile-photo', - template: `Your profile photo`, - styles: `img { border-radius: 50%; }`, -}) -export class ProfilePhoto { } - - -デフォルトでは、コンポーネントのスタイルは、そのコンポーネントのテンプレートで定義された要素のみに影響します。Angular のスタイリングに関するアプローチの詳細については、[コンポーネントのスタイリング](guide/components/styling)を参照してください。 - -テンプレートとスタイルを別々のファイルに書くこともできます。 - - -@Component({ - selector: 'profile-photo', - templateUrl: 'profile-photo.html', - styleUrl: 'profile-photo.css', -}) -export class ProfilePhoto { } - - -これにより、プロジェクト内の_プレゼンテーション_と_動作_の懸念を分離できます。プロジェクト全体で一貫したアプローチを選択することも、コンポーネントごとにどちらを使用するかを決定することもできます。 - -`templateUrl` と `styleUrl` はどちらも、コンポーネントが存在するディレクトリからの相対パスです。 - -## コンポーネントの使用 - -すべてのコンポーネントは、[CSS セレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) を定義しています。 - - -@Component({ - selector: 'profile-photo', - ... -}) -export class ProfilePhoto { } - - -Angular がサポートするセレクターの種類や、セレクターの選択に関するガイダンスについては、[コンポーネントセレクター](guide/components/selectors)を参照してください。 - -コンポーネントは、_他の_コンポーネントのテンプレートで一致する HTML 要素を作成することで使用します。 - - -@Component({ - selector: 'user-profile', - template: ` - - `, - ..., -}) -export class UserProfile { } - - -テンプレートで他のコンポーネントを参照して使用する方法は、[コンポーネントのインポートと使用](guide/components/importing)を参照してください。 - -Angular は、遭遇したすべてのマッチする HTML 要素に対して、コンポーネントのインスタンスを作成します。コンポーネントのセレクターと一致する DOM 要素は、そのコンポーネントの**ホスト要素**と呼ばれます。コンポーネントのテンプレートの内容は、そのホスト要素内にレンダリングされます。 - -コンポーネントによってレンダリングされた DOM(コンポーネントのテンプレートに対応)は、そのコンポーネントの**ビュー**と呼ばれます。 - -このようにコンポーネントを組み合わせることで、**Angular アプリケーションはコンポーネントのツリーとして考えることができます**。 - -```mermaid -flowchart TD - A[AccountSettings]-->B - A-->C - B[UserProfile]-->D - B-->E - C[PaymentInfo] - D[ProfilePic] - E[UserBio] -``` - - -このツリー構造は、[依存性の注入](guide/di)や[子クエリ](guide/components/queries)など、他のいくつかの Angular の概念を理解する上で重要です。 From b049c92ea23196ac32ebdafcb0afae3f7e654ea1 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 17 Jul 2024 11:12:40 +0900 Subject: [PATCH 029/253] chore: use angular-jp search index (#937) --- tools/adev-patches/change-analytics-id.patch | 11 ----------- .../replace-environment-values.patch | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) delete mode 100644 tools/adev-patches/change-analytics-id.patch create mode 100644 tools/adev-patches/replace-environment-values.patch diff --git a/tools/adev-patches/change-analytics-id.patch b/tools/adev-patches/change-analytics-id.patch deleted file mode 100644 index 104086fd26..0000000000 --- a/tools/adev-patches/change-analytics-id.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/adev/src/app/environment.ts b/adev/src/app/environment.ts -index 30f0d78db3..c6c18b5183 100644 ---- a/adev/src/app/environment.ts -+++ b/adev/src/app/environment.ts -@@ -15,5 +15,5 @@ export default { - apiKey: 'dfca7ed184db27927a512e5c6668b968', - indexName: 'angular_v17', - }, -- googleAnalyticsId: 'G-XB6NEVW32B', -+ googleAnalyticsId: 'G-ZE76R447BW', - }; diff --git a/tools/adev-patches/replace-environment-values.patch b/tools/adev-patches/replace-environment-values.patch new file mode 100644 index 0000000000..f4bcb621df --- /dev/null +++ b/tools/adev-patches/replace-environment-values.patch @@ -0,0 +1,18 @@ +diff --git a/adev/src/app/environment.ts b/adev/src/app/environment.ts +index 30f0d78db3..ed6d52c14a 100644 +--- a/adev/src/app/environment.ts ++++ b/adev/src/app/environment.ts +@@ -11,9 +11,9 @@ export default { + // Those values are publicly visible in the search request headers, and presents search-only keys. + // https://www.algolia.com/doc/guides/security/api-keys/#search-only-api-key + algolia: { +- appId: 'L1XWT2UJ7F', +- apiKey: 'dfca7ed184db27927a512e5c6668b968', +- indexName: 'angular_v17', ++ appId: 'D4RZISVST0', ++ apiKey: '77e5a0684280325e2e1f313e0fcc11b8', ++ indexName: 'angular_jp_v18', + }, +- googleAnalyticsId: 'G-XB6NEVW32B', ++ googleAnalyticsId: 'G-ZE76R447BW', + }; From 0181a019b0ded6222ffa2d11ab0aa8633e693c48 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 17 Jul 2024 12:07:50 +0900 Subject: [PATCH 030/253] fix: translate signals (#938) --- .textlintrc | 4 +- adev-ja/src/content/guide/signals/inputs.md | 147 ++++++++++ adev-ja/src/content/guide/signals/model.md | 138 ++++++++++ adev-ja/src/content/guide/signals/overview.md | 251 ++++++++++++++++++ adev-ja/src/content/guide/signals/queries.md | 191 +++++++++++++ .../src/content/guide/signals/rxjs-interop.md | 131 +++++++++ .../steps/20-inject-based-di/README.md | 2 +- aio-ja/content/guide/signals.en.md | 250 ----------------- aio-ja/content/guide/signals.md | 250 ----------------- prh.yml | 39 ++- 10 files changed, 891 insertions(+), 512 deletions(-) create mode 100644 adev-ja/src/content/guide/signals/inputs.md create mode 100644 adev-ja/src/content/guide/signals/model.md create mode 100644 adev-ja/src/content/guide/signals/overview.md create mode 100644 adev-ja/src/content/guide/signals/queries.md create mode 100644 adev-ja/src/content/guide/signals/rxjs-interop.md delete mode 100644 aio-ja/content/guide/signals.en.md delete mode 100644 aio-ja/content/guide/signals.md diff --git a/.textlintrc b/.textlintrc index a53ae4e481..3454f71103 100644 --- a/.textlintrc +++ b/.textlintrc @@ -20,7 +20,9 @@ }, "no-doubled-joshi": { "allow": [ - "の" + "の", + "が", + "に" ] }, "no-exclamation-question-mark": false, diff --git a/adev-ja/src/content/guide/signals/inputs.md b/adev-ja/src/content/guide/signals/inputs.md new file mode 100644 index 0000000000..8ab7c75e24 --- /dev/null +++ b/adev-ja/src/content/guide/signals/inputs.md @@ -0,0 +1,147 @@ +# シグナル入力 + +シグナル入力を使用すると、親コンポーネントから値をバインドできます。 +これらの値は `Signal` を使用して公開され、コンポーネントのライフサイクル中に変化する可能性があります。 + +役に立つ情報: シグナル入力は現在、[開発者プレビュー](/reference/releases#developer-preview)にあります。 + +Angularは、2種類の入力をサポートしています。 + +**オプション入力** +`input.required` を使用しない限り、入力はデフォルトでオプショナルです。 +明示的な初期値を指定できます。指定しない場合、Angularは暗黙的に `undefined` を使用します。 + +**必須入力** +必須入力は常に、指定された入力タイプの値を持ちます。 +`input.required` 関数を使用して宣言されます。 + +```typescript +import {Component, input} from '@angular/core'; + +@Component({...}) +export class MyComp { + // オプション + firstName = input(); // InputSignal + age = input(0); // InputSignal + + // 必須 + lastName = input.required(); // InputSignal +} +``` + +クラスメンバーのイニシャライザーとして `input` または `input.required` 関数を使用すると、Angularは自動的に入力を認識します。 + +## 入力に別名をつける + +Angularは、クラスメンバー名を入力の名前として使用します。 +別名を使用すると、公開名を変更できます。 + +```typescript +class StudentDirective { + age = input(0, {alias: 'studentAge'}); +} +``` + +これにより、ユーザーは `[studentAge]` を使用して入力にバインドできます。一方、コンポーネント内では `this.age` を使用して入力値にアクセスできます。 + +## テンプレートでの使用 + +シグナル入力は、読み取り専用のシグナルです。 +`signal()` を使用して宣言されたシグナルと同様に、入力シグナルを呼び出すことで、入力の現在の値にアクセスできます。 + +```html +

First name: {{firstName()}}

+

Last name: {{lastName()}}

+``` + +この値へのアクセスは、リアクティブなコンテキストでキャプチャされ、入力値が変更されるたびに、Angular自身などのアクティブなコンシューマーに通知できます。 + +実際には、入力シグナルは、[シグナルガイド](guide/signals)で知られているシグナルの単純な拡張です。 + +```typescript +export class InputSignal extends Signal { ... }`. +``` + +## 値の派生 + +シグナルと同様に、`computed` を使用して入力から値を派生できます。 + +```typescript +import {Component, input, computed} from '@angular/core'; + +@Component({...}) +export class MyComp { + age = input(0); + + // 年齢を 2 倍した値。 + ageMultiplied = computed(() => this.age() * 2); +} +``` + +算出シグナルは、値をメモ化します。 +詳細については、[計算されたシグナルに関するセクション](guide/signals#computed-signals)を参照してください。 + +## 変更の監視 + +シグナル入力を使用すると、ユーザーは `effect` 関数を利用できます。 +この関数は、入力が変更されるたびに実行されます。 + +次の例を考えてみましょう。 +`firstName` 入力が変更されるたびに、新しい値がコンソールに出力されます。 + +```typescript +import {input, effect} from '@angular/core'; + +class MyComp { + firstName = input.required(); + + constructor() { + effect(() => { + console.log(this.firstName()); + }); + } +} +``` + +`console.log` 関数は、`firstName` 入力が変更されるたびに呼び出されます。 +これは、`firstName` が使用可能になった直後と、`MyComp` のライフサイクル中の後続の変更に対して発生します。 + +## 値の変換 + +入力の値を、その意味を変更せずに、強制変換または解析したい場合があります。 +変換は、親テンプレートからの生の値を、期待される型に変換します。 +変換は、[純粋関数](https://en.wikipedia.org/wiki/Pure_function)である必要があります。 + +```typescript +class MyComp { + disabled = input(false, { + transform: (value: boolean|string) => typeof value === 'string' ? value === '' : value, + }); +} +``` + +上記の例では、`disabled` という名前の入力を宣言しています。この入力は、`boolean` 型と `string` 型の値を受け入れます。 +これは、`transform` オプションの `value` の明示的なパラメーター型によってキャプチャされます。 +これらの値は、変換によって `boolean` に解析され、`boolean` になります。 + +このように、`this.disabled()` を呼び出す際に、コンポーネント内では `boolean` のみを使用できます。一方、コンポーネントのユーザーは、空の文字列を省略記号として渡して、コンポーネントを無効にできます。 + +```html + +``` + +重要: 入力の意味を変更する場合、または[不純な](https://en.wikipedia.org/wiki/Pure_function#Impure_functions)関数の場合は、変換を使用しないでください。 +代わりに、意味が異なる変換には `computed` を、入力が変更されるたびに実行されるべき不純なコードには `effect` を使用してください。 + +## なぜシグナル入力を使用すべきなのか、`@Input()` を使用すべきではないのか + +シグナル入力は、デコレーターベースの `@Input()` のリアクティブな代替手段です。 + +デコレーターベースの `@Input` と比較して、シグナル入力は多くの利点があります。 + +1. シグナル入力は、より**型安全**です。 +
• 必須入力は、初期値や、入力に常に値があることをTypeScriptに伝えるためのトリックを必要としません。 +
• 変換は、受け入れられた入力値と一致するように自動的にチェックされます。 +2. テンプレートで使用されるシグナル入力は、`OnPush` コンポーネントを**自動的に**ダーティにします。 +3. 入力が変更されるたびに、`computed` を使用して簡単に値を**派生**できます。 +4. `ngOnChanges` やセッターの代わりに、`effect` を使用することで、入力の監視が簡単になり、より局所的になります。 diff --git a/adev-ja/src/content/guide/signals/model.md b/adev-ja/src/content/guide/signals/model.md new file mode 100644 index 0000000000..6a7c8f8a92 --- /dev/null +++ b/adev-ja/src/content/guide/signals/model.md @@ -0,0 +1,138 @@ +# モデル入力 + +**モデル入力** は、コンポーネントが新しい値を別のコンポーネントに伝播できるようにする、 +特殊な入力です。 + +HELPFUL: モデル入力は現在 [開発者プレビュー](/reference/releases#developer-preview) です。 + +コンポーネントを作成するときは、普通の入力を作成する方法と同様に、 +モデル入力を定義できます。 + +```typescript +import {Component, model, input} from '@angular/core'; + +@Component({...}) +export class CustomCheckbox { + // これはモデル入力です。 + checked = model(false); + + // これは普通の入力です。 + disabled = input(false); +} +``` + +2種類の入力はどちらも、値をプロパティにバインドすることを可能にします。 +ただし、**モデル入力を使用すると、コンポーネントの作者はプロパティに値を書き込むことができます**。 + +その他の点では、モデル入力を普通の入力と同じように使用できます。 +`computed` や `effect` などのリアクティブコンテキストを含め、シグナル関数を呼び出して値を読み取ることができます。 + +```typescript +import {Component, model, input} from '@angular/core'; + +@Component({ + selector: 'custom-checkbox', + template: '
...
', +}) +export class CustomCheckbox { + checked = model(false); + disabled = input(false); + + toggle() { + // 普通の入力は読み取り専用ですが、モデル入力には直接書き込むことができます。 + this.checked.set(!this.checked()); + } +} +``` + +コンポーネントがモデル入力に新しい値を書き込むと、 +Angularはその入力に値をバインドしているコンポーネントに新しい値を伝播できます。 +これは、値が双方向に流れるため、**双方向バインディング** と呼ばれます。 + +## シグナルによる双方向バインディング + +書き込み可能なシグナルをモデル入力にバインドできます。 + +```typescript +@Component({ + ..., + // `checked` はモデル入力です。 + // 括弧内角括弧構文(別名「バナナインボックス」)は、双方向バインディングを作成します。 + template: '', +}) +export class UserProfile { + protected isAdmin = signal(false); +} +``` + +上記の例では、`CustomCheckbox` は `checked` モデル入力に値を書き込むことができ、 +その値は `UserProfile` の `isAdmin` シグナルに伝播されます。 +このバインディングにより、`checked` と `isAdmin` の値が同期されます。 +バインディングは `isAdmin` シグナル自体を渡し、シグナルの _値_ は渡さないことに注意してください。 + +## プレーンなプロパティによる双方向バインディング + +プレーンなJavaScriptプロパティをモデル入力にバインドできます。 + +```typescript +@Component({ + ..., + // `checked` はモデル入力です。 + // 括弧内角括弧構文(別名「バナナインボックス」)は、双方向バインディングを作成します。 + template: '', +}) +export class UserProfile { + protected isAdmin = false; +} +``` + +上記の例では、`CustomCheckbox` は `checked` モデル入力に値を書き込むことができ、 +その値は `UserProfile` の `isAdmin` プロパティに伝播されます。 +このバインディングにより、`checked` と `isAdmin` の値が同期されます。 + +## 暗黙的な `change` イベント + +コンポーネントまたはディレクティブでモデル入力を宣言すると、 +Angularはそのモデルに対応する [出力](guide/components/outputs) を自動的に作成します。 +出力の名前は、モデル入力の名前の後に「Change」が付加されたものです。 + +```typescript +@Directive({...}) +export class CustomCheckbox { + // これは、自動的に「checkedChange」という名前の出力を作成します。 + // テンプレートで `(checkedChange)="handler()"` を使用して購読できます。 + checked = model(false); +} +``` + +`set` または `update` メソッドを呼び出してモデル入力に新しい値を書き込むたびに、 +Angularはこの変更イベントを発行します。 + +## モデル入力のカスタマイズ + +普通の入力と同様に、モデル入力を必須としてマークしたり、 +別名を提供したりできます。 + +モデル入力は、入力の変換をサポートしていません。 + +## `model()` と `input()` の違い + +`input()` と `model()` の両方の関数は、Angularで信号ベースの入力を定義する方法ですが、 +いくつかの違いがあります。 +1. `model()` は、**入力と出力の両方** を定義します。 +出力の名前は常に、双方向バインディングをサポートするために、入力名に `Change` が付加されたものです。 +ディレクティブの利用者は、入力のみ、出力のみ、または両方を使用するかを決定します。 +2. `ModelSignal` は `WritableSignal` であり、 +`set` メソッドと `update` メソッドを使用して、どこからでも値を変更できます。 +新しい値が割り当てられると、`ModelSignal` は出力にイベントを発行します。 +これは、読み取り専用で、テンプレートを通じてのみ変更できる `InputSignal` とは異なります。 +3. モデル入力は入力変換をサポートしませんが、信号入力はサポートします。 + +## いつモデル入力を使用すべきか + +ユーザーの操作に基づいて値を変更するために存在するコンポーネントでモデル入力を使用します。 +日付ピッカーやコンボボックスなどのカスタムフォームコントロールは、 +主要な値にモデル入力を使用する必要があります。 + +ローカルな状態を保持するための追加のクラスプロパティを導入することを避けるための便宜として、 +モデル入力を使用しないでください。 diff --git a/adev-ja/src/content/guide/signals/overview.md b/adev-ja/src/content/guide/signals/overview.md new file mode 100644 index 0000000000..1a004ed59a --- /dev/null +++ b/adev-ja/src/content/guide/signals/overview.md @@ -0,0 +1,251 @@ + +Angularシグナルは、アプリケーション全体で状態がどのように使用されているかを細かく追跡するシステムであり、フレームワークがレンダリングの更新を最適化することを可能にします。 + + +ヒント: この包括的なガイドを読む前に、Angularの[基本概念](essentials/managing-dynamic-data)をご覧ください。 + +## シグナルとは何か? + +**シグナル**は、値が変更されたときに興味のあるコンシューマーに通知する、値をラップしたものです。シグナルは、プリミティブから複雑なデータ構造まで、あらゆる値を含めることができます。 + +シグナルの値は、そのゲッター関数を呼び出すことで読み取ることができます。これは、Angularがシグナルがどこで使用されているかを追跡することを可能にします。 + +シグナルは、*書き込み可能*または*読み取り専用*のいずれかになります。 + +### 書き込み可能なシグナル + +書き込み可能なシグナルは、値を直接更新するためのAPIを提供します。書き込み可能なシグナルは、シグナルの初期値を指定して`signal`関数を呼び出すことで作成します。 + +```ts +const count = signal(0); + +// シグナルはゲッター関数です - 関数を呼び出すことで値を読み取ります。 +console.log('The count is: ' + count()); +``` + +書き込み可能なシグナルの値を変更するには、`.set()`で直接設定します。 + +```ts +count.set(3); +``` + +または、`.update()`メソッドを使用して、前の値から新しい値を計算します。 + +```ts +// カウントを1増やす。 +count.update(value => value + 1); +``` + +書き込み可能なシグナルは、`WritableSignal`という型になります。 + +### 算出シグナル + +**算出シグナル**は、他のシグナルから値を派生させる読み取り専用のシグナルです。算出シグナルは、`computed`関数を使用して、派生を指定することで定義します。 + +```typescript +const count: WritableSignal = signal(0); +const doubleCount: Signal = computed(() => count() * 2); +``` + +`doubleCount` シグナルは、`count` シグナルに依存しています。`count`が更新されるたびに、Angularは`doubleCount`も更新する必要があることを認識します。 + +#### 算出シグナルは、遅延評価とメモ化が行われる + +`doubleCount`の派生関数は、最初に`doubleCount`を読み取るまで、その値を計算するために実行されません。計算された値はキャッシュされ、`doubleCount`を再び読み取ると、再計算せずにキャッシュされた値が返されます。 + +その後、`count`を変更すると、Angularは`doubleCount`のキャッシュされた値がもはや有効ではなくなり、次に`doubleCount`を読み取るときに新しい値が計算されることを認識します。 + +その結果、配列のフィルタリングなど、計算量が多い派生を算出シグナルで安全に実行できます。 + +#### 算出シグナルは、書き込み可能なシグナルではない + +算出シグナルに値を直接割り当てることはできません。つまり、 + +```ts +doubleCount.set(3); +``` + +はコンパイルエラーになります。なぜなら、`doubleCount`は`WritableSignal`ではないからです。 + +#### 算出シグナルの依存関係は動的である + +派生中に実際に読み取られたシグナルのみが追跡されます。たとえば、この`computed`では、`count` シグナルは`showCount` シグナルが真の場合にのみ読み取られます。 + +```ts +const showCount = signal(false); +const count = signal(0); +const conditionalCount = computed(() => { + if (showCount()) { + return `The count is ${count()}.`; + } else { + return 'Nothing to see here!'; + } +}); +``` + +`conditionalCount`を読み取ると、`showCount`が偽の場合、`count` シグナルを読み取ることなく、「Nothing to see here!」というメッセージが返されます。これは、後で`count`を更新しても、`conditionalCount`のキャッシュされた値は再計算されないことを意味します。 + +`showCount`を真に設定して`conditionalCount`を再び読み取ると、派生が再実行されます。`showCount`が真のブランチに移り、`count`の値を示すメッセージが返されます。その後、`count`を変更すると、`conditionalCount`のキャッシュされた値が無効になります。 + +依存関係は、派生中に追加されるだけでなく、削除されることもできます。後で`showCount`を再び偽に設定すると、`count`は`conditionalCount`の依存関係として扱われなくなります。 + +## `OnPush`コンポーネントでのシグナルの読み取り + +`OnPush`コンポーネントのテンプレート内でシグナルを読み取ると、Angularはシグナルをそのコンポーネントの依存関係として追跡します。そのシグナルの値が変更されると、Angularは自動的にコンポーネントを[マーク](api/core/ChangeDetectorRef#markforcheck)して、次に変更検知が実行されたとき更新されるようにします。`OnPush`コンポーネントの詳細については、[コンポーネントのサブツリーをスキップする](best-practices/skipping-subtrees)ガイドを参照してください。 + +## エフェクト + +シグナルは、変更時に興味のあるコンシューマーへ通知するのに役立ちます。**エフェクト**は、1つ以上のシグナル値が変更されたときに実行される操作です。`effect`関数を使用してエフェクトを作成できます。 + +```ts +effect(() => { + console.log(`The current count is: ${count()}`); +}); +``` + +エフェクトは常に**少なくとも1回**実行されます。エフェクトが実行されると、シグナル値の読み取りをすべて追跡します。これらのシグナル値のいずれかが変更されると、エフェクトが再び実行されます。算出シグナルと同様に、エフェクトは依存関係を動的に追跡し、最新の処理で読み取られたシグナルのみを追跡します。 + +エフェクトは常に変更検知プロセス中に**非同期**で実行されます。 + +### エフェクトのユースケース + +エフェクトは、ほとんどのアプリケーションコードでは必要ありませんが、特定の状況では役立つ場合があります。以下は、`effect`が適切なソリューションとなる状況の例です。 + +* 表示されているデータとその変更をログに記録する。これは、分析やデバッグツールの目的で行います。 +* データを`window.localStorage`と同期させる。 +* テンプレート構文では表現できないカスタムDOM動作を追加する。 +* ``、チャートライブラリ、またはその他のサードパーティUIライブラリにカスタムレンダリングを実行する。 + + +状態変更の伝播にエフェクトを使用することは避けてください。これは、`ExpressionChangedAfterItHasBeenChecked`エラー、無限の循環更新、または不要な変更検知サイクルが発生する可能性があります。 + +これらのリスクのため、Angularはデフォルトで、エフェクト内でシグナルを設定することを防ぎます。これは、エフェクトを作成する際に`allowSignalWrites`フラグを設定することで有効にできます。 + +代わりに、`computed` シグナルを使用して、他の状態に依存する状態をモデル化してください。 + + +### 注入コンテキスト + +デフォルトでは、[インジェクションコンテキスト](guide/di/dependency-injection-context)内(`inject`関数にアクセスできる場所)でのみ`effect()`を作成できます。この要件を満たす最も簡単な方法は、コンポーネント、ディレクティブ、またはサービスの`constructor`内で`effect`を呼び出すことです。 + +```ts +@Component({...}) +export class EffectiveCounterComponent { + readonly count = signal(0); + constructor() { + // 新しいEffectを登録する。 + effect(() => { + console.log(`The count is: ${this.count()}`); + }); + } +} +``` + +または、エフェクトをフィールドに割り当てることができます(これにより、説明的な名前も付けられます)。 + +```ts +@Component({...}) +export class EffectiveCounterComponent { + readonly count = signal(0); + + private loggingEffect = effect(() => { + console.log(`The count is: ${this.count()}`); + }); +} +``` + +コンストラクターの外でエフェクトを作成するには、`Injector`を`effect`に渡すことができます。これは、`effect`のオプションを使用して行います。 + +```ts +@Component({...}) +export class EffectiveCounterComponent { + readonly count = signal(0); + constructor(private injector: Injector) {} + + initializeLogging(): void { + effect(() => { + console.log(`The count is: ${this.count()}`); + }, {injector: this.injector}); + } +} +``` + +### エフェクトの破棄 + +エフェクトを作成すると、それが含まれているコンテキストが破棄されると、自動的に破棄されます。つまり、コンポーネント内で作成されたエフェクトは、コンポーネントが破棄されると破棄されます。ディレクティブ、サービスなどでも同じです。 + +エフェクトは`EffectRef`を返し、これを用いて`destroy()`メソッドを呼び出して手動で破棄できます。これと`manualCleanup`オプションを組み合わせることで、手動で破棄されるまで続くエフェクトを作成できます。不要になったエフェクトは確実にクリーンアップしてください。 + +## 詳細なトピック + +### シグナルの等価関数 + +シグナルを作成する際には、オプションで等価関数を指定できます。これは、新しい値が前の値と実際に異なるかどうかを確認するために使用されます。 + +```ts +import _ from 'lodash'; + +const data = signal(['test'], {equal: _.isEqual}); + +// これは別の配列インスタンスですが、 +// 深い等価関数を使用することで値は等しいと判断され、 +// シグナルは更新をトリガーしません。 +data.set(['test']); +``` + +等価関数は、書き込み可能なシグナルと算出シグナルの両方に指定できます。 + +ヒント: デフォルトでは、シグナルは参照の等価性(`===`比較)を使用します。 + +### 依存関係を追跡せずに読み取る + +まれに、`computed`や`effect`などのリアクティブ関数内でシグナルを読み取るコードを実行する必要があり、依存関係を作成しない場合があります。 + +たとえば、`currentUser`が変更されたときに、`counter`の値をログに記録する必要があるとします。両方のシグナルを読み取る`effect`を作成できます。 + +```ts +effect(() => { + console.log(`User set to ${currentUser()} and the counter is ${counter()}`); +}); +``` + +この例では、`currentUser`または`counter`のいずれかが変更されると、メッセージがログに記録されます。しかし、`currentUser`のみが変更されたときにエフェクトを実行する必要がある場合、`counter`の読み取りは単なる付随的なものであり、`counter`が変更されても新しいメッセージはログに記録されるべきではありません。 + +シグナルのゲッターを`untracked`で呼び出すことで、シグナルの読み取りが追跡されないようにできます。 + +```ts +effect(() => { + console.log(`User set to ${currentUser()} and the counter is ${untracked(counter)}`); +}); +``` + +`untracked`は、エフェクトが、依存関係として扱われない外部のコードを呼び出す必要がある場合にも役立ちます。 + +```ts +effect(() => { + const user = currentUser(); + untracked(() => { + // `loggingService`がSignalを読み取っても、 + // このEffectの依存関係として扱われません。 + this.loggingService.log(`User set to ${user}`); + }); +}); +``` + +### エフェクトのクリーンアップ関数 + +エフェクトは長時間実行される操作を開始する可能性があります。これは、エフェクトが破棄された場合、または最初の操作が完了する前にエフェクトが再び実行された場合はキャンセルする必要があります。エフェクトを作成する際に、関数はオプションで最初の引数として`onCleanup`関数を許可できます。この`onCleanup`関数は、エフェクトの次回の実行が始まる前に、もしくはエフェクトが破棄されたときに呼び出されるコールバックを登録できます。 + +```ts +effect((onCleanup) => { + const user = currentUser(); + + const timer = setTimeout(() => { + console.log(`1 second ago, the user became ${user}`); + }, 1000); + + onCleanup(() => { + clearTimeout(timer); + }); +}); +``` diff --git a/adev-ja/src/content/guide/signals/queries.md b/adev-ja/src/content/guide/signals/queries.md new file mode 100644 index 0000000000..ea31477cfe --- /dev/null +++ b/adev-ja/src/content/guide/signals/queries.md @@ -0,0 +1,191 @@ +# シグナルクエリ + +コンポーネントまたはディレクティブは、子要素を見つけ、インジェクターから値を読み取るクエリを定義できます。 + +開発者は、クエリを使って、コンポーネント、ディレクティブ、DOM要素などの参照を取得することがよくあります。 + +クエリには、ビュークエリとコンテンツクエリの2つのカテゴリーがあります。 + +シグナルクエリは、クエリ結果をリアクティブなシグナルプリミティブとして提供します。クエリ結果を `computed` や `effect` で使用し、これらの結果を他のシグナルと組み合わせることができます。 + +**重要:** シグナルクエリは [開発者プレビュー](reference/releases#developer-preview) です。APIは、Angularの非推奨サイクルを経ることなく、フィードバックに基づいて変更される可能性があります。 + +Angularのクエリに既に詳しい場合は、[シグナルベースのクエリとデコレーターベースのクエリの比較](#comparing-signal-based-queries-to-decorator-based-queries) に直接進むことができます。 + +## ビュークエリ + +ビュークエリは、コンポーネント自身のテンプレート(ビュー)内の要素から結果を取得します。 + +### `viewChild` + +`viewChild` 関数を使って、単一の結果をターゲットとするクエリを宣言できます。 + +```ts +@Component({ + template: ` +
+ + ` +}) +export class TestComponent { + // 文字列述語による単一結果のクエリ + divEl = viewChild('el'); // Signal + // 型述語による単一結果のクエリ + cmp = viewChild(MyComponent); // Signal +} +``` + +### `viewChildren` + +`viewChildren` 関数を使って、複数の結果をクエリできます。 + +```ts + @Component({ + template: ` +
+ @if (show) { +
+ } + ` +}) +export class TestComponent { + show = true; + + // 複数の結果に対するクエリ + divEls = viewChildren('el'); // Signal> +} +``` + +### ビュークエリオプション + +`viewChild` と `viewChildren` のクエリ宣言関数は、2つの引数を受け取る似たようなシグネチャを持っています。 + +* クエリターゲットを指定する**ロケーター** - これは、`string` または注入可能なトークンです。 +* 指定されたクエリの動作を調整する**オプション**のセット。 + +シグナルベースのビュークエリは、`read` という1つのオプションのみを受け付けます。`read` オプションは、一致したノードから注入して最終結果で返す結果の型を示します。 + +```ts +@Component({ + template: `` +}) +export class TestComponent { + // オプション付きの単一結果に対するクエリ + cmp = viewChild(MyComponent, {read: ElementRef}); // Signal +} +``` + +## コンテンツクエリ + +コンテンツクエリは、コンポーネントのコンテンツ、つまりコンポーネントが使用されるテンプレート内のコンポーネントタグ内にネストされた要素から結果を取得します。 + +### `contentChild` + +`contentChild` 関数を使って、単一の結果をクエリできます。 + +```ts +@Component({...}) + export class TestComponent { + // 文字列述語によるクエリ + headerEl = contentChild('h'); // Signal + + // 型述語によるクエリ + header = contentChild(MyHeader); // Signal +} +``` + + ### `contentChildren` + +`contentChildren` 関数を使って、複数の結果をクエリできます。 + +```ts + @Component({...}) + export class TestComponent { + // 複数の結果に対するクエリ + divEls = contentChildren('h'); // Signal> + } +``` + +### コンテンツクエリオプション + +`contentChild` と `contentChildren` のクエリ宣言関数は、2つの引数を受け取る似たようなシグネチャを持っています。 + +* クエリターゲットを指定する**ロケーター** - これは、`string` または注入可能なトークンです。 +* 指定されたクエリの動作を調整する**オプション**のセット。 + +コンテンツクエリは、次のオプションを受け付けます。 + +* `descendants` デフォルトでは、コンテンツクエリはコンポーネントの直接の子のみを見つけ、子孫にはトラバースしません。このオプションが `true` に変更された場合、クエリ結果は要素のすべての子孫を含みます。ただし、`true` でも、クエリは*決して*コンポーネント内に降りていきません。 +* `read` は、一致したノードから取得して最終結果で返す結果の型を示します。 + +### 必須の子クエリ + +子クエリ (`viewChild` または `contentChild`) が結果を見つけられない場合、その値は `undefined` になります。これは、`@if` や `@for` などの制御フローステートメントによってターゲット要素が非表示になっている場合に発生する可能性があります。 + +このため、子クエリは `undefined` の値を持つ可能性があるシグナルを返します。ほとんどの場合、特にビューの子クエリの場合、開発者はコードを次のように記述します。 +* 少なくとも1つのマッチする結果がある。 +* 結果は、テンプレートが処理され、クエリ結果が利用可能となったときにアクセスされる。 + +このような場合、子クエリを `required` とマークすることで、少なくとも1つのマッチする結果の存在を強制できます。これにより、結果型シグネチャから `undefined` が削除されます。`required` クエリが結果を見つけられない場合、Angularはエラーをスローします。 + +```ts +@Component({ + selector: 'app-root', + standalone: true, + template: ` +
+ `, +}) +export class App { + existingEl = viewChild.required('requiredEl'); // 必須で存在する結果 + missingEl = viewChild.required('notInATemplate'); // 必須だが存在しない結果 + + ngAfterViewInit() { + console.log(this.existingEl()); // OK :-) + console.log(this.missingEl()); // ランタイムエラー: 結果は必須とマークされているが、利用できません! + } +} +``` + +## 結果の利用可能性タイミング + +シグナルクエリを作る関数は、ディレクティブインスタンスの構築の一部として実行されます。これは、クエリインスタンスを作成して、テンプレートの作成モードを実行して一致するものを収集する前に発生します。結果として、シグナルインスタンスが作成され(読み取ることが可能)、クエリ結果が収集できない期間があります。デフォルトでは、Angularは結果が利用可能となる前に `undefined`(子クエリの場合)または空の配列(子クエリの場合)を返します。必須クエリは、この時点でアクセスされるとスローします。 + +Angularは、シグナルベースのクエリ結果を必要に応じて遅延評価します。つまり、クエリ結果が収集されるのは、シグナルを読み取るコードパスがある場合のみです。 + +クエリ結果は、ビューの操作によって時間の経過とともに変化する可能性があります。これは、Angularの制御フロー(`@if`、`@for` など)または `ViewContainerRef` APIへの直接呼び出しのいずれかによって行われます。クエリ結果のシグナルから値を読み取ると、時間の経過とともに異なる値を受け取る可能性があります。 + +注: テンプレートがレンダリングされている間に不完全なクエリ結果を返さないよう、Angularは指定されたテンプレートのレンダリングが完了するまでクエリ解決を遅らせます。 + +## クエリ宣言関数と関連するルール + +`viewChild`、`contentChild`、`viewChildren`、`contentChildren` 関数は、Angularコンパイラによって認識される特別な関数です。これらの関数を使って、コンポーネントまたはディレクティブプロパティを初期化することでクエリを宣言できます。これらの関数をコンポーネントとディレクティブのプロパティイニシャライザー以外で呼び出すことはできません。 + +```ts +@Component({ + selector: 'app-root', + standalone: true, + template: ` +
+ `, +}) +export class App { + el = viewChild('el'); // 問題なし! + + constructor() { + const myConst = viewChild('el'); // サポートされていません + } +} +``` + +## シグナルベースのクエリとデコレーターベースのクエリの比較 + +シグナルクエリは、`@ContentChild`、`@ContentChildren`、`@ViewChild` または `@ViewChildren` デコレーターを使って宣言されたクエリに対する代替アプローチです。新しいアプローチでは、クエリ結果がシグナルとして公開されるため、クエリ結果を他のシグナル(`computed` または `effect` を使用して)と組み合わせ、変更検知を駆動できます。さらに、シグナルベースのクエリシステムは、次のような利点も提供します。 + +* **より予測可能なタイミング。** クエリ結果が利用可能になったらすぐにアクセスできます。 +* **よりシンプルなAPIサーフェス。** すべてのクエリがシグナルを返し、複数の結果を持つクエリでは標準の配列を操作できます。 +* **改善された型安全性。** より少ないクエリのユースケースで、`undefined` が可能な結果に含まれます。 +* **より正確な型推論。** TypeScriptは、型述語を使用する場合や、明示的な `read` オプションを指定する場合に、より正確な型を推論できます。 +* **より遅延した更新。** Angularは、シグナルベースのクエリ結果を遅延して更新します。フレームワークは、コードが明示的にクエリ結果を読み取らない限り、何も動作しません。 + +クエリのメカニズムは本質的にほとんど変わりません。概念的には、Angularは依然としてテンプレート(ビュー)またはコンテンツ内の要素をターゲットとする単一の「子」クエリまたは複数の「子」クエリを作成します。違いは、結果の型と結果の利用可能性のタイミングです。シグナルベースのクエリを宣言するための記述形式も変更されました。クラスメンバーのイニシャライザーとして使用される `viewChild`、`viewChildren`、`contentChild`、`contentChildren` 関数は、Angularによって自動的に認識されます。 diff --git a/adev-ja/src/content/guide/signals/rxjs-interop.md b/adev-ja/src/content/guide/signals/rxjs-interop.md new file mode 100644 index 0000000000..47189cf7c0 --- /dev/null +++ b/adev-ja/src/content/guide/signals/rxjs-interop.md @@ -0,0 +1,131 @@ +# RxJSとの相互運用 + +**重要:** RxJS Interopパッケージは [開発者プレビュー](reference/releases#developer-preview) で利用可能です。お試しいただけますが、安定版になるまでは変更される可能性があります。 + +Angularの `@angular/core/rxjs-interop` パッケージは、[Angularシグナル](guide/signals) とRxJSのObservablesを統合するための便利なユーティリティを提供します。 + +## `toSignal` + +`toSignal` 関数を使用して、Observableの値を追跡するシグナルを作成します。これはテンプレート内の `async` パイプと似ていますが、より柔軟で、アプリケーション内のどこでも使用できます。 + +```ts +import { Component } from '@angular/core'; +import { AsyncPipe } from '@angular/common'; +import { interval } from 'rxjs'; +import { toSignal } from '@angular/core/rxjs-interop'; + +@Component({ + template: `{{ counter() }}`, +}) +export class Ticker { + counterObservable = interval(1000); + + // `counterObservable` の値を表す `Signal` を取得します。 + counter = toSignal(this.counterObservable, {initialValue: 0}); +} +``` + +`async` パイプと同様に、`toSignal` はObservableをすぐに購読します。これにより副作用が発生する可能性があります。`toSignal` によって作成された購読は、`toSignal` を呼び出すコンポーネントまたはサービスが破棄されると、指定されたObservableから自動的に購読解除されます。 + +**重要:** `toSignal` は購読を作成します。同じObservableに対して繰り返し呼び出すことは避け、代わりに返されたシグナルを再利用してください。 + +### 注入コンテキスト + +`toSignal` は、コンポーネントまたはサービスの構築時など、[注入コンテキスト](guide/di/dependency-injection-context) で実行する必要があります。注入コンテキストが利用できない場合は、代わりに使用する `Injector` を手動で指定できます。 + +### 初期値 + +Observableは購読時に同期的に値を生成するとは限りませんが、シグナルは常に現在の値を必要とします。`toSignal` シグナルのこの「初期」値を扱う方法はいくつかあります。 + +#### `initialValue` オプション + +上記の例のように、Observableが初めて値を発行する前にシグナルが返す値を `initialValue` オプションで指定できます。 + +#### `undefined` 初期値 + +`initialValue` を指定しない場合、生成されたシグナルは、Observableが値を発行するまでは `undefined` を返します。これは、`async` パイプが `null` を返す動作に似ています。 + +#### `requireSync` オプション + +`BehaviorSubject` のように、同期的に値を発行することが保証されているObservableもあります。このような場合は、`requireSync: true` オプションを指定できます。 + +`requiredSync` が `true` の場合、`toSignal` はObservableが購読時に同期的に値を発行することを強制します。これにより、シグナルは常に値を持ち、`undefined` 型または初期値は不要になります。 + +### `manualCleanup` + +デフォルトでは、`toSignal` は、それを作成したコンポーネントまたはサービスが破棄されると、Observableから自動的に購読解除されます。 + +この動作をオーバーライドするには、`manualCleanup` オプションを渡すことができます。この設定は、自然に完了するObservableに使用できます。 + +### エラーと完了 + +`toSignal` で使用されるObservableがエラーを発生させた場合、そのエラーはシグナルが読み取られるときにスローされます。 + +`toSignal` で使用されるObservableが完了した場合、シグナルは完了前に発行された最後の値を返します。 + +## `toObservable` + +`toObservable` ユーティリティを使用して、シグナルの値を追跡する `Observable` を作成します。シグナルの値は、値が変更されるとObservableに値を発行する `effect` で監視されます。 + +```ts +import { Component, signal } from '@angular/core'; + +@Component(...) +export class SearchResults { + query: Signal = inject(QueryService).query; + query$ = toObservable(this.query); + + results$ = this.query$.pipe( + switchMap(query => this.http.get('/search?q=' + query )) + ); +} +``` + +`query` シグナルが変更されると、`query$` Observableは最新のクエリを発行し、新しいHTTPリクエストをトリガーします。 + +### 注入コンテキスト + +`toObservable` は、コンポーネントまたはサービスの構築時など、[注入コンテキスト](guide/di/dependency-injection-context) で実行する必要があります。注入コンテキストが利用できない場合は、代わりに使用する `Injector` を手動で指定できます。 + +### `toObservable` のタイミング + +`toObservable` は、`ReplaySubject` 内でシグナルの値を追跡するために `effect` を使用します。購読時に、最初の値(存在する場合)は同期的に発行される可能性があり、その後のすべての値は非同期になります。 + +Observableとは異なり、シグナルは同期的な変更通知を提供しません。シグナルの値を複数回更新しても、`toObservable` はシグナルが安定した後にのみ値を発行します。 + +```ts +const obs$ = toObservable(mySignal); +obs$.subscribe(value => console.log(value)); + +mySignal.set(1); +mySignal.set(2); +mySignal.set(3); +``` + +ここでは、最後の値 (3) のみがログに出力されます。 + +### `outputFromObservable` + +`outputFromObservable(...)` は、RxJSのObservableに基づいて値を発行するAngularの出力を宣言します。 + +```ts +class MyDir { + nameChange$ = new Observable(/* ... */); + nameChange = outputFromObservable(this.nameChange$); // OutputRef +} +``` + +[output() APIガイド](/guide/components/output-fn)で詳細を確認してください。 + +### `outputToObservable` + +`outputToObservable(...)` はAngularの出力をObservableに変換します。 +これにより、Angularの出力をRxJSストリームに簡単に統合できます。 + +```ts +outputToObservable(myComp.instance.onNameChange) + .pipe(...) + .subscribe(...) +``` + +[output() APIガイド](/guide/components/output-fn)で詳細を確認してください。 diff --git a/adev-ja/src/content/tutorials/learn-angular/steps/20-inject-based-di/README.md b/adev-ja/src/content/tutorials/learn-angular/steps/20-inject-based-di/README.md index 5230272a81..4a4e0f30d6 100644 --- a/adev-ja/src/content/tutorials/learn-angular/steps/20-inject-based-di/README.md +++ b/adev-ja/src/content/tutorials/learn-angular/steps/20-inject-based-di/README.md @@ -2,7 +2,7 @@ 注入可能なサービスを作成することは、Angularの依存性の注入 (DI) システムの最初の部分です。サービスをコンポーネントにどのように注入するか?Angularには、適切なコンテキストで使用できる便利な関数 `inject()` があります。 -注: インジェクションコンテキストはこのチュートリアルでは扱いませんが、詳細については [Angular ドキュメント](guide/di/dependency-injection-context) を参照してください。 +注: 注入コンテキストはこのチュートリアルでは扱いませんが、詳細については [Angular ドキュメント](guide/di/dependency-injection-context) を参照してください。 このアクティビティでは、サービスを注入してコンポーネントで使用する方法を学びます。 diff --git a/aio-ja/content/guide/signals.en.md b/aio-ja/content/guide/signals.en.md deleted file mode 100644 index 6b51bf28d0..0000000000 --- a/aio-ja/content/guide/signals.en.md +++ /dev/null @@ -1,250 +0,0 @@ -# Angular Signals - -**Angular Signals** is a system that granularly tracks how and where your state is used throughout an application, allowing the framework to optimize -rendering updates. - -## What are signals? - -A **signal** is a wrapper around a value that can notify interested consumers when that value changes. Signals can contain any value, from simple primitives to complex data structures. - -A signal's value is always read through a getter function, which allows Angular to track where the signal is used. - -Signals may be either _writable_ or _read-only_. - -### Writable signals - -Writable signals provide an API for updating their values directly. You create writable signals by calling the `signal` function with the signal's initial value: - -```ts -const count = signal(0); - -// Signals are getter functions - calling them reads their value. -console.log('The count is: ' + count()); -``` - -To change the value of a writable signal, you can either `.set()` it directly: - -```ts -count.set(3); -``` - -or use the `.update()` operation to compute a new value from the previous one: - -```ts -// Increment the count by 1. -count.update(value => value + 1); -``` - -Writable signals have the type `WritableSignal`. - -### Computed signals - -A **computed signal** derives its value from other signals. Define one using `computed` and specifying a derivation function: - -```typescript -const count: WritableSignal = signal(0); -const doubleCount: Signal = computed(() => count() * 2); -``` - -The `doubleCount` signal depends on `count`. Whenever `count` updates, Angular knows that anything which depends on either `count` or `doubleCount` needs to update as well. - -#### Computed signals are both lazily evaluated and memoized - -`doubleCount`'s derivation function does not run to calculate its value until the first time `doubleCount` is read. Once calculated, this value is cached, and future reads of `doubleCount` will return the cached value without recalculating. - -When `count` changes, it tells `doubleCount` that its cached value is no longer valid, and the value is only recalculated on the next read of `doubleCount`. - -As a result, it's safe to perform computationally expensive derivations in computed signals, such as filtering arrays. - -#### Computed signals are not writable signals - -You cannot directly assign values to a computed signal. That is, - -```ts -doubleCount.set(3); -``` - -produces a compilation error, because `doubleCount` is not a `WritableSignal`. - -#### Computed signal dependencies are dynamic - -Only the signals actually read during the derivation are tracked. For example, in this computed the `count` signal is only read conditionally: - -```ts -const showCount = signal(false); -const count = signal(0); -const conditionalCount = computed(() => { - if (showCount()) { - return `The count is ${count()}.`; - } else { - return 'Nothing to see here!'; - } -}); -``` - -When reading `conditionalCount`, if `showCount` is `false` the "Nothing to see here!" message is returned _without_ reading the `count` signal. This means that updates to `count` will not result in a recomputation. - -If `showCount` is later set to `true` and `conditionalCount` is read again, the derivation will re-execute and take the branch where `showCount` is `true`, returning the message which shows the value of `count`. Changes to `count` will then invalidate `conditionalCount`'s cached value. - -Note that dependencies can be removed as well as added. If `showCount` is later set to `false` again, then `count` will no longer be considered a dependency of `conditionalCount`. - -## Reading signals in `OnPush` components - -When an `OnPush` component uses a signal's value in its template, Angular will track the signal as a dependency of that component. When that signal is updated, Angular automatically [marks](/api/core/ChangeDetectorRef#markforcheck) the component to ensure it gets updated the next time change detection runs. Refer to the [Skipping component subtrees](/guide/change-detection-skipping-subtrees) guide for more information about `OnPush` components. - -## Effects - -Signals are useful because they can notify interested consumers when they change. An **effect** is an operation that runs whenever one or more signal values change. You can create an effect with the `effect` function: - -```ts -effect(() => { - console.log(`The current count is: ${count()}`); -}); -``` - -Effects always run **at least once.** When an effect runs, it tracks any signal value reads. Whenever any of these signal values change, the effect runs again. Similar to computed signals, effects keep track of their dependencies dynamically, and only track signals which were read in the most recent execution. - -Effects always execute **asynchronously**, during the change detection process. - -Note: the `effect()` API is still in [developer preview](/guide/releases#developer-preview) as we work to integrate signal-based reactivity into the core framework. - -### Use cases for effects - -Effects are rarely needed in most application code, but may be useful in specific circumstances. Here are some examples of situations where an `effect` might be a good solution: - -* Logging data being displayed and when it changes, either for analytics or as a debugging tool -* Keeping data in sync with `window.localStorage` -* Adding custom DOM behavior that can't be expressed with template syntax -* Performing custom rendering to a ``, charting library, or other third party UI library - -#### When not to use effects - -Avoid using effects for propagation of state changes. This can result in `ExpressionChangedAfterItHasBeenChecked` errors, infinite circular updates, or unnecessary change detection cycles. - -Because of these risks, setting signals is disallowed by default in effects, but can be enabled if absolutely necessary. - -### Injection context - -By default, registering a new effect with the `effect()` function requires an [injection context](/guide/dependency-injection-context) (access to the `inject` function). The easiest way to provide this is to call `effect` within a component, directive, or service `constructor`: - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - constructor() { - // Register a new effect. - effect(() => { - console.log(`The count is: ${this.count()})`); - }); - } -} -``` - -Alternatively, the effect can be assigned to a field (which also gives it a descriptive name). - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - - private loggingEffect = effect(() => { - console.log(`The count is: ${this.count()})`); - }); -} -``` - -To create an effect outside of the constructor, you can pass an `Injector` to `effect` via its options: - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - constructor(private injector: Injector) {} - - initializeLogging(): void { - effect(() => { - console.log(`The count is: ${this.count()})`); - }, {injector: this.injector}); - } -} -``` - -### Destroying effects - -When you create an effect, it is automatically destroyed when its enclosing context is destroyed. This means that effects created within components are destroyed when the component is destroyed. The same goes for effects within directives, services, etc. - -Effects return an `EffectRef` that can be used to destroy them manually, via the `.destroy()` operation. This can also be combined with the `manualCleanup` option to create an effect that lasts until it is manually destroyed. Be careful to actually clean up such effects when they're no longer required. - -## Advanced topics - -### Signal equality functions - -When creating a signal, you can optionally provide an equality function, which will be used to check whether the new value is actually different than the previous one. - -```ts -import _ from 'lodash'; - -const data = signal(['test'], {equal: _.isEqual}); - -// Even though this is a different array instance, the deep equality -// function will consider the values to be equal, and the signal won't -// trigger any updates. -data.set(['test']); -``` - -Equality functions can be provided to both writable and computed signals. - -### Reading without tracking dependencies - -Rarely, you may want to execute code which may read signals in a reactive function such as `computed` or `effect` _without_ creating a dependency. - -For example, suppose that when `currentUser` changes, the value of a `counter` should be logged. Creating an `effect` which reads both signals: - -```ts -effect(() => { - console.log(`User set to `${currentUser()}` and the counter is ${counter()}`); -}); -``` - -This example logs a message when _either_ `currentUser` or `counter` changes. However, if the effect should only run when `currentUser` changes, then the read of `counter` is only incidental and changes to `counter` shouldn't log a new message. - -You can prevent a signal read from being tracked by calling its getter with `untracked`: - -```ts -effect(() => { - console.log(`User set to `${currentUser()}` and the counter is ${untracked(counter)}`); -}); -``` - -`untracked` is also useful when an effect needs to invoke some external code which shouldn't be treated as a dependency: - -```ts -effect(() => { - const user = currentUser(); - untracked(() => { - // If the `loggingService` reads signals, they won't be counted as - // dependencies of this effect. - this.loggingService.log(`User set to ${user}`); - }); -}); -``` - -### Effect cleanup functions - -Effects might start long-running operations, which should be cancelled if the effect is destroyed or runs again before the first operation finished. When you create an effect, your function can optionally accept an `onCleanup` function as its first parameter. This `onCleanup` function lets you register a callback that is invoked before the next run of the effect begins, or when the effect is destroyed. - -```ts -effect((onCleanup) => { - const user = currentUser(); - - const timer = setTimeout(() => { - console.log(`1 second ago, the user became ${user}`); - }, 1000); - - onCleanup(() => { - clearTimeout(timer); - }); -}); -``` - -@reviewed 2023-06-21 diff --git a/aio-ja/content/guide/signals.md b/aio-ja/content/guide/signals.md deleted file mode 100644 index 6d15417769..0000000000 --- a/aio-ja/content/guide/signals.md +++ /dev/null @@ -1,250 +0,0 @@ -# Angular Signals - -**Angular Signals**は、アプリケーションのどこでどのように状態が使用されているかを細かく追跡するシステムで、 -フレームワークがレンダリングの更新を最適化できるようにします。 - -## Signal とは何か? - -**Signal** は、値が変化したときに関心をもつ利用者に対して通知できる、値のラッパーです。 - -Signalの値は常にgetter関数を通して読み取られるため、AngularはSignalがどこで使用されたかを追跡できます。 - -Signalは、_書き込み可能_または_読み取り専用_のいずれかになります。 - -### 書き込み可能Signal - -書き込み可能Signalは、その値を直接更新するためのAPIを提供します。書き込み可能Signalを作成するには、初期値を指定して `signal` 関数を呼び出します: - -```ts -const count = signal(0); - -// Signalはgetter関数で、これを呼び出すと値が読み取られます。 -console.log('The count is: ' + count()); -``` - -直接 `.set()` することで、書き込み可能Signalの値を変更できます。 - -```ts -count.set(3); -``` - -あるいは、`.update()` 使用して、前の値から新しい値を計算します。 - -```ts -// カウントを1だけ増加させます。 -count.update(value => value + 1); -``` - -書き込み可能Signalは `WritableSignal` という型を持っています。 - -### 算出Signal {@a computed-signals} - -**算出Signal**は、他のSignalから派生する値を持ちます。`computed` を使用して、生成関数を指定して定義します。 - -```typescript -const count: WritableSignal = signal(0); -const doubleCount: Signal = computed(() => count() * 2); -``` - -`doubleCount` Signalは `count` に依存します。`count` が更新されるたびに、Angular は `count` と `doubleCount` のいずれかに依存するものも更新する必要があることを知ります。 - -#### 算出Signalは遅延評価とメモ化の両方を行う - -`doubleCount`の生成関数は、`doubleCount`がはじめて読み取られるまで実行されません。計算されたらこの値はキャッシュされ、その後の`doubleCount`の読み取りは、再計算せずにキャッシュされた値を返します。 - -`count`が変更されると、`doubleCount`にキャッシュされた値が無効であることを伝え、`doubleCount`の次の読み取り時に値が再計算されます。 - -結果として、配列のフィルタリングなど、計算コストの高い処理を算出Signalで安全に実行できます。 - -#### 算出Signalは書き込み可能Signalではない - -算出Signalに値を直接割り当てることはできません。 - -```ts -doubleCount.set(3); -``` - -これは、`doubleCount`が `WritableSignal` ではないため、コンパイルエラーになります。 - -#### 算出Signalの依存関係は動的である - -算出時に実際に読み込まれたSignalのみが追跡されます。たとえば、この計算では `count` Signalは条件付きでしか読み込まれていません: - -```ts -const showCount = signal(false); -const count = signal(0); -const conditionalCount = computed(() => { - if (showCount()) { - return `The count is ${count()}.`; - } else { - return 'Nothing to see here!'; - } -}); -``` - -`conditionalCount`を読み込む際、`showCount`が`false`の場合、`count`シグナルを読み込まずに "Nothing to see here!"というメッセージを返します。つまり、`count`を更新しても再計算されることはありません。 - -その後 `showCount` を `true` に設定して `conditionalCount` を再度読み込むと算出処理は再実行され、 `showCount` が `true` である分岐をとり、`count` の値を示すメッセージを返します。`count`を変更すると、`conditionalCount`のキャッシュされた値は無効化されます。 - -依存関係は追加だけでなく、削除も可能であることに注意してください。後に `showCount` が再び `false` に設定された場合、`count` はもはや `conditionalCount` の依存関係とみなされません。 - -## `OnPush` コンポーネントでシグナルを読み取る - -`OnPush`コンポーネントがテンプレートでSignalの値を使用すると、Angularはそのコンポーネントの依存関係としてSignalを追跡します。`OnPush`コンポーネントの詳細については、[Skipping component subtrees](/guide/change-detection-skipping-subtrees) ガイドを参照してください。 - -## Effect - -Signalが役立つのは、それが変化したときに関心のある利用者に対して通知できるためです。 **Effect** は、1つまたは複数のSignalの値が変化するたびに実行される操作です。`effect`関数で Effect を作成します: - -```ts -effect(() => { - console.log(`The current count is: ${count()}`); -}); -``` - -Effectは**少なくとも1回は**必ず実行される。Effect が実行されると、読み取った任意の Signal の値を追跡します。これらのSignalの値が変化するたびに、Effectが再度実行されます。算出Signalと同様に、Effectは依存関係を動的に追跡し、直近の実行で読み込まれたSignalのみを追跡します。 - -Effectは常に**非同期的**に、変更検知のプロセス中に実行されます。 - -Note: `effect()`APIはまだ[開発者プレビュー](/guide/releases#developer-preview)であり、シグナルベースのリアクティビティをコアフレームワークに統合するための作業中です。 - -### Effectのユースケース - -Effect は、大部分のアプリケーションコードではほとんど必要とされませんが、特定の状況下では役に立つことがあります。以下は、`effect`がよい解決策となるような状況の例です: - -* 表示されているデータと、それが変化したときのログを、解析やデバッグのために記録する。 -* `window.localStorage`とデータを同期させる。 -* テンプレート構文で表現できない独自の DOM の振る舞いを追加する。 -* ``、チャートライブラリ、その他のサードパーティ製UIライブラリへのカスタムレンダリングを実行する。 - -#### Effectを使用すべきでない状況 - -状態変化の伝達に Effect を使用しないでください。その結果、`ExpressionChangedAfterItHasBeenChecked`エラー、無限の循環更新、または不必要な変更検知サイクルが発生することがあります。 - -このようなリスクがあるため、EffectではSignalの書き込みはデフォルトで禁止されていますが、どうしても必要な場合は有効にすることができます。 - -### インジェクションコンテキスト - -デフォルトでは、`effect()`関数で新しいエフェクトを登録するには、「[インジェクションコンテキスト](/guide/dependency-injection-context)」(`inject`関数へのアクセス)が必要です。これを提供するもっとも簡単な方法は、コンポーネント、ディレクティブ、またはサービスの `constructor` の中で `effect` を呼び出すことです: - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - constructor() { - // Register a new effect. - effect(() => { - console.log(`The count is: ${this.count()})`); - }); - } -} -``` - -また、Effectをフィールドに割り当てることもできます(この場合、Effectに説明的な名前を付けることもできます)。 - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - - private loggingEffect = effect(() => { - console.log(`The count is: ${this.count()})`); - }); -} -``` - -コンストラクターの外で Effect を作成するには、`Injector` をオプションで `effect` に渡します: - -```ts -@Component({...}) -export class EffectiveCounterCmp { - readonly count = signal(0); - constructor(private injector: Injector) {} - - initializeLogging(): void { - effect(() => { - console.log(`The count is: ${this.count()})`); - }, {injector: this.injector}); - } -} -``` - -### Effect の破棄 - -Effect を作成すると、それを包含するコンテキストが破棄されたときに、自動的に破棄されます。つまり、コンポーネント内で作成されたEffectは、コンポーネントが破棄された時点で破棄されます。ディレクティブやサービスなどの中のEffectも同様です。 - -Effectは `EffectRef` を返し、それを使って `.destroy()` 操作によって手動でエフェクトを破棄することもできます。また、`manualCleanup`オプションと組み合わせることで、手動で破壊するまで持続するEffectを作成することもできます。このようなEffectが不要になった場合は、必ずクリーンアップするように注意してください。 - -## 高度なトピック - -### Signal の等価関数 - -Signal を作成する際、オプションで等価関数を指定することができ、この関数は新しい値が前の値と実際に異なるかどうかをチェックするために使用されます。 - -```ts -import _ from 'lodash'; - -const data = signal(['test'], {equal: _.isEqual}); - -// Even though this is a different array instance, the deep equality -// function will consider the values to be equal, and the signal won't -// trigger any updates. -data.set(['test']); -``` - -書き込み可能Signal と算出Signalの両方に対して等価関数を設定できます。 - -### 依存関係を追跡せずに読み取る - -まれに、`computed`や`effect`のようなリアクティブ関数でSignalを読み込むようなコードを、依存関係を作成せずに実行したいことがあります。 - -たとえば、`currentUser`が変化したときに、`counter`の値をログに記録する必要があるとします。両方のSignalを読み取る `effect` を作成します: - -```ts -effect(() => { - console.log(`User set to `${currentUser()}` and the counter is ${counter()}`); -}); -``` - -この例では、`currentUser`または`counter`のどちらかが変更されたときにメッセージをログに記録します。しかし、もし `currentUser` が変更されたときだけ実行されるのであれば、`counter` の読み取りは付随的なものに過ぎず、`counter` の変更によって新しいメッセージが記録されるべきではありません。 - -Signal の getter を `untracked` で呼び出すことで、読み込んだ Signal が追跡されないようにできます: - -```ts -effect(() => { - console.log(`User set to `${currentUser()}` and the counter is ${untracked(counter)}`); -}); -``` - -`untracked`は、Effectが依存関係として扱われるべきでない外部コードを呼び出す必要がある場合にも便利です: - -```ts -effect(() => { - const user = currentUser(); - untracked(() => { - // If the `loggingService` reads signals, they won't be counted as - // dependencies of this effect. - this.loggingService.log(`User set to ${user}`); - }); -}); -``` - -### Effectのクリーンアップ関数 - -Effect は長時間実行されるオペレーションを開始する可能性があり、最初のオペレーションが終了する前に Effect が破棄されたり、再度実行された場合にキャンセルされることがあります。Effect を作成するとき、その関数はオプションで `onCleanup` 関数を第一引数として受け取ることができます。この `onCleanup` 関数は、Effectの次の実行が始まる前、またはエフェクトが破棄されたときに呼び出されるコールバックを登録できます。 - -```ts -effect((onCleanup) => { - const user = currentUser(); - - const timer = setTimeout(() => { - console.log(`1 second ago, the user became ${user}`); - }, 1000); - - onCleanup(() => { - clearTimeout(timer); - }); -}); -``` - -@reviewed 2023-06-21 diff --git a/prh.yml b/prh.yml index ed6e38f99c..d68b968a58 100644 --- a/prh.yml +++ b/prh.yml @@ -2,7 +2,7 @@ version: 1 imports: - path: ./node_modules/prh-rules/files/markdown.yml rules: -# 言い換え + # 言い換え - expected: 依存性の注入 pattern: 依存性注入 @@ -32,10 +32,15 @@ rules: pattern: インジェクトする - expected: 注入可能 - pattern: + pattern: - インジェクト可能 - インジェクタブル + - expected: 注入コンテキスト + pattern: + - インジェクトコンテキスト + - インジェクションコンテキスト + - expected: 参照してください pattern: ご参照ください @@ -47,7 +52,7 @@ rules: - expected: 静的な pattern: staticな - + - expected: しやすく pattern: し易く @@ -56,12 +61,12 @@ rules: - expected: 購読する pattern: サブスクライブする - + - expected: ため pattern: /(行|作)?為(?!替)/ regexpMustEmpty: $1 -# カタカナ語 + # カタカナ語 - expected: アプリケーション pattern: /アプリ(?!ケーション)/ @@ -70,10 +75,10 @@ rules: pattern: /オブザーバ(?!ー)/ - expected: プロバイダー - pattern: /プロバイダ(?!ー)/ + pattern: /プロバイダ(?!ー)/ - expected: コンシューマー - pattern: /コンシューマ(?!ー)/ + pattern: /コンシューマ(?!ー)/ - expected: サーバー pattern: /サーバ(?!ー)/ @@ -86,7 +91,7 @@ rules: - expected: ルーター pattern: /ルータ(?!ー)/ - + - expected: デコレーター pattern: /デコレータ(?!ー)/ @@ -116,7 +121,7 @@ rules: - expected: トリガー pattern: /トリガ(?!ー)/ - + - expected: ハンドラー pattern: /ハンドラ(?!ー)/ @@ -157,7 +162,7 @@ rules: pattern: 接頭辞 - expected: サフィックス - pattern: + pattern: - 接尾辞 - ポストフィックス @@ -169,3 +174,17 @@ rules: pattern: - かっこ - カッコ + + - expected: シグナル + pattern: + - Signals + - Signal + + - expected: 算出シグナル + pattern: + - 計算されたシグナル + + - expected: エフェクト + pattern: + - Effects + - Effect From 4c0b246c26350e13c7c3483436f6d40c827c7d72 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 17 Jul 2024 15:45:44 +0900 Subject: [PATCH 031/253] chore(tools): refactor translate script --- tools/lib/fsutils.ts | 3 ++ tools/translate.ts | 79 ++++++++++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/tools/lib/fsutils.ts b/tools/lib/fsutils.ts index 6d673cf507..1483d5a1dc 100644 --- a/tools/lib/fsutils.ts +++ b/tools/lib/fsutils.ts @@ -1,5 +1,8 @@ +import { globby } from 'globby'; import { access, cp, readFile, rm, writeFile } from 'node:fs/promises'; +export const glob = globby; + export async function rmrf(path: string) { try { await rm(path, { recursive: true }); diff --git a/tools/translate.ts b/tools/translate.ts index ca81acf3ce..0b1a595a47 100644 --- a/tools/translate.ts +++ b/tools/translate.ts @@ -10,21 +10,65 @@ import { GoogleGenerativeAI } from '@google/generative-ai'; import { GoogleAIFileManager } from '@google/generative-ai/server'; import { consola } from 'consola'; import assert from 'node:assert'; -import { writeFile } from 'node:fs/promises'; +import { stat, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { parseArgs } from 'node:util'; +import { glob } from './lib/fsutils'; import { rootDir } from './lib/workspace'; +const apiKey = process.env.GOOGLE_API_KEY; +assert(apiKey, 'GOOGLE_API_KEY 環境変数が設定されていません。'); +const genAI = new GoogleGenerativeAI(apiKey); +const fileManager = new GoogleAIFileManager(apiKey); +const model = genAI.getGenerativeModel({ + model: 'gemini-1.5-flash', + systemInstruction: ` +あなたはWebフロントエンドに関する技術文書の翻訳アシスタントです。 +Markdownファイルを受け取り、英語を日本語に翻訳した結果を出力してください。 +翻訳作業は以下のルールを遵守してください。 +- 元のMarkdownの文書構造を維持してください。 +- 内容の説明は含めず、翻訳結果のみを出力してください。 +- コードブロックの中身は翻訳しないでください。 +`.trim(), +}); + async function main() { - const apiKey = process.env.GOOGLE_API_KEY; - assert(apiKey, 'GOOGLE_API_KEY 環境変数が設定されていません。'); - const genAI = new GoogleGenerativeAI(apiKey); - const fileManager = new GoogleAIFileManager(apiKey); + const args = parseArgs({ + options: { write: { type: 'boolean', default: false } }, + allowPositionals: true, + }); + const { write } = args.values; + const [target] = args.positionals; + assert(target, 'ファイルまたはディレクトリを指定してください。'); - const args = parseArgs({ allowPositionals: true }); - const [file] = args.positionals; - assert(file, 'ファイルを指定してください。'); + const stats = await stat(target); + if (stats.isFile()) { + await translateFile(target, write); + } else if (stats.isDirectory()) { + await translateDir(target, write); + } +} +async function translateDir(dir: string, forceWrite = false) { + const files = await glob('**/*.en.md', { cwd: dir }); + const selectedFiles = await consola.prompt( + `翻訳するファイルを選択してください`, + { + type: 'multiselect', + required: false, + options: files, + } + ); + if (selectedFiles.length === 0) { + return; + } + + for (const file of selectedFiles) { + await translateFile(resolve(dir, file), forceWrite); + } +} + +async function translateFile(file: string, forceWrite = false) { consola.start(`ファイルを翻訳します: ${file}`); // Upload files for translation const prhFile = await fileManager.uploadFile(resolve(rootDir, 'prh.yml'), { @@ -36,15 +80,6 @@ async function main() { displayName: `content.md`, }); // Execute translation - const model = genAI.getGenerativeModel({ - model: 'gemini-1.5-flash', - systemInstruction: ` -あなたはWebフロントエンドに関する技術文書の翻訳アシスタントです。 -翻訳を行う際は元のテキストの形式や構造を維持してください。初心者にもわかりやすく平易な日本語に翻訳してください。 -入力: 英語を含むテキストファイル -出力: 翻訳後のテキスト - `.trim(), - }); const result = await model.generateContentStream([ { fileData: { @@ -76,10 +111,12 @@ async function main() { // 元のファイル拡張子が .en.* の場合は .* として保存する const outFilePath = file.replace(/\.en\.([^.]+)$/, '.$1'); - const save = await consola.prompt( - `翻訳結果を保存しますか?\n保存先: ${outFilePath}`, - { type: 'confirm', initial: false } - ); + const save = + forceWrite || + (await consola.prompt(`翻訳結果を保存しますか?\n保存先: ${outFilePath}`, { + type: 'confirm', + initial: false, + })); if (!save) { return; } From c8e452c7ea0230255c3f81580329857ea0e26c87 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Wed, 17 Jul 2024 15:56:06 +0900 Subject: [PATCH 032/253] fix: translate guide/components (#939) --- .textlintrc | 7 +- .../components/advanced-configuration.md | 48 +++ .../guide/components/anatomy-of-components.md | 102 +++++ .../guide/components/content-projection.md | 185 +++++++++ .../src/content/guide/components/dom-apis.md | 83 +++++ .../content/guide/components/host-elements.md | 127 +++++++ .../src/content/guide/components/importing.md | 35 ++ .../content/guide/components/inheritance.md | 109 ++++++ .../src/content/guide/components/inputs.md | 198 ++++++++++ .../src/content/guide/components/lifecycle.md | 350 ++++++++++++++++++ .../guide/components/output-function.md | 109 ++++++ .../src/content/guide/components/outputs.md | 102 +++++ .../components/programmatic-rendering.md | 127 +++++++ .../src/content/guide/components/queries.md | 298 +++++++++++++++ .../src/content/guide/components/selectors.md | 147 ++++++++ .../src/content/guide/components/styling.md | 114 ++++++ package.json | 1 + prh.yml | 4 + yarn.lock | 20 + 19 files changed, 2165 insertions(+), 1 deletion(-) create mode 100644 adev-ja/src/content/guide/components/advanced-configuration.md create mode 100644 adev-ja/src/content/guide/components/anatomy-of-components.md create mode 100644 adev-ja/src/content/guide/components/content-projection.md create mode 100644 adev-ja/src/content/guide/components/dom-apis.md create mode 100644 adev-ja/src/content/guide/components/host-elements.md create mode 100644 adev-ja/src/content/guide/components/importing.md create mode 100644 adev-ja/src/content/guide/components/inheritance.md create mode 100644 adev-ja/src/content/guide/components/inputs.md create mode 100644 adev-ja/src/content/guide/components/lifecycle.md create mode 100644 adev-ja/src/content/guide/components/output-function.md create mode 100644 adev-ja/src/content/guide/components/outputs.md create mode 100644 adev-ja/src/content/guide/components/programmatic-rendering.md create mode 100644 adev-ja/src/content/guide/components/queries.md create mode 100644 adev-ja/src/content/guide/components/selectors.md create mode 100644 adev-ja/src/content/guide/components/styling.md diff --git a/.textlintrc b/.textlintrc index 3454f71103..8d1af6394d 100644 --- a/.textlintrc +++ b/.textlintrc @@ -1,6 +1,11 @@ { "filters": { - "comments": true + "comments": true, + "allowlist": { + "allow": [ + "//m" + ] + } }, "rules": { "prh": { diff --git a/adev-ja/src/content/guide/components/advanced-configuration.md b/adev-ja/src/content/guide/components/advanced-configuration.md new file mode 100644 index 0000000000..f91f81c3cc --- /dev/null +++ b/adev-ja/src/content/guide/components/advanced-configuration.md @@ -0,0 +1,48 @@ +# コンポーネントの高度な設定 + +ヒント: このガイドでは、すでに[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使用する場合は、最初にそちらを読んでください。 + +## ChangeDetectionStrategy + +`@Component` デコレーターは、コンポーネントの**変更検知モード**を制御する `changeDetection` オプションを受け取ります。 +変更検知モードには2つのオプションがあります。 + +**`ChangeDetectionStrategy.Default`** は、名前が示す通り、デフォルトの戦略です。 +このモードでは、Angularは、アプリケーション全体で何か活動が行われた可能性があるたびに、 +コンポーネントのDOMが更新を必要とするかどうかを確認します。 +このチェックをトリガーする活動には、ユーザー操作、ネットワーク応答、タイマーなどがあります。 + +**`ChangeDetectionStrategy.OnPush`** は、Angularが実行する必要があるチェックの量を減らすオプションのモードです。 +このモードでは、フレームワークは、コンポーネントのDOMが更新を必要とするかどうかを次の場合にのみ確認します。 + +- コンポーネントの入力に、テンプレートのバインディングの結果として変更があった場合、または +- このコンポーネントのイベントリスナーが実行された場合 +- コンポーネントが `ChangeDetectorRef.markForCheck` またはそれをラップする何か(`AsyncPipe` など)を介して、明示的にチェックのためにマークされている場合 + +さらに、OnPushコンポーネントがチェックされると、 +Angularはアプリケーションツリーを上向きにたどりながら、すべての祖先コンポーネントもチェックします。 + +## PreserveWhitespace + +デフォルトでは、Angularはテンプレート内の余分な空白を削除し、折りたたみます。 +これは、改行やインデントから最もよく見られます。 +この設定は、コンポーネントのメタデータで `preserveWhitespace` を明示的に `false` に設定することで変更できます。 + +## カスタム要素スキーマ + +デフォルトでは、Angularは未知のHTML要素に出会うとエラーをスローします。 +コンポーネントのメタデータの `schemas` プロパティに +`CUSTOM_ELEMENTS_SCHEMA` を含めることで、この動作を無効にできます。 + +```ts +import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; + +@Component({ + ..., + schemas: [CUSTOM_ELEMENTS_SCHEMA], + template: '' +}) +export class ComponentWithCustomElements { } +``` + +Angularは、現時点で他のスキーマをサポートしていません。 diff --git a/adev-ja/src/content/guide/components/anatomy-of-components.md b/adev-ja/src/content/guide/components/anatomy-of-components.md new file mode 100644 index 0000000000..38eb258a3a --- /dev/null +++ b/adev-ja/src/content/guide/components/anatomy-of-components.md @@ -0,0 +1,102 @@ + + + +ヒント: このガイドでは、すでに[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使う場合は、まずそちらをお読みください。 + +すべてのコンポーネントには次のものが必要です。 + +* ユーザー入力の処理やサーバーからのデータ取得などの*動作*を定義するTypeScriptクラス +* DOMにレンダリングされる内容を制御するHTMLテンプレート +* HTMLでコンポーネントがどのように使用されるかを定義する[CSSセレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors) + +TypeScriptクラスの上部に `@Component` [デコレーター](https://www.typescriptlang.org/docs/handbook/decorators.html) を追加することで、コンポーネントにAngular固有の情報を与えます。 + + +@Component({ + selector: 'profile-photo', + template: `Your profile photo`, +}) +export class ProfilePhoto { } + + +Angularテンプレートの書き方については、[テンプレートガイド](guide/templates)を参照してください。 + +`@Component` デコレーターに渡されるオブジェクトは、コンポーネントの**メタデータ**と呼ばれます。これには、このガイドで説明されている `selector`、`template`、その他のプロパティが含まれています。 + +コンポーネントには、オプションでそのコンポーネントのDOMに適用されるCSSスタイルのリストを含めることができます。 + + +@Component({ + selector: 'profile-photo', + template: `Your profile photo`, + styles: `img { border-radius: 50%; }`, +}) +export class ProfilePhoto { } + + +デフォルトでは、コンポーネントのスタイルは、そのコンポーネントのテンプレートで定義された要素にのみ影響を与えます。Angularのスタイリングアプローチの詳細については、[コンポーネントのスタイリング](guide/components/styling)を参照してください。 + +代わりに、テンプレートとスタイルを別々のファイルに書くこともできます。 + + +@Component({ + selector: 'profile-photo', + templateUrl: 'profile-photo.html', + styleUrl: 'profile-photo.css', +}) +export class ProfilePhoto { } + + +これにより、プロジェクト内の*プレゼンテーション*と*動作*の懸念を分離できます。プロジェクト全体で一貫したアプローチを選択することも、コンポーネントごとに使用するものを決定できます。 + +`templateUrl` と `styleUrl` はどちらも、コンポーネントが存在するディレクトリを基準とした相対パスです。 + +## コンポーネントの使用 + +すべてのコンポーネントは[CSSセレクター](https://developer.mozilla.org/docs/Learn/CSS/Building_blocks/Selectors)を定義します。 + + +@Component({ + selector: 'profile-photo', + ... +}) +export class ProfilePhoto { } + + +Angularがサポートするセレクターの種類と、セレクターを選択する際のガイダンスについては、[コンポーネントセレクター](guide/components/selectors)を参照してください。 + +他のコンポーネントのテンプレートに一致するHTML要素を作成することで、コンポーネントを使用します。 + + +@Component({ + selector: 'user-profile', + template: ` + + `, + ..., +}) +export class UserProfile { } + + +テンプレートで他のコンポーネントを参照して使用する方法は、[コンポーネントのインポートと使用](guide/components/importing)を参照してください。 + +Angularは、遭遇した一致するHTML要素ごとに、コンポーネントのインスタンスを作成します。コンポーネントのセレクターに一致するDOM要素は、そのコンポーネントの**ホスト要素**と呼ばれます。コンポーネントのテンプレートの内容は、そのホスト要素内にレンダリングされます。 + +コンポーネントによってレンダリングされたDOM (コンポーネントのテンプレートに対応)は、 +そのコンポーネントの**ビュー**と呼ばれます。 + +このようにコンポーネントを組み合わせることで、**Angular アプリケーションはコンポーネントのツリーとして考えることができます**。 + +```mermaid +flowchart TD + A[AccountSettings]-->B + A-->C + B[UserProfile]-->D + B-->E + C[PaymentInfo] + D[ProfilePic] + E[UserBio] +``` + + +このツリー構造は、[依存性の注入](guide/di)や[子クエリ](guide/components/queries)など、その他のAngularの概念を理解する上で重要です。 diff --git a/adev-ja/src/content/guide/components/content-projection.md b/adev-ja/src/content/guide/components/content-projection.md new file mode 100644 index 0000000000..de585eb1cd --- /dev/null +++ b/adev-ja/src/content/guide/components/content-projection.md @@ -0,0 +1,185 @@ +# `ng-content` を使ったコンテンツの投影 + +ヒント: このガイドは、すでに [基本概念のガイド](essentials) を読んだことを前提としています。Angularを初めて使う場合は、まずそちらを読んでください。 + +多くの場合、さまざまな種類のコンテンツを格納するコンポーネントを作成する必要があります。 +例えば、カスタムカードコンポーネントを作成したいとします。 + +```ts +@Component({ + selector: 'custom-card', + template: '
', +}) +export class CustomCard {/* ... */} +``` + +**`` 要素は、コンテンツを配置する場所を示すプレースホルダーとして使用できます。**: + +```ts +@Component({ + selector: 'custom-card', + template: '
', +}) +export class CustomCard {/* ... */} +``` + +ヒント: `` は、 +[ネイティブの `` 要素](https://developer.mozilla.org/docs/Web/HTML/Element/slot) と似ていますが、 +Angular固有の機能も備えています。 + +`` を使用したコンポーネントを使用する場合、 +コンポーネントホスト要素の子要素はすべて、その `` の場所にレンダリング、あるいは **投影されます**: + +```ts +// コンポーネントソース +@Component({ + selector: 'custom-card', + template: ` +
+ +
+ `, +}) +export class CustomCard {/* ... */} +``` + +```html + + +

これは投影されたコンテンツです

+
+``` + +```html + + +
+

これは投影されたコンテンツです

+
+
+``` + +Angularは、このように渡されるコンポーネントの子要素を、そのコンポーネントの **コンテンツ** と呼びます。 +これはコンポーネントの **ビュー** とは異なります。 +ビューは、コンポーネントのテンプレートで定義された要素を指します。 + +**`` 要素は、コンポーネントでも DOM 要素でもありません。** +代わりに、コンテンツをレンダリングする場所をAngularに伝える特別なプレースホルダーです。 +Angularのコンパイラは、ビルド時にすべての `` 要素を処理します。 +実行時に `` を挿入、削除、または変更できません。 +**ディレクティブ**、スタイル、または任意の属性を `` に追加できません。 + +`` を `ngIf`、`ngFor`、または `ngSwitch` で条件付きで含めるべきではありません。 +コンポーネントコンテンツの条件付きレンダリングについては、 +[テンプレートフラグメント](api/core/ng-template) を参照してください。 + +## 複数のコンテンツプレースホルダー + +Angularは、CSSセレクターに基づいて、複数の異なる要素を異なる `` プレースホルダーへの投影をサポートしています。 +上記のカードの例を拡張して、`select` 属性を使用して、カードのタイトルと本文の2つのプレースホルダーを作成できます。 + +```html + +
+ +
+ +
+``` + +```html + + + こんにちは + 例へようこそ + +``` + +```html + + +
+ こんにちは +
+ 例へようこそ +
+
+``` + +`` プレースホルダーは、 +[コンポーネントセレクター](guide/components/selectors) と同じCSSセレクターをサポートしています。 + +`select` 属性を持つ `` プレースホルダーを1つ以上、 +`select` 属性を持たない `` プレースホルダーを1つ含める場合、 +後者は `select` 属性に一致しなかったすべての要素をキャプチャします。 + +```html + +
+ +
+ + +
+``` + +```html + + + こんにちは + +

例へようこそ

+
+``` + +```html + + +
+ こんにちは +
+ +

例へようこそ>

+
+
+``` + +コンポーネントに `select` 属性を持たない `` プレースホルダーが含まれていない場合、 +コンポーネントのいずれかのプレースホルダーに一致しない要素はDOMにレンダリングされません。 + +## 投影のためのコンテンツのエイリアシング + +Angularは、任意の要素にCSSセレクターを指定できる特殊な属性 `ngProjectAs` をサポートしています。 +`ngProjectAs` を持つ要素が `` プレースホルダーに対してチェックされると、 +Angularは要素のIDではなく `ngProjectAs` の値と比較します。 + +```html + +
+ +
+ +
+``` + +```html + + +

こんにちは

+ +

例へようこそ

+
+``` + +```html + + +
+

こんにちは

+
+

例へようこそ>

+
+
+``` + +`ngProjectAs` は静的な値のみをサポートし、動的な式にはバインドできません。 diff --git a/adev-ja/src/content/guide/components/dom-apis.md b/adev-ja/src/content/guide/components/dom-apis.md new file mode 100644 index 0000000000..2fa05d84fc --- /dev/null +++ b/adev-ja/src/content/guide/components/dom-apis.md @@ -0,0 +1,83 @@ +# DOM API の使用 + +ヒント:このガイドは、[基本概念のガイド](essentials) をすでに読んでいることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 + +Angularは、ほとんどのDOM作成、更新、および削除を自動的に処理します。 +ただし、コンポーネントのDOMと直接対話する必要がある場合もあるかもしれません。 +コンポーネントは `ElementRef` を注入して、コンポーネントのホスト要素への参照を取得できます。 + +```ts +@Component({...}) +export class ProfilePhoto { + constructor(elementRef: ElementRef) { + console.log(elementRef.nativeElement); + } +} +``` + +`nativeElement` プロパティは、 +ホスト [Element](https://developer.mozilla.org/docs/Web/API/Element) インスタンスを参照します。 + +Angularの `afterRender` および `afterNextRender` 関数を使用して、 +Angularがページのレンダリングを完了したときに実行される **レンダリングコールバック** を登録できます。 + +```ts +@Component({...}) +export class ProfilePhoto { + constructor(elementRef: ElementRef) { + afterRender(() => { + // このコンポーネント内の最初の入力要素にフォーカスします。 + elementRef.nativeElement.querySelector('input')?.focus(); + }); + } +} +``` + +`afterRender` および `afterNextRender` は、通常はコンポーネントのコンストラクターである +*注入コンテキスト*で呼び出される必要があります。 + +**可能な限り、DOM 操作を直接行うことは避けてください。** +コンポーネントテンプレートでDOMの構造を表現し、バインディングを使用してそのDOMを更新することを常に優先してください。 + +**レンダリングコールバックは、サーバーサイドレンダリングまたはビルド時の事前レンダリング中には実行されません。** + +**他の Angular ライフサイクルフック内で DOM を直接操作しないでください。** +Angularは、レンダリングコールバック以外では、コンポーネントのDOMが完全にレンダリングされていることを保証しません。 +さらに、他のライフサイクルフック中にDOMを読み取ったり変更したりすると、 +[レイアウトのちらつき](https://web.dev/avoid-large-complex-layouts-and-layout-thrashing)を引き起こすなど、 +ページのパフォーマンスに悪影響を与える可能性があります。 + +## コンポーネントのレンダラーを使用する + +コンポーネントは、`Renderer2` のインスタンスを注入して、 +他のAngular機能に関連付けられた特定のDOM操作ができます。 + +コンポーネントの `Renderer2` によって作成されたDOM要素はすべて、 +そのコンポーネントの [スタイルのカプセル化](guide/components/styling#style-scoping) に組み込まれます。 + +このような `Renderer2` APIは、Angularのアニメーションシステムにも関連付けられています。 +`setProperty` メソッドを使用して合成アニメーションプロパティを更新し、`listen` メソッドを使用して合成アニメーションイベントのイベントリスナーを追加できます。 +詳細については、[アニメーション](guide/animations) ガイドを参照してください。 + +これらの2つの狭いユースケース以外では、 +`Renderer2` とネイティブDOM APIを使用することに違いはありません。 +`Renderer2` APIは、サーバーサイドレンダリングまたはビルド時の事前レンダリングコンテキストでのDOM操作をサポートしていません。 + +## DOM API を使用するタイミング + +Angularはほとんどのレンダリングの問題を処理しますが、一部の動作ではDOM APIを使用する必要がある場合があります。 +一般的なユースケースには、以下のようなものがあります。 + +- 要素のフォーカスを管理する +- `getBoundingClientRect` などの要素のジオメトリを測定する +- 要素のテキストコンテンツを読み取る +- [`MutationObserver`](https://developer.mozilla.org/docs/Web/API/MutationObserver)、 + [`ResizeObserver`](https://developer.mozilla.org/docs/Web/API/ResizeObserver)、 + または[`IntersectionObserver`](https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API) などの + ネイティブオブザーバーを設定する。 + +DOM要素の挿入、削除、および変更は避けてください。 +特に、**要素の `innerHTML` プロパティを直接設定しないでください。** +これは、アプリケーションを [クロスサイトスクリプティング (XSS) 攻撃](https://developer.mozilla.org/docs/Glossary/Cross-site_scripting) に対して脆弱にする可能性があります。 +Angularのテンプレートバインディングには、`innerHTML` のバインディングを含め、XSS攻撃から保護する安全対策が含まれています。 +詳細については、[セキュリティガイド](best-practices/security) を参照してください。 diff --git a/adev-ja/src/content/guide/components/host-elements.md b/adev-ja/src/content/guide/components/host-elements.md new file mode 100644 index 0000000000..14362a07ae --- /dev/null +++ b/adev-ja/src/content/guide/components/host-elements.md @@ -0,0 +1,127 @@ +# コンポーネントのホスト要素 + +ヒント: このガイドでは、既に[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使用する場合は、まずこちらをお読みください。 + +Angularは、コンポーネントのセレクターに一致するすべてのHTML要素に対して、コンポーネントのインスタンスを作成します。 +コンポーネントのセレクターに一致するDOM要素は、そのコンポーネントの**ホスト要素**です。 +コンポーネントのテンプレートの内容は、ホスト要素内にレンダリングされます。 + +```ts +// コンポーネントソース +@Component({ + selector: 'profile-photo', + template: ` + Your profile photo + `, +}) +export class ProfilePhoto {} +``` + +```html + +

Your profile photo

+ + +``` + +```html + +

Your profile photo

+ + Your profile photo + + +``` + +上記の例では、``は`ProfilePhoto`コンポーネントのホスト要素です。 + +## ホスト要素へのバインディング + +コンポーネントは、ホスト要素にプロパティ、属性、イベントをバインドできます。 +これは、コンポーネントのテンプレート内の要素のバインディングと同じように動作しますが、 +`@Component`デコレーターの`host`プロパティで定義されます。 + +```ts +@Component({ + ..., + host: { + 'role': 'slider', + '[attr.aria-valuenow]': 'value', + '[tabIndex]': 'disabled ? -1 : 0', + '(keydown)': 'updateValue($event)', + }, +}) +export class CustomSlider { + value: number = 0; + disabled: boolean = false; + + updateValue(event: KeyboardEvent) { /* ... */ } + + /* ... */ +} +``` + +## `@HostBinding`および`@HostListener`デコレーター + +クラスメンバーに`@HostBinding`および`@HostListener`デコレーターを適用することにより、 +ホスト要素にバインドできます。 + +`@HostBinding`を使用すると、ホストのプロパティと属性を、プロパティとメソッドにバインドできます。 + +```ts +@Component({ + /* ... */ +}) +export class CustomSlider { + @HostBinding('attr.aria-valuenow') + value: number = 0; + + @HostBinding('tabIndex') + getTabIndex() { + return this.disabled ? -1 : 0; + } + + /* ... */ +} +``` + +`@HostListener`を使用すると、ホスト要素にイベントリスナーをバインドできます。 +デコレーターは、イベント名とオプションの引数の配列を受け取ります。 + +```ts +export class CustomSlider { + @HostListener('keydown', ['$event']) + updateValue(event: KeyboardEvent) { + /* ... */ + } +} +``` + +**常に`@HostBinding`と`@HostListener`よりも`host`プロパティの使用を優先してください。** +これらのデコレーターは、下位互換性のためにのみ存在します。 + +## バインディングの衝突 + +テンプレートでコンポーネントを使用する場合、そのコンポーネントインスタンスの要素にバインディングを追加できます。 +コンポーネントは、同じプロパティまたは属性に対するホストバインディングを定義することもあります。 + +```ts +@Component({ + ..., + host: { + 'role': 'presentation', + '[id]': 'id', + } +}) +export class ProfilePhoto { /* ... */ } +``` + +```html + +``` + +このような場合、以下のルールによってどの値が優先されるかが決まります。 + +- 両方の値が静的な場合、インスタンスバインディングが優先されます。 +- 一方の値が静的で他方が動的な場合、動的な値が優先されます。 +- 両方の値が動的な場合、コンポーネントのホストバインディングが優先されます。 diff --git a/adev-ja/src/content/guide/components/importing.md b/adev-ja/src/content/guide/components/importing.md new file mode 100644 index 0000000000..88733075e8 --- /dev/null +++ b/adev-ja/src/content/guide/components/importing.md @@ -0,0 +1,35 @@ +# コンポーネントのインポートと使用 + +ヒント: このガイドでは、すでに[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 + +Angularでは、他のコンポーネントでコンポーネントを使用可能にするために、2つの方法がサポートされています。スタンドアロンコンポーネントとして、または `NgModule` で。 + +## スタンドアロンコンポーネント + +**スタンドアロンコンポーネント** は、コンポーネントメタデータで `standalone: true` を設定したコンポーネントです。 +スタンドアロンコンポーネントは、テンプレートで使用されている他のコンポーネント、 +ディレクティブ、パイプを直接インポートします。 + + +@Component({ + standalone: true, + selector: 'profile-photo', +}) +export class ProfilePhoto { } + +@Component({ + standalone: true, + imports:[ProfilePhoto], + template: `` +}) +export class UserProfile { } + + +スタンドアロンコンポーネントは、他のスタンドアロンコンポーネントに直接インポートできます。 + +Angularチームでは、新規開発にはスタンドアロンコンポーネントを使用することを推奨しています。 + +## NgModules + +スタンドアロンコンポーネントが導入される前のAngularコードでは、`NgModule` を使用して、他のコンポーネントをインポートし、使用していました。 +詳細については、[`NgModule` ガイド](guide/ngmodules) を参照してください。 diff --git a/adev-ja/src/content/guide/components/inheritance.md b/adev-ja/src/content/guide/components/inheritance.md new file mode 100644 index 0000000000..b20c587732 --- /dev/null +++ b/adev-ja/src/content/guide/components/inheritance.md @@ -0,0 +1,109 @@ +# 継承 + +ヒント: このガイドは、すでに[基本概念のガイド](essentials)を読んだことを前提としています。Angular初心者の方は、最初にそちらをお読みください。 + +AngularコンポーネントはTypeScriptクラスであり、 +標準のJavaScript継承セマンティクスに従います。 + +コンポーネントは、任意の基底クラスを拡張できます。 + +```ts +export class ListboxBase { + value: string; +} + +@Component({ ... }) +export class CustomListbox extends ListboxBase { + // CustomListboxは`value`プロパティを継承します。 +} +``` + +## 他のコンポーネントとディレクティブの拡張 + +コンポーネントが別のコンポーネントまたはディレクティブを拡張する場合、基底クラスのデコレーターで定義されたすべてのメタデータと、基底クラスのデコレートされたメンバーを継承します。 +これには、セレクター、テンプレート、スタイル、ホストバインディング、入力、出力、ライフサイクルメソッド、 +およびその他の設定が含まれます。 + +```ts +@Component({ + selector: 'base-listbox', + template: ` + ... + `, + host: { + '(keydown)': 'handleKey($event)', + }, +}) +export class ListboxBase { + @Input() value: string; + handleKey(event: KeyboardEvent) { + /* ... */ + } +} + +@Component({ + selector: 'custom-listbox', + template: ` + ... + `, + host: { + '(click)': 'focusActiveOption()', + }, +}) +export class CustomListbox extends ListboxBase { + @Input() disabled = false; + focusActiveOption() { + /* ... */ + } +} +``` + +上記の例では、`CustomListbox`は`ListboxBase`に関連付けられたすべての情報を継承し、 +セレクターとテンプレートを独自の値で上書きしています。 +`CustomListbox`には2つの入力(`value`や`disabled`)と、2つのイベントリスナー(`keydown`や`click`)があります。 + +子クラスは、最終的にすべての祖先クラスの入力、出力、ホストバインディング、 +および独自の入力、出力、ホストバインディングの_ユニオン_を持ちます。 + +### 注入された依存性の転送 + +基底クラスが依存性の注入に依存している場合、 +子クラスはこれらの依存性を明示的に`super`に渡す必要があります。 + +```ts +@Component({ ... }) +export class ListboxBase { + constructor(private element: ElementRef) { } +} + +@Component({ ... }) +export class CustomListbox extends ListboxBase { + constructor(element: ElementRef) { + super(element); + } +} +``` + +### ライフサイクルメソッドのオーバーライド + +基底クラスが`ngOnInit`などのライフサイクルメソッドを定義する場合、 +`ngOnInit`も実装する子クラスは、基底クラスの実装を*上書き*します。 +基底クラスのライフサイクルメソッドを保持したい場合は、`super`で明示的にメソッドを呼び出します。 + +```ts +@Component({ ... }) +export class ListboxBase { + protected isInitialized = false; + ngOnInit() { + this.isInitialized = true; + } +} + +@Component({ ... }) +export class CustomListbox extends ListboxBase { + override ngOnInit() { + super.ngOnInit(); + /* ... */ + } +} +``` diff --git a/adev-ja/src/content/guide/components/inputs.md b/adev-ja/src/content/guide/components/inputs.md new file mode 100644 index 0000000000..a604815607 --- /dev/null +++ b/adev-ja/src/content/guide/components/inputs.md @@ -0,0 +1,198 @@ +# 入力プロパティでデータを受け取る + +ヒント: このガイドは、[基本概念のガイド](essentials) を既読していることを前提としています。Angularを初めて使う場合は、まずそちらをお読みください。 + +ヒント: 他のウェブフレームワークに精通している場合は、入力プロパティは*props*に似ています。 + +コンポーネントを作成する際、特定のクラスプロパティに `@Input` デコレーターを追加することで、そのプロパティを **バインド可能** にできます。 + + +@Component({...}) +export class CustomSlider { + @Input() value = 0; +} + + +これにより、テンプレートでプロパティにバインドできます。 + +```html + +``` + +Angularは、`@Input` デコレーターでマークされたプロパティを **入力** と呼びます。コンポーネントを使用する際、入力に値を設定することでコンポーネントにデータを渡します。 + +**Angular はコンパイル時に静的に入力を記録します。** 入力は、実行時に追加または削除はできません。 + +コンポーネントクラスを拡張する場合、**入力は子クラスによって継承されます。** + +**入力名は、大文字と小文字が区別されます。** + +## 入力のカスタマイズ + +`@Input` デコレーターは、入力の動作を変更できる設定オブジェクトを受け取ります。 + +### 必須入力 + +`required` オプションを指定することで、特定の入力に常に値が設定されていることを強制できます。 + + +@Component({...}) +export class CustomSlider { + @Input({required: true}) value = 0; +} + + +必須入力をすべて指定せずにコンポーネントを使用しようとすると、Angularはビルド時にエラーを報告します。 + +### 入力変換 + +`transform` 関数を指定することで、Angularによって入力値が設定されるときに、入力値を変更できます。 + + +@Component({ + selector: 'custom-slider', + ... +}) +export class CustomSlider { + @Input({transform: trimString}) label = ''; +} + +function trimString(value: string | undefined) { + return value?.trim() ?? ''; +} + + +```html + +``` + +上記の例では、`systemVolume` の値が変更されるたびに、Angularは `trimString` を実行し、`label` に結果を設定します。 + +入力変換の最も一般的なユースケースは、テンプレートでより幅広い値の種類(多くの場合、`null` や `undefined` を含む)を受け入れることです。 + +**入力変換の関数は、ビルド時に静的に解析可能である必要があります。** 変換関数を条件付きで設定したり、式評価の結果として設定したりできません。 + +**入力変換の関数は、常に [純粋関数](https://en.wikipedia.org/wiki/Pure_function) である必要があります。** 変換関数の外の状態に依存すると、予期しない動作につながる可能性があります。 + +#### 型チェック + +入力変換を指定すると、変換関数のパラメーターの型によって、テンプレートで入力に設定できる値の型が決まります。 + + +@Component({...}) +export class CustomSlider { + @Input({transform: appendPx}) widthPx: string = ''; +} + +function appendPx(value: number) { + return `${value}px`; +} + + +上記の例では、`widthPx` 入力値は `number` を受け取りますが、クラスのプロパティは `string` です。 + +#### 組み込み変換 + +Angularには、最も一般的な2つのシナリオに対応する2つの組み込み変換関数が含まれています。ブール値と数値への値の強制変換です。 + + +import {Component, Input, booleanAttribute, numberAttribute} from '@angular/core'; + +@Component({...}) +export class CustomSlider { + @Input({transform: booleanAttribute}) disabled = false; + @Input({transform: numberAttribute}) number = 0; +} + + +`booleanAttribute` は、標準のHTML [ブール属性](https://developer.mozilla.org/docs/Glossary/Boolean/HTML) の動作を模倣します。 +属性の存在は "true" 値を示します。ただし、Angularの `booleanAttribute` は、リテラル文字列 `"false"` をブール値 `false` として扱います。 + +`numberAttribute` は、指定された値を数値に解析しようとします。解析に失敗すると、`NaN` を生成します。 + +### 入力エイリアス + +`alias` オプションを指定することで、テンプレートでの入力の名前を変更できます。 + + +@Component({...}) +export class CustomSlider { + @Input({alias: 'sliderValue'}) value = 0; +} + + +```html + +``` + +このエイリアスは、TypeScriptコードでのプロパティの使用には影響しません。 + +一般的にコンポーネントの入力にエイリアスを使用することは避けるべきですが、この機能はプロパティの名前を変更しながら元の名前のエイリアスを保持する場合や、ネイティブDOM要素プロパティの名前との衝突を避ける場合に役立ちます。 + +`@Input` デコレーターは、設定オブジェクトの代わりにエイリアスを最初の引数として受け取ります。 + +## ゲッターとセッターを使用した入力 + +ゲッターとセッターを使用して実装されたプロパティは、入力にできます。 + + +export class CustomSlider { + @Input() + get value(): number { + return this.internalValue; + } + + set value(newValue: number) { + this.internalValue = newValue; + } + + private internalValue = 0; +} + + +公開セッターのみを定義することで、*書き込み専用*の入力を作成できます。 + + +export class CustomSlider { + @Input() + set value(newValue: number) { + this.internalValue = newValue; + } + + private internalValue = 0; +} + + +可能な場合は、ゲッターとセッターの代わりに 入力変換 を使用することをお勧めします。 + +複雑なゲッターやセッターは避けてください。Angularは、入力のセッターを複数回呼び出す場合があります。セッターがDOM操作などのコストのかかる動作をする場合、アプリケーションのパフォーマンスに悪影響を及ぼす可能性があります。 + +## `@Component` デコレーターで入力を指定する + +`@Input` デコレーターに加えて、`@Component` デコレーターの `inputs` プロパティでコンポーネントの入力を指定できます。これは、コンポーネントが基本クラスからプロパティを継承する場合に役立ちます。 + + +// `CustomSlider` は、`BaseSlider` から `disabled` プロパティを継承します。 +@Component({ + ..., + inputs: ['disabled'], +}) +export class CustomSlider extends BaseSlider { } + + +さらに、`inputs` リストで入力エイリアスを指定できます。エイリアスをコロンの後に文字列に記述します。 + + +// `CustomSlider` は、`BaseSlider` から `disabled` プロパティを継承します。 +@Component({ + ..., + inputs: ['disabled: sliderDisabled'], +}) +export class CustomSlider extends BaseSlider { } + + +## 入力名の選択 + +DOM要素(HTMLElementなど)のプロパティと衝突する入力名は避けてください。名前の衝突は、バインドされたプロパティがコンポーネントに属しているのか、DOM要素に属しているのか混乱を生じさせます。 + +コンポーネントセレクターのように、コンポーネント入力にプレフィックスを追加することは避けてください。特定の要素は、1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属すると見なすことができます。 diff --git a/adev-ja/src/content/guide/components/lifecycle.md b/adev-ja/src/content/guide/components/lifecycle.md new file mode 100644 index 0000000000..0b6d3b3f51 --- /dev/null +++ b/adev-ja/src/content/guide/components/lifecycle.md @@ -0,0 +1,350 @@ +# コンポーネントライフサイクル + +ヒント: このガイドは、[Essentials ガイド](essentials) を既にお読みになっていることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 + +コンポーネントの**ライフサイクル**とは、コンポーネントの作成から破棄までの間に起こる一連のステップのことです。 +各ステップは、Angularがコンポーネントをレンダリングし、 +時間の経過とともに更新をチェックするプロセスにおける異なる部分を表しています。 + +コンポーネントでは、これらのステップ中にコードを実行するために**ライフサイクルフック**を実装できます。 +特定のコンポーネントインスタンスに関連するライフサイクルフックは、コンポーネントクラスのメソッドとして実装されます。 +Angularアプリケーション全体に関連するライフサイクルフックは、 +コールバックを受け取る関数として実装されます。 + +コンポーネントのライフサイクルは、Angularが時間の経過とともにコンポーネントの変更をチェックする方法と密接に関連しています。 +このライフサイクルを理解するために必要なのは、Angularがアプリケーションツリーを上から下に歩き、 +テンプレートバインディングの変更をチェックすることだけです。 +以下で説明するライフサイクルフックは、Angularがこのトラバーサルを実行している間に実行されます。 +このトラバーサルは、各コンポーネントをちょうど1回だけ訪問するため、 +プロセス中にさらに状態を変更することは避けるべきです。 + +## 概要 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
フェーズメソッド概要
作成constructor + + 標準の JavaScript クラスコンストラクター + 。Angular がコンポーネントをインスタンス化するときに実行されます。 +
変更検知ngOnInit + Angular がコンポーネントのすべての入力を初期化した後に1回実行されます。
ngOnChangesコンポーネントの入力が変更されるたびに実行されます。
ngDoCheckこのコンポーネントが変更のためにチェックされるたびに実行されます。
ngAfterViewInitコンポーネントのビューが初期化された後に1回実行されます。
ngAfterContentInitコンポーネントのコンテンツが初期化された後に1回実行されます。
ngAfterViewCheckedコンポーネントのビューが変更のためにチェックされるたびに実行されます。
ngAfterContentCheckedこのコンポーネントのコンテンツが変更のためにチェックされるたびに実行されます。
レンダリングafterNextRenderすべてのコンポーネントがDOMにレンダリングされた次の時間に1回実行されます。
afterRenderすべてのコンポーネントがDOMにレンダリングされるたびに実行されます。
破棄ngOnDestroyコンポーネントが破棄される直前に1回実行されます。
+
+ +### ngOnInit + +`ngOnInit` メソッドは、Angularがすべてのコンポーネントの入力を初期値で初期化した後に実行されます。 +コンポーネントの `ngOnInit` は、ちょうど1回だけ実行されます。 + +このステップは、コンポーネント自身のテンプレートが初期化されるに発生します。 +これは、初期入力値に基づいてコンポーネントの状態を更新できることを意味します。 + +### ngOnChanges + +`ngOnChanges` メソッドは、コンポーネントの入力が変更された後に実行されます。 + +このステップは、コンポーネント自身のテンプレートがチェックされるに発生します。 +これは、初期入力値に基づいてコンポーネントの状態を更新できることを意味します。 + +初期化中は、最初の `ngOnChanges` は `ngOnInit` の前に実行されます。 + +#### 変更の検査 + +`ngOnChanges` メソッドは、1つの `SimpleChanges` 引数を受け取ります。 +このオブジェクトは、[`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) であり、 +各コンポーネントの入力名を `SimpleChange` オブジェクトにマッピングします。 +各 `SimpleChange` には、入力の以前の値、現在の値、 +および入力が初めて変更されたかどうかを示すフラグが含まれています。 + +```ts +@Component({ + /* ... */ +}) +export class UserProfile { + @Input() name: string = ''; + + ngOnChanges(changes: SimpleChanges) { + for (const inputName in changes) { + const inputValues = changes[inputName]; + console.log(`Previous ${inputName} == ${inputValues.previousValue}`); + console.log(`Current ${inputName} == ${inputValues.currentValue}`); + console.log(`Is first ${inputName} change == ${inputValues.firstChange}`); + } + } +} +``` + +入力プロパティに `alias` を指定した場合、`SimpleChanges` Recordは、エイリアスではなく、 +TypeScriptプロパティ名をキーとして使用します。 + +### ngOnDestroy + +`ngOnDestroy` メソッドは、コンポーネントが破棄される直前に1回だけ実行されます。 +Angularは、コンポーネントがページに表示されなくなった場合(`NgIf` によって隠された場合や、別のページに移動した場合など)、 +コンポーネントを破棄します。 + +#### DestroyRef + +`ngOnDestroy` メソッドの代わりに、`DestroyRef` のインスタンスを注入できます。 +コンポーネントの破棄時に呼び出されるコールバックを登録するには、 +`DestroyRef` の `onDestroy` メソッドを呼び出します。 + +```ts +@Component({ + /* ... */ +}) +export class UserProfile { + constructor(private destroyRef: DestroyRef) { + destroyRef.onDestroy(() => { + console.log('UserProfile destruction'); + }); + } +} +``` + +`DestroyRef` インスタンスを、 +コンポーネント外部の関数やクラスに渡すことができます。 +このパターンは、コンポーネントが破棄されたときにクリーンアップを実行する必要がある他のコードがある場合に使用します。 + +`DestroyRef` を使用して、クリーンアップコードをすべて `ngOnDestroy` メソッドに置くのではなく、 +クリーンアップコードに近い場所にセットアップコードを保持できます。 + +### ngDoCheck + +`ngDoCheck` メソッドは、 +Angularがコンポーネントのテンプレートの変更をチェックするたびに実行されます。 + +このライフサイクルフックを使用して、Angularの通常の変更検知の外部で状態の変更を手動でチェックし、コンポーネントの状態を手動で更新できます。 + +このメソッドは非常に頻繁に実行され、ページのパフォーマンスに大きく影響する可能性があります。 +可能な限り、このフックの定義を避け、代替手段がない場合にのみ使用してください。 + +初期化中は、最初の `ngDoCheck` は `ngOnInit` の後に実行されます。 + +### ngAfterViewInit + +`ngAfterViewInit` メソッドは、 +コンポーネントのテンプレート(その*ビュー*)内のすべての子が初期化された後に1回だけ実行されます。 + +このライフサイクルフックを使用して、 +[ビュークエリ](guide/components/queries#view-queries) の結果を読み取ることができます。 +これらのクエリの初期化された状態にアクセスできますが、このメソッドで状態を変更しようとすると、 +[ExpressionChangedAfterItHasBeenCheckedError](errors/NG0100) が発生します。 + +### ngAfterContentInit + +`ngAfterContentInit` メソッドは、 +コンポーネント(その*コンテンツ*)内にネストされたすべての子が初期化された後に1回だけ実行されます。 + +このライフサイクルフックを使用して、 +[コンテンツクエリ](guide/components/queries#content-queries) の結果を読み取ることができます。 +これらのクエリの初期化された状態にアクセスできますが、このメソッドで状態を変更しようとすると、 +[ExpressionChangedAfterItHasBeenCheckedError](errors/NG0100) が発生します。 + +### ngAfterViewChecked + +`ngAfterViewChecked` メソッドは、 +コンポーネントのテンプレート(その*ビュー*)内のすべての子が変更のためにチェックされるたびに実行されます。 + +このメソッドは非常に頻繁に実行され、ページのパフォーマンスに大きく影響する可能性があります。 +可能な限り、このフックの定義を避け、代替手段がない場合にのみ使用してください。 + +[ビュークエリ](guide/components/queries#view-queries) +の更新された状態にアクセスできますが、 +このメソッドで状態を変更しようとすると、 +[ExpressionChangedAfterItHasBeenCheckedError](errors/NG0100) が発生します。 + +### ngAfterContentChecked + +`ngAfterContentChecked` メソッドは、 +コンポーネント(その*コンテンツ*)内にネストされたすべての子が変更のためにチェックされるたびに実行されます。 + +このメソッドは非常に頻繁に実行され、ページのパフォーマンスに大きく影響する可能性があります。 +可能な限り、このフックの定義を避け、代替手段がない場合にのみ使用してください。 + +[コンテンツクエリ](guide/components/queries#content-queries) +の更新された状態にアクセスできますが、 +このメソッドで状態を変更しようとすると、 +[ExpressionChangedAfterItHasBeenCheckedError](errors/NG0100) が発生します。 + +### afterRender と afterNextRender + +`afterRender` と `afterNextRender` 関数は、 +Angularがページ上の*すべてのコンポーネント*をDOMにレンダリングし終えた後に呼び出される**レンダリングコールバック** を登録できます。 + +これらの関数は、このガイドで説明した他のライフサイクルフックとは異なります。 +クラスメソッドではなく、コールバックを受け取るスタンドアロン関数です。 +レンダリングコールバックの実行は、特定のコンポーネントインスタンスに結び付けられるのではなく、アプリケーション全体のフックに結び付けられます。 + +`afterRender` と `afterNextRender` は、 +[注入コンテキスト](guide/di/dependency-injection-context)(通常はコンポーネントのコンストラクター) +で呼び出す必要があります。 + +レンダリングコールバックを使用して、手動でDOMを操作できます。 +AngularでDOMを操作する方法については、[DOM API の使用](guide/components/dom-apis) を参照してください。 + +レンダリングコールバックは、サーバーサイドレンダリング中またはビルド時の事前レンダリング中は実行されません。 + +#### afterRender フェーズ + +`afterRender` または `afterNextRender` を使用する場合、 +オプションで作業をフェーズに分割できます。 +フェーズを使用すると、DOM操作のシーケンスを制御でき、[レイアウトのスラッシング](https://web.dev/avoid-large-complex-layouts-and-layout-thrashing) を最小限に抑えるために、 +*書き込み*操作を*読み込み*操作の前にシーケンスできます。 +フェーズ間で通信するために、フェーズ関数は、 +次のフェーズでアクセスできる結果値を返すことができます。 + +```ts +import {Component, ElementRef, afterNextRender} from '@angular/core'; + +@Component({...}) +export class UserProfile { + private prevPadding = 0; + private elementHeight = 0; + + constructor(elementRef: ElementRef) { + const nativeElement = elementRef.nativeElement; + + afterNextRender({ + // `Write` フェーズを使用して、ジオメトリのプロパティに書き込みます。 + write: () => { + const padding = computePadding(); + const changed = padding !== prevPadding; + if (changed) { + nativeElement.style.padding = padding; + } + return changed; // 何か変更があったかどうかを `Read` フェーズに伝えます。 + }, + + // `Read` フェーズを使用して、すべての書き込みが完了した後にジオメトリのプロパティを読み取ります。 + read: (didWrite) => { + if (didWrite) { + this.elementHeight = nativeElement.getBoundingClientRect().height; + } + } + }); + } +} +``` + +フェーズは4つあり、次の順序で実行されます。 + +| フェーズ | 説明 | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `earlyRead` | このフェーズを使用して、その後の計算に厳密に必要な、レイアウトに影響を与えるDOMプロパティとスタイルを読み取ります。可能な限り、このフェーズを避け、`write` フェーズと `read` フェーズを優先します。 | +| `mixedReadWrite` | デフォルトのフェーズ。レイアウトに影響を与えるプロパティとスタイルを読み書きする必要がある操作に使用します。可能な限り、このフェーズを避け、明示的な `write` フェーズと `read` フェーズを優先します。 | +| `write` | このフェーズを使用して、レイアウトに影響を与えるDOMプロパティとスタイルを書き込みます。 | +| `read` | このフェーズを使用して、レイアウトに影響を与えるDOMプロパティを読み取ります。 | + +## ライフサイクルインターフェース + +Angularは、各ライフサイクルメソッド用のTypeScriptインターフェースを提供します。 +これらのインターフェースをインポートして `implement` することで、 +実装に誤字脱字がないことを保証できます。 + +各インターフェースは、`ng` プレフィックスのない対応するメソッドと同じ名前を持っています。 +たとえば、`ngOnInit` のインターフェースは `OnInit` です。 + +```ts +@Component({ + /* ... */ +}) +export class UserProfile implements OnInit { + ngOnInit() { + /* ... */ + } +} +``` + +## 実行順序 + +次の図は、Angularのライフサイクルフックの実行順序を示しています。 + +### 初期化中 + +```mermaid +graph TD; +id[constructor]-->CHANGE; +subgraph CHANGE [変更検知] +direction TB +ngOnChanges-->ngOnInit; +ngOnInit-->ngDoCheck; +ngDoCheck-->ngAfterContentInit; +ngDoCheck-->ngAfterViewInit +ngAfterContentInit-->ngAfterContentChecked +ngAfterViewInit-->ngAfterViewChecked +end +CHANGE--レンダリング-->afterRender +``` + +### 後続の更新 + +```mermaid +graph TD; +subgraph CHANGE [変更検知] +direction TB +ngOnChanges-->ngDoCheck +ngDoCheck-->ngAfterContentChecked; +ngDoCheck-->ngAfterViewChecked +end +CHANGE--レンダリング-->afterRender +``` + +### ディレクティブとの順序付け + +テンプレートまたは `hostDirectives` プロパティで、コンポーネントと同じ要素に1つ以上のディレクティブを配置する場合、 +フレームワークは単一の要素上のコンポーネントとディレクティブの間で特定のライフサイクルフックの順序を保証しません。 +観察された順序に依存しないでください。 +これは、Angularの以降のバージョンで変更される可能性があります。 diff --git a/adev-ja/src/content/guide/components/output-function.md b/adev-ja/src/content/guide/components/output-function.md new file mode 100644 index 0000000000..2292575645 --- /dev/null +++ b/adev-ja/src/content/guide/components/output-function.md @@ -0,0 +1,109 @@ +# 関数ベースの出力 + +`output()` 関数は、ディレクティブまたはコンポーネントで出力を宣言します。 +出力を使用すると、親コンポーネントに出力を送信できます。 + +役に立つ情報: `output()` 関数は現在、[開発プレビュー](/reference/releases#developer-preview)です。 + + +import {Component, output} from '@angular/core'; + +@Component({...}) +export class MyComp { + onNameChange = output() // OutputEmitterRef + + setNewName(newName: string) { + this.onNameChange.emit(newName); + } +} + + +出力は、`output` 関数をクラスメンバーのイニシャライザーとして使用すると、Angularによって自動的に認識されます。 +親コンポーネントは、イベントバインディング構文を使用して、テンプレート内の出力を購読できます。 + +```html + +``` + +## 出力のエイリアス + +Angularは、クラスメンバーの名前を出力の名前として使用します。 +出力にエイリアスを付けることで、公開名を変更できます。 + +```typescript +class MyComp { + onNameChange = output({alias: 'ngxNameChange'}); +} +``` + +これにより、ユーザーは `(ngxNameChange)` を使用して出力にバインドできます。コンポーネント内では、`this.onNameChange` を使用して出力エミッターにアクセスできます。 + +## プログラムによる購読 + +コンシューマーは、`ComponentRef` への参照を使用して、コンポーネントを動的に作成できます。 +そのような場合、親は `OutputRef` タイプのプロパティに直接アクセスすることで出力に購読できます。 + +```ts +const myComp = viewContainerRef.createComponent(...); + +myComp.instance.onNameChange.subscribe(newName => { + console.log(newName); +}); +``` + +`myComp` が破棄されると、Angularは自動的に購読をクリーンアップします。 +または、より早く購読を解除するための関数を含むオブジェクトが返されます。 + +## RxJS Observableをソースとして使用 + +場合によっては、RxJS Observableに基づいて出力値を送信したいことがあります。 +Angularは、RxJS Observableをアウトプットのソースとして使用する方法を提供します。 + +`outputFromObservable` 関数は、`output()` 関数と同様にコンパイラのプリミティブであり、RxJS Observableによって駆動される出力を宣言します。 + + +import {Directive} from '@angular/core'; +import {outputFromObservable} from '@angular/core/rxjs-interop'; + +@Directive(...) +class MyDir { + nameChange$ = this.dataService.get(); // Observable + nameChange = outputFromObservable(this.nameChange$); +} + + +AngularはObservableへの購読を転送しますが、所有するディレクティブが破棄されると値の転送を停止します。 +上記の例では、`MyDir` が破棄されると、`nameChange` は値を送信しなくなります。 + +役に立つ情報: ほとんどの場合、`output()` を使用すれば十分で、値を命令的に送信できます。 + +## 出力をObservableに変換する + +`OutputRef` の `.subscribe` メソッドを呼び出すことで、出力に購読できます。 +他のケースでは、Angularは `OutputRef` をObservableに変換するヘルパー関数を提供します。 + + +import {outputToObservable} from '@angular/core/rxjs-interop'; + +@Component(...) +class MyComp { + onNameChange = output(); +} + +// `MyComp` へのインスタンス参照。 +const myComp: MyComp; + +outputToObservable(this.myComp.instance.onNameChange) // Observable + .pipe(...) + .subscribe(...); + + +## なぜデコレーターベースの `@Output()` よりも `output()` を使用する必要があるのか? + +`output()` 関数は、デコレーターベースの `@Output` と `EventEmitter` に比べて、多くの利点があります。 + +1. よりシンプルなメンタルモデルとAPI: +
• RxJSのエラーチャネル、完了チャネル、またはその他のAPIの概念はありません。 +
• 出力は単純なエミッターです。 `.emit` 関数を使用して値を送信できます。 +2. より正確な型。 +
• `OutputEmitterRef.emit(value)` は、正しく型付けされていますが、`EventEmitter` の型は壊れており、ランタイムエラーが発生する可能性があります。 diff --git a/adev-ja/src/content/guide/components/outputs.md b/adev-ja/src/content/guide/components/outputs.md new file mode 100644 index 0000000000..adc9f9cd5d --- /dev/null +++ b/adev-ja/src/content/guide/components/outputs.md @@ -0,0 +1,102 @@ +# カスタムイベントと出力 + +ヒント: このガイドは、[基本概念のガイド](essentials) を既読していることを前提としています。Angularを初めて使用する場合は、まずそちらをお読みください。 + +Angularコンポーネントは、新しい `EventEmitter` にプロパティを割り当てて `@Output` デコレーターを追加することで、カスタムイベントを定義できます。 + + +@Component({...}) +export class ExpandablePanel { + @Output() panelClosed = new EventEmitter(); +} + + +```html + +``` + +`EventEmitter` の `emit` メソッドを呼び出すことで、イベントを送信できます。 + + + this.panelClosed.emit(); + + +Angularは、`@Output` デコレーターでマークされたプロパティを**出力**と呼びます。出力を使用して、`click` のようなネイティブブラウザイベントと同様に、他のコンポーネントにデータを渡すことができます。 + +**Angular カスタムイベントは DOM を伝播しません。** + +**出力名は、大文字と小文字が区別されます。** + +コンポーネントクラスを拡張する場合、**出力は子クラスによって継承されます。** + +## イベントデータの送信 + +`emit` を呼び出す際にイベントデータを渡すことができます。 + + +// プリミティブ値を送信できます。 +this.valueChanged.emit(7); + +// カスタムイベントオブジェクトを送信できます +this.thumbDropped.emit({ + pointerX: 123, + pointerY: 456, +}) + + +テンプレートでイベントリスナーを定義する場合、`$event` 変数からイベントデータにアクセスできます。 + +```html + +``` + +## 出力名のカスタマイズ + +`@Output` デコレーターは、テンプレートでイベントに異なる名前を指定できるパラメータを受け取ります。 + + +@Component({...}) +export class CustomSlider { + @Output('valueChanged') changed = new EventEmitter(); +} + + +```html + +``` + +このエイリアスは、TypeScriptコードでのプロパティの使用には影響しません。 + +コンポーネントの出力のエイリアスは一般的に避けるべきですが、この機能は、元の名前のエイリアスを保持しながらプロパティの名前を変更したり、ネイティブDOMイベントの名前との衝突を回避したりするのに役立ちます。 + +## `@Component` デコレーターで出力名を指定する + +`@Output` デコレーターに加えて、`@Component` デコレーターの `outputs` プロパティを使用して、コンポーネントの出力名を指定できます。これは、コンポーネントが基本クラスからプロパティを継承する場合に役立ちます。 + + +// `CustomSlider` は、`BaseSlider` から `valueChanged` プロパティを継承します。 +@Component({ + ..., + outputs: ['valueChanged'], +}) +export class CustomSlider extends BaseSlider {} + + +さらに、`outputs` リストでコロンの後にエイリアスを置くことで、出力のエイリアスを指定できます。 + + +// `CustomSlider` は、`BaseSlider` から `valueChanged` プロパティを継承します。 +@Component({ + ..., + outputs: ['valueChanged: volumeChanged'], +}) +export class CustomSlider extends BaseSlider {} + + +## イベント名の選択 + +`HTMLElement` などのDOM要素のイベントと衝突する出力名を選ぶことは避けてください。名前が衝突すると、バインドされているプロパティがコンポーネントに属しているのか、DOM要素に属しているのかがわかりにくくなります。 + +コンポーネントセレクターのように、コンポーネント出力にプレフィックスを追加することは避けてください。特定の要素には、1つのコンポーネントしかホストできないため、カスタムプロパティはすべてコンポーネントに属していると見なすことができます。 + +出力名には常に[キャメルケース](https://en.wikipedia.org/wiki/Camel_case)を使用してください。出力名の前に「on」を付けることは避けてください。 diff --git a/adev-ja/src/content/guide/components/programmatic-rendering.md b/adev-ja/src/content/guide/components/programmatic-rendering.md new file mode 100644 index 0000000000..12a88373f7 --- /dev/null +++ b/adev-ja/src/content/guide/components/programmatic-rendering.md @@ -0,0 +1,127 @@ +# プログラムでコンポーネントをレンダリングする + +ヒント: このガイドでは、[基本概念のガイド](essentials)をすでに読んでいることを前提としています。Angularを初めて使用する場合は、まずこのガイドを読んでください。 + +コンポーネントはテンプレートで直接使用できるだけでなく、動的にもレンダリングできます。 +コンポーネントを動的にレンダリングする主な方法は2つあります。 +テンプレートで`NgComponentOutlet`を使用するか、TypeScriptコードで`ViewContainerRef`を使用します。 + +## NgComponentOutletを使用する + +`NgComponentOutlet`は、 +テンプレートで指定されたコンポーネントを動的にレンダリングする構造ディレクティブです。 + +```ts +@Component({ ... }) +export class AdminBio { /* ... */ } + +@Component({ ... }) +export class StandardBio { /* ... */ } + +@Component({ + ..., + template: ` +

Profile for {{user.name}}

+ ` +}) +export class CustomDialog { + @Input() user: User; + + getBioComponent() { + return this.user.isAdmin ? AdminBio : StandardBio; + } +} +``` + +ディレクティブの機能の詳細については、 +[NgComponentOutlet APIリファレンス](api/common/NgComponentOutlet)を参照してください。 + +## ViewContainerRefを使用する + +**ビューコンテナ**は、Angularのコンポーネントツリー内のコンテンツを含むことができるノードです。 +どのコンポーネントまたはディレクティブでも`ViewContainerRef`を注入して、 +DOM内のそのコンポーネントまたはディレクティブの場所に対応するビューコンテナへの参照を取得できます。 + +`ViewContainerRef`の`createComponent`メソッドを使用すると、コンポーネントを動的に作成してレンダリングできます。 +`ViewContainerRef`で新しいコンポーネントを作成すると、 +Angularはそのコンポーネントを、`ViewContainerRef`を注入したコンポーネントまたはディレクティブの次の兄弟としてDOMに追加します。 + +```ts +@Component({ + selector: 'leaf-content', + template: ` + This is the leaf content + `, +}) +export class LeafContent {} + +@Component({ + selector: 'outer-container', + template: ` +

This is the start of the outer container

+ +

This is the end of the outer container

+ `, +}) +export class OuterContainer {} + +@Component({ + selector: 'inner-item', + template: ` + + `, +}) +export class InnerItem { + constructor(private viewContainer: ViewContainerRef) {} + + loadContent() { + this.viewContainer.createComponent(LeafContent); + } +} +``` + +上記の例では、「コンテンツの読み込み」ボタンをクリックすると、次のDOM構造が生成されます。 + +```html + +

This is the start of the outer container

+ + + + This is the leaf content +

This is the end of the outer container

+
+``` + +## コンポーネントの遅延読み込み + +上記で説明した`NgComponentOutlet`と`ViewContainerRef`の両方の方法を使用して、 +標準のJavaScript [動的インポート](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/import)で +遅延読み込みされるコンポーネントをレンダリングできます。 + +```ts +@Component({ + ..., + template: ` +
+

Basic settings

+ +
+
+

Advanced settings

+ + +
` +}) +export class AdminSettings { + advancedSettings: {new(): AdminSettings} | undefined; + + async loadAdvanced() { + this.advancedSettings = await import('path/to/advanced_settings.js'); + } +} +``` + +上記の例では、ボタンをクリックすると`AdvancedSettings`が読み込まれて表示されます。 diff --git a/adev-ja/src/content/guide/components/queries.md b/adev-ja/src/content/guide/components/queries.md new file mode 100644 index 0000000000..f1c17f1d80 --- /dev/null +++ b/adev-ja/src/content/guide/components/queries.md @@ -0,0 +1,298 @@ +# クエリによるコンポーネントの子要素への参照 + +ヒント: このガイドでは、[基本概念のガイド](essentials)を読んでいることを前提としています。Angularを初めて使う場合は、まずこちらをお読みください。 + +コンポーネントは、子要素を見つけてそのインジェクターから値を読み取る**クエリ**を定義できます。 + +開発者は、クエリを使用して、子コンポーネント、ディレクティブ、DOM要素などの参照を取得することがほとんどです。 + +クエリには、**ビュークエリ**と**コンテンツクエリ**の2種類があります。 + +## ビュークエリ + +ビュークエリは、コンポーネントの*ビュー*(コンポーネント自身のテンプレートで定義されている要素)にある要素から結果を取得します。`@ViewChild` デコレーターを使用して、単一の結果をクエリできます。 + + +@Component({ + selector: 'custom-card-header', + ... +}) +export class CustomCardHeader { + text: string; +} + +@Component({ + selector: 'custom-card', + template: 'Visit sunny California!', +}) +export class CustomCard { + @ViewChild(CustomCardHeader) header: CustomCardHeader; + + ngAfterViewInit() { + console.log(this.header.text); + } +} + + +この例では、`CustomCard`コンポーネントは子要素の`CustomCardHeader`をクエリし、`ngAfterViewInit`で結果にアクセスしています。 + +クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が`NgIf`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、`@ViewChild`の結果を最新の状態に保ちます。 + +**ビュークエリの結果は、`ngAfterViewInit`ライフサイクルメソッドで利用可能になります**。この時点以前は、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションをご覧ください。 + +`@ViewChildren` デコレーターを使用して、複数の結果をクエリできます。 + + +@Component({ + selector: 'custom-card-action', + ..., +}) +export class CustomCardAction { + text: string; +} + +@Component({ + selector: 'custom-card', + template: ` + Save + Cancel + `, +}) +export class CustomCard { + @ViewChildren(CustomCardAction) actions: QueryList; + + ngAfterViewInit() { + this.actions.forEach(action => { + console.log(action.text); + }); + } +} + + +`@ViewChildren`は、クエリの結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。 + +**クエリはコンポーネントの境界を越えることは決してありません。** ビュークエリは、コンポーネントのテンプレートからのみ結果を取得できます。 + +## コンテンツクエリ + +コンテンツクエリは、コンポーネントの_コンテンツ_(コンポーネントが使用されているテンプレート内でコンポーネントにネストされた要素)にある要素から結果を取得します。`@ContentChild` デコレーターを使用して、単一の結果をクエリできます。 + + +@Component({ + selector: 'custom-toggle', + ... +}) +export class CustomToggle { + text: string; +} + +@Component({ + selector: 'custom-expando', + ... +}) +export class CustomExpando { + @ContentChild(CustomToggle) toggle: CustomToggle; + + ngAfterContentInit() { + console.log(this.toggle.text); + } +} + +@Component({ + selector: 'user-profile', + template: ` + + Show + + ` +}) + + +この例では、`CustomExpando`コンポーネントは子要素の`CustomToggle`をクエリし、`ngAfterContentInit`で結果にアクセスしています。 + +クエリが結果を見つけられない場合、その値は`undefined`になります。これは、ターゲット要素が存在しないか、`NgIf`によって非表示になっている場合に発生する可能性があります。Angularは、アプリケーションの状態が変化すると、`@ContentChild`の結果を最新の状態に保ちます。 + +デフォルトでは、コンテンツクエリはコンポーネントの*直接*の子要素のみを見つけ、子孫にトラバースすることはありません。 + +**コンテンツクエリの結果は、`ngAfterContentInit`ライフサイクルメソッドで利用可能になります**。この時点以前は、値は`undefined`です。コンポーネントライフサイクルの詳細については、[ライフサイクル](guide/components/lifecycle)セクションをご覧ください。 + +`@ContentChildren` デコレーターを使用して、複数の結果をクエリできます。 + + +@Component({ + selector: 'custom-menu-item', + ... +}) +export class CustomMenuItem { + text: string; +} + +@Component({ + selector: 'custom-menu', + ..., +}) +export class CustomMenu { + @ContentChildren(CustomMenuItem) items: QueryList; + + ngAfterContentInit() { + this.items.forEach(item => { + console.log(item.text); + }); + } +} + +@Component({ + selector: 'user-profile', + template: ` + + Cheese + Tomato + + ` +}) + + +`@ContentChildren`は、クエリの結果を含む`QueryList`オブジェクトを作成します。`changes`プロパティを使用して、クエリの結果が時間とともに変化した場合に購読できます。 + +**クエリはコンポーネントの境界を越えることは決してありません。** コンテンツクエリは、コンポーネント自身と同じテンプレートからのみ結果を取得できます。 + +## クエリロケーター + +各クエリのデコレーターの最初の引数は、**ロケーター**です。 + +ほとんどの場合、コンポーネントまたはディレクティブをロケーターとして使用します。 + +代わりに、[テンプレート参照変数](guide/templates/reference-variables) +に対応する文字列ロケーターを指定できます。 + +```ts +@Component({ + ..., + template: ` + + + ` +}) +export class ActionBar { + @ViewChild('save') saveButton: ElementRef; +} +``` + +同じテンプレート参照変数を定義している要素が複数ある場合、クエリは最初に一致する要素を取得します。 + +Angularは、CSSセレクターをクエリのロケーターとしてサポートしていません。 + +### クエリとインジェクターツリー + +ヒント: プロバイダーとAngularのインジェクションツリーの詳細については、[依存性注入](guide/di)を参照してください。 + +より高度なケースでは、`ProviderToken`をロケーターとして使用できます。これにより、コンポーネントとディレクティブのプロバイダーに基づいて要素を特定できます。 + +```ts +const SUB_ITEM = new InjectionToken('sub-item'); + +@Component({ + ..., + providers: [{provide: SUB_ITEM, useValue: 'special-item'}], +}) +export class SpecialItem { } + +@Component({...}) +export class CustomList { + @ContentChild(SUB_ITEM) subItemType: string; +} +``` + +上記の例では、`InjectionToken`をロケーターとして使用していますが、特定の要素を特定するために、任意の`ProviderToken`を使用できます。 + +## クエリオプション + +すべてのクエリデコレーターは、2番目のパラメーターとしてオプションオブジェクトを受け取ります。これらのオプションは、クエリが結果をどのように見つけるかを制御します。 + +### 静的クエリ + +`@ViewChild`および`@ContentChild`クエリは、`static`オプションを受け取ります。 + +```ts +@Component({ + selector: 'custom-card', + template: 'Visit sunny California!', +}) +export class CustomCard { + @ViewChild(CustomCardHeader, {static: true}) header: CustomCardHeader; + + ngOnInit() { + console.log(this.header.text); + } +} +``` + +`static: true`を設定することで、Angularにこのクエリのターゲットが*常に*存在し、条件付きでレンダリングされていないことを保証します。これにより、結果はより早い段階で、`ngOnInit`ライフサイクルメソッドで利用可能になります。 + +静的クエリの結果は、初期化後に更新されません。 + +`static`オプションは、`@ViewChildren`および`@ContentChildren`クエリでは使用できません。 + +### コンテンツ子孫 + +デフォルトでは、コンテンツクエリはコンポーネントの_直接_の子要素のみを見つけ、子孫にトラバースすることはありません。 + + +@Component({ + selector: 'custom-expando', + ... +}) +export class CustomExpando { + @ContentChild(CustomToggle) toggle: CustomToggle; +} + +@Component({ + selector: 'user-profile', + template: ` + + + + Show + + + ` +}) + + +上記の例では、`CustomExpando`は、``が``の直接の子要素ではないため、``を見つけることができません。`descendants: true`を設定することで、クエリが同じテンプレート内のすべての子孫をトラバースするように構成できます。ただし、クエリは、_決して_コンポーネントに侵入して他のテンプレート内の要素をトラバースすることはありません。 + +ビュークエリには、子孫を_常に_トラバースするため、このオプションはありません。 + +### 要素のインジェクターからの特定の値の読み取り + +デフォルトでは、クエリロケーターは、検索する要素と取得する値の両方を示します。代わりに、`read`オプションを指定して、ロケーターによって一致する要素から別の値を取得できます。 + +```ts +@Component({...}) +export class CustomExpando { + @ContentChild(ExpandoContent, {read: TemplateRef}) toggle: TemplateRef; +} +``` + +上記の例では、`ExpandoContent`ディレクティブを持つ要素を特定し、 +その要素に関連付けられた`TemplateRef`を取得します。 + +開発者は、`read`を使用して、`ElementRef`と`TemplateRef`を取得することがほとんどです。 + +## QueryList の使用 + +`@ViewChildren`と`@ContentChildren`はどちらも、結果のリストを含む`QueryList`オブジェクトを提供します。 + +`QueryList`は、`map`、`reduce`、`forEach`などの配列のような方法で結果を操作するための便利なAPIをいくつか提供します。`toArray`を呼び出すことで、現在の結果の配列を取得できます。 + +`changes`プロパティを購読して、結果が変更されるたびに何かを行うことができます。 + +## クエリの一般的な落とし穴 + +クエリを使用する際に、コードの理解と保守を難しくする一般的な落とし穴があります。 + +複数のコンポーネント間で共有される状態には、常に単一の真実の源を維持します。これにより、異なるコンポーネントで状態が繰り返し使用され、同期が乱れるシナリオを防ぐことができます。 + +子コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい、もろいコードにつながる可能性があります。 + +親または祖先コンポーネントに直接状態を書き込まないでください。このパターンは、理解が難しく、[ExpressionChangedAfterItHasBeenChecked](errors/NG0100)エラーが発生しやすい、もろいコードにつながる可能性があります。 diff --git a/adev-ja/src/content/guide/components/selectors.md b/adev-ja/src/content/guide/components/selectors.md new file mode 100644 index 0000000000..16978d1216 --- /dev/null +++ b/adev-ja/src/content/guide/components/selectors.md @@ -0,0 +1,147 @@ +# コンポーネントセレクター + +ヒント: このガイドは、[基本概念のガイド](essentials) を既にお読みになっていることを前提としています。Angularを初めて使用する場合は、まずこちらをお読みください。 + +各コンポーネントは、 +コンポーネントの使用方法を決定する +[CSS セレクター](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors) を定義します。 + + +@Component({ + selector: 'profile-photo', + ... +}) +export class ProfilePhoto { } + + +コンポーネントを使用するには、*他の*コンポーネントのテンプレートに一致するHTML要素を作成します。 + + +@Component({ + template: ` + + `, + ..., +}) +export class UserProfile { } + + +**Angular はコンパイル時にセレクターを静的にマッチングします。** +AngularバインディングやDOM APIを介して実行時にDOMを変更しても、レンダリングされるコンポーネントには影響しません。 + +**1 つの要素は、1 つのコンポーネント セレクターにのみマッチングできます。** +複数のコンポーネント セレクターが1つの要素にマッチングする場合、Angularはエラーを報告します。 + +**コンポーネント セレクターは大文字と小文字を区別します。** + +## セレクターの種類 + +Angularは、コンポーネントセレクターで +[基本的なCSSセレクターの種類](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors) +の一部をサポートしています。 + +| **セレクターの種類** | **説明** | **例** | +| ------------------ | --------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| タイプセレクター | HTML タグ名またはノード名に基づいて要素をマッチングします。 | `profile-photo` | +| 属性セレクター | HTML 属性の存在に基づいて要素をマッチングし、オプションでその属性の正確な値を指定します。 | `[dropzone]` `[type="reset"]` | +| クラスセレクター | CSS クラスの存在に基づいて要素をマッチングします。 | `.menu-item` | + +属性値の場合、Angularは等号 (`=`) 演算子を使用して、正確な属性値をマッチングすることをサポートしています。 +Angularは他の属性値の演算子をサポートしていません。 + +Angularコンポーネントセレクターは、 +[子孫結合子](https://developer.mozilla.org/docs/Web/CSS/Descendant_combinator) や +[子結合子](https://developer.mozilla.org/docs/Web/CSS/Child_combinator) を含む結合子をサポートしていません。 + +Angularコンポーネントセレクターは、 +[名前空間](https://developer.mozilla.org/docs/Web/SVG/Namespaces_Crash_Course) を指定することをサポートしていません。 + +### `:not` 擬似クラス + +Angularは [`:not` 擬似クラス](https://developer.mozilla.org/docs/Web/CSS/:not) をサポートしています。 +他のセレクターにこの擬似クラスを追加することで、コンポーネントのセレクターがマッチングする要素を絞り込むことができます。 +たとえば、`[dropzone]` 属性セレクターを定義して、 +`textarea` 要素のマッチングを防ぐことができます。 + + +@Component({ + selector: '[dropzone]:not(textarea)', + ... +}) +export class DropZone { } + + +Angularは、コンポーネント セレクターで他の擬似クラスまたは擬似要素をサポートしていません。 + +### セレクターの組み合わせ + +複数のセレクターを連結することで、組み合わせられます。 +たとえば、`type="reset"` を指定した `