diff --git a/.bazelignore b/.bazelignore index 94034ac8db9d..3f8f9df52ffd 100644 --- a/.bazelignore +++ b/.bazelignore @@ -8,32 +8,32 @@ dist # All integration test node_modules folders integration/animations/node_modules +integration/animations-async/node_modules integration/cli-elements-universal/node_modules integration/cli-hello-world/node_modules integration/cli-hello-world-ivy-i18n/node_modules integration/cli-hello-world-lazy/node_modules integration/cli-hello-world-mocha/node_modules integration/cli-signal-inputs/node_modules -integration/platform-server-hydration/node_modules integration/defer/node_modules integration/dynamic-compiler/node_modules integration/forms/node_modules integration/injectable-def/node_modules integration/ivy-i18n/node_modules -integration/ng-add-localize/node_modules integration/ng_elements/node_modules integration/ng_update/node_modules integration/ng_update_migrations/node_modules +integration/ng-add-localize/node_modules integration/nodenext_resolution/node_modules -integration/npm_package_archives.bzl/node_modules integration/platform-server/node_modules +integration/platform-server-hydration/node_modules integration/service-worker-schema/node_modules integration/side-effects/node_modules integration/standalone-bootstrap/node_modules integration/terser/node_modules integration/trusted-types/node_modules integration/typings_test_rxjs7/node_modules -integration/typings_test_ts49/node_modules -integration/typings_test_ts50/node_modules -integration/typings_test_ts51/node_modules -integration/typings_test_ts52/node_modules +integration/typings_test_ts55/node_modules +integration/typings_test_ts56/node_modules +integration/typings_test_ts57/node_modules +modules/ssr-benchmarks/node_modules diff --git a/.bazelrc b/.bazelrc index aca58640cc79..5049882e2dc5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -78,12 +78,6 @@ query --output=label_kind # By default, failing tests don't print any output, it goes to the log file test --test_output=errors -################################ -# Settings for CircleCI # -################################ - -# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc - ################################## # Remote Build Execution support # # Turn on these settings with # diff --git a/.circleci/README.md b/.circleci/README.md deleted file mode 100644 index cddaf76fe8c6..000000000000 --- a/.circleci/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Encryption - -Based on https://github.com/circleci/encrypted-files - -In the CircleCI web UI, we have a secret variable called `KEY` -https://circleci.com/gh/angular/angular/edit#env-vars -which is only exposed to non-fork builds -(see "Pass secrets to builds from forked pull requests" under -https://circleci.com/gh/angular/angular/edit#advanced-settings) - -We use this as a symmetric AES encryption key to encrypt tokens like -a GitHub token that enables publishing snapshots. - -To create the github_token file, we take this approach: -- Find the angular-builds:token in the internal pw database -- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12` -- echo "https://[token]:@github.com" > credentials -- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY -- If needed, base64-encode the result so you can copy-paste it out of docker: `base64 github_token` diff --git a/.circleci/bazel.common.rc b/.circleci/bazel.common.rc deleted file mode 100644 index 2b4c9ec625d1..000000000000 --- a/.circleci/bazel.common.rc +++ /dev/null @@ -1,16 +0,0 @@ -# Settings in this file should be OS agnostic. Use the bazel..rc files for OS specific settings. - -# 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 - -# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309 -# By default this is set in the project `.bazelrc` to `1`. -test --flaky_test_attempts=3 - -# More details on failures -build --verbose_failures=true - -# CI supports colors but Bazel does not detect it. -common --color=yes diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc deleted file mode 100644 index fa1b482ed881..000000000000 --- a/.circleci/bazel.linux.rc +++ /dev/null @@ -1,23 +0,0 @@ -# These options are enabled when running on CI -# We do this by copying this file to /etc/bazel.bazelrc at the start of the build. -# See documentation in /docs/BAZEL.md - -# Import config items common to both Linux and Windows setups. -# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics -try-import %workspace%/.circleci/bazel.common.rc - -# Save downloaded repositories in a location that can be cached by CircleCI. This helps us -# speeding up the analysis time significantly with Bazel managed node dependencies on the CI. -build --repository_cache=/home/circleci/bazel_repository_cache - -# Workaround https://github.com/bazelbuild/bazel/issues/3645 -# Bazel doesn't calculate the memory ceiling correctly when running under Docker. -# Limit Bazel to consuming resources that fit in CircleCI "2xlarge+" class -# https://circleci.com/docs/2.0/configuration-reference/#resource_class -build --local_cpu_resources=20 -build --local_ram_resources=40960 - -# All build executed remotely should be done using our RBE configuration. -build:remote --google_default_credentials - -build --config=remote diff --git a/.circleci/bazel.windows.rc b/.circleci/bazel.windows.rc deleted file mode 100644 index 309b1cba1321..000000000000 --- a/.circleci/bazel.windows.rc +++ /dev/null @@ -1,17 +0,0 @@ -# These options are enabled when running on CI -# We do this by copying this file to $env:ProgramData\bazel.bazelrc at the start of the build. -# See documentation in /docs/BAZEL.md - -# Import config items common to both Linux and Windows setups. -# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics -try-import %workspace%/.circleci/bazel.common.rc - -# Manually set the local resources used in windows CI runs -build --local_ram_resources=120000 -build --local_cpu_resources=32 - -# All windows jobs run on master and should use http caching -build --remote_http_cache=https://storage.googleapis.com/angular-team-cache -build --remote_accept_cached=true -build --remote_upload_local_results=true -build --google_default_credentials diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9454be12fd03..000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,16 +0,0 @@ -# This config is remaining in place to prevent pull requests failing because of CircleCI config missing. - -version: 2.1 - -jobs: - pass: - docker: - - image: cimg/base:2022.05 - steps: - - run: echo "This too shall pass (always)" - -workflows: - version: 2 - default_workflow: - jobs: - - pass diff --git a/.circleci/env-helpers.inc.sh b/.circleci/env-helpers.inc.sh deleted file mode 100644 index ff818d24b80a..000000000000 --- a/.circleci/env-helpers.inc.sh +++ /dev/null @@ -1,76 +0,0 @@ -#################################################################################################### -# Helpers for defining environment variables for CircleCI. -# -# In CircleCI, each step runs in a new shell. The way to share ENV variables across steps is to -# export them from `$BASH_ENV`, which is automatically sourced at the beginning of every step (for -# the default `bash` shell). -# -# See also https://circleci.com/docs/2.0/env-vars/#using-bash_env-to-set-environment-variables. -#################################################################################################### - -# Set and print an environment variable. -# -# Use this function for setting environment variables that are public, i.e. it is OK for them to be -# visible to anyone through the CI logs. -# -# Usage: `setPublicVar ` -function setPublicVar() { - setSecretVar $1 "$2"; - echo "$1=$2"; -} - -# Set (without printing) an environment variable. -# -# Use this function for setting environment variables that are secret, i.e. should not be visible to -# everyone through the CI logs. -# -# Usage: `setSecretVar ` -function setSecretVar() { - # WARNING: Secrets (e.g. passwords, access tokens) should NOT be printed. - # (Keep original shell options to restore at the end.) - local -r originalShellOptions=$(set +o); - set +x -eu -o pipefail; - - local assignmentStatement="export $1=\"${2:-}\";" - - echo "${assignmentStatement}" >> $BASH_ENV; - eval "${assignmentStatement}" - - # Restore original shell options. - eval "$originalShellOptions"; -} - - -# Create a function to set an environment variable, when called. -# -# Use this function for creating setter for public environment variables that require expensive or -# time-consuming computations and may not be needed. When needed, you can call this function to set -# the environment variable (which will be available through `$BASH_ENV` from that point onwards). -# -# Arguments: -# - ``: The name of the environment variable. The generated setter function will be -# `setPublicVar_`. -# - ``: The code to run to compute the value for the variable. Since this code should be -# executed lazily, it must be properly escaped. For example: -# ```sh -# # DO NOT do this: -# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly -# -# # DO this isntead: -# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly -# ``` -# -# Usage: `createPublicVarSetter ` -# -# Example: -# ```sh -# createPublicVarSetter MY_VAR 'echo "FOO"'; -# echo $MY_VAR; # Not defined -# -# setPublicVar_MY_VAR; -# source $BASH_ENV; -# echo $MY_VAR; # FOO -# ``` -function createPublicVarSetter() { - echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV; -} diff --git a/.circleci/env.linux.sh b/.circleci/env.linux.sh deleted file mode 100755 index 6fab1c71143c..000000000000 --- a/.circleci/env.linux.sh +++ /dev/null @@ -1,4 +0,0 @@ -#################################################################################################### -# Set bazel configuration for CircleCI runs. -#################################################################################################### -cp "${PROJECT_ROOT}/.circleci/bazel.linux.rc" ".bazelrc.user"; diff --git a/.circleci/env.sh b/.circleci/env.sh deleted file mode 100755 index 9522f3c56a73..000000000000 --- a/.circleci/env.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -# Variables -readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") -readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; - -# Load helpers and make them available everywhere (through `$BASH_ENV`). -source $envHelpersPath; -echo "source $envHelpersPath;" >> $BASH_ENV; - - -#################################################################################################### -# Define PUBLIC environment variables for CircleCI. -#################################################################################################### -# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. -#################################################################################################### -setPublicVar CI "$CI" -setPublicVar PROJECT_ROOT "$projectDir"; -# This is the branch being built; e.g. `pull/12345` for PR builds. -setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; -setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; -setPublicVar CI_COMMIT "$CIRCLE_SHA1"; -# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun -# workflows of such builds). -setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; -setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; -setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION"; -setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; -setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; -setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; -setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; -setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME"; - - -#################################################################################################### -# Define "lazy" PUBLIC environment variables for CircleCI. -# (I.e. functions to set an environment variable when called.) -#################################################################################################### -createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')"; - - -#################################################################################################### -# Define SECRET environment variables for CircleCI. -#################################################################################################### -setSecretVar CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN "$AIO_DEPLOY_TOKEN"; -setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN"; - - -#################################################################################################### -# Define SauceLabs environment variables for CircleCI. -#################################################################################################### -setPublicVar SAUCE_USERNAME "angular-framework"; -setSecretVar SAUCE_ACCESS_KEY "f4bf7c639c5a-c6bb-d6a4-a4b5-800aa111"; -# TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method. -setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log -setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock -setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock -setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" -# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not -# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. -setPublicVar SAUCE_READY_FILE_TIMEOUT 120 - -#################################################################################################### -# Create shell script in /tmp for Bazel actions to access CI envs without -# busting the cache. Used by payload-size.sh script in integration tests. -#################################################################################################### -readonly bazelVarEnv="/tmp/bazel-ci-env.sh" -echo "# Setup by /.circle/env.sh" > $bazelVarEnv -echo "export PROJECT_ROOT=\"${PROJECT_ROOT}\";" >> $bazelVarEnv -echo "export CI_BRANCH=\"${CI_BRANCH}\";" >> $bazelVarEnv -echo "export CI_BUILD_URL=\"${CI_BUILD_URL}\";" >> $bazelVarEnv -echo "export CI_COMMIT=\"${CI_COMMIT}\";" >> $bazelVarEnv -echo "export CI_PULL_REQUEST=\"${CI_PULL_REQUEST}\";" >> $bazelVarEnv -echo "export CI_REPO_NAME=\"${CI_REPO_NAME}\";" >> $bazelVarEnv -echo "export CI_REPO_OWNER=\"${CI_REPO_OWNER}\";" >> $bazelVarEnv -echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOKEN}\";" >> $bazelVarEnv - -#################################################################################################### -# Platform-specific environment setup (which can leverage the base variables from here) -#################################################################################################### - -# Conditionally, load additional environment settings based on the current VM -# operating system running. We detect Windows by checking for `%AppData%`. -if [[ -n "${APPDATA}" ]]; then - source ${projectDir}/.circleci/env.windows.sh -else - source ${projectDir}/.circleci/env.linux.sh -fi diff --git a/.circleci/env.windows.sh b/.circleci/env.windows.sh deleted file mode 100755 index 19a6fbe1c65f..000000000000 --- a/.circleci/env.windows.sh +++ /dev/null @@ -1,13 +0,0 @@ -#################################################################################################### -# Set bazel configuration for CircleCI runs. -#################################################################################################### -cp "${PROJECT_ROOT}/.circleci/bazel.windows.rc" ".bazelrc.user"; - -# Override the `PATH` environment variable so that the windows-nvm NodeJS version -# always has precedence over potential existing NodeJS versions from the image. -setPublicVar PATH "/c/Program Files/nodejs/:$PATH" - -# Expose the Bazelisk version. We need to run Bazelisk globally since Windows has problems launching -# Bazel from a node modules directoy that might be modified by the Bazel Yarn install then. -setPublicVar BAZELISK_VERSION \ - "$(cd ${PROJECT_ROOT}; node -p 'require("./package.json").devDependencies["@bazel/bazelisk"]')" diff --git a/.circleci/setup_cache.sh b/.circleci/setup_cache.sh deleted file mode 100755 index 232596df4a98..000000000000 --- a/.circleci/setup_cache.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# Install bazel remote cache proxy -# This is temporary until the feature is no longer experimental on CircleCI. -# See remote cache documentation in /docs/BAZEL.md - -set -u -e - -readonly DOWNLOAD_URL="https://5-116431813-gh.circle-artifacts.com/0/pkg/bazel-remote-proxy-$(uname -s)_$(uname -m)" - -curl --fail -o ~/bazel-remote-proxy "$DOWNLOAD_URL" -chmod +x ~/bazel-remote-proxy diff --git a/.devcontainer/recommended-Dockerfile b/.devcontainer/recommended-Dockerfile index b12a0a70ed11..d128851219bc 100644 --- a/.devcontainer/recommended-Dockerfile +++ b/.devcontainer/recommended-Dockerfile @@ -1,6 +1,5 @@ # Image metadata and config. # Ideally, the Node.js version should match what we use on CI. -# See `executors > default-executor` in `.circleci/config.yml`. FROM cimg/node:18.13.0@sha256:45d0ee279f522c4562a464df71108104cc9b2187205333ffd0707d4f05589731 @@ -12,7 +11,7 @@ LABEL name="Angular dev environment" \ EXPOSE 4000 4200 4433 5000 8080 9876 -# Switch to `root` (CircleCI images use `circleci` as the user). +# Switch to `root`. USER root diff --git a/.github/actions/deploy-docs-site/main.js b/.github/actions/deploy-docs-site/main.js index 1331973969c6..d7ef66750092 100644 --- a/.github/actions/deploy-docs-site/main.js +++ b/.github/actions/deploy-docs-site/main.js @@ -320,7 +320,7 @@ var require_tunnel = __commonJS({ var http = __require("http"); var https = __require("https"); var events = __require("events"); - var assert = __require("assert"); + var assert2 = __require("assert"); var util = __require("util"); exports.httpOverHttp = httpOverHttp; exports.httpsOverHttp = httpsOverHttp; @@ -10151,7 +10151,8 @@ var supportsColor2 = { var supports_color_default2 = supportsColor2; // -import { spawn as _spawn, spawnSync as _spawnSync } from "child_process"; +import { spawn as _spawn, spawnSync as _spawnSync, exec as _exec } from "child_process"; +import assert from "assert"; var ChildProcess = class { static spawnInteractive(command, args, options = {}) { return new Promise((resolve, reject) => { @@ -10161,52 +10162,9 @@ var ChildProcess = class { childProcess.on("close", (status) => status === 0 ? resolve() : reject(status)); }); } - static spawn(command, args, options = {}) { - return new Promise((resolve, reject) => { - const commandText = `${command} ${args.join(" ")}`; - const outputMode = options.mode; - const env3 = getEnvironmentForNonInteractiveSpawn(options.env); - Log.debug(`Executing command: ${commandText}`); - const childProcess = _spawn(command, args, { ...options, env: env3, shell: true, stdio: "pipe" }); - let logOutput = ""; - let stdout = ""; - let stderr = ""; - if (options.input !== void 0) { - childProcess.stdin.write(options.input); - childProcess.stdin.end(); - } - childProcess.stderr.on("data", (message) => { - stderr += message; - logOutput += message; - if (outputMode === void 0 || outputMode === "enabled") { - process.stderr.write(message); - } - }); - childProcess.stdout.on("data", (message) => { - stdout += message; - logOutput += message; - if (outputMode === void 0 || outputMode === "enabled") { - process.stderr.write(message); - } - }); - childProcess.on("close", (exitCode, signal) => { - const exitDescription = exitCode !== null ? `exit code "${exitCode}"` : `signal "${signal}"`; - const printFn = outputMode === "on-error" ? Log.error : Log.debug; - const status = statusFromExitCodeAndSignal(exitCode, signal); - printFn(`Command "${commandText}" completed with ${exitDescription}.`); - printFn(`Process output: -${logOutput}`); - if (status === 0 || options.suppressErrorOnFailingExitCode) { - resolve({ stdout, stderr, status }); - } else { - reject(outputMode === "silent" ? logOutput : void 0); - } - }); - }); - } static spawnSync(command, args, options = {}) { const commandText = `${command} ${args.join(" ")}`; - const env3 = getEnvironmentForNonInteractiveSpawn(options.env); + const env3 = getEnvironmentForNonInteractiveCommand(options.env); Log.debug(`Executing command: ${commandText}`); const { status: exitCode, signal, stdout, stderr } = _spawnSync(command, args, { ...options, env: env3, encoding: "utf8", shell: true, stdio: "pipe" }); const status = statusFromExitCodeAndSignal(exitCode, signal); @@ -10215,14 +10173,64 @@ ${logOutput}`); } throw new Error(stderr); } + static spawn(command, args, options = {}) { + const commandText = `${command} ${args.join(" ")}`; + const env3 = getEnvironmentForNonInteractiveCommand(options.env); + return processAsyncCmd(commandText, options, _spawn(command, args, { ...options, env: env3, shell: true, stdio: "pipe" })); + } + static exec(command, options = {}) { + const env3 = getEnvironmentForNonInteractiveCommand(options.env); + return processAsyncCmd(command, options, _exec(command, { ...options, env: env3 })); + } }; function statusFromExitCodeAndSignal(exitCode, signal) { return exitCode ?? signal ?? -1; } -function getEnvironmentForNonInteractiveSpawn(userProvidedEnv) { +function getEnvironmentForNonInteractiveCommand(userProvidedEnv) { const forceColorValue = supports_color_default2.stdout !== false ? supports_color_default2.stdout.level.toString() : void 0; return { FORCE_COLOR: forceColorValue, ...userProvidedEnv ?? process.env }; } +function processAsyncCmd(command, options, childProcess) { + return new Promise((resolve, reject) => { + var _a, _b; + let logOutput = ""; + let stdout = ""; + let stderr = ""; + Log.debug(`Executing command: ${command}`); + if (options.input !== void 0) { + assert(childProcess.stdin, "Cannot write process `input` if there is no pipe `stdin` channel."); + childProcess.stdin.write(options.input); + childProcess.stdin.end(); + } + (_a = childProcess.stderr) == null ? void 0 : _a.on("data", (message) => { + stderr += message; + logOutput += message; + if (options.mode === void 0 || options.mode === "enabled") { + process.stderr.write(message); + } + }); + (_b = childProcess.stdout) == null ? void 0 : _b.on("data", (message) => { + stdout += message; + logOutput += message; + if (options.mode === void 0 || options.mode === "enabled") { + process.stderr.write(message); + } + }); + childProcess.on("close", (exitCode, signal) => { + const exitDescription = exitCode !== null ? `exit code "${exitCode}"` : `signal "${signal}"`; + const printFn = options.mode === "on-error" ? Log.error : Log.debug; + const status = statusFromExitCodeAndSignal(exitCode, signal); + printFn(`Command "${command}" completed with ${exitDescription}.`); + printFn(`Process output: +${logOutput}`); + if (status === 0 || options.suppressErrorOnFailingExitCode) { + resolve({ stdout, stderr, status }); + } else { + reject(options.mode === "silent" ? logOutput : void 0); + } + }); + }); +} // function determineRepoBaseDirFromCwd() { diff --git a/.github/actions/saucelabs-legacy/action.yml b/.github/actions/saucelabs-legacy/action.yml index 99b000a421f9..27557b38bf2a 100644 --- a/.github/actions/saucelabs-legacy/action.yml +++ b/.github/actions/saucelabs-legacy/action.yml @@ -5,9 +5,9 @@ runs: using: 'composite' steps: - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Saucelabs Variables - uses: angular/dev-infra/github-actions/saucelabs@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/saucelabs@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Starting Saucelabs tunnel service shell: bash run: ./tools/saucelabs/sauce-service.sh run & diff --git a/.github/actions/yarn-install/action.yml b/.github/actions/yarn-install/action.yml index 65e5fb8c5dcb..3dd8d7ed6595 100644 --- a/.github/actions/yarn-install/action.yml +++ b/.github/actions/yarn-install/action.yml @@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn' runs: using: 'composite' steps: - - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + - uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 with: path: | ./node_modules/ diff --git a/.github/workflows/adev-preview-build.yml b/.github/workflows/adev-preview-build.yml index bd7559610dec..4ed5325b68cb 100644 --- a/.github/workflows/adev-preview-build.yml +++ b/.github/workflows/adev-preview-build.yml @@ -21,16 +21,16 @@ jobs: (github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview')) steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Build adev to ensure it continues to work run: yarn bazel build //adev:build --full_build_adev --config=release - - uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@289aa644e65a557bcb21adcf75ad60605a9c9859 with: workflow-artifact-name: 'adev-preview' pull-number: '${{github.event.pull_request.number}}' diff --git a/.github/workflows/adev-preview-deploy.yml b/.github/workflows/adev-preview-deploy.yml index b7d5946ffc6f..901b371aee1a 100644 --- a/.github/workflows/adev-preview-deploy.yml +++ b/.github/workflows/adev-preview-deploy.yml @@ -40,7 +40,7 @@ jobs: npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}} - - uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@289aa644e65a557bcb21adcf75ad60605a9c9859 with: github-token: '${{secrets.GITHUB_TOKEN}}' workflow-artifact-name: 'adev-preview' diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml index fccb0fa92e03..faad5f28e26a 100644 --- a/.github/workflows/assistant-to-the-branch-manager.yml +++ b/.github/workflows/assistant-to-the-branch-manager.yml @@ -16,6 +16,6 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - uses: angular/dev-infra/github-actions/branch-manager@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/branch-manager@289aa644e65a557bcb21adcf75ad60605a9c9859 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/benchmark-compare.yml b/.github/workflows/benchmark-compare.yml index bc32c4a82596..3b8f5b84b83c 100644 --- a/.github/workflows/benchmark-compare.yml +++ b/.github/workflows/benchmark-compare.yml @@ -38,7 +38,7 @@ jobs: - uses: ./.github/actions/yarn-install - - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 with: bazelrc: ./.bazelrc.user diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d024b9490dd..6380f97b9513 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Install node modules @@ -41,13 +41,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Run unit tests @@ -59,13 +59,13 @@ jobs: runs-on: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile --network-timeout 100000 - name: Run CI tests for framework @@ -76,32 +76,30 @@ jobs: labels: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Build adev in fast mode to ensure it continues to work run: yarn bazel build //adev:build --config=release - # TODO: re-enable tests once the next release is shipped - # Tests are broken because of https://github.com/angular/angular/issues/54858 - # - name: Run tests - # run: yarn bazel test //adev/... + - name: Run tests + run: yarn bazel test //adev/... publish-snapshots: runs-on: labels: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials @@ -113,7 +111,7 @@ jobs: labels: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true node-module-directories: | @@ -121,9 +119,9 @@ jobs: ./packages/zone.js/node_modules ./packages/zone.js/test/typings/node_modules - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - run: | @@ -160,7 +158,7 @@ jobs: SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Install node modules @@ -173,11 +171,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Build adev to ensure it continues to work diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index 0651d747e06b..dec5276091ec 100644 --- a/.github/workflows/dev-infra.yml +++ b/.github/workflows/dev-infra.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: angular/dev-infra/github-actions/commit-message-based-labels@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/commit-message-based-labels@289aa644e65a557bcb21adcf75ad60605a9c9859 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} post_approval_changes: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: angular/dev-infra/github-actions/post-approval-changes@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/post-approval-changes@289aa644e65a557bcb21adcf75ad60605a9c9859 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/google-internal-tests.yml b/.github/workflows/google-internal-tests.yml index 42ca3cd0ebba..c06d486970ec 100644 --- a/.github/workflows/google-internal-tests.yml +++ b/.github/workflows/google-internal-tests.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: angular/dev-infra/github-actions/google-internal-tests@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/google-internal-tests@289aa644e65a557bcb21adcf75ad60605a9c9859 with: run-tests-guide-url: http://go/angular-g3sync-start github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/manual.yml b/.github/workflows/manual.yml index fbfac928555b..062bd915a3f2 100644 --- a/.github/workflows/manual.yml +++ b/.github/workflows/manual.yml @@ -13,17 +13,17 @@ jobs: JOBS: 2 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Install node modules run: yarn install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Saucelabs Variables - uses: angular/dev-infra/github-actions/saucelabs@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/saucelabs@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Set up Sauce Tunnel Daemon run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS & env: diff --git a/.github/workflows/merge-ready-status.yml b/.github/workflows/merge-ready-status.yml index 6a8b3a7791a8..84e1f4b2c975 100644 --- a/.github/workflows/merge-ready-status.yml +++ b/.github/workflows/merge-ready-status.yml @@ -9,6 +9,6 @@ jobs: status: runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/unified-status-check@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + - uses: angular/dev-infra/github-actions/unified-status-check@289aa644e65a557bcb21adcf75ad60605a9c9859 with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml new file mode 100644 index 000000000000..5c462853df65 --- /dev/null +++ b/.github/workflows/perf.yml @@ -0,0 +1,53 @@ +name: Performance Tracking + +on: + push: + branches: + - main + +permissions: + contents: 'read' + id-token: 'write' + +defaults: + run: + shell: bash + +jobs: + list: + timeout-minutes: 3 + runs-on: ubuntu-latest + outputs: + workflows: ${{ steps.workflows.outputs.workflows }} + steps: + - name: Initialize environment + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 + - name: Install node modules + run: yarn -s install --frozen-lockfile + - id: workflows + run: echo "workflows=$(yarn -s ng-dev perf workflows --list)" >> "$GITHUB_OUTPUT" + + workflow: + timeout-minutes: 30 + runs-on: ubuntu-latest + needs: list + strategy: + matrix: + workflow: ${{ fromJSON(needs.list.outputs.workflows) }} + steps: + - name: Initialize environment + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 + - name: Setup Bazel + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 + - name: Install node modules + run: yarn -s install --frozen-lockfile + # We utilize the google-github-actions/auth action to allow us to get an active credential using workflow + # identity federation. This allows us to request short lived credentials on demand, rather than storing + # credentials in secrets long term. More information can be found at: + # https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform + - uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' # v2 + with: + project_id: 'internal-200822' + workload_identity_provider: 'projects/823469418460/locations/global/workloadIdentityPools/measurables-tracking/providers/angular' + service_account: 'measures-uploader@internal-200822.iam.gserviceaccount.com' + - run: yarn ng-dev perf workflows --name ${{ matrix.workflow }} --commit-sha ${{github.sha}} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 1b432a6413ad..51312a3dd6e1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Install node modules @@ -39,7 +39,7 @@ jobs: - name: Check code format run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }} - name: Check Package Licenses - uses: angular/dev-infra/github-actions/linting/licenses@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/linting/licenses@289aa644e65a557bcb21adcf75ad60605a9c9859 with: allow-dependencies-licenses: 'pkg:npm/google-protobuf@' @@ -47,13 +47,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Run unit tests @@ -65,13 +65,13 @@ jobs: runs-on: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile --network-timeout 100000 - name: Run CI tests for framework @@ -83,13 +83,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel Remote Caching - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile --network-timeout 100000 - name: Run CI tests for framework @@ -105,26 +105,24 @@ jobs: labels: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - name: Build adev in fast mode to ensure it continues to work run: yarn bazel build //adev:build --config=release - # TODO: re-enable tests once the next release is shipped - # Tests are broken because of https://github.com/angular/angular/issues/54858 - # - name: Run tests - # run: yarn bazel test //adev/... + - name: Run tests + run: yarn bazel test //adev/... zone-js: runs-on: labels: ubuntu-latest-4core steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true node-module-directories: | @@ -132,9 +130,9 @@ jobs: ./packages/zone.js/node_modules ./packages/zone.js/test/typings/node_modules - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/setup@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/bazel/configure-remote@289aa644e65a557bcb21adcf75ad60605a9c9859 - name: Install node modules run: yarn install --frozen-lockfile - run: | @@ -171,7 +169,7 @@ jobs: SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@289aa644e65a557bcb21adcf75ad60605a9c9859 with: cache-node-modules: true - name: Install node modules diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 50749762093f..d9ba79011e4a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9 with: sarif_file: results.sarif diff --git a/.github/workflows/update-cli-help.yml b/.github/workflows/update-cli-help.yml index 5ecca9271e73..ab56712d9854 100644 --- a/.github/workflows/update-cli-help.yml +++ b/.github/workflows/update-cli-help.yml @@ -32,7 +32,7 @@ jobs: env: ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }} - name: Create a PR (if necessary) - uses: angular/dev-infra/github-actions/create-pr-for-changes@2137a36261ceb2e74dc5ddafdb171ac9fc62a6ea + uses: angular/dev-infra/github-actions/create-pr-for-changes@289aa644e65a557bcb21adcf75ad60605a9c9859 with: branch-prefix: update-cli-help pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]' diff --git a/.gitignore b/.gitignore index 487a4952c502..8c124604a98f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,8 @@ /bazel-out /integration/bazel/bazel-* *.log -/node_modules/ - -# CircleCI temporary file for cache key computation. -# See `save_month_to_file` in `.circleci/config.yml`. -month.txt +node_modules/ +modules/ssr-benchmarks/node_modules/ # Include when developing application packages. pubspec.lock @@ -57,10 +54,5 @@ baseline.json # Husky .husky/_ - # Ignore cache created with the Angular CLI. .angular/ - -# Ignore AIO files, useful when changing branches -aio/node_modules -aio/out-tsc \ No newline at end of file diff --git a/.ng-dev/caretaker.mts b/.ng-dev/caretaker.mts index a8f884d4a2d0..f4404dd93563 100644 --- a/.ng-dev/caretaker.mts +++ b/.ng-dev/caretaker.mts @@ -6,7 +6,7 @@ export const caretaker: CaretakerConfig = { githubQueries: [ { name: 'Merge Queue', - query: `is:pr is:open status:success label:"action: merge"`, + query: `is:pr is:open label:"action: merge"`, }, { name: 'Merge Assistance Queue', diff --git a/.ng-dev/dx-perf-workflows.yml b/.ng-dev/dx-perf-workflows.yml new file mode 100644 index 000000000000..8da43f1f68c2 --- /dev/null +++ b/.ng-dev/dx-perf-workflows.yml @@ -0,0 +1,7 @@ +workflows: + build-adev: + name: Build adev + prepare: + - bazel clean + workflow: + - bazel build //adev:build diff --git a/.ng-dev/format.mts b/.ng-dev/format.mts index 89453c4ca845..62d2028c1f3f 100644 --- a/.ng-dev/format.mts +++ b/.ng-dev/format.mts @@ -8,6 +8,7 @@ export const format: FormatConfig = { 'matchers': [ '**/*.{yaml,yml}', '**/*.{js,ts}', + 'devtools/**/*.{js,ts,html,scss}', // Do not format d.ts files as they are generated '!**/*.d.ts', diff --git a/.ng-dev/google-sync-config.json b/.ng-dev/google-sync-config.json index 8363f89def0a..89d77442a048 100644 --- a/.ng-dev/google-sync-config.json +++ b/.ng-dev/google-sync-config.json @@ -3,15 +3,7 @@ "separateFilePatterns": ["packages/core/primitives/**"], "alwaysExternalFilePatterns": [ "packages/*", - "packages/bazel/*", - "packages/bazel/src/*", - "packages/bazel/src/api-extractor/**", - "packages/bazel/src/builders/**", - "packages/bazel/src/ng_module/**", - "packages/bazel/src/ng_package/**", - "packages/bazel/src/protractor/**", - "packages/bazel/src/schematics/**", - "packages/bazel/src/types_bundle/**", + "packages/bazel/**", "packages/compiler-cli/linker/**", "packages/compiler-cli/src/ngtsc/sourcemaps/**", "packages/compiler-cli/src/ngtsc/testing/**", diff --git a/.pullapprove.yml b/.pullapprove.yml index fb1bd8897590..22217c82f955 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -57,7 +57,7 @@ version: 3 availability: - users_unavailable: ['atscott'] + users_unavailable: ['atscott', 'devversion'] # Meta field that goes unused by PullApprove to allow for defining aliases to be # used throughout the config. @@ -111,24 +111,6 @@ overrides: explanation: 'Passing as globally approved by global approvers' groups: - # ========================================================= - # Framework: Animations - # ========================================================= - fw-animations: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'packages/animations/**/{*,.*}', - 'packages/platform-browser/animations/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - crisbeto - - ~jelbourn - - thePunderWoman - # ========================================================= # Framework: Compiler # ========================================================= @@ -140,7 +122,8 @@ groups: 'packages/compiler/**/{*,.*}', 'packages/examples/compiler/**/{*,.*}', 'packages/compiler-cli/**/{*,.*}', - ]) + 'packages/language-service/**/{*,.*}', + ]) reviewers: users: - alxhub @@ -148,273 +131,53 @@ groups: - atscott - crisbeto - devversion + - kirjs - JoostK # ========================================================= - # Framework: Migrations + # Framework: General (most code in our packages) # ========================================================= - fw-migrations: - <<: *defaults - conditions: - - files.include("packages/core/schematics/*") - reviewers: - users: - - alxhub - - crisbeto - - devversion - - thePunderWoman - - AndrewKushnir - - # ========================================================= - # Framework: Core - # ========================================================= - fw-core: + fw-general: <<: *defaults conditions: - > - contains_any_globs(files.exclude("packages/core/schematics/*").exclude('packages/core/primitives/*'), [ + contains_any_globs(files.exclude('packages/core/primitives/*'), [ + 'contributing-docs/public-api-surface.md', + 'goldens/circular-deps/packages.json', + 'integration/**/{*,.*}', + 'modules/**/{*,.*}', + 'packages/animations/**/{*,.*}', + 'packages/benchpress/**/{*,.*}', + 'packages/common/**/{*,.*}', 'packages/core/**/{*,.*}', - 'packages/examples/core/**/{*,.*}', - 'packages/platform-browser/**/{*,.*}', - 'packages/examples/platform-browser/**/{*,.*}', - 'packages/platform-browser-dynamic/**/{*,.*}', 'packages/docs/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - crisbeto - - devversion - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Common - # ========================================================= - fw-common: - <<: *defaults - conditions: - - > - contains_any_globs(files.exclude("packages/common/http/*"), [ - 'packages/common/**/{*,.*}', - 'packages/examples/common/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Http - # ========================================================= - fw-http: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'packages/common/http/**/{*,.*}', - 'packages/examples/http/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Elements - # ========================================================= - fw-elements: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ 'packages/elements/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - andrewseguin - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Forms - # ========================================================= - fw-forms: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ + 'packages/examples/**/{*,.*}', 'packages/forms/**/{*,.*}', - 'packages/examples/forms/**/{*,.*}', - ]) - reviewers: - users: - - AndrewKushnir - - alxhub - - # ========================================================= - # Framework: i18n - # ========================================================= - fw-i18n: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'packages/core/src/i18n/**/{*,.*}', - 'packages/core/src/render3/i18n/**/{*,.*}', - 'packages/core/src/render3/instructions/i18n.ts', - 'packages/core/src/render3/interfaces/i18n.ts', - 'packages/common/locales/**/{*,.*}', - 'packages/common/src/i18n/**/{*,.*}', - 'packages/common/src/pipes/date_pipe.ts', - 'packages/common/src/pipes/i18n_plural_pipe.ts', - 'packages/common/src/pipes/i18n_select_pipe.ts', - 'packages/common/src/pipes/number_pipe.ts', - 'packages/compiler/src/i18n/**/{*,.*}', - 'packages/compiler/src/render3/view/i18n/**/{*,.*}', - 'packages/compiler-cli/src/extract_i18n.ts', 'packages/localize/**/{*,.*}', - ]) - reviewers: - users: - - AndrewKushnir - - dgp1130 - - # ========================================================= - # Framework: Platform Server - # ========================================================= - fw-platform-server: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ + 'packages/misc/**/{*,.*}', + 'packages/platform-browser/**/{*,.*}', + 'packages/platform-browser-dynamic/**/{*,.*}', 'packages/platform-server/**/{*,.*}', - ]) - reviewers: - users: - - alan-agius4 - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Router - # ========================================================= - fw-router: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ + 'packages/ssr/**/{*,.*}', 'packages/router/**/{*,.*}', - 'packages/examples/router/**/{*,.*}', - ]) - reviewers: - users: - - AndrewKushnir - - atscott - - # ========================================================= - # Framework: Service Worker - # ========================================================= - fw-service-worker: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ 'packages/service-worker/**/{*,.*}', - 'packages/examples/service-worker/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - # ========================================================= - # Framework: Upgrade - # ========================================================= - fw-upgrade: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ 'packages/upgrade/**/{*,.*}', - 'packages/common/upgrade/**/{*,.*}', - 'packages/examples/upgrade/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - thePunderWoman - - # ========================================================= - # Framework: Testing - # ========================================================= - fw-testing: - <<: *defaults - conditions: - - > - contains_any_globs(files.exclude('packages/compiler-cli/*').exclude('packages/language-service/*').exclude('packages/service-worker/*').exclude('packages/core/schematics/*'), [ - 'packages/**/testing/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Benchmarks - # ========================================================= - fw-benchmarks: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'modules/benchmarks/**/{*,.*}', - 'modules/ssr-benchmarks/**/{*,.*}' - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Framework: Playground - # ========================================================= - fw-playground: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'modules/playground/**/{*,.*}' - ]) + ]) reviewers: users: - alxhub - AndrewKushnir - atscott + - crisbeto + - devversion + - kirjs + - ~jelbourn - thePunderWoman - pkozlowski-opensource # ========================================================= - # Framework: Security + # Framework: Security-sensitive files which require extra review # ========================================================= fw-security: <<: *defaults @@ -428,6 +191,8 @@ groups: 'packages/tsconfig-tsec-base.json', 'packages/tsec-exemption.json', 'tools/tsec.bzl', + 'adev/src/content/guide/security.md', + 'adev/src/content/examples/security/**/{*,.*}', ]) reviewers: users: @@ -455,22 +220,6 @@ groups: - devversion - josephperrott - # ========================================================= - # Language Service - # ========================================================= - language-service: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'packages/language-service/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - atscott - - clydin - # ========================================================= # zone.js # ========================================================= @@ -485,76 +234,6 @@ groups: users: - JiaLiPassion - # ========================================================= - # in-memory-web-api - # ========================================================= - in-memory-web-api: - conditions: - - > - contains_any_globs(files, [ - 'packages/misc/angular-in-memory-web-api/**/{*,.*}', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - crisbeto - - thePunderWoman - - pkozlowski-opensource - - # ========================================================= - # Benchpress - # ========================================================= - benchpress: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'packages/benchpress/**/{*,.*}' - ]) - reviewers: - users: - - devversion - - josephperrott - - pkozlowski-opensource - - # ========================================================= - # Integration Tests - # ========================================================= - integration-tests: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'integration/**/{*,.*}' - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - josephperrott - - # ========================================================= - # Docs: Packaging, Tooling, Releasing - # ========================================================= - docs-packaging-and-releasing: - <<: *defaults - conditions: - - > - contains_any_globs(files, [ - 'contributing-docs/public-api-surface.md', - ]) - reviewers: - users: - - alxhub - - AndrewKushnir - - atscott - - thePunderWoman - - jelbourn - # ========================================================= # Tooling: Compiler API shared with Angular CLI # @@ -579,30 +258,33 @@ groups: reviewed_for: required # ========================================================= - # Docs: Angular Dev Site + # Documentation content # ========================================================= angular-dev: <<: *defaults conditions: - > contains_any_globs(files, [ - 'adev/**/{.*,*}', - 'aio/**/{.*,*}' - ]) + 'adev/**/{*,.*}', + ]) reviewers: users: - - josephperrott - - jelbourn - - bencodezen - - thePunderWoman - - AndrewKushnir + - alan-agius4 - alxhub + - AndrewKushnir + - atscott + - bencodezen - crisbeto + - kirjs + - ~JeanMeche + - jelbourn + - thePunderWoman - devversion - - atscott + - josephperrott - pkozlowski-opensource + - mgechev - MarkTechson - - ~JeanMeche + - kirjs # ========================================================= # Angular DevTools @@ -633,7 +315,6 @@ groups: - > contains_any_globs(files.exclude('.pullapprove.yml'), [ '{*,.*}', - '.circleci/**/{*,.*}', '.devcontainer/**/{*,.*}', '.github/**/{*,.*}', '.husky/**/{*,.*}', @@ -684,6 +365,8 @@ groups: conditions: - author in ["angular-robot"] reviewers: + users: + - ~alan-agius4 teams: - framework-team @@ -698,7 +381,7 @@ groups: - > contains_any_globs(files.exclude("goldens/public-api/manage.js"), [ 'goldens/public-api/**/{*,.*}', - ]) + ]) reviewers: users: - AndrewKushnir @@ -707,6 +390,7 @@ groups: - ~jelbourn - thePunderWoman - pkozlowski-opensource + - kirjs - ~iteriani - ~tbondwilkinson - ~rahatarmanahmed @@ -732,6 +416,7 @@ groups: - alxhub - AndrewKushnir - atscott + - kirjs - ~jelbourn - thePunderWoman - pkozlowski-opensource @@ -760,6 +445,7 @@ groups: - ~jelbourn - thePunderWoman - pkozlowski-opensource + - kirjs #################################################################################### # Special Cases @@ -774,7 +460,7 @@ groups: - > contains_any_globs(files, [ '.pullapprove.yml' - ]) + ]) reviewers: users: - alxhub diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4e3cf05241..706969c6bad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,211 +1,379 @@ - -# 19.0.0-rc.1 (2024-11-06) -### compiler -| Commit | Type | Description | -| -- | -- | -- | -| [b25121ee4a](https://github.com/angular/angular/commit/b25121ee4aba427954fef074a967b9332654be84) | fix | avoid having to duplicate core environment ([#58444](https://github.com/angular/angular/pull/58444)) | -### compiler-cli -| Commit | Type | Description | -| -- | -- | -- | -| [d4d76ead80](https://github.com/angular/angular/commit/d4d76ead802837bc6cc7908bc9ebfefa73eb9969) | fix | do not fail fatal when references to non-existent module are discovered ([#58515](https://github.com/angular/angular/pull/58515)) | -| [22cd6869ef](https://github.com/angular/angular/commit/22cd6869ef453c342b206f84e857ef6c34922fa5) | fix | make the unused imports diagnostic easier to read ([#58468](https://github.com/angular/angular/pull/58468)) | + +# 19.1.0-next.4 (2024-12-18) ### core | Commit | Type | Description | | -- | -- | -- | -| [8ae84be3df](https://github.com/angular/angular/commit/8ae84be3df4e1393c6f00e2d28ccacd5b2159a0f) | fix | avoid slow stringification when checking for duplicates in dev mode ([#58521](https://github.com/angular/angular/pull/58521)) | -| [6e0af6dbbb](https://github.com/angular/angular/commit/6e0af6dbbbe5e9a9e2e5809ada0b7b5a7e456402) | fix | resolve forward-referenced host directives during directive matching ([#58492](https://github.com/angular/angular/pull/58492)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [c5e676bb87](https://github.com/angular/angular/commit/c5e676bb8715bcde42e56eb08a41cc1ba5c95f91) | fix | flip the default standalone flag in route-lazy-loading migration ([#58474](https://github.com/angular/angular/pull/58474)) | -| [e85ac5c7cb](https://github.com/angular/angular/commit/e85ac5c7cb06dc0fba757a9b931e79e07978f2a9) | fix | properly bundle shared compiler code into migrations ([#58515](https://github.com/angular/angular/pull/58515)) | -### platform-browser +| [57f3550219](https://github.com/angular/angular/commit/57f3550219f2a57c7c26c9183e48ee66845e0439) | feat | add utility for resolving defer block information to ng global ([#59184](https://github.com/angular/angular/pull/59184)) | +| [22f191f763](https://github.com/angular/angular/commit/22f191f76339a08bb8f0f2dfbc60dde0f2e38e73) | feat | extend the set of profiler events ([#59183](https://github.com/angular/angular/pull/59183)) | +| [1f4ff2fa36](https://github.com/angular/angular/commit/1f4ff2fa36f5d6240cbc4a40839d3d89501519d8) | fix | avoid triggering `on timer` and `on idle` on the server ([#59177](https://github.com/angular/angular/pull/59177)) | +| [cf89f14766](https://github.com/angular/angular/commit/cf89f14766b0ed0204f7012d44a4732fccb35398) | fix | Fix nested timer serialization ([#59173](https://github.com/angular/angular/pull/59173)) | +### platform-server | Commit | Type | Description | | -- | -- | -- | -| [c36a1c023b](https://github.com/angular/angular/commit/c36a1c023b34f9b2056e1bef6364787e8495bfad) | fix | correctly add external stylesheets to ShadowDOM components ([#58482](https://github.com/angular/angular/pull/58482)) | +| [300b141cc8](https://github.com/angular/angular/commit/300b141cc8652fd714b02f05c943cb79167ea844) | fix | Warn user when transfer state happens more than once ([#58935](https://github.com/angular/angular/pull/58935)) | - -# 18.2.11 (2024-11-06) + +# 19.0.5 (2024-12-18) ### core | Commit | Type | Description | | -- | -- | -- | -| [5f2d98a1b1](https://github.com/angular/angular/commit/5f2d98a1b1262a9cca84143fdf9829537138fc5c) | fix | avoid slow stringification when checking for duplicates in dev mode ([#58521](https://github.com/angular/angular/pull/58521)) | -| [3aa45a2fa1](https://github.com/angular/angular/commit/3aa45a2fa11ad568d12c622e0a9a94bbf1552118) | fix | resolve forward-referenced host directives during directive matching ([#58492](https://github.com/angular/angular/pull/58492)) ([#58500](https://github.com/angular/angular/pull/58500)) | +| [3793218e77](https://github.com/angular/angular/commit/3793218e77d699ddfae95a53ad048d4bfb9f042c) | fix | avoid triggering `on timer` and `on idle` on the server ([#59177](https://github.com/angular/angular/pull/59177)) | +| [cfc96ed82c](https://github.com/angular/angular/commit/cfc96ed82cbe958ea7548718f76a2e7a3d6826a9) | fix | Fix nested timer serialization ([#59173](https://github.com/angular/angular/pull/59173)) | +### platform-server +| Commit | Type | Description | +| -- | -- | -- | +| [9085a8fbd8](https://github.com/angular/angular/commit/9085a8fbd8cb61e3ce45adfa9ca2e96ba0be6f62) | fix | Warn user when transfer state happens more than once ([#58935](https://github.com/angular/angular/pull/58935)) | - -# 19.0.0-rc.0 (2024-10-30) -### compiler -| Commit | Type | Description | -| -- | -- | -- | -| [98804fd4beb](https://github.com/angular/angular/commit/98804fd4beb6292f5a50ce728424fdb33c47f654) | fix | add more specific matcher for hydrate never block ([#58360](https://github.com/angular/angular/pull/58360)) | + +# 19.1.0-next.3 (2024-12-12) ### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [dbe612f2cd5](https://github.com/angular/angular/commit/dbe612f2cd59adecdab3abb270b014c4b26e472c) | fix | disable standalone by default on older versions of Angular ([#58405](https://github.com/angular/angular/pull/58405)) | +| [c5c20e9d86](https://github.com/angular/angular/commit/c5c20e9d86d72b33840dcf0adea02876437a589f) | fix | check event side of two-way bindings ([#59002](https://github.com/angular/angular/pull/59002)) | +| [0dee2681f7](https://github.com/angular/angular/commit/0dee2681f782106fdb0fdcf9bc6ad1bca562751d) | fix | consider pre-release versions when detecting feature support ([#59061](https://github.com/angular/angular/pull/59061)) | +| [1b9492edf8](https://github.com/angular/angular/commit/1b9492edf88f8a217c0fd1a8203df489d91b623b) | fix | error in unused standalone imports diagnostic ([#59064](https://github.com/angular/angular/pull/59064)) | ### core | Commit | Type | Description | | -- | -- | -- | -| [c095679f927](https://github.com/angular/angular/commit/c095679f927ad67fec6c18cb140ea550ae02639e) | fix | avoid breaking change with apps using rxjs 6.x ([#58341](https://github.com/angular/angular/pull/58341)) | -| [fd7716440be](https://github.com/angular/angular/commit/fd7716440bec8f7ed042d79bafacf3048d45cd47) | fix | Prevents trying to trigger incremental hydration on CSR ([#58366](https://github.com/angular/angular/pull/58366)) | -### localize -| Commit | Type | Description | -| -- | -- | -- | -| [0730d9d748a](https://github.com/angular/angular/commit/0730d9d748a8b82f5a3e071e8756afbdd22901a1) | fix | Adding arb format to the list of valid formats in the localization extractor cli ([#58287](https://github.com/angular/angular/pull/58287)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [90c7ec39a06](https://github.com/angular/angular/commit/90c7ec39a06e5c14711e0a42e2d6a478cde2b9cc) | fix | inject migration always inserting generated variables before super call ([#58393](https://github.com/angular/angular/pull/58393)) | -| [7a65cdd911c](https://github.com/angular/angular/commit/7a65cdd911cbbf22445c916fc754d3a3304bc5fe) | fix | inject migration not inserting generated code after super call in some cases ([#58393](https://github.com/angular/angular/pull/58393)) | -| [616b411a6d9](https://github.com/angular/angular/commit/616b411a6d94d3dbc3e072b91c1194466c0a1add) | fix | properly migrate output aliases ([#58411](https://github.com/angular/angular/pull/58411)) | -| [d504452e2f1](https://github.com/angular/angular/commit/d504452e2f193d3b494a0b2944cddb028c0a2231) | fix | properly replace imports across files ([#58414](https://github.com/angular/angular/pull/58414)) | -### router +| [d010e11b73](https://github.com/angular/angular/commit/d010e11b735562ded439989ddb84cc83c6c00e81) | feat | add event listener options to renderer ([#59092](https://github.com/angular/angular/pull/59092)) | +| [30e676098d](https://github.com/angular/angular/commit/30e676098d72e9e11a6628b9716668df08f18c62) | fix | Fix a bug where snapshotted functions are being run twice if they return a nullish/falsey value. ([#59073](https://github.com/angular/angular/pull/59073)) | +### platform-browser | Commit | Type | Description | | -- | -- | -- | -| [a49c35ec769](https://github.com/angular/angular/commit/a49c35ec769461b9eb490719f0aa3e5aea8e243f) | fix | remove setter for `injector` on `OutletContext` ([#58343](https://github.com/angular/angular/pull/58343)) | +| [52be35118f](https://github.com/angular/angular/commit/52be35118feee587d2efe5a6c55502c171caaa97) | fix | collect external component styles from server rendering ([#59031](https://github.com/angular/angular/pull/59031)) | - -# 18.2.10 (2024-10-30) -### compiler + +# 19.0.4 (2024-12-12) +### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [69dce38e778](https://github.com/angular/angular/commit/69dce38e778cb4c15aa06347031765a84e3ac6a5) | fix | transform pseudo selectors correctly for the encapsulated view. ([#58417](https://github.com/angular/angular/pull/58417)) | -### localize +| [7e612171709](https://github.com/angular/angular/commit/7e6121717098462b4f53dc7212064243f2bcf024) | fix | consider pre-release versions when detecting feature support ([#59061](https://github.com/angular/angular/pull/59061)) | +| [cd764a31152](https://github.com/angular/angular/commit/cd764a31152004d37aa621efc4990c090d86f1e0) | fix | error in unused standalone imports diagnostic ([#59064](https://github.com/angular/angular/pull/59064)) | +### core | Commit | Type | Description | | -- | -- | -- | -| [3b989ac5bd9](https://github.com/angular/angular/commit/3b989ac5bd951a3d28bcd0ada150fc81503a016a) | fix | Adding arb format to the list of valid formats in the localization extractor cli ([#58287](https://github.com/angular/angular/pull/58287)) | +| [34ded10fa60](https://github.com/angular/angular/commit/34ded10fa6061a12531de8837a436cf0a1ac20b8) | fix | Fix a bug where snapshotted functions are being run twice if they return a nullish/falsey value. ([#59073](https://github.com/angular/angular/pull/59073)) | +### platform-browser +| Commit | Type | Description | +| -- | -- | -- | +| [ae0802d63c5](https://github.com/angular/angular/commit/ae0802d63c50307791e8a5d765573836dfe89075) | fix | collect external component styles from server rendering ([#59031](https://github.com/angular/angular/pull/59031)) | - -# 19.0.0-next.11 (2024-10-23) -### common -| Commit | Type | Description | -| -- | -- | -- | -| [24c6373820](https://github.com/angular/angular/commit/24c6373820231faf9d012a2e4d7ea945d3e8513b) | feat | add optional rounded transform support in cloudinary image loader ([#55364](https://github.com/angular/angular/pull/55364)) | -| [13c13067bc](https://github.com/angular/angular/commit/13c13067bc3ed50cb80b0a86e62655448adb3051) | feat | disable keyvalue sorting using null compareFn ([#57487](https://github.com/angular/angular/pull/57487)) | + +# 19.1.0-next.2 (2024-12-04) + + + + +# 19.0.3 (2024-12-04) + + + + +# 19.1.0-next.1 (2024-12-04) ### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [d0c74f3891](https://github.com/angular/angular/commit/d0c74f3891eed2feae913256ab15ac1e7435c379) | fix | report when NgModule imports or exports itself ([#58231](https://github.com/angular/angular/pull/58231)) | +| [f280467398](https://github.com/angular/angular/commit/f280467398c6980878b5e755a78606251814447b) | fix | account for multiple generated namespace imports in HMR ([#58924](https://github.com/angular/angular/pull/58924)) | ### core | Commit | Type | Description | | -- | -- | -- | -| [69fc5ae922](https://github.com/angular/angular/commit/69fc5ae9229b872a9ad70eb920087af2a378fead) | feat | Add incremental hydration public api ([#58249](https://github.com/angular/angular/pull/58249)) | -| [8ebbae88ca](https://github.com/angular/angular/commit/8ebbae88ca48b8aa78cd85deedbed19d44b8227e) | feat | Add rxjs operator prevent app stability until an event ([#56533](https://github.com/angular/angular/pull/56533)) | -| [19edf2c057](https://github.com/angular/angular/commit/19edf2c057f7587bc16812685d31a556521ad414) | feat | add syntactic sugar for initializers ([#53152](https://github.com/angular/angular/pull/53152)) | -| [ab25a192ba](https://github.com/angular/angular/commit/ab25a192ba664863ad68d224b9b2df78da22769a) | feat | allow running output migration on a subset of paths ([#58299](https://github.com/angular/angular/pull/58299)) | -| [18d8d44b1f](https://github.com/angular/angular/commit/18d8d44b1f3d56a4eda68f2cafded7529e08d0f1) | feat | experimental `resource()` API for async dependencies ([#58255](https://github.com/angular/angular/pull/58255)) | -| [9762b24b5e](https://github.com/angular/angular/commit/9762b24b5e8d7ab3ed2321959492a77b01d8ae57) | feat | experimental impl of `rxResource()` ([#58255](https://github.com/angular/angular/pull/58255)) | -| [ec386e7f12](https://github.com/angular/angular/commit/ec386e7f1216e0047392e75ab686b310b073eb42) | feat | introduce debugName optional arg to framework signal functions ([#57073](https://github.com/angular/angular/pull/57073)) | -| [8311f00faa](https://github.com/angular/angular/commit/8311f00faaf282d1a5b1ddca29247a2fba94a692) | feat | introduce the reactive linkedSignal ([#58189](https://github.com/angular/angular/pull/58189)) | -### forms +| [e894a5daea](https://github.com/angular/angular/commit/e894a5daea401b4e1173b0e66557ae40140eb9a0) | feat | set kind field on template and effect nodes ([#58865](https://github.com/angular/angular/pull/58865)) | +| [3b765367f3](https://github.com/angular/angular/commit/3b765367f31b6d1bb32406505f18151acdf1f2b2) | fix | Explicitly manage TracingSnapshot lifecycle and dispose of it once it's been used. ([#58929](https://github.com/angular/angular/pull/58929)) | +### migrations | Commit | Type | Description | | -- | -- | -- | -| [3e7d724037](https://github.com/angular/angular/commit/3e7d724037cca4d256b1442eda20d6c6ad91d279) | feat | add ability to clear a FormRecord ([#50750](https://github.com/angular/angular/pull/50750)) | -| [18b6f3339f](https://github.com/angular/angular/commit/18b6f3339f46b37ee67fce2fa8a900cc73b2f23c) | fix | fix FormRecord type inference ([#50750](https://github.com/angular/angular/pull/50750)) | -### http +| [e31e52e177](https://github.com/angular/angular/commit/e31e52e1771ea565a6869b4ed252d6ff7097d4ad) | fix | class content being deleted in some edge cases ([#58959](https://github.com/angular/angular/pull/58959)) | +| [508d3a1b3b](https://github.com/angular/angular/commit/508d3a1b3bc5770f18e3e46e2105bf0ba6178a87) | fix | correctly strip away parameters surrounded by comments in inject migration ([#58959](https://github.com/angular/angular/pull/58959)) | +| [7191aa6e09](https://github.com/angular/angular/commit/7191aa6e09ca3b85efd3fd14a18944eac4384763) | fix | don't migrate classes with parameters that can't be injected ([#58959](https://github.com/angular/angular/pull/58959)) | +| [a4924af6d5](https://github.com/angular/angular/commit/a4924af6d580c5bdaa185c4c97277c4effb55af9) | fix | inject migration aggressively removing imports ([#58959](https://github.com/angular/angular/pull/58959)) | +| [35165d152d](https://github.com/angular/angular/commit/35165d152d7f9c3c8789ebdf792037aafdc1cc66) | fix | inject migration dropping code if everything except super is removed ([#58959](https://github.com/angular/angular/pull/58959)) | +| [68e5ba7a3a](https://github.com/angular/angular/commit/68e5ba7a3a44c2f1647c4c6cc7ed66b010f85d15) | fix | preserve type literals and tuples in inject migrations ([#58959](https://github.com/angular/angular/pull/58959)) | +### platform-server | Commit | Type | Description | | -- | -- | -- | -| [4b9accdf16](https://github.com/angular/angular/commit/4b9accdf166f3990b3706de83ada15937fe786e2) | feat | promote `withRequestsMadeViaParent` to stable. ([#58221](https://github.com/angular/angular/pull/58221)) | -### language-service +| [1cfbfc66d3](https://github.com/angular/angular/commit/1cfbfc66d3a24b6c41abf13550e7c2911e20b550) | fix | remove peer dependency on animations ([#58997](https://github.com/angular/angular/pull/58997)) | + + + + +# 19.0.2 (2024-12-04) +### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [6342befff8](https://github.com/angular/angular/commit/6342befff8ee491f37e8912cccb0099bbbf01042) | feat | support migrating full classes to signal queries ([#58263](https://github.com/angular/angular/pull/58263)) | +| [9f99196d23](https://github.com/angular/angular/commit/9f99196d239479bcba0b42a18a5155ed5a1764ff) | fix | account for multiple generated namespace imports in HMR ([#58924](https://github.com/angular/angular/pull/58924)) | +### core +| Commit | Type | Description | +| -- | -- | -- | +| [4792db9a6d](https://github.com/angular/angular/commit/4792db9a6d3a7dc076c9b200cd31a53a4fd30683) | fix | Explicitly manage TracingSnapshot lifecycle and dispose of it once it's been used. ([#58929](https://github.com/angular/angular/pull/58929)) | ### migrations | Commit | Type | Description | | -- | -- | -- | -| [dff4de0f75](https://github.com/angular/angular/commit/dff4de0f75741bc629462bb8da833b876c754453) | feat | add a combined migration for all signals APIs ([#58259](https://github.com/angular/angular/pull/58259)) | -| [bb286f65e7](https://github.com/angular/angular/commit/bb286f65e7a38c21ae61807b9a7811908a9030ed) | feat | capture output migration stats ([#58321](https://github.com/angular/angular/pull/58321)) | -| [2bfc64daf1](https://github.com/angular/angular/commit/2bfc64daf1cad9be8099759e8de7a361555ad5d1) | feat | expose output as function migration ([#58299](https://github.com/angular/angular/pull/58299)) | -| [cf70d626cd](https://github.com/angular/angular/commit/cf70d626cdedfd978d058b973420d8f31980555d) | feat | print output migration stats in ng generate ([#58321](https://github.com/angular/angular/pull/58321)) | -| [42607bf0f2](https://github.com/angular/angular/commit/42607bf0f28a2421a0d41809485c09dca26ea599) | fix | add outputs migration to combined shorthand ([#58318](https://github.com/angular/angular/pull/58318)) | -| [00e2001351](https://github.com/angular/angular/commit/00e20013512f75327e5644ad5ac7829fc0e866d4) | fix | migrate more .next output usages ([#58282](https://github.com/angular/angular/pull/58282)) | -### service-worker +| [7b5bacc228](https://github.com/angular/angular/commit/7b5bacc2285803e6ac9691c2dae2361ddca9da9a) | fix | class content being deleted in some edge cases ([#58959](https://github.com/angular/angular/pull/58959)) | +| [d1cbdd6acb](https://github.com/angular/angular/commit/d1cbdd6acb228773e0fb33958978a14e12be178f) | fix | correctly strip away parameters surrounded by comments in inject migration ([#58959](https://github.com/angular/angular/pull/58959)) | +| [e17ff71c31](https://github.com/angular/angular/commit/e17ff71c318a1b32d5207b7516856f330f2bcf5a) | fix | don't migrate classes with parameters that can't be injected ([#58959](https://github.com/angular/angular/pull/58959)) | +| [7c5f990001](https://github.com/angular/angular/commit/7c5f990001c4aac9f48c5461421579c398295356) | fix | inject migration aggressively removing imports ([#58959](https://github.com/angular/angular/pull/58959)) | +| [4392ccedf9](https://github.com/angular/angular/commit/4392ccedf997e79486af7ad60172eea98ed3351f) | fix | inject migration dropping code if everything except super is removed ([#58959](https://github.com/angular/angular/pull/58959)) | +| [9cbebc6dda](https://github.com/angular/angular/commit/9cbebc6dda89d2fdfc52799aef1ea895dcac2d00) | fix | preserve type literals and tuples in inject migrations ([#58959](https://github.com/angular/angular/pull/58959)) | +### platform-server | Commit | Type | Description | | -- | -- | -- | -| [8ddce80a0b](https://github.com/angular/angular/commit/8ddce80a0bab4ebbd0f7db1c85ee27e4f0249db9) | feat | allow specifying maxAge for entire application ([#49601](https://github.com/angular/angular/pull/49601)) | +| [f3c388ecda](https://github.com/angular/angular/commit/f3c388ecda5e836946031a554827cdaee9801734) | fix | remove peer dependency on animations ([#58997](https://github.com/angular/angular/pull/58997)) | - -# 18.2.9 (2024-10-23) -### compiler-cli + +# 18.2.13 (2024-11-26) +### migrations | Commit | Type | Description | | -- | -- | -- | -| [b0ab653965](https://github.com/angular/angular/commit/b0ab653965cf88fcfde23fc6a6cc78ce3121a30f) | fix | report when NgModule imports or exports itself ([#58231](https://github.com/angular/angular/pull/58231)) | +| [06d70a25ea](https://github.com/angular/angular/commit/06d70a25ea7a6ef32f47516fcb8542d98ac45e14) | fix | take care of tests that import both HttpClientModule & HttpClientTestingModule. ([#58777](https://github.com/angular/angular/pull/58777)) | - -# 19.0.0-next.10 (2024-10-16) -## Breaking Changes + +# 19.1.0-next.0 (2024-11-26) +### common +| Commit | Type | Description | +| -- | -- | -- | +| [e4c50b3bea](https://github.com/angular/angular/commit/e4c50b3bea22ca2afba74465893c36730952f4b9) | feat | expose component instance in NgComponentOutlet ([#58698](https://github.com/angular/angular/pull/58698)) | ### core -- Angular directives, components and pipes are now standalone by default. Specify `standalone: false` for declarations that are currently declared in `@NgModule`s. `ng update` for v19 will take care of this automatically. -### compiler | Commit | Type | Description | | -- | -- | -- | -| [0c9d721ac1](https://github.com/angular/angular/commit/0c9d721ac157662b2602cf0278ba4b79325f6882) | feat | add support for the `typeof` keyword in template expressions. ([#58183](https://github.com/angular/angular/pull/58183)) | -| [a3cb530d84](https://github.com/angular/angular/commit/a3cb530d846bf4d15802b9f42b6dee5c9a3a08ee) | fix | handle typeof expressions in serializer ([#58217](https://github.com/angular/angular/pull/58217)) | -| [ba4340875a](https://github.com/angular/angular/commit/ba4340875ac8e338ff1390fc7897eecc704ef7c5) | fix | ignore placeholder-only i18n messages ([#58154](https://github.com/angular/angular/pull/58154)) | +| [bd1f1294ae](https://github.com/angular/angular/commit/bd1f1294aeb0d47b24421b7b7a608988689a459f) | feat | support TypeScript 5.7 ([#58609](https://github.com/angular/angular/pull/58609)) | + + + + +# 19.0.1 (2024-11-26) ### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [231e6ff6ca](https://github.com/angular/angular/commit/231e6ff6ca0dae0289a03615bcaed29455c2d4b8) | feat | generate the HMR replacement module ([#58205](https://github.com/angular/angular/pull/58205)) | -| [fb44323c51](https://github.com/angular/angular/commit/fb44323c51da5a86853aafd8a70ce0c25d6c0d7f) | fix | incorrectly generating relative file paths on case-insensitive platforms ([#58150](https://github.com/angular/angular/pull/58150)) | +| [fb1fa8b0fc](https://github.com/angular/angular/commit/fb1fa8b0fc04c9cfac6551ca27bee89dcd7c72ac) | fix | more accurate diagnostics for host binding parser errors ([#58870](https://github.com/angular/angular/pull/58870)) | ### core | Commit | Type | Description | | -- | -- | -- | -| [6b8c494d05](https://github.com/angular/angular/commit/6b8c494d05e545830fffb9626153480af6339ddc) | feat | flipping the default value for `standalone` to `true` ([#58169](https://github.com/angular/angular/pull/58169)) | -| [3f1e7ab6ae](https://github.com/angular/angular/commit/3f1e7ab6ae984149004c449c04301b434ea64d2a) | feat | promote `outputFromObservable` & `outputToObservable` to stable. ([#58214](https://github.com/angular/angular/pull/58214)) | -| [97c44a1d6c](https://github.com/angular/angular/commit/97c44a1d6c41be250d585fba5af2bc2af4d98ae2) | feat | Promote `takeUntilDestroyed` to stable. ([#58200](https://github.com/angular/angular/pull/58200)) | -| [819ff034ce](https://github.com/angular/angular/commit/819ff034ce7cf014cedef60510b83af9340efa71) | feat | treat directives, pipes, components as by default ([#58229](https://github.com/angular/angular/pull/58229)) | +| [502ee0e722](https://github.com/angular/angular/commit/502ee0e7221a9e7cfa0fa3bd92298d3b650a7713) | fix | correctly clear template HMR internal renderer cache ([#58724](https://github.com/angular/angular/pull/58724)) | +| [99715104a1](https://github.com/angular/angular/commit/99715104a1a787c3899dfbfac6b44f28c7d24356) | fix | correctly perform lazy routes migration for components with additional decorators ([#58796](https://github.com/angular/angular/pull/58796)) | +| [118803035f](https://github.com/angular/angular/commit/118803035f366acdffc577ec857b888f764bb338) | fix | Ensure _tick is always run within the TracingSnapshot. ([#58881](https://github.com/angular/angular/pull/58881)) | +| [08b9452f01](https://github.com/angular/angular/commit/08b9452f012b2ef660f767c2f0a4bf86bb15bb61) | fix | Ensure resource sets an error ([#58855](https://github.com/angular/angular/pull/58855)) | +| [84f45ea3ff](https://github.com/angular/angular/commit/84f45ea3ffe02003350c6c19fdafdc6f4d521ccb) | fix | make component id generation more stable between client and server builds ([#58813](https://github.com/angular/angular/pull/58813)) | +| [d3491c7cee](https://github.com/angular/angular/commit/d3491c7cee3d110da1adb51f8047b4e1976ece71) | fix | Prevents race condition of cleanup for incremental hydration ([#58722](https://github.com/angular/angular/pull/58722)) | +### forms +| Commit | Type | Description | +| -- | -- | -- | +| [4dfe5b6cef](https://github.com/angular/angular/commit/4dfe5b6cefd7901a466b37b660f8b3a051a06cb3) | fix | work around TypeScript 5.7 issue ([#58731](https://github.com/angular/angular/pull/58731)) | +### language-service +| Commit | Type | Description | +| -- | -- | -- | +| [a983865bff](https://github.com/angular/angular/commit/a983865bffa828a982ef7e56204924d9c2989ead) | fix | add fix for individual unused imports ([#58719](https://github.com/angular/angular/pull/58719)) | +| [e6e7a4e22b](https://github.com/angular/angular/commit/e6e7a4e22b0a654808e5eb88a30cd6effa383332) | fix | allow fixes to run without template info ([#58719](https://github.com/angular/angular/pull/58719)) | +### migrations +| Commit | Type | Description | +| -- | -- | -- | +| [5ce10264a4](https://github.com/angular/angular/commit/5ce10264a434ffc8e31cdc68208d2c3c7f8378ed) | fix | fix provide-initializer migration when using useFactory ([#58518](https://github.com/angular/angular/pull/58518)) | +| [d4f5c85f60](https://github.com/angular/angular/commit/d4f5c85f60133550303d59b3f9e3e34f14ca63ce) | fix | handle parameters with initializers in inject migration ([#58769](https://github.com/angular/angular/pull/58769)) | +| [a6d2d2dc10](https://github.com/angular/angular/commit/a6d2d2dc104608f14c3850b21bc23ba75ca04e4d) | fix | Mark hoisted properties as removed in inject migration ([#58804](https://github.com/angular/angular/pull/58804)) | - -# 19.0.0-next.9 (2024-10-10) + +# 19.0.0 (2024-11-19) + +Blog post: https://blog.angular.dev/meet-angular-v19-7b29dfd05b84 + ## Breaking Changes ### compiler - `this.foo` property reads no longer refer to template context variables. If you intended to read the template variable, do not use `this.`. +- changes to CSS selectors parsing where introduced, mainly to: pseudo selectors `:where()` and `:is()`, + parsing of `:host` and `host-context`, parsing selectors within pseudo selector arguments (for instance comma separated selectors). + These changes could lead to a different specificity of the resulting selectors and/or previously broken selectors being applied now, + for example `:where(:host)` used to transform to `:where()[ng-host]` and is being `:where([ng-host])` now. Unlike the previous outcome, + the new result can target elements and therefore could lead to breakages. ### core +- Angular directives, components and pipes are now standalone by default. + * Specify `standalone: false` for declarations that are currently declared in `@NgModule`s. + * `ng update` for v19 will take care of this automatically. +- TypeScript versions less than 5.5 are no longer supported. +- Timing changes for `effect` API (in developer preview): + + * effects which are triggered outside of change detection run as part of + the change detection process instead of as a microtask. Depending on the + specifics of application/test setup, this can result in them executing + earlier or later (or requiring additional test steps to trigger; see below + examples). + + * effects which are triggered during change detection (e.g. by input + signals) run _earlier_, before the component's template. + +- `ExperimentalPendingTasks` has been renamed to `PendingTasks`. +- The `autoDetect` feature of `ComponentFixture` will now + attach the fixture to the `ApplicationRef`. As a result, errors during + automatic change detection of the fixture be reported to the `ErrorHandler`. + This change may cause custom error handlers to observe new failures that were previously unreported. +- `createComponent` will now render default fallback with empty `projectableNodes`. + + * When passing an empty array to `projectableNodes` in the `createComponent` API, the default fallback content + of the `ng-content` will be rendered if present. To prevent rendering the default content, pass `document.createTextNode('')` as a `projectableNode`. + + ```ts + // The first ng-content will render the default fallback content if present + createComponent(MyComponent. { projectableNodes: [[], [secondNode]] }); + + // To prevent projecting the default fallback content: + createComponent(MyComponent. { projectableNodes: [[document.createTextNode('')], [secondNode]] }); + + ``` +- Errors that are thrown during `ApplicationRef.tick` + will now be rethrown when using `TestBed`. These errors should be + resolved by ensuring the test environment is set up correctly to + complete change detection successfully. There are two alternatives to + catch the errors: + + * Instead of waiting for automatic change detection to happen, trigger + it synchronously and expect the error. For example, a jasmine test + could write `expect(() => TestBed.inject(ApplicationRef).tick()).toThrow()` + * `TestBed` will reject any outstanding `ComponentFixture.whenStable` promises. A jasmine test, + for example, could write `expectAsync(fixture.whenStable()).toBeRejected()`. + + As a last resort, you can configure errors to _not_ be rethrown by + setting `rethrowApplicationErrors` to `false` in `TestBed.configureTestingModule`. +- The timers that are used for zone coalescing and hybrid + mode scheduling (which schedules an application state synchronization + when changes happen outside the Angular zone) will now run in the zone + above Angular rather than the root zone. This will mostly affect tests + which use `fakeAsync`: these timers will now be visible to `fakeAsync` + and can be affected by `tick` or `flush`. - The deprecated `factories` property in `KeyValueDiffers` has been removed. +### elements +- as part of switching away from custom CD behavior to the + hybrid scheduler, timing of change detection around custom elements has + changed subtly. These changes make elements more efficient, but can cause + tests which encoded assumptions about how or when elements would be checked + to require updating. ### localize -- The `name` option in the `ng add @localize` schematic has been removed in favor of the `project` option. +- The `name` option in the `ng add `@localize`` schematic has been removed in favor of the `project` option. ### platform-browser - The deprecated `BrowserModule.withServerTransition` method has been removed. Please use the `APP_ID` DI token to set the application id instead. +### router +- The `Router.errorHandler` property has been removed. + Adding an error handler should be configured in either + `withNavigationErrorHandler` with `provideRouter` or the `errorHandler` + property in the extra options of `RouterModule.forRoot`. In addition, + the error handler cannot be used to change the return value of the + router navigation promise or prevent it from rejecting. Instead, if you + want to prevent the promise from rejecting, use `resolveNavigationPromiseOnError`. +- The return type of the `Resolve` interface now includes + `RedirectCommand`. +### common +| Commit | Type | Description | +| -- | -- | -- | +| [24c6373820](https://github.com/angular/angular/commit/24c6373820231faf9d012a2e4d7ea945d3e8513b) | feat | add optional rounded transform support in cloudinary image loader ([#55364](https://github.com/angular/angular/pull/55364)) | +| [50f08e6c4b](https://github.com/angular/angular/commit/50f08e6c4bf1caeeb08d3505ce7fabd466b9c76b) | feat | automatically use sizes auto in NgOptimizedImage ([#57479](https://github.com/angular/angular/pull/57479)) | +| [13c13067bc](https://github.com/angular/angular/commit/13c13067bc3ed50cb80b0a86e62655448adb3051) | feat | disable keyvalue sorting using null compareFn ([#57487](https://github.com/angular/angular/pull/57487)) | ### compiler | Commit | Type | Description | | -- | -- | -- | +| [a2e4ee0cb3](https://github.com/angular/angular/commit/a2e4ee0cb3d40cadc05e28d58b06853973944456) | feat | add diagnostic for unused standalone imports ([#57605](https://github.com/angular/angular/pull/57605)) | +| [0c9d721ac1](https://github.com/angular/angular/commit/0c9d721ac157662b2602cf0278ba4b79325f6882) | feat | add support for the `typeof` keyword in template expressions. ([#58183](https://github.com/angular/angular/pull/58183)) | | [09f589f000](https://github.com/angular/angular/commit/09f589f0006f4b428b675b83c12c0dc8ebb7e45f) | fix | `this.a` should always refer to class property `a` ([#55183](https://github.com/angular/angular/pull/55183)) | -| [e8d1944999](https://github.com/angular/angular/commit/e8d1944999e1fdfbd67630d475334c0d7f41a0eb) | fix | add multiple :host and nested selectors support ([#57796](https://github.com/angular/angular/pull/57796)) | -| [82144b6d63](https://github.com/angular/angular/commit/82144b6d63d072d112d1a7f4dcc018a1d64bb994) | fix | allow combinators inside pseudo selectors ([#57796](https://github.com/angular/angular/pull/57796)) | -| [292ea4714f](https://github.com/angular/angular/commit/292ea4714fb7e76cf1748d2f9059991e05c42574) | fix | fix comment typo ([#57796](https://github.com/angular/angular/pull/57796)) | -| [69529d8873](https://github.com/angular/angular/commit/69529d8873fbd7888ab68fddc6e7c654c5065764) | fix | fix parsing of the :host-context with pseudo selectors ([#57796](https://github.com/angular/angular/pull/57796)) | -| [2374b87b64](https://github.com/angular/angular/commit/2374b87b643e0373f85cf126d4b01b2fff785f64) | fix | preserve attributes attached to :host selector ([#57796](https://github.com/angular/angular/pull/57796)) | -| [46a6324c82](https://github.com/angular/angular/commit/46a6324c82a41b69c16a4c8c9f3fc52d1ecf6917) | fix | scope :host-context inside pseudo selectors, do not decrease specificity ([#57796](https://github.com/angular/angular/pull/57796)) | -| [bc5f1175e9](https://github.com/angular/angular/commit/bc5f1175e9f39dfa2699c4de19ee9af4ce4b50d1) | fix | transform pseudo selectors correctly for the encapsulated view ([#57796](https://github.com/angular/angular/pull/57796)) | +| [98804fd4be](https://github.com/angular/angular/commit/98804fd4beb6292f5a50ce728424fdb33c47f654) | fix | add more specific matcher for hydrate never block ([#58360](https://github.com/angular/angular/pull/58360)) | +| [b25121ee4a](https://github.com/angular/angular/commit/b25121ee4aba427954fef074a967b9332654be84) | fix | avoid having to duplicate core environment ([#58444](https://github.com/angular/angular/pull/58444)) | +| [560282aa9b](https://github.com/angular/angular/commit/560282aa9b3204ad8311017905beed63072c7303) | fix | control flow nodes with root at the end projected incorrectly ([#58607](https://github.com/angular/angular/pull/58607)) | +| [2be161d015](https://github.com/angular/angular/commit/2be161d015ce6bab0142b6e6c34a8ede6341f627) | fix | fix `:host` parsing in pseudo-selectors ([#58681](https://github.com/angular/angular/pull/58681)) | +| [806a61b5a6](https://github.com/angular/angular/commit/806a61b5a619d98c0226ba6a566b1562f6e16e5a) | fix | fix multiline selectors ([#58681](https://github.com/angular/angular/pull/58681)) | +| [a3cb530d84](https://github.com/angular/angular/commit/a3cb530d846bf4d15802b9f42b6dee5c9a3a08ee) | fix | handle typeof expressions in serializer ([#58217](https://github.com/angular/angular/pull/58217)) | +| [ba4340875a](https://github.com/angular/angular/commit/ba4340875ac8e338ff1390fc7897eecc704ef7c5) | fix | ignore placeholder-only i18n messages ([#58154](https://github.com/angular/angular/pull/58154)) | +| [e5d3abb298](https://github.com/angular/angular/commit/e5d3abb29842412f82a67562aceff245d493ec53) | fix | resolve `:host:host-context(.foo)` ([#58681](https://github.com/angular/angular/pull/58681)) | +| [80f56954ce](https://github.com/angular/angular/commit/80f56954cecf763e36bdcfbbd592a82d693eeef7) | fix | transform chained pseudo-selectors ([#58681](https://github.com/angular/angular/pull/58681)) | ### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [8d8c03abc4](https://github.com/angular/angular/commit/8d8c03abc40099da268d7301f029954f3e3f1c90) | fix | defer symbols only used in types ([#58104](https://github.com/angular/angular/pull/58104)) | +| [d9687f43dd](https://github.com/angular/angular/commit/d9687f43dd2ccfcf7dd3ee4f9066ce727f3224c6) | feat | 'strictStandalone' flag enforces standalone ([#57935](https://github.com/angular/angular/pull/57935)) | +| [9e87593055](https://github.com/angular/angular/commit/9e87593055a5314a67090bd15d5552c23b538050) | feat | ensure template style elements are preprocessed as inline styles ([#57429](https://github.com/angular/angular/pull/57429)) | +| [231e6ff6ca](https://github.com/angular/angular/commit/231e6ff6ca0dae0289a03615bcaed29455c2d4b8) | feat | generate the HMR replacement module ([#58205](https://github.com/angular/angular/pull/58205)) | +| [dbe612f2cd](https://github.com/angular/angular/commit/dbe612f2cd59adecdab3abb270b014c4b26e472c) | fix | disable standalone by default on older versions of Angular ([#58405](https://github.com/angular/angular/pull/58405)) | +| [d4d76ead80](https://github.com/angular/angular/commit/d4d76ead802837bc6cc7908bc9ebfefa73eb9969) | fix | do not fail fatal when references to non-existent module are discovered ([#58515](https://github.com/angular/angular/pull/58515)) | +| [33fe252c58](https://github.com/angular/angular/commit/33fe252c588ee94d6ef99e8070d35c483ec24fda) | fix | do not report unused declarations coming from an imported array ([#57940](https://github.com/angular/angular/pull/57940)) | +| [fb44323c51](https://github.com/angular/angular/commit/fb44323c51da5a86853aafd8a70ce0c25d6c0d7f) | fix | incorrectly generating relative file paths on case-insensitive platforms ([#58150](https://github.com/angular/angular/pull/58150)) | +| [22cd6869ef](https://github.com/angular/angular/commit/22cd6869ef453c342b206f84e857ef6c34922fa5) | fix | make the unused imports diagnostic easier to read ([#58468](https://github.com/angular/angular/pull/58468)) | +| [9bbb01c85e](https://github.com/angular/angular/commit/9bbb01c85e763b0457456a2393a834db15008671) | fix | report individual diagnostics for unused imports ([#58589](https://github.com/angular/angular/pull/58589)) | +| [4716c3b966](https://github.com/angular/angular/commit/4716c3b9660b01f4ef3642fb774270b7f4a13d1a) | perf | reduce duplicate component style resolution ([#57502](https://github.com/angular/angular/pull/57502)) | ### core | Commit | Type | Description | | -- | -- | -- | +| [6ea8e1e9aa](https://github.com/angular/angular/commit/6ea8e1e9aae028572873cf97aa1949c8153f458f) | feat | Add a schematics to migrate to `standalone: false`. ([#57643](https://github.com/angular/angular/pull/57643)) | +| [3ebe6b4ad4](https://github.com/angular/angular/commit/3ebe6b4ad401337e18619edc34477ae98226fa3e) | feat | Add async `run` method on `ExperimentalPendingTasks` ([#56546](https://github.com/angular/angular/pull/56546)) | +| [69fc5ae922](https://github.com/angular/angular/commit/69fc5ae9229b872a9ad70eb920087af2a378fead) | feat | Add incremental hydration public api ([#58249](https://github.com/angular/angular/pull/58249)) | +| [8ebbae88ca](https://github.com/angular/angular/commit/8ebbae88ca48b8aa78cd85deedbed19d44b8227e) | feat | Add rxjs operator prevent app stability until an event ([#56533](https://github.com/angular/angular/pull/56533)) | +| [19edf2c057](https://github.com/angular/angular/commit/19edf2c057f7587bc16812685d31a556521ad414) | feat | add syntactic sugar for initializers ([#53152](https://github.com/angular/angular/pull/53152)) | +| [c93b510f9b](https://github.com/angular/angular/commit/c93b510f9b2e23aa7a3848a04c05249fde14a9b1) | feat | allow passing `undefined` without needing to include it in the type argument of `input` ([#57621](https://github.com/angular/angular/pull/57621)) | +| [ab25a192ba](https://github.com/angular/angular/commit/ab25a192ba664863ad68d224b9b2df78da22769a) | feat | allow running output migration on a subset of paths ([#58299](https://github.com/angular/angular/pull/58299)) | +| [fc59e2a7b7](https://github.com/angular/angular/commit/fc59e2a7b7afa491a5ea740284a742574805eb36) | feat | change effect() execution timing & no-op `allowSignalWrites` ([#57874](https://github.com/angular/angular/pull/57874)) | +| [8bcc663a53](https://github.com/angular/angular/commit/8bcc663a53888717cdf4ce0c23404caa00abb1b2) | feat | drop support for TypeScript 5.4 ([#57577](https://github.com/angular/angular/pull/57577)) | +| [18d8d44b1f](https://github.com/angular/angular/commit/18d8d44b1f3d56a4eda68f2cafded7529e08d0f1) | feat | experimental `resource()` API for async dependencies ([#58255](https://github.com/angular/angular/pull/58255)) | +| [9762b24b5e](https://github.com/angular/angular/commit/9762b24b5e8d7ab3ed2321959492a77b01d8ae57) | feat | experimental impl of `rxResource()` ([#58255](https://github.com/angular/angular/pull/58255)) | +| [6b8c494d05](https://github.com/angular/angular/commit/6b8c494d05e545830fffb9626153480af6339ddc) | feat | flipping the default value for `standalone` to `true` ([#58169](https://github.com/angular/angular/pull/58169)) | +| [e6e5d29e83](https://github.com/angular/angular/commit/e6e5d29e830a0a74d7677d5f2345f29391064853) | feat | initial version of the output migration ([#57604](https://github.com/angular/angular/pull/57604)) | +| [be2e49639b](https://github.com/angular/angular/commit/be2e49639bda831831ad62d49253db942a83fd46) | feat | introduce `afterRenderEffect` ([#57549](https://github.com/angular/angular/pull/57549)) | +| [ec386e7f12](https://github.com/angular/angular/commit/ec386e7f1216e0047392e75ab686b310b073eb42) | feat | introduce debugName optional arg to framework signal functions ([#57073](https://github.com/angular/angular/pull/57073)) | +| [8311f00faa](https://github.com/angular/angular/commit/8311f00faaf282d1a5b1ddca29247a2fba94a692) | feat | introduce the reactive linkedSignal ([#58189](https://github.com/angular/angular/pull/58189)) | +| [1b1519224d](https://github.com/angular/angular/commit/1b1519224d10c1cd25d05d7b958772b9adee1e1a) | feat | mark input, output and model APIs as stable ([#57804](https://github.com/angular/angular/pull/57804)) | +| [a7eff3ffaa](https://github.com/angular/angular/commit/a7eff3ffaaecbcb3034130d475ff7b4e41a1e1cc) | feat | mark signal-based query APIs as stable ([#57921](https://github.com/angular/angular/pull/57921)) | +| [a1f229850a](https://github.com/angular/angular/commit/a1f229850ad36da009f772faa831da173a60268c) | feat | migrate ExperimentalPendingTasks to PendingTasks ([#57533](https://github.com/angular/angular/pull/57533)) | +| [3f1e7ab6ae](https://github.com/angular/angular/commit/3f1e7ab6ae984149004c449c04301b434ea64d2a) | feat | promote `outputFromObservable` & `outputToObservable` to stable. ([#58214](https://github.com/angular/angular/pull/58214)) | +| [97c44a1d6c](https://github.com/angular/angular/commit/97c44a1d6c41be250d585fba5af2bc2af4d98ae2) | feat | Promote `takeUntilDestroyed` to stable. ([#58200](https://github.com/angular/angular/pull/58200)) | +| [e5adf92965](https://github.com/angular/angular/commit/e5adf9296595644e415d5c147df08890be01ba77) | feat | stabilize `@let` syntax ([#57813](https://github.com/angular/angular/pull/57813)) | +| [b063468027](https://github.com/angular/angular/commit/b0634680272569501146bb7a9cdfe53033e25971) | feat | support TypeScript 5.6 ([#57424](https://github.com/angular/angular/pull/57424)) | +| [819ff034ce](https://github.com/angular/angular/commit/819ff034ce7cf014cedef60510b83af9340efa71) | feat | treat directives, pipes, components as by default ([#58229](https://github.com/angular/angular/pull/58229)) | | [ee426c62f0](https://github.com/angular/angular/commit/ee426c62f07579ec7dc89ce9582972cc1e3471d4) | fix | allow signal write error ([#57973](https://github.com/angular/angular/pull/57973)) | -| [67db4305c2](https://github.com/angular/angular/commit/67db4305c2261625fd54d284c29e94e26cb19488) | fix | clean up afterRender after it is executed ([#58119](https://github.com/angular/angular/pull/58119)) | +| [c095679f92](https://github.com/angular/angular/commit/c095679f927ad67fec6c18cb140ea550ae02639e) | fix | avoid breaking change with apps using rxjs 6.x ([#58341](https://github.com/angular/angular/pull/58341)) | +| [71ee81af2c](https://github.com/angular/angular/commit/71ee81af2c4c5854a54cf94a48d5829da41878a7) | fix | clean up event contract once hydration is done ([#58174](https://github.com/angular/angular/pull/58174)) | +| [f03d274e87](https://github.com/angular/angular/commit/f03d274e87c919514a70d02c0699523957de7386) | fix | ComponentFixture autoDetect feature works like production ([#55228](https://github.com/angular/angular/pull/55228)) | +| [950a5540f1](https://github.com/angular/angular/commit/950a5540f15118e7360506ad82ec9dab5a11f789) | fix | Ensure the `ViewContext` is retained after closure minification ([#57903](https://github.com/angular/angular/pull/57903)) | +| [7b1e5be20b](https://github.com/angular/angular/commit/7b1e5be20b99c88246c6be78a4dcd64eb55cee1a) | fix | fallback to default ng-content with empty projectable nodes. ([#57480](https://github.com/angular/angular/pull/57480)) | +| [0300dd2e18](https://github.com/angular/angular/commit/0300dd2e18f064f2f57f7371e0dc5c01218b5019) | fix | Fix fixture.detectChanges with autoDetect disabled and zoneless ([#57416](https://github.com/angular/angular/pull/57416)) | +| [5fe57d4fbb](https://github.com/angular/angular/commit/5fe57d4fbb578c35a8e8ef037ae8c19c8a0e901c) | fix | fixes issues with control flow and incremental hydration ([#58644](https://github.com/angular/angular/pull/58644)) | +| [51933ef5a6](https://github.com/angular/angular/commit/51933ef5a6ce62df37945fa22e87e3868288e318) | fix | prevent errors on contract cleanup ([#58614](https://github.com/angular/angular/pull/58614)) | +| [fd7716440b](https://github.com/angular/angular/commit/fd7716440bec8f7ed042d79bafacf3048d45cd47) | fix | Prevents trying to trigger incremental hydration on CSR ([#58366](https://github.com/angular/angular/pull/58366)) | | [656b5d3e78](https://github.com/angular/angular/commit/656b5d3e78004229a76488e0de1eb1d3508d8f6d) | fix | Re-assign error codes to be within core bounds (<1000) ([#53455](https://github.com/angular/angular/pull/53455)) | +| [6e0af6dbbb](https://github.com/angular/angular/commit/6e0af6dbbbe5e9a9e2e5809ada0b7b5a7e456402) | fix | resolve forward-referenced host directives during directive matching ([#58492](https://github.com/angular/angular/pull/58492)) | +| [468d3fb9b1](https://github.com/angular/angular/commit/468d3fb9b1c3dd6dff86afcb6d8f89cc4c29b24b) | fix | rethrow errors during ApplicationRef.tick in TestBed ([#57200](https://github.com/angular/angular/pull/57200)) | +| [226a67dabb](https://github.com/angular/angular/commit/226a67dabba90a488ad09ce7bb026b8883c90d4a) | fix | Schedulers run in zone above Angular rather than root ([#57553](https://github.com/angular/angular/pull/57553)) | | [97fb86d331](https://github.com/angular/angular/commit/97fb86d3310ae891ba4d894a8d3479eda08bd4c2) | perf | set encapsulation to `None` for empty component styles ([#57130](https://github.com/angular/angular/pull/57130)) | | [c15ec36bd1](https://github.com/angular/angular/commit/c15ec36bd1dcff4c7c387337a5bcfd928994db2f) | refactor | remove deprecated `factories` Property in `KeyValueDiffers` ([#58064](https://github.com/angular/angular/pull/58064)) | +### elements +| Commit | Type | Description | +| -- | -- | -- | +| [fe5c4e086a](https://github.com/angular/angular/commit/fe5c4e086add655bf53315d71b0736ff758c7199) | fix | support `output()`-shaped outputs ([#57535](https://github.com/angular/angular/pull/57535)) | +| [0cebfd7462](https://github.com/angular/angular/commit/0cebfd7462c6a7c6c3b0d66720c436a4b0eea19d) | fix | switch to `ComponentRef.setInput` & remove custom scheduler ([#56728](https://github.com/angular/angular/pull/56728)) | +### forms +| Commit | Type | Description | +| -- | -- | -- | +| [3e7d724037](https://github.com/angular/angular/commit/3e7d724037cca4d256b1442eda20d6c6ad91d279) | feat | add ability to clear a FormRecord ([#50750](https://github.com/angular/angular/pull/50750)) | +| [18b6f3339f](https://github.com/angular/angular/commit/18b6f3339f46b37ee67fce2fa8a900cc73b2f23c) | fix | fix FormRecord type inference ([#50750](https://github.com/angular/angular/pull/50750)) | +### http +| Commit | Type | Description | +| -- | -- | -- | +| [4b9accdf16](https://github.com/angular/angular/commit/4b9accdf166f3990b3706de83ada15937fe786e2) | feat | promote `withRequestsMadeViaParent` to stable. ([#58221](https://github.com/angular/angular/pull/58221)) | +| [057cf7fb6b](https://github.com/angular/angular/commit/057cf7fb6bd2ac37a7a30d3a143e6737e386247f) | fix | preserve all headers from Headers object ([#57802](https://github.com/angular/angular/pull/57802)) | ### language-service | Commit | Type | Description | | -- | -- | -- | +| [8da9fb49b5](https://github.com/angular/angular/commit/8da9fb49b54e50de2d028691f73fb773def62ecd) | feat | add code fix for unused standalone imports ([#57605](https://github.com/angular/angular/pull/57605)) | +| [1f067f4507](https://github.com/angular/angular/commit/1f067f4507b6e908fe991d5de0dc4d3a627ab2f9) | feat | add code reactoring action to migrate `@Input` to signal-input ([#57214](https://github.com/angular/angular/pull/57214)) | +| [56ee47f2ec](https://github.com/angular/angular/commit/56ee47f2ec6e983e2ffdf59476ab29a92590811e) | feat | allow code refactorings to compute edits asynchronously ([#57214](https://github.com/angular/angular/pull/57214)) | | [bc83fc1e2e](https://github.com/angular/angular/commit/bc83fc1e2ebac1a99b6e8ed63cea48f48dd7c863) | feat | support converting to signal queries in VSCode extension ([#58106](https://github.com/angular/angular/pull/58106)) | +| [5c4305f024](https://github.com/angular/angular/commit/5c4305f0248ac3cc1adc76aebd3ef8af041039dc) | feat | support migrating full classes to signal inputs in VSCode ([#57975](https://github.com/angular/angular/pull/57975)) | +| [6342befff8](https://github.com/angular/angular/commit/6342befff8ee491f37e8912cccb0099bbbf01042) | feat | support migrating full classes to signal queries ([#58263](https://github.com/angular/angular/pull/58263)) | +| [7ecfd89592](https://github.com/angular/angular/commit/7ecfd8959219b6e2ec19e1244a6694711daf1782) | fix | The suppress diagnostics option should work for external templates ([#57873](https://github.com/angular/angular/pull/57873)) | ### localize | Commit | Type | Description | | -- | -- | -- | @@ -213,19 +381,76 @@ ### migrations | Commit | Type | Description | | -- | -- | -- | -| [c1aa411cf1](https://github.com/angular/angular/commit/c1aa411cf13259d991c8f224a2bafc3e9763fe8d) | fix | properly resolve tsconfig paths on windows ([#58137](https://github.com/angular/angular/pull/58137)) | -### platform-browser +| [dff4de0f75](https://github.com/angular/angular/commit/dff4de0f75741bc629462bb8da833b876c754453) | feat | add a combined migration for all signals APIs ([#58259](https://github.com/angular/angular/pull/58259)) | +| [b6bc93803c](https://github.com/angular/angular/commit/b6bc93803c246d47aac0d2d8619271d42b249a4a) | feat | add schematic to migrate to signal queries ([#58032](https://github.com/angular/angular/pull/58032)) | +| [2bfc64daf1](https://github.com/angular/angular/commit/2bfc64daf1cad9be8099759e8de7a361555ad5d1) | feat | expose output as function migration ([#58299](https://github.com/angular/angular/pull/58299)) | +| [59fe9bc772](https://github.com/angular/angular/commit/59fe9bc77236f1374427a851e55b0fa5216d9cf9) | feat | introduce signal input migration as `ng generate` schematic ([#57805](https://github.com/angular/angular/pull/57805)) | +| [90c7ec39a0](https://github.com/angular/angular/commit/90c7ec39a06e5c14711e0a42e2d6a478cde2b9cc) | fix | inject migration always inserting generated variables before super call ([#58393](https://github.com/angular/angular/pull/58393)) | +| [7a65cdd911](https://github.com/angular/angular/commit/7a65cdd911cbbf22445c916fc754d3a3304bc5fe) | fix | inject migration not inserting generated code after super call in some cases ([#58393](https://github.com/angular/angular/pull/58393)) | +| [c1aa411cf1](https://github.com/angular/angular/commit/c1aa411cf13259d991c8f224a2bafc3e9763fe8d) | fix | properly resolve tsconfig paths on windows ([#58137](https://github.com/angular/angular/pull/58137)) | +| [e26797b38e](https://github.com/angular/angular/commit/e26797b38efe0ac813601c10581f34b7591954c1) | fix | replace removed NgModules in tests with their exports ([#58627](https://github.com/angular/angular/pull/58627)) | +### platform-browser +| Commit | Type | Description | +| -- | -- | -- | +| [c36a1c023b](https://github.com/angular/angular/commit/c36a1c023b34f9b2056e1bef6364787e8495bfad) | fix | correctly add external stylesheets to ShadowDOM components ([#58482](https://github.com/angular/angular/pull/58482)) | +| [5c61f46409](https://github.com/angular/angular/commit/5c61f46409855bb8fe66d71a9c16c00753032987) | refactor | remove deprecated `BrowserModule.withServerTransition` method ([#58062](https://github.com/angular/angular/pull/58062)) | +### platform-server +| Commit | Type | Description | +| -- | -- | -- | +| [9e82559de4](https://github.com/angular/angular/commit/9e82559de4e99a1aedf645a05b01fc08d3f4b1b1) | fix | destroy `PlatformRef` when error happens during the `bootstrap()` phase ([#58112](https://github.com/angular/angular/pull/58112)) | +### router +| Commit | Type | Description | +| -- | -- | -- | +| [f271021e19](https://github.com/angular/angular/commit/f271021e190ede70bfd181d46f0a468a8e7fa144) | feat | Add `routerOutletData` input to `RouterOutlet` directive ([#57051](https://github.com/angular/angular/pull/57051)) | +| [b2790813a6](https://github.com/angular/angular/commit/b2790813a62e4dfdd77e27d1bb82201788476d06) | fix | Align RouterModule.forRoot errorHandler with provider error handler ([#57050](https://github.com/angular/angular/pull/57050)) | +| [a49c35ec76](https://github.com/angular/angular/commit/a49c35ec769461b9eb490719f0aa3e5aea8e243f) | fix | remove setter for `injector` on `OutletContext` ([#58343](https://github.com/angular/angular/pull/58343)) | +| [7436d3180e](https://github.com/angular/angular/commit/7436d3180ea5ad2c0b58d920bd45f8641a14cc8d) | fix | Update Resolve interface to include RedirectCommand like ResolveFn ([#57309](https://github.com/angular/angular/pull/57309)) | +### service-worker +| Commit | Type | Description | +| -- | -- | -- | +| [8ddce80a0b](https://github.com/angular/angular/commit/8ddce80a0bab4ebbd0f7db1c85ee27e4f0249db9) | feat | allow specifying maxAge for entire application ([#49601](https://github.com/angular/angular/pull/49601)) | +| [1479af978c](https://github.com/angular/angular/commit/1479af978cd2bbe4ee9f1ca9682684b8e5135fa7) | feat | finish implementation of refreshAhead feature ([#53356](https://github.com/angular/angular/pull/53356)) | + + + + +# 18.2.12 (2024-11-14) +### compiler-cli +| Commit | Type | Description | +| -- | -- | -- | +| [4c38160853](https://github.com/angular/angular/commit/4c3816085363614497eecf6b722a91e15e1b2051) | fix | correct extraction of generics from type aliases ([#58548](https://github.com/angular/angular/pull/58548)) | + + + + +# 18.2.11 (2024-11-06) +### core +| Commit | Type | Description | +| -- | -- | -- | +| [5f2d98a1b1](https://github.com/angular/angular/commit/5f2d98a1b1262a9cca84143fdf9829537138fc5c) | fix | avoid slow stringification when checking for duplicates in dev mode ([#58521](https://github.com/angular/angular/pull/58521)) | +| [3aa45a2fa1](https://github.com/angular/angular/commit/3aa45a2fa11ad568d12c622e0a9a94bbf1552118) | fix | resolve forward-referenced host directives during directive matching ([#58492](https://github.com/angular/angular/pull/58492)) ([#58500](https://github.com/angular/angular/pull/58500)) | + + + + +# 18.2.10 (2024-10-30) +### compiler | Commit | Type | Description | | -- | -- | -- | -| [5c61f46409](https://github.com/angular/angular/commit/5c61f46409855bb8fe66d71a9c16c00753032987) | refactor | remove deprecated `BrowserModule.withServerTransition` method ([#58062](https://github.com/angular/angular/pull/58062)) | -### platform-server +| [69dce38e778](https://github.com/angular/angular/commit/69dce38e778cb4c15aa06347031765a84e3ac6a5) | fix | transform pseudo selectors correctly for the encapsulated view. ([#58417](https://github.com/angular/angular/pull/58417)) | +### localize | Commit | Type | Description | | -- | -- | -- | -| [9e82559de4](https://github.com/angular/angular/commit/9e82559de4e99a1aedf645a05b01fc08d3f4b1b1) | fix | destroy `PlatformRef` when error happens during the `bootstrap()` phase ([#58112](https://github.com/angular/angular/pull/58112)) | -### service-worker +| [3b989ac5bd9](https://github.com/angular/angular/commit/3b989ac5bd951a3d28bcd0ada150fc81503a016a) | fix | Adding arb format to the list of valid formats in the localization extractor cli ([#58287](https://github.com/angular/angular/pull/58287)) | + + + + +# 18.2.9 (2024-10-23) +### compiler-cli | Commit | Type | Description | | -- | -- | -- | -| [1479af978c](https://github.com/angular/angular/commit/1479af978cd2bbe4ee9f1ca9682684b8e5135fa7) | feat | finish implementation of refreshAhead feature ([#53356](https://github.com/angular/angular/pull/53356)) | +| [b0ab653965](https://github.com/angular/angular/commit/b0ab653965cf88fcfde23fc6a6cc78ce3121a30f) | fix | report when NgModule imports or exports itself ([#58231](https://github.com/angular/angular/pull/58231)) | @@ -256,45 +481,6 @@ - -# 19.0.0-next.8 (2024-10-02) -### common -| Commit | Type | Description | -| -- | -- | -- | -| [c3115b882e](https://github.com/angular/angular/commit/c3115b882ebbe4f971e1f06bb1ce2cdf43327bb0) | fix | execute checks and remove placeholder when image is already loaded ([#55444](https://github.com/angular/angular/pull/55444)) | -| [b7bd429951](https://github.com/angular/angular/commit/b7bd42995130f80ea74ed5b905446f09a98a9480) | fix | prevent warning about oversize image twice ([#58021](https://github.com/angular/angular/pull/58021)) | -| [e8b2d5fad8](https://github.com/angular/angular/commit/e8b2d5fad8aece3b2e3f4a7ed1356642f68ee275) | fix | skip checking whether SVGs are oversized ([#57966](https://github.com/angular/angular/pull/57966)) | -### compiler-cli -| Commit | Type | Description | -| -- | -- | -- | -| [d9687f43dd](https://github.com/angular/angular/commit/d9687f43dd2ccfcf7dd3ee4f9066ce727f3224c6) | feat | 'strictStandalone' flag enforces standalone ([#57935](https://github.com/angular/angular/pull/57935)) | -| [39ccaf4cc4](https://github.com/angular/angular/commit/39ccaf4cc457894a3cf0455349e1c016a858751a) | fix | correctly get the type of nested function call expressions ([#57010](https://github.com/angular/angular/pull/57010)) | -| [33fe252c58](https://github.com/angular/angular/commit/33fe252c588ee94d6ef99e8070d35c483ec24fda) | fix | do not report unused declarations coming from an imported array ([#57940](https://github.com/angular/angular/pull/57940)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [3240598158](https://github.com/angular/angular/commit/32405981582030d7eb5d307f44b9c00fb384c480) | fix | provide flag to opt into manual cleanup for after render hooks ([#57917](https://github.com/angular/angular/pull/57917)) | -### http -| Commit | Type | Description | -| -- | -- | -- | -| [22dafa658b](https://github.com/angular/angular/commit/22dafa658bdf5536c3002e7f312efe0dfbc7236d) | fix | cleanup JSONP script listeners once loading completed ([#57877](https://github.com/angular/angular/pull/57877)) | -| [057cf7fb6b](https://github.com/angular/angular/commit/057cf7fb6bd2ac37a7a30d3a143e6737e386247f) | fix | preserve all headers from Headers object ([#57802](https://github.com/angular/angular/pull/57802)) | -### language-service -| Commit | Type | Description | -| -- | -- | -- | -| [5c4305f024](https://github.com/angular/angular/commit/5c4305f0248ac3cc1adc76aebd3ef8af041039dc) | feat | support migrating full classes to signal inputs in VSCode ([#57975](https://github.com/angular/angular/pull/57975)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [b6bc93803c](https://github.com/angular/angular/commit/b6bc93803c246d47aac0d2d8619271d42b249a4a) | feat | add schematic to migrate to signal queries ([#58032](https://github.com/angular/angular/pull/58032)) | -| [ef577b2d2b](https://github.com/angular/angular/commit/ef577b2d2b62ba0a48a120b9631296812e9049d3) | fix | delete constructor if it only has super call ([#58013](https://github.com/angular/angular/pull/58013)) | -### upgrade -| Commit | Type | Description | -| -- | -- | -- | -| [5f56a65837](https://github.com/angular/angular/commit/5f56a6583753f5aaff8a43e1e5f9a376433d0c0c) | fix | support input signal bindings ([#57020](https://github.com/angular/angular/pull/57020)) | - - - # 18.2.7 (2024-10-02) ### common @@ -326,80 +512,11 @@ - -# 19.0.0-next.7 (2024-09-25) -## Breaking Changes -### core -- Changes to effect timing which generally has two implications: - - * effects which are triggered outside of change detection run as part of - the change detection process instead of as a microtask. Depending on the - specifics of application/test setup, this can result in them executing - earlier or later (or requiring additional test steps to trigger; see below - examples). - - * effects which are triggered during change detection (e.g. by input - signals) run _earlier_, before the component's template. - - We've seen a few common failure cases: - - * Tests which used to rely on the `Promise` timing of effects now need to - `await whenStable()` or call `.detectChanges()` in order for effects to - run. - - * Tests which use faked clocks may need to fast-forward/flush the clock to - cause effects to run. - - * `effect()`s triggered during CD could rely on the application being fully - rendered (for example, they could easily read computed styles, etc). With - the change, they run before the component's updates and can get incorrect - answers. The recent `afterRenderEffect()` API is a natural replacement for - this style of effect. - - * `effect()`s which synchronize with the forms system are particularly - timing-sensitive and might need to adjust their initialization timing. -- `ExperimentalPendingTasks` has been renamed to - `PendingTasks`. -### core -| Commit | Type | Description | -| -- | -- | -- | -| [fc59e2a7b7](https://github.com/angular/angular/commit/fc59e2a7b7afa491a5ea740284a742574805eb36) | feat | change effect() execution timing & no-op `allowSignalWrites` ([#57874](https://github.com/angular/angular/pull/57874)) | -| [a7eff3ffaa](https://github.com/angular/angular/commit/a7eff3ffaaecbcb3034130d475ff7b4e41a1e1cc) | feat | mark signal-based query APIs as stable ([#57921](https://github.com/angular/angular/pull/57921)) | -| [a1f229850a](https://github.com/angular/angular/commit/a1f229850ad36da009f772faa831da173a60268c) | feat | migrate ExperimentalPendingTasks to PendingTasks ([#57533](https://github.com/angular/angular/pull/57533)) | -| [950a5540f1](https://github.com/angular/angular/commit/950a5540f15118e7360506ad82ec9dab5a11f789) | fix | Ensure the `ViewContext` is retained after closure minification ([#57903](https://github.com/angular/angular/pull/57903)) | -### language-service -| Commit | Type | Description | -| -- | -- | -- | -| [7ecfd89592](https://github.com/angular/angular/commit/7ecfd8959219b6e2ec19e1244a6694711daf1782) | fix | The suppress diagnostics option should work for external templates ([#57873](https://github.com/angular/angular/pull/57873)) | - - - # 18.2.6 (2024-09-25) - -# 19.0.0-next.6 (2024-09-18) -### compiler-cli -| Commit | Type | Description | -| -- | -- | -- | -| [f611faadfe](https://github.com/angular/angular/commit/f611faadfedd8dc2c3291da98e2c2c60fe3984bd) | fix | extended diagnostics not validating ICUs ([#57845](https://github.com/angular/angular/pull/57845)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [3ebe6b4ad4](https://github.com/angular/angular/commit/3ebe6b4ad401337e18619edc34477ae98226fa3e) | feat | Add async `run` method on `ExperimentalPendingTasks` ([#56546](https://github.com/angular/angular/pull/56546)) | -| [1b1519224d](https://github.com/angular/angular/commit/1b1519224d10c1cd25d05d7b958772b9adee1e1a) | feat | mark input, output and model APIs as stable ([#57804](https://github.com/angular/angular/pull/57804)) | -| [e5adf92965](https://github.com/angular/angular/commit/e5adf9296595644e415d5c147df08890be01ba77) | feat | stabilize `@let` syntax ([#57813](https://github.com/angular/angular/pull/57813)) | -| [4231e8f28f](https://github.com/angular/angular/commit/4231e8f28ffe8f55dddc2af0647b5b04fa8445d7) | fix | Handle `@let` declaration with array when `preparingForHydration` ([#57816](https://github.com/angular/angular/pull/57816)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [59fe9bc772](https://github.com/angular/angular/commit/59fe9bc77236f1374427a851e55b0fa5216d9cf9) | feat | introduce signal input migration as `ng generate` schematic ([#57805](https://github.com/angular/angular/pull/57805)) | -| [6144612940](https://github.com/angular/angular/commit/614461294041d7a2331bf7528907f37353205115) | fix | account for explicit standalone: false in migration ([#57803](https://github.com/angular/angular/pull/57803)) | - - - # 18.2.5 (2024-09-18) ### compiler-cli @@ -417,15 +534,6 @@ - -# 19.0.0-next.5 (2024-09-11) -### core -| Commit | Type | Description | -| -- | -- | -- | -| [c93b510f9b](https://github.com/angular/angular/commit/c93b510f9b2e23aa7a3848a04c05249fde14a9b1) | feat | allow passing `undefined` without needing to include it in the type argument of `input` ([#57621](https://github.com/angular/angular/pull/57621)) | - - - # 18.2.4 (2024-09-11) ### compiler @@ -439,60 +547,6 @@ - -# 19.0.0-next.4 (2024-09-09) -### compiler -| Commit | Type | Description | -| -- | -- | -- | -| [40ff18f87a](https://github.com/angular/angular/commit/40ff18f87a04fd1c00dea9fee1568bfe52acf25c) | fix | produce less noisy errors when parsing control flow ([#57711](https://github.com/angular/angular/pull/57711)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [6ea8e1e9aa](https://github.com/angular/angular/commit/6ea8e1e9aae028572873cf97aa1949c8153f458f) | feat | Add a schematics to migrate to `standalone: false`. ([#57643](https://github.com/angular/angular/pull/57643)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [cbec46a51d](https://github.com/angular/angular/commit/cbec46a51d22a1238cc8bf1ebdf343d031b8f0ba) | feat | migrate .pipe calls in outputs used in tests ([#57691](https://github.com/angular/angular/pull/57691)) | -| [68e5370a66](https://github.com/angular/angular/commit/68e5370a66633f4b069d6adffa95c2ea94291820) | feat | remove complete calls for migrated outputs ([#57671](https://github.com/angular/angular/pull/57671)) | -| [9da21f798d](https://github.com/angular/angular/commit/9da21f798de2033af9d39a8a9b255ef2fe74f948) | feat | replace .next usage on outputs ([#57654](https://github.com/angular/angular/pull/57654)) | -| [71f5ef2aa5](https://github.com/angular/angular/commit/71f5ef2aa53a74bab7d0543f98870d81c44c4978) | fix | change imports to be G3 compatible ([#57654](https://github.com/angular/angular/pull/57654)) | -| [3a264db866](https://github.com/angular/angular/commit/3a264db86611cba9b69780d7f01ee25787278320) | fix | properly handle comments in output migration ([#57691](https://github.com/angular/angular/pull/57691)) | -| [fc95a9adff](https://github.com/angular/angular/commit/fc95a9adff42da53dfeee5df8c42be25e8559708) | fix | replace leftover modules with their exports during pruning ([#57684](https://github.com/angular/angular/pull/57684)) | - - - - -# 19.0.0-next.3 (2024-09-04) -## Breaking Changes -### core -- * TypeScript versions less than 5.5 are no longer supported. -### compiler -| Commit | Type | Description | -| -- | -- | -- | -| [a2e4ee0cb3](https://github.com/angular/angular/commit/a2e4ee0cb3d40cadc05e28d58b06853973944456) | feat | add diagnostic for unused standalone imports ([#57605](https://github.com/angular/angular/pull/57605)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [8bcc663a53](https://github.com/angular/angular/commit/8bcc663a53888717cdf4ce0c23404caa00abb1b2) | feat | drop support for TypeScript 5.4 ([#57577](https://github.com/angular/angular/pull/57577)) | -| [e6e5d29e83](https://github.com/angular/angular/commit/e6e5d29e830a0a74d7677d5f2345f29391064853) | feat | initial version of the output migration ([#57604](https://github.com/angular/angular/pull/57604)) | -| [be2e49639b](https://github.com/angular/angular/commit/be2e49639bda831831ad62d49253db942a83fd46) | feat | introduce `afterRenderEffect` ([#57549](https://github.com/angular/angular/pull/57549)) | -### elements -| Commit | Type | Description | -| -- | -- | -- | -| [fe5c4e086a](https://github.com/angular/angular/commit/fe5c4e086add655bf53315d71b0736ff758c7199) | fix | support `output()`-shaped outputs ([#57535](https://github.com/angular/angular/pull/57535)) | -### http -| Commit | Type | Description | -| -- | -- | -- | -| [c2892fee58](https://github.com/angular/angular/commit/c2892fee58d28ffec0dfeaad6a5d6822c040cf03) | fix | Dynamicaly call the global fetch implementation ([#57531](https://github.com/angular/angular/pull/57531)) | -### language-service -| Commit | Type | Description | -| -- | -- | -- | -| [8da9fb49b5](https://github.com/angular/angular/commit/8da9fb49b54e50de2d028691f73fb773def62ecd) | feat | add code fix for unused standalone imports ([#57605](https://github.com/angular/angular/pull/57605)) | -| [1f067f4507](https://github.com/angular/angular/commit/1f067f4507b6e908fe991d5de0dc4d3a627ab2f9) | feat | add code refactoring action to migrate `@Input` to signal-input ([#57214](https://github.com/angular/angular/pull/57214)) | -| [56ee47f2ec](https://github.com/angular/angular/commit/56ee47f2ec6e983e2ffdf59476ab29a92590811e) | feat | allow code refactorings to compute edits asynchronously ([#57214](https://github.com/angular/angular/pull/57214)) | - - - # 18.2.3 (2024-09-04) ### http @@ -502,67 +556,6 @@ - -# 19.0.0-next.2 (2024-08-28) -## Breaking Changes -### core -- Render default fallback with empty `projectableNodes`. - - When passing an empty array to `projectableNodes` in the `createComponent` API, the default fallback content of the `ng-content` will be rendered if present. To prevent rendering the default content, pass `document.createTextNode('')` as a `projectableNode`. - - For example: - - ```ts - // The first ng-content will render the default fallback content if present - createComponent(MyComponent. { projectableNodes: [[], [secondNode]] }); - - // To prevent projecting the default fallback content: - createComponent(MyComponent. { projectableNodes: [[document.createTextNode('')], [secondNode]] }); - - ``` -- The timers that are used for zone coalescing and hybrid - mode scheduling (which schedules an application state synchronization - when changes happen outside the Angular zone) will now run in the zone - above Angular rather than the root zone. This will mostly affect tests - which use `fakeAsync`: these timers will now be visible to `fakeAsync` - and can be affected by `tick` or `flush`. -### elements -- as part of switching away from custom CD behavior to the - hybrid scheduler, timing of change detection around custom elements has - changed subtly. These changes make elements more efficient, but can cause - tests which encoded assumptions about how or when elements would be checked - to require updating. -### common -| Commit | Type | Description | -| -- | -- | -- | -| [50f08e6c4bf](https://github.com/angular/angular/commit/50f08e6c4bf1caeeb08d3505ce7fabd466b9c76b) | feat | automatically use sizes auto in NgOptimizedImage ([#57479](https://github.com/angular/angular/pull/57479)) | -### compiler-cli -| Commit | Type | Description | -| -- | -- | -- | -| [4716c3b9660](https://github.com/angular/angular/commit/4716c3b9660b01f4ef3642fb774270b7f4a13d1a) | perf | reduce duplicate component style resolution ([#57502](https://github.com/angular/angular/pull/57502)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [a3cdbfe87f5](https://github.com/angular/angular/commit/a3cdbfe87f5a8daef11a154ef3edb5a3a5c12f77) | fix | avoid leaking memory if component throws during creation ([#57546](https://github.com/angular/angular/pull/57546)) | -| [7a99815146e](https://github.com/angular/angular/commit/7a99815146eb78074aa3ed6db73c6e87042df692) | fix | Do not bubble capture events. ([#57476](https://github.com/angular/angular/pull/57476)) | -| [7b1e5be20b9](https://github.com/angular/angular/commit/7b1e5be20b99c88246c6be78a4dcd64eb55cee1a) | fix | fallback to default ng-content with empty projectable nodes. ([#57480](https://github.com/angular/angular/pull/57480)) | -| [0300dd2e18f](https://github.com/angular/angular/commit/0300dd2e18f064f2f57f7371e0dc5c01218b5019) | fix | Fix fixture.detectChanges with autoDetect disabled and zoneless ([#57416](https://github.com/angular/angular/pull/57416)) | -| [226a67dabba](https://github.com/angular/angular/commit/226a67dabba90a488ad09ce7bb026b8883c90d4a) | fix | Schedulers run in zone above Angular rather than root ([#57553](https://github.com/angular/angular/pull/57553)) | -### elements -| Commit | Type | Description | -| -- | -- | -- | -| [0cebfd7462c](https://github.com/angular/angular/commit/0cebfd7462c6a7c6c3b0d66720c436a4b0eea19d) | fix | switch to `ComponentRef.setInput` & remove custom scheduler ([#56728](https://github.com/angular/angular/pull/56728)) | -### router -| Commit | Type | Description | -| -- | -- | -- | -| [8f6308457f0](https://github.com/angular/angular/commit/8f6308457f0f03e9bbd09f4bc10d1c61fd41d22c) | fix | Do not unnecessarily run matcher twice on route matching ([#57530](https://github.com/angular/angular/pull/57530)) | -### upgrade -| Commit | Type | Description | -| -- | -- | -- | -| [c9d90786d0a](https://github.com/angular/angular/commit/c9d90786d0a6421bbb21b9d1649d031b34e3fa5d) | fix | Address Trusted Types violations in @angular/upgrade ([#57454](https://github.com/angular/angular/pull/57454)) | - - - # 18.2.2 (2024-08-28) ### core @@ -601,75 +594,6 @@ - -# 19.0.0-next.1 (2024-08-22) -## Breaking Changes -### core -- The `autoDetect` feature of `ComponentFixture` will now - attach the fixture to the `ApplicationRef`. As a result, errors during - automatic change detection of the fixture be reported to the `ErrorHandler`. - This change may cause custom error handlers to observe new failures that were previously unreported. -### compiler-cli -| Commit | Type | Description | -| -- | -- | -- | -| [9e87593055](https://github.com/angular/angular/commit/9e87593055a5314a67090bd15d5552c23b538050) | feat | ensure template style elements are preprocessed as inline styles ([#57429](https://github.com/angular/angular/pull/57429)) | -### core -| Commit | Type | Description | -| -- | -- | -- | -| [b063468027](https://github.com/angular/angular/commit/b0634680272569501146bb7a9cdfe53033e25971) | feat | support TypeScript 5.6 ([#57424](https://github.com/angular/angular/pull/57424)) | -| [3b0dca75d6](https://github.com/angular/angular/commit/3b0dca75d6dab6039253edd00c436715775bd0dd) | fix | Allow zoneless scheduler to run inside `fakeAsync` ([#56932](https://github.com/angular/angular/pull/56932)) | -| [f03d274e87](https://github.com/angular/angular/commit/f03d274e87c919514a70d02c0699523957de7386) | fix | ComponentFixture autoDetect feature works like production ([#55228](https://github.com/angular/angular/pull/55228)) | -| [d4449fce21](https://github.com/angular/angular/commit/d4449fce21bebbb18f9e1341f1675cdbec7e83ac) | fix | handle hydration of components that project content conditionally ([#57383](https://github.com/angular/angular/pull/57383)) | -### migrations -| Commit | Type | Description | -| -- | -- | -- | -| [4ae66f25d0](https://github.com/angular/angular/commit/4ae66f25d01ffd603872b3df3faf5c5c555b6446) | fix | account for members with doc strings and no modifiers ([#57389](https://github.com/angular/angular/pull/57389)) | -| [ac93839d69](https://github.com/angular/angular/commit/ac93839d694929fdf16c610994a369d4efb2823a) | fix | avoid migrating route component in tests ([#57317](https://github.com/angular/angular/pull/57317)) | -| [58a79b6e43](https://github.com/angular/angular/commit/58a79b6e435f2a46a7ab17ff5538083e05340b6f) | fix | preserve type when using inject decorator ([#57389](https://github.com/angular/angular/pull/57389)) | - - - - -# 19.0.0-next.0 (2024-08-14) -## Breaking Changes -### core -- Errors that are thrown during `ApplicationRef.tick` - will now be rethrown when using `TestBed`. These errors should be - resolved by ensuring the test environment is set up correctly to - complete change detection successfully. There are two alternatives to - catch the errors: - - * Instead of waiting for automatic change detection to happen, trigger - it synchronously and expect the error. For example, a jasmine test - could write `expect(() => TestBed.inject(ApplicationRef).tick()).toThrow()` - * `TestBed` will reject any outstanding `ComponentFixture.whenStable` promises. A jasmine test, - for example, could write `expectAsync(fixture.whenStable()).toBeRejected()`. - - As a last resort, you can configure errors to _not_ be rethrown by - setting `rethrowApplicationErrors` to `false` in `TestBed.configureTestingModule`. -### router -- The `Router.errorHandler` property has been removed. - Adding an error handler should be configured in either - `withNavigationErrorHandler` with `provideRouter` or the `errorHandler` - property in the extra options of `RouterModule.forRoot`. In addition, - the error handler cannot be used to change the return value of the - router navigation promise or prevent it from rejecting. Instead, if you - want to prevent the promise from rejecting, use `resolveNavigationPromiseOnError`. -- The return type of the `Resolve` interface now includes - `RedirectCommand`. -### core -| Commit | Type | Description | -| -- | -- | -- | -| [468d3fb9b1](https://github.com/angular/angular/commit/468d3fb9b1c3dd6dff86afcb6d8f89cc4c29b24b) | fix | rethrow errors during ApplicationRef.tick in TestBed ([#57200](https://github.com/angular/angular/pull/57200)) | -### router -| Commit | Type | Description | -| -- | -- | -- | -| [f271021e19](https://github.com/angular/angular/commit/f271021e190ede70bfd181d46f0a468a8e7fa144) | feat | Add `routerOutletData` input to `RouterOutlet` directive ([#57051](https://github.com/angular/angular/pull/57051)) | -| [b2790813a6](https://github.com/angular/angular/commit/b2790813a62e4dfdd77e27d1bb82201788476d06) | fix | Align RouterModule.forRoot errorHandler with provider error handler ([#57050](https://github.com/angular/angular/pull/57050)) | -| [7436d3180e](https://github.com/angular/angular/commit/7436d3180ea5ad2c0b58d920bd45f8641a14cc8d) | fix | Update Resolve interface to include RedirectCommand like ResolveFn ([#57309](https://github.com/angular/angular/pull/57309)) | - - - # 18.2.0 (2024-08-14) ### compiler diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9d69f1f8c89a..a8703ba25ab6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -260,7 +260,7 @@ The `` and `` fields are mandatory, the `()` field is opti Must be one of the following: * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) -* **ci**: Changes to our CI configuration files and scripts (examples: CircleCi, SauceLabs) +* **ci**: Changes to our CI configuration files and scripts (examples: Github Actions, SauceLabs) * **docs**: Documentation only changes * **feat**: A new feature * **fix**: A bug fix diff --git a/README.md b/README.md index b004d505801e..5bbc1ee6eab6 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,6 @@

- - CI status -   Angular on npm   @@ -35,12 +32,6 @@

-

- - InsightsSnapshot - -

-
## Documentation diff --git a/WORKSPACE b/WORKSPACE index 734dbad7c057..8c22277c9ffb 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -143,10 +143,10 @@ cldr_xml_data_repository( # sass rules http_archive( name = "io_bazel_rules_sass", - sha256 = "c23cef10c9d681683518500468340918b51f740acfbff841d6fa5673e72b2594", - strip_prefix = "rules_sass-c37592308cb40f682edc6e265bf2dfc4408d0988", + sha256 = "1b11ce2e7ced21522c83e6c64e9256eb18cd8d89afb8a69e18e6f3e2d3a138a8", + strip_prefix = "rules_sass-df7d2a95e1fa6e15bdb8a796756e276b2289f29a", urls = [ - "https://github.com/bazelbuild/rules_sass/archive/c37592308cb40f682edc6e265bf2dfc4408d0988.zip", + "https://github.com/bazelbuild/rules_sass/archive/df7d2a95e1fa6e15bdb8a796756e276b2289f29a.zip", ], ) diff --git a/adev/README.md b/adev/README.md index 866cb90b8f2b..08ab121e9fee 100644 --- a/adev/README.md +++ b/adev/README.md @@ -36,7 +36,7 @@ And if you're new, check out one of our issues labeled as [help wanted](htt ### Code of Conduct -Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md). +Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct](/CODE_OF_CONDUCT.md). ## FAQs diff --git a/adev/firebase.json b/adev/firebase.json index 90c4e52f566b..a466bc3ae922 100644 --- a/adev/firebase.json +++ b/adev/firebase.json @@ -3,6 +3,7 @@ "target": "angular-docs", "public": "./build/browser", "ignore": ["**/.*"], + "trailingSlash": false, "headers": [ { "source": "assets/images/tutorials/common/*.jpg", @@ -18,7 +19,7 @@ ] }, { - "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)", + "source": "/**/*-[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z].*", "headers": [ { "key": "Cache-Control", diff --git a/adev/karma.conf.js b/adev/karma.conf.js index 43a121e1067b..b16317906617 100644 --- a/adev/karma.conf.js +++ b/adev/karma.conf.js @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html diff --git a/adev/scripts/update-cli-help/index.mjs b/adev/scripts/update-cli-help/index.mjs index 0731f3702a6e..e36fc9344d03 100644 --- a/adev/scripts/update-cli-help/index.mjs +++ b/adev/scripts/update-cli-help/index.mjs @@ -1,3 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +//tslint:disable:no-console import {execSync} from 'node:child_process'; import {readFile, writeFile, readdir, mkdtemp, realpath, copyFile, unlink} from 'node:fs/promises'; import {tmpdir} from 'node:os'; diff --git a/adev/shared-docs/components/algolia-icon/algolia-icon.component.ts b/adev/shared-docs/components/algolia-icon/algolia-icon.component.ts index e6ef6973255e..ff8baec99c85 100644 --- a/adev/shared-docs/components/algolia-icon/algolia-icon.component.ts +++ b/adev/shared-docs/components/algolia-icon/algolia-icon.component.ts @@ -10,7 +10,6 @@ import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ selector: 'docs-algolia-icon', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], templateUrl: './algolia-icon.component.html', diff --git a/adev/shared-docs/components/breadcrumb/breadcrumb.component.ts b/adev/shared-docs/components/breadcrumb/breadcrumb.component.ts index f4b09c35e4af..df1cbabbe27e 100644 --- a/adev/shared-docs/components/breadcrumb/breadcrumb.component.ts +++ b/adev/shared-docs/components/breadcrumb/breadcrumb.component.ts @@ -13,7 +13,6 @@ import {RouterLink} from '@angular/router'; @Component({ selector: 'docs-breadcrumb', - standalone: true, imports: [RouterLink], templateUrl: './breadcrumb.component.html', styleUrls: ['./breadcrumb.component.scss'], diff --git a/adev/shared-docs/components/cookie-popup/cookie-popup.component.ts b/adev/shared-docs/components/cookie-popup/cookie-popup.component.ts index 4e0dd8b25cf5..ce9b704badf2 100644 --- a/adev/shared-docs/components/cookie-popup/cookie-popup.component.ts +++ b/adev/shared-docs/components/cookie-popup/cookie-popup.component.ts @@ -19,7 +19,6 @@ export const STORAGE_KEY = 'docs-accepts-cookies'; @Component({ selector: 'docs-cookie-popup', - standalone: true, templateUrl: './cookie-popup.component.html', styleUrls: ['./cookie-popup.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.spec.ts b/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.spec.ts index e8f430c72b19..bb8be5d1435d 100644 --- a/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.spec.ts +++ b/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.spec.ts @@ -120,7 +120,6 @@ describe('CopySourceCodeButton', () => { `, imports: [CopySourceCodeButton], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, }) class CodeSnippetWrapper { code = signal(''); diff --git a/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.ts b/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.ts index 9e11c2764f5d..6d9919d5b276 100644 --- a/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.ts +++ b/adev/shared-docs/components/copy-source-code-button/copy-source-code-button.component.ts @@ -24,7 +24,6 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 2000; @Component({ selector: 'button[docs-copy-source-code]', - standalone: true, imports: [CommonModule, IconComponent], templateUrl: './copy-source-code-button.component.html', host: { diff --git a/adev/shared-docs/components/icon/icon.component.html b/adev/shared-docs/components/icon/icon.component.html index 6dbc74306383..40b372640339 100644 --- a/adev/shared-docs/components/icon/icon.component.html +++ b/adev/shared-docs/components/icon/icon.component.html @@ -1 +1 @@ - + diff --git a/adev/shared-docs/components/icon/icon.component.ts b/adev/shared-docs/components/icon/icon.component.ts index 452a9064ac68..9f40be927681 100644 --- a/adev/shared-docs/components/icon/icon.component.ts +++ b/adev/shared-docs/components/icon/icon.component.ts @@ -18,7 +18,6 @@ import { @Component({ selector: 'docs-icon', - standalone: true, templateUrl: './icon.component.html', styleUrl: './icon.component.scss', host: { diff --git a/adev/shared-docs/components/navigation-list/navigation-list.component.html b/adev/shared-docs/components/navigation-list/navigation-list.component.html index 66987a0dac46..404d762bdf85 100644 --- a/adev/shared-docs/components/navigation-list/navigation-list.component.html +++ b/adev/shared-docs/components/navigation-list/navigation-list.component.html @@ -1,76 +1,75 @@ - + diff --git a/adev/shared-docs/components/navigation-list/navigation-list.component.scss b/adev/shared-docs/components/navigation-list/navigation-list.component.scss index 2df35c706eb8..3a4c3097a304 100644 --- a/adev/shared-docs/components/navigation-list/navigation-list.component.scss +++ b/adev/shared-docs/components/navigation-list/navigation-list.component.scss @@ -45,10 +45,6 @@ border: 0; } - .docs-navigation-link-hidden { - display: none; - } - .docs-nav-item-has-icon { &::after { // FIXME: for some reason this disappears when transformed @@ -77,7 +73,9 @@ font-family: var(--inter-font); line-height: 160%; letter-spacing: -0.00875rem; - transition: color 0.3s ease, background 0.3s ease; + transition: + color 0.3s ease, + background 0.3s ease; text-align: left; // forces left alignment of text in button &.docs-secondary-nav-button-active { diff --git a/adev/shared-docs/components/navigation-list/navigation-list.component.spec.ts b/adev/shared-docs/components/navigation-list/navigation-list.component.spec.ts index b1d58bbc86b9..6c2fb4e9011b 100644 --- a/adev/shared-docs/components/navigation-list/navigation-list.component.spec.ts +++ b/adev/shared-docs/components/navigation-list/navigation-list.component.spec.ts @@ -11,7 +11,7 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {NavigationList} from './navigation-list.component'; import {By} from '@angular/platform-browser'; import {NavigationItem} from '../../interfaces'; -import {RouterTestingModule} from '@angular/router/testing'; +import {provideRouter} from '@angular/router'; import {provideExperimentalZonelessChangeDetection, signal} from '@angular/core'; import {NavigationState} from '../../services'; @@ -37,18 +37,21 @@ describe('NavigationList', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [NavigationList, RouterTestingModule], + imports: [NavigationList], providers: [ + provideRouter([]), {provide: NavigationState, useClass: FakeNavigationListState}, provideExperimentalZonelessChangeDetection(), ], }).compileComponents(); fixture = TestBed.createComponent(NavigationList); + fixture.componentRef.setInput('navigationItems', []); + component = fixture.componentInstance; }); it('should display provided navigation structure', () => { - component.navigationItems = [...navigationItems]; + fixture.componentRef.setInput('navigationItems', [...navigationItems]); fixture.detectChanges(); const links = fixture.debugElement.queryAll(By.css('a')); @@ -59,7 +62,7 @@ describe('NavigationList', () => { }); it('should append `docs-navigation-list-dropdown` when isDropdownView is true', () => { - component.isDropdownView = true; + fixture.componentRef.setInput('isDropdownView', true); fixture.detectChanges(); const ulElement = fixture.debugElement.query(By.css('ul.docs-navigation-list-dropdown')); @@ -68,7 +71,7 @@ describe('NavigationList', () => { }); it('should not append `docs-navigation-list-dropdown` when isDropdownView is false', () => { - component.isDropdownView = false; + fixture.componentRef.setInput('isDropdownView', false); fixture.detectChanges(); const ulElement = fixture.debugElement.query(By.css('ul.docs-navigation-list-dropdown')); @@ -78,7 +81,7 @@ describe('NavigationList', () => { it('should emit linkClicked when user clicked on link', () => { const emitClickOnLinkSpy = spyOn(component, 'emitClickOnLink'); - component.navigationItems = [...navigationItems]; + fixture.componentRef.setInput('navigationItems', [...navigationItems]); fixture.detectChanges(true); const guideLink = fixture.debugElement.query(By.css('a[href="/guide"]')); @@ -102,7 +105,7 @@ describe('NavigationList', () => { const toggleItemSpy = spyOn(navigationState, 'toggleItem'); const itemToToggle = navigationItems[1]; - component.expandableLevel = 1; + fixture.componentRef.setInput('expandableLevel', 1); component.toggle(itemToToggle); expect(toggleItemSpy).toHaveBeenCalledOnceWith(itemToToggle); @@ -113,30 +116,40 @@ describe('NavigationList', () => { const toggleItemSpy = spyOn(navigationState, 'toggleItem'); const itemToToggle = navigationItems[1].children![1]; - component.collapsableLevel = 2; + fixture.componentRef.setInput('collapsableLevel', 2); component.toggle(itemToToggle); expect(toggleItemSpy).toHaveBeenCalledOnceWith(itemToToggle); }); - it('should display items to provided level', () => { - component.navigationItems = [...navigationItems]; - component.displayItemsToLevel = 1; + it('should display only items to provided level (Level 1)', () => { + fixture.componentRef.setInput('navigationItems', [...navigationItems]); + fixture.componentRef.setInput('displayItemsToLevel', 1); + fixture.detectChanges(true); + + const items = fixture.debugElement.queryAll(By.css('li.docs-faceted-list-item')); + + expect(items.length).toBe(2); + expect(items[0].nativeElement.innerText).toBe(navigationItems[0].label); + expect(items[1].nativeElement.innerText).toBe(navigationItems[1].label); + }); + + it('should display all items (Level 2)', () => { + fixture.componentRef.setInput('navigationItems', [...navigationItems]); + fixture.componentRef.setInput('displayItemsToLevel', 2); fixture.detectChanges(true); - const visibleItems = fixture.debugElement.queryAll( - By.css('li.docs-faceted-list-item:not(.docs-navigation-link-hidden)'), - ); - const hiddenItems = fixture.debugElement.queryAll( - By.css('li.docs-faceted-list-item.docs-navigation-link-hidden'), - ); - - expect(visibleItems.length).toBe(2); - expect(visibleItems[0].nativeElement.innerText).toBe(navigationItems[0].label); - expect(visibleItems[1].nativeElement.innerText).toBe(navigationItems[1].label); - expect(hiddenItems.length).toBe(2); - expect(hiddenItems[0].nativeElement.innerText).toBe(navigationItems[1].children![0].label); - expect(hiddenItems[1].nativeElement.innerText).toBe(navigationItems[1].children![1].label); + const items = fixture.debugElement.queryAll(By.css('li.docs-faceted-list-item')); + + expect(items.length).toBe(4); + + expect(items[0].nativeElement.innerText).toBe(navigationItems[0].label); + expect(items[1].nativeElement.innerText.startsWith(navigationItems[1].label)).toBeTrue(); + + const secondItemChildren = navigationItems[1].children || []; + + expect(items[2].nativeElement.innerText).toBe(secondItemChildren[0].label); + expect(items[3].nativeElement.innerText).toBe(secondItemChildren[1].label); }); }); diff --git a/adev/shared-docs/components/navigation-list/navigation-list.component.ts b/adev/shared-docs/components/navigation-list/navigation-list.component.ts index 535d12f5706d..d5a26d2a5135 100644 --- a/adev/shared-docs/components/navigation-list/navigation-list.component.ts +++ b/adev/shared-docs/components/navigation-list/navigation-list.component.ts @@ -6,41 +6,39 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ChangeDetectionStrategy, Component, Input, inject, output} from '@angular/core'; +import {ChangeDetectionStrategy, Component, inject, input, output} from '@angular/core'; import {NavigationItem} from '../../interfaces/index'; import {NavigationState} from '../../services/index'; import {RouterLink, RouterLinkActive} from '@angular/router'; -import {CommonModule} from '@angular/common'; import {IconComponent} from '../icon/icon.component'; import {IsActiveNavigationItem} from '../../pipes/is-active-navigation-item.pipe'; +import {NgTemplateOutlet} from '@angular/common'; @Component({ selector: 'docs-navigation-list', - standalone: true, - imports: [CommonModule, RouterLink, RouterLinkActive, IconComponent, IsActiveNavigationItem], + imports: [RouterLink, RouterLinkActive, IconComponent, IsActiveNavigationItem, NgTemplateOutlet], templateUrl: './navigation-list.component.html', styleUrls: ['./navigation-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class NavigationList { - @Input({required: true}) navigationItems: NavigationItem[] = []; - @Input() displayItemsToLevel: number = 2; - @Input() collapsableLevel: number | undefined = undefined; - @Input() expandableLevel: number = 2; - @Input() isDropdownView = false; + readonly navigationItems = input.required(); + readonly displayItemsToLevel = input(2); + readonly collapsableLevel = input(); + readonly expandableLevel = input(2); + readonly isDropdownView = input(false); readonly linkClicked = output(); private readonly navigationState = inject(NavigationState); - expandedItems = this.navigationState.expandedItems; - activeItem = this.navigationState.activeNavigationItem; + readonly activeItem = this.navigationState.activeNavigationItem; toggle(item: NavigationItem): void { if ( item.level === 1 && - item.level !== this.expandableLevel && - item.level !== this.collapsableLevel + item.level !== this.expandableLevel() && + item.level !== this.collapsableLevel() ) { return; } diff --git a/adev/shared-docs/components/search-dialog/search-dialog.component.html b/adev/shared-docs/components/search-dialog/search-dialog.component.html index 72a41dce50b8..4ed336bf0376 100644 --- a/adev/shared-docs/components/search-dialog/search-dialog.component.html +++ b/adev/shared-docs/components/search-dialog/search-dialog.component.html @@ -7,7 +7,7 @@ (ngModelChange)="updateSearchQuery($event)" class="docs-search-input" placeholder="Search docs" - > + /> @if (searchResults() && searchResults()!.length > 0) {
diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.scss b/adev/src/app/features/references/api-items-section/api-items-section.component.scss index 6f08851303d4..dec2b9080fc1 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.scss +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.scss @@ -57,39 +57,16 @@ margin-block-start: 3px; } - // star icon - .adev-api-items-section-item-featured { - opacity: 0; - border: none; - background-color: transparent; - color: var(--quinary-contrast); - cursor: pointer; - margin-top: -2px; - transition: color 0.3s ease; - - &-active { - opacity: 1; - color: var(--quinary-contrast); - } - } - &:hover { border-inline-start: 1px solid var(--quaternary-contrast); background-color: var(--septenary-contrast); a { color: var(--primary-contrast); } - .adev-api-items-section-item-featured-active { - color: var(--tertiary-contrast); - } } } } -.adev-api-item-in-featured-section { - display: none; -} - .adev-api-items-section-item { display: flex; flex: 1; @@ -106,9 +83,8 @@ margin-inline-start: 0.5rem; } - .adev-api-anchor { color: inherit; @include anchor.docs-anchor(); -} \ No newline at end of file +} diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts b/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts index 0df7f995ba32..7a79182ecfc5 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.spec.ts @@ -12,88 +12,51 @@ import ApiItemsSection from './api-items-section.component'; import {ApiItemsGroup} from '../interfaces/api-items-group'; import {ApiReferenceManager} from '../api-reference-list/api-reference-manager.service'; import {ApiItemType} from '../interfaces/api-item-type'; -import {RouterTestingModule} from '@angular/router/testing'; +import {provideRouter} from '@angular/router'; import {By} from '@angular/platform-browser'; describe('ApiItemsSection', () => { let component: ApiItemsSection; let fixture: ComponentFixture; - let apiReferenceManagerSpy: jasmine.SpyObj; - const fakeFeaturedGroup: ApiItemsGroup = { - title: 'Featured', - id: 'featured', - isFeatured: true, + const fakeGroup: ApiItemsGroup = { + title: 'Group', + id: 'group', items: [ - { - title: 'Fake Featured Title', - itemType: ApiItemType.CLASS, - url: 'api/fakeFeaturedTitle', - isFeatured: true, - }, { title: 'Fake Deprecated Title', itemType: ApiItemType.CONST, url: 'api/fakeDeprecatedTitle', - isFeatured: false, isDeprecated: true, }, { title: 'Fake Standard Title', itemType: ApiItemType.DIRECTIVE, url: 'api/fakeTitle', - isFeatured: false, }, ], }; - const fakeGroup: ApiItemsGroup = { - title: 'Example group', - id: 'example', - isFeatured: false, - items: [ - {title: 'Fake Title', itemType: ApiItemType.CONST, url: 'api/fakeTitle', isFeatured: false}, - ], - }; - beforeEach(() => { TestBed.configureTestingModule({ - imports: [ApiItemsSection, RouterTestingModule], - providers: [{provide: ApiReferenceManager, useValue: apiReferenceManagerSpy}], + imports: [ApiItemsSection], + providers: [provideRouter([])], }); fixture = TestBed.createComponent(ApiItemsSection); component = fixture.componentInstance; }); - it('should render star icon for featured group', () => { - fixture.componentRef.setInput('group', fakeFeaturedGroup); - fixture.detectChanges(); - - const starIcon = fixture.debugElement.query(By.css('.adev-api-items-section-header docs-icon')); - - expect(starIcon).toBeTruthy(); - }); - - it('should not render star icon for standard group', () => { - fixture.componentRef.setInput('group', fakeGroup); - fixture.detectChanges(); - - const starIcon = fixture.debugElement.query(By.css('.adev-api-items-section-header docs-icon')); - - expect(starIcon).toBeFalsy(); - }); - it('should render list of all APIs of provided group', () => { - fixture.componentRef.setInput('group', fakeFeaturedGroup); + fixture.componentRef.setInput('group', fakeGroup); fixture.detectChanges(); const apis = fixture.debugElement.queryAll(By.css('.adev-api-items-section-grid li')); - expect(apis.length).toBe(3); + expect(apis.length).toBe(2); }); it('should display deprecated icon for deprecated API', () => { - fixture.componentRef.setInput('group', fakeFeaturedGroup); + fixture.componentRef.setInput('group', fakeGroup); fixture.detectChanges(); const deprecatedApiIcons = fixture.debugElement.queryAll( @@ -105,18 +68,4 @@ describe('ApiItemsSection', () => { expect(deprecatedApiIcons[0]).toBeTruthy(); expect(deprecatedApiTitle?.nativeElement.innerText).toBe('Fake Deprecated Title'); }); - - it('should display star icon for featured API', () => { - fixture.componentRef.setInput('group', fakeFeaturedGroup); - fixture.detectChanges(); - - const featuredApiIcons = fixture.debugElement.queryAll( - By.css('.adev-api-items-section-grid li .adev-api-items-section-item-featured'), - ); - const featuredApiTitle = featuredApiIcons[0].parent?.query(By.css('.adev-item-title')); - - expect(featuredApiIcons.length).toBe(1); - expect(featuredApiIcons[0]).toBeTruthy(); - expect(featuredApiTitle?.nativeElement.innerText).toBe('Fake Featured Title'); - }); }); diff --git a/adev/src/app/features/references/api-items-section/api-items-section.component.ts b/adev/src/app/features/references/api-items-section/api-items-section.component.ts index 0244a42fe467..8833dbc0ff10 100644 --- a/adev/src/app/features/references/api-items-section/api-items-section.component.ts +++ b/adev/src/app/features/references/api-items-section/api-items-section.component.ts @@ -10,12 +10,10 @@ import {ChangeDetectionStrategy, Component, input} from '@angular/core'; import {RouterLink} from '@angular/router'; import {ApiItemsGroup} from '../interfaces/api-items-group'; import ApiItemLabel from '../api-item-label/api-item-label.component'; -import {IconComponent} from '@angular/docs'; @Component({ selector: 'adev-api-items-section', - standalone: true, - imports: [ApiItemLabel, RouterLink, IconComponent], + imports: [ApiItemLabel, RouterLink], templateUrl: './api-items-section.component.html', styleUrls: ['./api-items-section.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html index 5cc6a813f6ca..d61fcf0aa8c9 100644 --- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html +++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.html @@ -1,7 +1,13 @@ -
+
- + @for (tab of tabs(); track tab.url) {
@@ -12,12 +18,12 @@
-@if (isApiTabActive() && membersMarginTopInPx() > 0) { +@if (isApiTabActive()) { + (contentLoaded)="membersCardsLoaded()" + > } diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss index bdc261749683..a2ac3ac78023 100644 --- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss +++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.scss @@ -5,9 +5,7 @@ gap: 1rem; width: 100%; box-sizing: border-box; - @include mq.for-desktop-down { - flex-direction: column; - } + flex-direction: column; h1 { font-size: 1.5rem; @@ -34,32 +32,14 @@ .adev-header-and-tabs { padding: var(--layout-padding) 0 1rem var(--layout-padding); box-sizing: border-box; + width: 100%; + max-width: var(--page-width); - @include mq.for-desktop-up { - // this section should only be independently scrollable if - // the API tab is active & the screen is wide enough - &:has(.docs-reference-api-tab), - &.adev-cli-content { - position: sticky; - top: 0; - padding-inline-end: 1rem; - max-height: 100vh; - height: max-content; - overflow-y: scroll; - width: 50%; - } - &:has(.docs-reference-api-tab) { - width: 60%; - } - &:not(:has(.docs-reference-api-tab)) { - width: 100%; - max-width: var(--page-width); - } - } @include mq.for-desktop-down { padding: var(--layout-padding); - width: 100%; + max-width: none; } + &::-webkit-scrollbar-thumb { background-color: var(--septenary-contrast); border-radius: 10px; @@ -117,13 +97,6 @@ button { transition: background-color 0.3s ease; - font-family: monospace; - - &.line { - font-weight: 400; - text-align: left; - padding-block: 0.25rem; - } &.shiki-ln-line-highlighted { background-color: var(--senary-contrast); @@ -164,17 +137,15 @@ .docs-reference-members-container { width: 40%; - padding: 0 var(--layout-padding) 1rem 0; box-sizing: border-box; - max-width: 60ch; + width: 100%; + max-width: var(--page-width); + padding: 0 0 1rem var(--layout-padding); + @include mq.for-desktop-down { - width: 100%; padding: var(--layout-padding); + padding-top: 0; max-width: none; - // override javascript-applied margin-top: - // determined by how much space the sticky header takes up - // to align the selected card member with the code block - margin-block-start: 0 !important; } } @@ -272,7 +243,7 @@ header { display: flex; flex-direction: column; - border-radius: 0.25rem; + border-radius: 0.25rem 0.25rem 0 0; background-color: var(--octonary-contrast); position: relative; z-index: 10; @@ -301,9 +272,12 @@ h3 { display: inline-block; font-family: var(--code-font); - font-size: 1.25rem; + font-size: 1rem; letter-spacing: -0.025rem; margin: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; } code, diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts index 8c57874ce9e9..7bc5a6a40bc3 100644 --- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts +++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.spec.ts @@ -10,11 +10,11 @@ import {HarnessLoader} from '@angular/cdk/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; import {TestBed} from '@angular/core/testing'; import {MatTabGroupHarness} from '@angular/material/tabs/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {provideNoopAnimations} from '@angular/platform-browser/animations'; import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service'; import {signal} from '@angular/core'; -import {provideRouter} from '@angular/router'; -import {RouterTestingHarness, RouterTestingModule} from '@angular/router/testing'; +import {provideRouter, withComponentInputBinding} from '@angular/router'; +import {RouterTestingHarness} from '@angular/router/testing'; import ApiReferenceDetailsPage from './api-reference-details-page.component'; import {By} from '@angular/platform-browser'; @@ -40,20 +40,24 @@ describe('ApiReferenceDetailsPage', () => { beforeEach(async () => { TestBed.configureTestingModule({ - imports: [ApiReferenceDetailsPage, RouterTestingModule, NoopAnimationsModule], + imports: [ApiReferenceDetailsPage], providers: [ - provideRouter([ - { - path: '**', - component: ApiReferenceDetailsPage, - data: { - 'docContent': { - id: 'id', - contents: SAMPLE_CONTENT_WITH_TABS, + provideNoopAnimations(), + provideRouter( + [ + { + path: '**', + component: ApiReferenceDetailsPage, + data: { + 'docContent': { + id: 'id', + contents: SAMPLE_CONTENT_WITH_TABS, + }, }, }, - }, - ]), + ], + withComponentInputBinding(), + ), ], }); TestBed.overrideProvider(ReferenceScrollHandler, {useValue: fakeApiReferenceScrollHandler}); diff --git a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts index 1a4dcf94639a..5593f197fba6 100644 --- a/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts +++ b/adev/src/app/features/references/api-reference-details-page/api-reference-details-page.component.ts @@ -6,14 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import { - ChangeDetectionStrategy, - Component, - afterNextRender, - inject, - input, - computed, -} from '@angular/core'; +import {ChangeDetectionStrategy, Component, inject, input, computed} from '@angular/core'; import {DOCUMENT} from '@angular/common'; import {MatTabsModule} from '@angular/material/tabs'; import {DocContent, DocViewer} from '@angular/docs'; @@ -26,14 +19,12 @@ import { API_REFERENCE_TAB_ATTRIBUTE, API_REFERENCE_TAB_API_LABEL, API_TAB_CLASS_NAME, - API_REFERENCE_TAB_BODY_CLASS_NAME, API_REFERENCE_TAB_URL_ATTRIBUTE, } from '../constants/api-reference-prerender.constants'; import {AppScroller} from '../../../app-scroller'; @Component({ selector: 'adev-reference-page', - standalone: true, imports: [DocViewer, MatTabsModule], templateUrl: './api-reference-details-page.component.html', styleUrls: ['./api-reference-details-page.component.scss'], @@ -52,7 +43,6 @@ export default class ApiReferenceDetailsPage { // aliases ApiItemType = ApiItemType; - membersMarginTopInPx = this.scrollHandler.membersMarginTopInPx; // computed state parsedDocContent = computed(() => { @@ -108,13 +98,6 @@ export default class ApiReferenceDetailsPage { constructor() { this.appScroller.disableScrolling = true; - afterNextRender({ - write: () => { - if (this.isApiTabActive()) { - this.scrollHandler.updateMembersMarginTop(API_REFERENCE_TAB_BODY_CLASS_NAME); - } - }, - }); } ngOnDestroy() { diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.html b/adev/src/app/features/references/api-reference-list/api-reference-list.component.html index 90c7eef5b4c4..f8d9d9d14c62 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.html +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.html @@ -1,43 +1,38 @@ -
-
-
Getting Started
-

API Reference

-
- - @if (featuredGroup().items.length) { - - } -
-
    - @for (itemType of itemTypes; track itemType) { -
  • - - {{ itemType | adevApiLabel : 'full' }} -
  • - } -
+
+

API Reference

+
+
+
-
- -
+ +
- @for (group of filteredGroups(); track group.id) { - - } @empty { -
-

No API items found.

-
+

Filter by Identifier type

+
    + @for (itemType of itemTypes; track itemType) { +
  • + + {{ itemType | adevApiLabel : 'full' }} +
  • } - +
+ +@for (group of filteredGroups(); track group.id) { + +} @empty { +
+

No API items found.

+
+} diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.scss b/adev/src/app/features/references/api-reference-list/api-reference-list.component.scss index fc89ce01764b..f66c591508d9 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.scss +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.scss @@ -1,6 +1,7 @@ -.adev-reference-list-page { +:host { padding: var(--layout-padding); max-width: var(--page-width); + display: block; container: api-ref-page / inline-size; @@ -20,91 +21,86 @@ color: var(--secondary-contrast); } } -} -.adev-reference-list-legend { - display: grid; - grid-template-columns: repeat(6, 1fr); - margin-block-start: 0; - padding-inline: 0; - cursor: pointer; - width: 100%; + .adev-reference-list-filter { + border-block-end: 1px solid var(--senary-contrast); + padding-block-end: 2rem; + margin-block-end: 1rem; - @container api-ref-page (max-width: 775px) { - grid-template-columns: repeat(5, 1fr); - } - @container api-ref-page (max-width: 600px) { - grid-template-columns: repeat(4, 1fr); - } - @container api-ref-page (max-width: 500px) { - grid-template-columns: repeat(3, 1fr); - } - @container api-ref-page (max-width: 350px) { - grid-template-columns: repeat(2, 1fr); - } + .adev-reference-list-type-filter-label { + margin-block: 2.5rem 1rem; + } + + .adev-reference-list-type-filter { + display: grid; + grid-template-columns: repeat(6, 1fr); + margin-block: 0; + padding-inline: 0; + cursor: pointer; + width: 100%; + gap: 0.5rem; + + @container api-ref-page (max-width: 775px) { + grid-template-columns: repeat(5, 1fr); + } + @container api-ref-page (max-width: 600px) { + grid-template-columns: repeat(4, 1fr); + } + @container api-ref-page (max-width: 500px) { + grid-template-columns: repeat(3, 1fr); + } + @container api-ref-page (max-width: 350px) { + grid-template-columns: repeat(2, 1fr); + } - li { - display: flex; - gap: 0.5rem; - align-items: center; - padding: 0.3rem; - font-size: 0.875rem; - color: var(--quaternary-contrast); - font-weight: 500; - text-transform: capitalize; - border: 1px solid transparent; - border-radius: 0.25rem; - margin-inline-end: 0.5rem; - margin-block-end: 0.5rem; - transition: - color 0.3s ease, - background 0.3s ease, - border 0.3s ease; + li { + display: flex; + gap: 0.5rem; + align-items: center; + padding: 0.3rem; + font-size: 0.875rem; + color: var(--quaternary-contrast); + font-weight: 500; + text-transform: capitalize; + border: 1px solid transparent; + border-radius: 0.25rem; + transition: + color 0.3s ease, + background 0.3s ease, + border 0.3s ease; - &:hover { - color: var(--primary-contrast); - border: 1px solid var(--senary-contrast); + &:hover { + color: var(--primary-contrast); + border: 1px solid var(--senary-contrast); + } + + &.adev-reference-list-type-filter-item-active { + background: var(--septenary-contrast); + border: 1px solid var(--quinary-contrast); + color: var(--primary-contrast); + } + } } - &.adev-reference-list-legend-item-active { - background: var(--septenary-contrast); - border: 1px solid var(--quinary-contrast); - color: var(--primary-contrast); + .adev-reference-list-query-filter { + display: flex; + gap: 1.5rem; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; } } -} -.adev-reference-list-form { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - gap: 1.5rem; - padding-block-start: 1.5rem; - padding-block-end: 2rem; - border-block: 1px solid var(--senary-contrast); -} - -.adev-reference-list-form-part-two { - display: flex; - gap: 1.5rem; - flex-wrap: wrap; -} + .adev-reference-list-empty { + text-align: center; + margin-block-start: 2rem; -.adev-reference-list-empty { - text-align: center; - p { - font-size: 1rem; + p { + font-size: 1rem; + } } -} -.adev-featured-list { - display: block; - padding-block-end: 1rem; - margin-block: 1rem; - border-top: 0; - margin-top: -1.5rem; -} - -.docs-api-item-label-full { - white-space: nowrap; + .docs-api-item-label-full { + white-space: nowrap; + } } diff --git a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts index 1b784ad960bc..78f6d0a7f042 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-list.component.ts @@ -16,7 +16,7 @@ import { model, signal, viewChild, - afterRenderEffect, + afterNextRender, } from '@angular/core'; import ApiItemsSection from '../api-items-section/api-items-section.component'; import {FormsModule} from '@angular/forms'; @@ -32,7 +32,6 @@ export const ALL_STATUSES_KEY = 'All'; @Component({ selector: 'adev-reference-list', - standalone: true, imports: [ApiItemsSection, ApiItemLabel, FormsModule, SlideToggle, TextField, ApiLabel], templateUrl: './api-reference-list.component.html', styleUrls: ['./api-reference-list.component.scss'], @@ -51,21 +50,20 @@ export default class ApiReferenceList { itemTypes = Object.values(ApiItemType); // state - featuredGroup = this.apiReferenceManager.featuredGroup; // THINK: this is a shortcut - why would people write this? includeDeprecated = signal(false); // queries filterInput = viewChild.required(TextField, {read: ElementRef}); constructor() { - afterRenderEffect({ - write: () => { - // Lord forgive me for I have sinned - // Use the CVA to focus when https://github.com/angular/angular/issues/31133 is implemented - if (matchMedia('(hover: hover) and (pointer:fine)').matches) { + afterNextRender(() => { + // Lord forgive me for I have sinned + // Use the CVA to focus when https://github.com/angular/angular/issues/31133 is implemented + if (matchMedia('(hover: hover) and (pointer:fine)').matches) { + scheduleOnIdle(() => { this.filterInput().nativeElement.querySelector('input').focus(); - } - }, + }); + } }); effect(() => { @@ -91,7 +89,6 @@ export default class ApiReferenceList { .apiGroups() .map((group) => ({ title: group.title, - isFeatured: group.isFeatured, id: group.id, items: group.items.filter((apiItem) => { return ( @@ -110,3 +107,16 @@ export default class ApiReferenceList { this.type.update((currentType) => (currentType === itemType ? ALL_STATUSES_KEY : itemType)); } } + +/** + * Schedules a function to be run in a new macrotask. + * This is needed because the `requestIdleCallback` API is not available in all browsers. + * @param fn + */ +function scheduleOnIdle(fn: () => void): void { + if (typeof requestIdleCallback !== 'undefined') { + requestIdleCallback(fn); + } else { + setTimeout(fn, 0); + } +} diff --git a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts b/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts deleted file mode 100644 index 0f6c3404edf9..000000000000 --- a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {TestBed} from '@angular/core/testing'; - -import {ApiReferenceManager} from './api-reference-manager.service'; -import {LOCAL_STORAGE} from '@angular/docs'; - -describe('ApiReferenceManager', () => { - let service: ApiReferenceManager; - let localStorageSpy: jasmine.SpyObj; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - { - provide: LOCAL_STORAGE, - useValue: localStorageSpy, - }, - ], - }); - service = TestBed.inject(ApiReferenceManager); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.ts b/adev/src/app/features/references/api-reference-list/api-reference-manager.service.ts index 50dc6a7d9732..fda0260015d3 100644 --- a/adev/src/app/features/references/api-reference-list/api-reference-manager.service.ts +++ b/adev/src/app/features/references/api-reference-list/api-reference-manager.service.ts @@ -14,40 +14,12 @@ import {ApiItem} from '../interfaces/api-item'; import {ApiItemsGroup} from '../interfaces/api-items-group'; import {ApiManifest} from '../interfaces/api-manifest'; -export const FEATURED_API_ITEMS_KEY = 'apiFeaturedItems'; -export const FEATURED_GROUP_TITLE = 'Most Common'; - -export type FeaturedItemsByGroup = Record; - -export const FEATURED_ITEMS_URLS = [ - 'api/common/DatePipe', - 'api/common/NgIf', - 'api/common/NgFor', - 'api/common/NgClass', - 'api/core/ViewChild', - 'api/forms/NgModel', - 'api/router/RouterLink', - 'api/forms/FormControl', - 'api/common/http/HttpClient', - 'api/core/OnChanges', - 'api/forms/FormGroup', - 'api/router/CanActivate', -]; - const manifest = API_MANIFEST_JSON as ApiManifest; @Injectable({ providedIn: 'root', }) export class ApiReferenceManager { - // Represents group of the featured items. - featuredGroup = signal({ - title: FEATURED_GROUP_TITLE, - id: 'featured', - items: [], - isFeatured: true, - }); - apiGroups = signal(this.mapManifestToApiGroups()); private mapManifestToApiGroups(): ApiItemsGroup[] { @@ -60,23 +32,12 @@ export class ApiReferenceManager { items: module.entries .map((api) => { const url = getApiUrl(module, api.name); - const isFeatured = FEATURED_ITEMS_URLS.some((featuredUrl) => featuredUrl === url); - const apiItem = { + return { itemType: api.type, title: api.name, isDeprecated: !!api.isDeprecated, - isFeatured, url, }; - - if (isFeatured) { - this.featuredGroup.update((group) => { - group.items.push(apiItem); - return group; - }); - } - - return apiItem; }) .sort((a, b) => a.title.localeCompare(b.title)), }); diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss index a47eb078a0fc..e1d1fcc38e8d 100644 --- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss +++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.scss @@ -15,26 +15,25 @@ .adev-header-and-tabs { &.adev-cli-content { - width: 50%; + width: 100%; + max-width: var(--page-width); + @include mq.for-desktop-down { - width: 100%; + max-width: none; } } } .adev-cli-members-container { - width: 50%; - padding: 0 var(--layout-padding) 1rem 0; - - margin-top: 8.3125rem; + padding: 0 0 var(--layout-padding) var(--layout-padding); padding-bottom: 1rem; box-sizing: border-box; - max-width: 60ch; + max-width: var(--page-width); + @include mq.for-desktop-down { width: 100%; padding: var(--layout-padding); - // override js applied margin top - margin-block-start: 0 !important; + padding-top: 0; max-width: none; } } @@ -103,7 +102,10 @@ background: transparent; border-radius: 0.25rem; position: relative; - transition: color 0.3s ease, background 0.3s ease, border 0.3s ease; + transition: + color 0.3s ease, + background 0.3s ease, + border 0.3s ease; &:hover { color: var(--primary-contrast); diff --git a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts index 4f656153c91d..75414c361643 100644 --- a/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts +++ b/adev/src/app/features/references/cli-reference-details-page/cli-reference-details-page.component.ts @@ -27,7 +27,6 @@ export const CLI_TOC = '.adev-reference-cli-toc'; @Component({ selector: 'adev-cli-reference-page', - standalone: true, imports: [DocViewer], templateUrl: './cli-reference-details-page.component.html', styleUrls: [ @@ -45,7 +44,6 @@ export default class CliReferenceDetailsPage implements OnInit { cardsInnerHtml = signal(''); mainContentInnerHtml = signal(''); - membersMarginTopInPx = this.scrollHandler.membersMarginTopInPx; ngOnInit(): void { this.setPageContent(); @@ -53,7 +51,6 @@ export default class CliReferenceDetailsPage implements OnInit { contentLoaded(): void { this.scrollHandler.setupListeners(CLI_TOC); - this.scrollHandler.updateMembersMarginTop(CLI_TOC); } // Fetch the content for CLI Reference page based on the active route. diff --git a/adev/src/app/features/references/constants/api-reference-prerender.constants.ts b/adev/src/app/features/references/constants/api-reference-prerender.constants.ts index 7ec2777028d5..f0a9dd731e2b 100644 --- a/adev/src/app/features/references/constants/api-reference-prerender.constants.ts +++ b/adev/src/app/features/references/constants/api-reference-prerender.constants.ts @@ -6,16 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -export const API_REFERENCE_DETAILS_PAGE_CONTENT_CLASS_NAME = '.adev-reference-content'; export const API_REFERENCE_DETAILS_PAGE_HEADER_CLASS_NAME = '.docs-reference-header'; export const API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME = '.docs-reference-members-container'; -export const API_REFERENCE_TAB_BODY_CLASS_NAME = '.adev-reference-tab-body'; export const API_REFERENCE_TAB_ATTRIBUTE = 'data-tab'; export const API_REFERENCE_TAB_URL_ATTRIBUTE = 'data-tab-url'; export const API_REFERENCE_TAB_API_LABEL = 'API'; -export const API_REFERENCE_TAB_QUERY_PARAM = 'tab'; export const API_TAB_CLASS_NAME = '.docs-reference-api-tab'; -export const API_REFERENCE_MEMBER_CARD_CLASS_NAME = '.docs-reference-member-card'; -export const API_TAB_ACTIVE_CODE_LINE = 'shiki-ln-line-highlighted'; -export const HIGHLIGHT_JS_CODE_LINE_CLASS_NAME = 'shiki-ln-line'; export const MEMBER_ID_ATTRIBUTE = 'member-id'; diff --git a/adev/src/app/features/references/helpers/manifest.helper.ts b/adev/src/app/features/references/helpers/manifest.helper.ts index 3c6b00e066fa..b3ff2022990e 100644 --- a/adev/src/app/features/references/helpers/manifest.helper.ts +++ b/adev/src/app/features/references/helpers/manifest.helper.ts @@ -43,12 +43,10 @@ export function getApiNavigationItems(): NavigationItem[] { for (const packageEntry of manifest) { const packageNavigationItem: NavigationItem = { label: packageEntry.moduleLabel, - children: packageEntry.entries - .map((api) => ({ - path: getApiUrl(packageEntry, api.name), - label: api.name, - })) - .sort((a, b) => a.label.localeCompare(b.label)), + children: packageEntry.entries.map((api) => ({ + path: getApiUrl(packageEntry, api.name), + label: api.name, + })), }; apiNavigationItems.push(packageNavigationItem); diff --git a/adev/src/app/features/references/interfaces/api-items-group.ts b/adev/src/app/features/references/interfaces/api-items-group.ts index 2104b552d64b..f59577ecc5a1 100644 --- a/adev/src/app/features/references/interfaces/api-items-group.ts +++ b/adev/src/app/features/references/interfaces/api-items-group.ts @@ -12,5 +12,4 @@ export interface ApiItemsGroup { title: string; id: string; items: ApiItem[]; - isFeatured?: boolean; } diff --git a/adev/src/app/features/references/pipes/api-label.pipe.ts b/adev/src/app/features/references/pipes/api-label.pipe.ts index e1288601ccf5..55998f1a2920 100644 --- a/adev/src/app/features/references/pipes/api-label.pipe.ts +++ b/adev/src/app/features/references/pipes/api-label.pipe.ts @@ -12,7 +12,6 @@ import {ApiItemType} from '../interfaces/api-item-type'; @Pipe({ name: 'adevApiLabel', - standalone: true, }) export class ApiLabel implements PipeTransform { private readonly shortLabelsMap: Record = { diff --git a/adev/src/app/features/references/services/reference-scroll-handler.service.ts b/adev/src/app/features/references/services/reference-scroll-handler.service.ts index 6bad630401e2..afd894e9cafc 100644 --- a/adev/src/app/features/references/services/reference-scroll-handler.service.ts +++ b/adev/src/app/features/references/services/reference-scroll-handler.service.ts @@ -7,60 +7,32 @@ */ import {DOCUMENT, isPlatformBrowser} from '@angular/common'; -import { - DestroyRef, - EnvironmentInjector, - Injectable, - OnDestroy, - PLATFORM_ID, - afterNextRender, - inject, - signal, -} from '@angular/core'; +import {DestroyRef, Injectable, PLATFORM_ID, inject} from '@angular/core'; import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {fromEvent} from 'rxjs'; -import {auditTime} from 'rxjs/operators'; -import { - API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME, - API_REFERENCE_MEMBER_CARD_CLASS_NAME, - API_TAB_ACTIVE_CODE_LINE, - MEMBER_ID_ATTRIBUTE, -} from '../constants/api-reference-prerender.constants'; +import {MEMBER_ID_ATTRIBUTE} from '../constants/api-reference-prerender.constants'; import {WINDOW} from '@angular/docs'; import {Router} from '@angular/router'; import {AppScroller} from '../../../app-scroller'; -export const SCROLL_EVENT_DELAY = 20; -export const SCROLL_THRESHOLD = 20; +// Adds some space/margin between the top of the target element and the top of viewport. +const SCROLL_MARGIN_TOP = 100; @Injectable() -export class ReferenceScrollHandler implements OnDestroy { +export class ReferenceScrollHandler { private readonly destroyRef = inject(DestroyRef); private readonly document = inject(DOCUMENT); - private readonly injector = inject(EnvironmentInjector); private readonly window = inject(WINDOW); private readonly router = inject(Router); private readonly appScroller = inject(AppScroller); private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID)); - private readonly cardOffsetTop = new Map(); - private resizeObserver: ResizeObserver | null = null; - - membersMarginTopInPx = signal(0); - - ngOnDestroy(): void { - this.resizeObserver?.disconnect(); - } - setupListeners(tocSelector: string): void { if (!this.isBrowser) { return; } this.setupCodeToCListeners(tocSelector); - this.setupMemberCardListeners(); - this.setScrollEventHandlers(); - this.listenToResizeCardContainer(); this.setupFragmentChangeListener(); } @@ -76,22 +48,11 @@ export class ReferenceScrollHandler implements OnDestroy { } const card = this.document.getElementById(fragment) as HTMLDivElement | null; + card?.focus(); this.scrollToCard(card); }); } - updateMembersMarginTop(selectorOfTheElementToAlign: string): void { - if (!this.isBrowser) { - return; - } - - const elementToAlign = this.document.querySelector(selectorOfTheElementToAlign); - - if (elementToAlign) { - this.updateMarginTopWhenTabBodyIsResized(elementToAlign); - } - } - private setupCodeToCListeners(tocSelector: string): void { const tocContainer = this.document.querySelector(tocSelector); @@ -120,89 +81,6 @@ export class ReferenceScrollHandler implements OnDestroy { }); } - private setupMemberCardListeners(): void { - this.getAllMemberCards().forEach((card) => { - this.cardOffsetTop.set(card.id, card.offsetTop); - const header = card.querySelector('header'); - - if (!header) { - return; - } - fromEvent(header, 'click') - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((event) => { - const target = event.target as HTMLElement; - if (target instanceof HTMLAnchorElement) { - return; - } - - this.router.navigate([], {fragment: card.id, replaceUrl: true}); - }); - }); - } - - private setScrollEventHandlers(): void { - const scroll$ = fromEvent(this.document, 'scroll').pipe( - auditTime(SCROLL_EVENT_DELAY), - takeUntilDestroyed(this.destroyRef), - ); - - scroll$.subscribe(() => this.setActiveCodeLine()); - } - - private listenToResizeCardContainer(): void { - const membersCardContainer = this.document.querySelector( - API_REFERENCE_DETAILS_PAGE_MEMBERS_CLASS_NAME, - ); - if (membersCardContainer) { - afterNextRender( - () => { - const resizeObserver = new ResizeObserver(() => { - this.updateCardsOffsetTop(); - this.setActiveCodeLine(); - }); - resizeObserver.observe(membersCardContainer); - this.destroyRef.onDestroy(() => resizeObserver.disconnect()); - }, - {injector: this.injector}, - ); - } - } - - private setActiveCodeLine(): void { - const activeCard = Array.from(this.cardOffsetTop) - .filter(([_, offsetTop]) => { - return offsetTop < this.window.scrollY + this.membersMarginTopInPx() + SCROLL_THRESHOLD; - }) - .pop(); - - if (!activeCard) { - return; - } - - const activeLines = this.document.querySelectorAll( - `button.${API_TAB_ACTIVE_CODE_LINE}`, - ); - - const activeLine = activeLines.length > 0 ? activeLines.item(0) : null; - const previousActiveMemberId = this.getMemberId(activeLine); - const currentActiveMemberId = activeCard[0]; - - if (previousActiveMemberId && previousActiveMemberId !== currentActiveMemberId) { - for (const line of Array.from(activeLines)) { - line.classList.remove(API_TAB_ACTIVE_CODE_LINE); - } - } else { - const lines = this.document.querySelectorAll( - `button[${MEMBER_ID_ATTRIBUTE}="${currentActiveMemberId}"]`, - ); - for (const line of Array.from(lines)) { - line.classList.add(API_TAB_ACTIVE_CODE_LINE); - } - this.document.getElementById(`${currentActiveMemberId}`)?.focus({preventScroll: true}); - } - } - private scrollToCard(card: HTMLDivElement | null): void { if (!card) { return; @@ -213,23 +91,11 @@ export class ReferenceScrollHandler implements OnDestroy { } this.window.scrollTo({ - top: card!.offsetTop - this.membersMarginTopInPx(), + top: card!.offsetTop - SCROLL_MARGIN_TOP, behavior: 'smooth', }); } - private updateCardsOffsetTop(): void { - this.getAllMemberCards().forEach((card) => { - this.cardOffsetTop.set(card.id, card.offsetTop); - }); - } - - private getAllMemberCards(): NodeListOf { - return this.document.querySelectorAll( - `${API_REFERENCE_MEMBER_CARD_CLASS_NAME}`, - ); - } - private getMemberId(lineButton: HTMLButtonElement | null): string | undefined { if (!lineButton) { return undefined; @@ -237,19 +103,6 @@ export class ReferenceScrollHandler implements OnDestroy { return lineButton.attributes.getNamedItem(MEMBER_ID_ATTRIBUTE)?.value; } - private updateMarginTopWhenTabBodyIsResized(tabBody: HTMLElement): void { - this.resizeObserver?.disconnect(); - - this.resizeObserver = new ResizeObserver((_) => { - const offsetTop = tabBody.getBoundingClientRect().top; - if (offsetTop) { - this.membersMarginTopInPx.set(offsetTop); - } - }); - - this.resizeObserver.observe(tabBody); - } - private findButtonElement(element: HTMLElement) { let parent = element.parentElement; diff --git a/adev/src/app/features/tutorial/tutorial-navigation.scss b/adev/src/app/features/tutorial/tutorial-navigation.scss index 4884779ea4c7..a714087ae600 100644 --- a/adev/src/app/features/tutorial/tutorial-navigation.scss +++ b/adev/src/app/features/tutorial/tutorial-navigation.scss @@ -4,13 +4,6 @@ .adev-tutorial-nav-container { position: sticky; top: 0; - - @include mq.for-tablet-landscape-down { - top: 60px; - } - @include mq.for-phone-only { - top: 55px; - } width: 100%; background-color: var(--page-background); padding-block-start: var(--layout-padding); @@ -21,6 +14,14 @@ transition: background-color 0.3s ease; container: nav-container / inline-size; + @include mq.for-tablet-landscape-down { + top: 60px; + } + + @include mq.for-phone-only { + top: 55px; + } + &:has(.docs-reveal-answer-button) { @container tutorial-content (max-width: 430px) { padding-block-end: calc(1.5rem + 85px); diff --git a/adev/src/app/features/tutorial/tutorial.component.spec.ts b/adev/src/app/features/tutorial/tutorial.component.spec.ts index 07bc4cbf48ca..d0af41c957f3 100644 --- a/adev/src/app/features/tutorial/tutorial.component.spec.ts +++ b/adev/src/app/features/tutorial/tutorial.component.spec.ts @@ -6,12 +6,12 @@ * found in the LICENSE file at https://angular.dev/license */ -import {DOCS_VIEWER_SELECTOR, DocViewer, WINDOW} from '@angular/docs'; +import {DOCS_VIEWER_SELECTOR, DocViewer, WINDOW, TutorialConfig, TutorialType} from '@angular/docs'; import {Component, Input, signal} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; -import {RouterTestingModule} from '@angular/router/testing'; +import {provideNoopAnimations} from '@angular/platform-browser/animations'; +import {provideRouter} from '@angular/router'; import {of} from 'rxjs'; import { @@ -23,19 +23,16 @@ import { import {mockAsyncProvider} from '../../core/services/inject-async'; import Tutorial from './tutorial.component'; -import {TutorialConfig, TutorialType} from '@angular/docs'; @Component({ selector: EMBEDDED_EDITOR_SELECTOR, template: '
FakeEmbeddedEditor
', - standalone: true, }) class FakeEmbeddedEditor {} @Component({ selector: DOCS_VIEWER_SELECTOR, template: '
FakeDocsViewer
', - standalone: true, }) class FakeDocViewer { @Input('documentFilePath') documentFilePath: string | undefined; @@ -96,8 +93,10 @@ describe('Tutorial', () => { beforeEach(async () => { TestBed.configureTestingModule({ - imports: [Tutorial, RouterTestingModule, EmbeddedEditor, DocViewer, NoopAnimationsModule], + imports: [Tutorial, EmbeddedEditor, DocViewer], providers: [ + provideNoopAnimations(), + provideRouter([]), { provide: WINDOW, useValue: fakeWindow, diff --git a/adev/src/app/features/tutorial/tutorial.component.ts b/adev/src/app/features/tutorial/tutorial.component.ts index 4f910a6a1cd4..5ed9051ae2a2 100644 --- a/adev/src/app/features/tutorial/tutorial.component.ts +++ b/adev/src/app/features/tutorial/tutorial.component.ts @@ -30,6 +30,9 @@ import { IconComponent, NavigationItem, NavigationList, + TutorialType, + TutorialNavigationData, + TutorialNavigationItem, } from '@angular/docs'; import {ActivatedRoute, RouterLink} from '@angular/router'; import {filter} from 'rxjs/operators'; @@ -42,14 +45,11 @@ import { EmbeddedEditor, } from '../../editor/index'; import {SplitResizerHandler} from './split-resizer-handler.service'; -import {TutorialType} from '@angular/docs'; -import {TutorialNavigationData, TutorialNavigationItem} from '@angular/docs'; const INTRODUCTION_LABEL = 'Introduction'; @Component({ selector: 'adev-tutorial', - standalone: true, imports: [ NgComponentOutlet, NgTemplateOutlet, diff --git a/adev/src/app/features/update/recommendations.ts b/adev/src/app/features/update/recommendations.ts index 1d5fe99a9c3f..0ea9a1169991 100644 --- a/adev/src/app/features/update/recommendations.ts +++ b/adev/src/app/features/update/recommendations.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + export enum ApplicationComplexity { Basic = 1, Medium = 2, @@ -2397,4 +2405,136 @@ export const RECOMMENDATIONS: Step[] = [ action: 'You may experience tests failures if you have tests that rely on change detection execution order when using `ComponentFixture.autoDetect` because it now executes change detection for fixtures within `ApplicationRef.tick`. For example, this will cause test fixture to refresh before any dialogs that it creates whereas this may have been the other way around in the past.', }, + { + action: + "In the application's project directory, run `ng update @angular/core@19 @angular/cli@19` to update your application to Angular v19.", + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_ng_update', + }, + { + possibleIn: 1900, + necessaryAsOf: 1900, + level: ApplicationComplexity.Basic, + material: true, + step: 'update @angular/material', + action: 'Run `ng update @angular/material@19`.', + }, + { + action: + 'Angular directives, components and pipes are now standalone by default. Specify "standalone: false" for declarations that are currently declared in an NgModule. The Angular CLI will automatically update your code to reflect that.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-standalone-declarations', + }, + { + action: + 'Remove `this.` prefix when accessing template reference variables. For example, refactor `
{{ this.foo }}` to `
{{ foo }}`', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-this', + }, + { + action: + 'Replace usages of `BrowserModule.withServerTransition()` with injection of the `APP_ID` token to set the application `id` instead.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-browser-module-with-server-transition', + }, + { + action: 'The `factories` property in `KeyValueDiffers` has been removed.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-remove-key-value-differs-factories', + }, + { + action: + 'In angular.json, replace the "name" option with "project" for the `@angular/localize` builder.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_localize_builder_project_option', + }, + { + action: 'Rename `ExperimentalPendingTasks` to `PendingTasks`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0_rename_experimental_pending_tasks', + }, + { + action: + "Update tests that relied on the `Promise` timing of effects to use `await whenStable()` or call `.detectChanges()` to trigger effects. For effects triggered during change detection, ensure they don't depend on the application being fully rendered or consider using `afterRenderEffect()`. Tests using faked clocks may need to fast-forward/flush the clock.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.1', + }, + { + action: 'Upgrade to TypeScript version 5.5 or later.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0.2', + }, + { + action: + 'Update tests using `fakeAsync` that rely on specific timing of zone coalescing and scheduling when a change happens outside the Angular zone (hybrid mode scheduling) as these timers are now affected by `tick` and `flush`.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-timers-in-zone', + }, + { + action: + "When using `createComponent` API and not passing content for the first `ng-content`, provide `document.createTextNode('')` as a `projectableNode` to prevent rendering the default fallback content.", + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-render-default-fallback', + }, + { + action: + 'Update tests that rely on specific timing or ordering of change detection around custom elements, as the timing may have changed due to the switch to the hybrid scheduler.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-hybrid-scheduler-timing', + }, + { + action: + 'Migrate from using `Router.errorHandler` to `withNavigationErrorHandler` from `provideRouter` or `errorHandler` from `RouterModule.forRoot`.', + level: ApplicationComplexity.Basic, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-router-error-handler', + }, + { + action: + 'Update tests to handle errors thrown during `ApplicationRef.tick` by either triggering change detection synchronously or rejecting outstanding `ComponentFixture.whenStable` promises.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-testbed-error-handling', + }, + { + action: 'Update usages of `Resolve` interface to include `RedirectCommand` in its return type.', + level: ApplicationComplexity.Medium, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-resolve-interface-return-type', + }, + { + action: + '`fakeAsync` will flush pending timers by default. For tests that require the previous behavior, explicitly pass `{flush: false}` in the options parameter.', + level: ApplicationComplexity.Advanced, + necessaryAsOf: 1900, + possibleIn: 1900, + step: '19.0.0-update-fakeasync-to-flush-pending-timers', + }, ]; diff --git a/adev/src/app/features/update/update.component.ts b/adev/src/app/features/update/update.component.ts index 840f859cffd4..4adb7ea39fc8 100644 --- a/adev/src/app/features/update/update.component.ts +++ b/adev/src/app/features/update/update.component.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + import {ChangeDetectionStrategy, Component, HostListener, inject} from '@angular/core'; import {Step, RECOMMENDATIONS} from './recommendations'; import {Clipboard} from '@angular/cdk/clipboard'; @@ -30,7 +38,6 @@ interface Option { CdkMenuModule, IconComponent, ], - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export default class AppComponent { @@ -56,6 +63,7 @@ export default class AppComponent { protected afterRecommendations: Step[] = []; protected readonly versions = [ + {name: '19.0', number: 1900}, {name: '18.0', number: 1800}, {name: '17.0', number: 1700}, {name: '16.0', number: 1600}, @@ -93,7 +101,7 @@ export default class AppComponent { ]; protected from = this.versions.find((version) => version.name === '17.0')!; protected to = this.versions.find((version) => version.name === '18.0')!; - protected futureVersion = 1900; + protected futureVersion = 2000; protected readonly steps: Step[] = RECOMMENDATIONS; diff --git a/adev/src/app/routes.ts b/adev/src/app/routes.ts index 9a76f2bd3413..9b02021638b1 100644 --- a/adev/src/app/routes.ts +++ b/adev/src/app/routes.ts @@ -122,45 +122,97 @@ const FOOTER_ROUTES: Route[] = mapNavigationItemsToRoutes( const API_REFERENCE_ROUTES: Route[] = mapApiManifestToRoutes(); const REDIRECT_ROUTES: Route[] = [ + { + path: 'guide/defer', + redirectTo: '/guide/templates/defer', + }, + { + path: 'guide/components/importing', + redirectTo: '/guide/components/anatomy-of-components#using-components', + }, { path: 'guide/templates/attribute-binding', - redirectTo: 'guide/templates/binding#binding-dynamic-properties-and-attributes', + redirectTo: '/guide/templates/binding#binding-dynamic-properties-and-attributes', }, { path: 'guide/templates/interpolation', - redirectTo: 'guide/templates/binding#render-dynamic-text-with-text-interpolation', + redirectTo: '/guide/templates/binding#render-dynamic-text-with-text-interpolation', }, { path: 'guide/templates/class-binding', - redirectTo: 'guide/templates/binding#css-class-and-style-property-bindings', + redirectTo: '/guide/templates/binding#css-class-and-style-property-bindings', }, { path: 'guide/templates/event-binding', - redirectTo: 'guide/templates/event-listeners', + redirectTo: '/guide/templates/event-listeners', }, { path: 'guide/templates/let-template-variables', - redirectTo: 'guide/templates/variables#local-template-variables-with-let', + redirectTo: '/guide/templates/variables#local-template-variables-with-let', }, { path: 'guide/templates/property-binding', - redirectTo: 'guide/templates/binding#binding-dynamic-properties-and-attributes', + redirectTo: '/guide/templates/binding#binding-dynamic-properties-and-attributes', }, { path: 'guide/templates/property-binding-best-practices', - redirectTo: 'guide/templates/binding#binding-dynamic-properties-and-attributes', + redirectTo: '/guide/templates/binding#binding-dynamic-properties-and-attributes', }, { path: 'guide/templates/reference-variables', - redirectTo: 'guide/templates/variables#template-reference-variables', + redirectTo: '/guide/templates/variables#template-reference-variables', }, { path: 'guide/templates/svg-in-templates', - redirectTo: 'guide/templates/binding', + redirectTo: '/guide/templates/binding', }, { path: 'guide/templates/template-statements', - redirectTo: 'guide/templates/event-listeners', + redirectTo: '/guide/templates/event-listeners', + }, + { + path: 'guide/signals/rxjs-interop', + redirectTo: '/ecosystem/rxjs-interop', + }, + { + path: 'guide/components/output-function', + redirectTo: '/guide/components/outputs', + }, + { + path: 'guide/signals/queries', + redirectTo: '/guide/components/queries', + }, + { + path: 'guide/signals/model', + redirectTo: '/guide/signals/inputs', + }, + { + path: 'guide/signals/inputs', + redirectTo: '/guide/components/inputs', + }, + { + path: 'guide/ngmodules', + redirectTo: '/guide/ngmodules/overview', + }, + { + path: 'guide/ngmodules/providers', + redirectTo: '/guide/ngmodules/overview', + }, + { + path: 'guide/ngmodules/singleton-services', + redirectTo: '/guide/ngmodules/overview', + }, + { + path: 'guide/ngmodules/lazy-loading', + redirectTo: '/guide/ngmodules/overview', + }, + { + path: 'guide/ngmodules/faq', + redirectTo: '/guide/ngmodules/overview', + }, + { + path: 'guide/components/anatomy-of-components', + redirectTo: '/guide/components', }, { path: 'guide', diff --git a/adev/src/app/sub-navigation-data.ts b/adev/src/app/sub-navigation-data.ts index a2f9cfe50346..d08dbede1435 100644 --- a/adev/src/app/sub-navigation-data.ts +++ b/adev/src/app/sub-navigation-data.ts @@ -46,34 +46,24 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'introduction/essentials/overview', }, { - label: 'Composing with Components', + label: 'Composition with components', path: 'essentials/components', contentPath: 'introduction/essentials/components', }, { - label: 'Managing Dynamic Data', - path: 'essentials/managing-dynamic-data', - contentPath: 'introduction/essentials/managing-dynamic-data', + label: 'Reactivity with signals', + path: 'essentials/signals', + contentPath: 'introduction/essentials/signals', }, { - label: 'Rendering Dynamic Templates', - path: 'essentials/rendering-dynamic-templates', - contentPath: 'introduction/essentials/rendering-dynamic-templates', + label: 'Dynamic interfaces with templates', + path: 'essentials/templates', + contentPath: 'introduction/essentials/templates', }, { - label: 'Conditionals and Loops', - path: 'essentials/conditionals-and-loops', - contentPath: 'introduction/essentials/conditionals-and-loops', - }, - { - label: 'Handling User Interaction', - path: 'essentials/handling-user-interaction', - contentPath: 'introduction/essentials/handling-user-interaction', - }, - { - label: 'Sharing Logic', - path: 'essentials/sharing-logic', - contentPath: 'introduction/essentials/sharing-logic', + label: 'Modular design with dependency injection', + path: 'essentials/dependency-injection', + contentPath: 'introduction/essentials/dependency-injection', }, { label: 'Next Steps', @@ -91,6 +81,26 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ { label: 'In-depth Guides', children: [ + { + label: 'Signals', + children: [ + { + label: 'Overview', + path: 'guide/signals', + contentPath: 'guide/signals/overview', + }, + { + label: 'Dependent state with linkedSignal', + path: 'guide/signals/linked-signal', + contentPath: 'guide/signals/linked-signal', + }, + { + label: 'Async reactivity with resources', + path: 'guide/signals/resource', + contentPath: 'guide/signals/resource', + }, + ], + }, { label: 'Components', children: [ @@ -99,11 +109,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components', contentPath: 'guide/components/anatomy-of-components', }, - { - label: 'Importing and using components', - path: 'guide/components/importing', - contentPath: 'guide/components/importing', - }, { label: 'Selectors', path: 'guide/components/selectors', @@ -124,11 +129,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/components/outputs', contentPath: 'guide/components/outputs', }, - { - label: 'output() function', - path: 'guide/components/output-fn', - contentPath: 'guide/components/output-function', - }, { label: 'Content projection with ng-content', path: 'guide/components/content-projection', @@ -269,6 +269,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/directives/directive-composition-api', contentPath: 'guide/directives/directive-composition-api', }, + { + label: 'Optimizing images with NgOptimizedImage', + path: 'guide/image-optimization', + contentPath: 'guide/image-optimization', + }, ], }, { @@ -316,36 +321,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'Signals', - children: [ - { - label: 'Overview', - path: 'guide/signals', - contentPath: 'guide/signals/overview', - }, - { - label: 'RxJS Interop', - path: 'guide/signals/rxjs-interop', - contentPath: 'guide/signals/rxjs-interop', - }, - { - label: 'Inputs as signals', - path: 'guide/signals/inputs', - contentPath: 'guide/signals/inputs', - }, - { - label: 'Model inputs', - path: 'guide/signals/model', - contentPath: 'guide/signals/model', - }, - { - label: 'Queries as signals', - path: 'guide/signals/queries', - contentPath: 'guide/signals/queries', - }, - ], - }, { label: 'Routing', children: [ @@ -442,7 +417,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ ], }, { - label: 'Performance', + label: 'Server-side & hybrid-rendering', children: [ { label: 'Overview', @@ -450,17 +425,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ contentPath: 'guide/performance/overview', }, { - label: 'Deferrable views', - path: 'guide/defer', - contentPath: 'guide/defer', - }, - { - label: 'Image Optimization', - path: 'guide/image-optimization', - contentPath: 'guide/image-optimization', - }, - { - label: 'Server-side Rendering', + label: 'Server-side rendering', path: 'guide/ssr', contentPath: 'guide/ssr', }, @@ -469,11 +434,21 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'guide/prerendering', contentPath: 'guide/prerendering', }, + { + label: 'Hybrid rendering with server routing', + path: 'guide/hybrid-rendering', + contentPath: 'guide/hybrid-rendering', + }, { label: 'Hydration', path: 'guide/hydration', contentPath: 'guide/hydration', }, + { + label: 'Incremental Hydration', + path: 'guide/incremental-hydration', + contentPath: 'guide/incremental-hydration', + }, ], }, { @@ -586,36 +561,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'Animations', - children: [ - { - label: 'Overview', - path: 'guide/animations', - contentPath: 'guide/animations/overview', - }, - { - label: 'Transition and Triggers', - path: 'guide/animations/transition-and-triggers', - contentPath: 'guide/animations/transition-and-triggers', - }, - { - label: 'Complex Sequences', - path: 'guide/animations/complex-sequences', - contentPath: 'guide/animations/complex-sequences', - }, - { - label: 'Reusable Animations', - path: 'guide/animations/reusable-animations', - contentPath: 'guide/animations/reusable-animations', - }, - { - label: 'Route transition animations', - path: 'guide/animations/route-animations', - contentPath: 'guide/animations/route-animations', - }, - ], - }, { label: 'Experimental features', children: [ @@ -797,6 +742,56 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ { label: 'Extended Ecosystem', children: [ + { + label: 'NgModules', + path: 'guide/ngmodules/overview', + contentPath: 'guide/ngmodules/overview', + }, + { + label: 'Animations', + children: [ + { + label: 'Overview', + path: 'guide/animations', + contentPath: 'guide/animations/overview', + }, + { + label: 'Transition and Triggers', + path: 'guide/animations/transition-and-triggers', + contentPath: 'guide/animations/transition-and-triggers', + }, + { + label: 'Complex Sequences', + path: 'guide/animations/complex-sequences', + contentPath: 'guide/animations/complex-sequences', + }, + { + label: 'Reusable Animations', + path: 'guide/animations/reusable-animations', + contentPath: 'guide/animations/reusable-animations', + }, + { + label: 'Route transition animations', + path: 'guide/animations/route-animations', + contentPath: 'guide/animations/route-animations', + }, + ], + }, + { + label: 'Using RxJS with Angular', + children: [ + { + label: 'Signals interop', + path: 'ecosystem/rxjs-interop', + contentPath: 'ecosystem/rxjs-interop/signals-interop', + }, + { + label: 'Component output interop', + path: 'ecosystem/rxjs-interop/output-interop', + contentPath: 'ecosystem/rxjs-interop/output-interop', + }, + ], + }, { label: 'Service Workers & PWAs', children: [ @@ -950,7 +945,7 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'cli/cache', }, { - label: 'clear', + label: 'clean', path: 'cli/cache/clean', }, { @@ -1449,6 +1444,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'reference/migrations/signal-inputs', contentPath: 'reference/migrations/signal-inputs', }, + { + label: 'Outputs', + path: 'reference/migrations/outputs', + contentPath: 'reference/migrations/outputs', + }, { label: 'Signal queries', path: 'reference/migrations/signal-queries', @@ -1456,81 +1456,6 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ }, ], }, - { - label: 'Concepts', - children: [ - { - label: 'Overview', - path: 'reference/concepts', - contentPath: 'reference/concepts/overview', - }, - { - label: 'NgModule', - children: [ - { - label: 'Overview', - path: 'guide/ngmodules', - contentPath: 'guide/ngmodules/overview', - }, - { - label: 'JS Modules vs NgModules', - path: 'guide/ngmodules/vs-jsmodule', - contentPath: 'guide/ngmodules/vs-jsmodule', - }, - { - label: 'Launching your app with a root module', - path: 'guide/ngmodules/bootstrapping', - contentPath: 'guide/ngmodules/bootstrapping', - }, - { - label: 'Sharing NgModules', - path: 'guide/ngmodules/sharing', - contentPath: 'guide/ngmodules/sharing', - }, - { - label: 'Frequently used NgModules', - path: 'guide/ngmodules/frequent', - contentPath: 'guide/ngmodules/frequent', - }, - { - label: 'Feature modules', - path: 'guide/ngmodules/feature-modules', - contentPath: 'guide/ngmodules/feature-modules', - }, - { - label: 'Types of feature modules', - path: 'guide/ngmodules/module-types', - contentPath: 'guide/ngmodules/module-types', - }, - { - label: 'Providing dependencies', - path: 'guide/ngmodules/providers', - contentPath: 'guide/ngmodules/providers', - }, - { - label: 'Singleton services', - path: 'guide/ngmodules/singleton-services', - contentPath: 'guide/ngmodules/singleton-services', - }, - { - label: 'Lazy-loading feature modules', - path: 'guide/ngmodules/lazy-loading', - contentPath: 'guide/ngmodules/lazy-loading', - }, - { - label: 'NgModule API', - path: 'guide/ngmodules/api', - contentPath: 'guide/ngmodules/api', - }, - { - label: 'NgModule FAQs', - path: 'guide/ngmodules/faq', - contentPath: 'guide/ngmodules/faq', - }, - ], - }, - ], - }, ]; const FOOTER_NAVIGATION_DATA: NavigationItem[] = [ diff --git a/adev/src/assets/BUILD.bazel b/adev/src/assets/BUILD.bazel index 5af491b27336..99599093c287 100644 --- a/adev/src/assets/BUILD.bazel +++ b/adev/src/assets/BUILD.bazel @@ -10,6 +10,7 @@ copy_to_directory( "//adev/src/content/best-practices/runtime-performance", "//adev/src/content/cli/help:cli_docs", "//adev/src/content/ecosystem", + "//adev/src/content/ecosystem/rxjs-interop", "//adev/src/content/ecosystem/service-workers", "//adev/src/content/guide", "//adev/src/content/guide/animations", @@ -68,6 +69,8 @@ copy_to_directory( "//packages/router/testing:router_testing_docs", "//packages/router/upgrade:router_upgrade_docs", "//packages/service-worker:service-worker_docs", + "//packages/ssr:ssr_docs", + "//packages/ssr:ssr_node_docs", "//packages/upgrade:upgrade_docs", "//packages/upgrade/static:upgrade_static_docs", "//packages/upgrade/static/testing:upgrade_static_testing_docs", diff --git a/adev/src/content/api-examples/common/location/ts/e2e_test/location_component_spec.ts b/adev/src/content/api-examples/common/location/ts/e2e_test/location_component_spec.ts deleted file mode 100644 index 61831d2f453e..000000000000 --- a/adev/src/content/api-examples/common/location/ts/e2e_test/location_component_spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, protractor} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = (protractor).ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('Location', () => { - afterEach(verifyNoBrowserErrors); - - it('should verify paths', () => { - browser.get('/location/#/bar/baz'); - waitForElement('hash-location'); - expect(element.all(by.css('path-location code')).get(0).getText()).toEqual('/location'); - expect(element.all(by.css('hash-location code')).get(0).getText()).toEqual('/bar/baz'); - }); -}); diff --git a/adev/src/content/api-examples/common/location/ts/hash_location_component.ts b/adev/src/content/api-examples/common/location/ts/hash_location_component.ts deleted file mode 100644 index 22fd8b2f6dcb..000000000000 --- a/adev/src/content/api-examples/common/location/ts/hash_location_component.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion LocationComponent -import {HashLocationStrategy, Location, LocationStrategy} from '@angular/common'; -import {Component} from '@angular/core'; - -@Component({ - selector: 'hash-location', - providers: [Location, {provide: LocationStrategy, useClass: HashLocationStrategy}], - template: ` -

HashLocationStrategy

- Current URL is: - {{ location.path() }} -
- Normalize: - /foo/bar/ - is: - {{ location.normalize('foo/bar') }} -
- `, - standalone: false, -}) -export class HashLocationComponent { - location: Location; - constructor(location: Location) { - this.location = location; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/location/ts/module.ts b/adev/src/content/api-examples/common/location/ts/module.ts deleted file mode 100644 index 002450d39add..000000000000 --- a/adev/src/content/api-examples/common/location/ts/module.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {APP_BASE_HREF} from '@angular/common'; -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -import {HashLocationComponent} from './hash_location_component'; -import {PathLocationComponent} from './path_location_component'; - -@Component({ - selector: 'example-app', - template: ` - - - `, - standalone: false, -}) -export class AppComponent {} - -@NgModule({ - declarations: [AppComponent, PathLocationComponent, HashLocationComponent], - providers: [{provide: APP_BASE_HREF, useValue: '/'}], - imports: [BrowserModule], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/common/location/ts/path_location_component.ts b/adev/src/content/api-examples/common/location/ts/path_location_component.ts deleted file mode 100644 index 98d1a72d81e9..000000000000 --- a/adev/src/content/api-examples/common/location/ts/path_location_component.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion LocationComponent -import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common'; -import {Component} from '@angular/core'; - -@Component({ - selector: 'path-location', - providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}], - template: ` -

PathLocationStrategy

- Current URL is: - {{ location.path() }} -
- Normalize: - /foo/bar/ - is: - {{ location.normalize('foo/bar') }} -
- `, - standalone: false, -}) -export class PathLocationComponent { - location: Location; - constructor(location: Location) { - this.location = location; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/main.ts b/adev/src/content/api-examples/common/main.ts deleted file mode 100644 index 0c83c8112df0..000000000000 --- a/adev/src/content/api-examples/common/main.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {TestsAppModule} from './test_module'; - -platformBrowserDynamic().bootstrapModule(TestsAppModule); diff --git a/adev/src/content/api-examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts b/adev/src/content/api-examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts deleted file mode 100644 index b6ca7a0d84c1..000000000000 --- a/adev/src/content/api-examples/common/ngComponentOutlet/ts/e2e_test/ngComponentOutlet_spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, ExpectedConditions} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('ngComponentOutlet', () => { - const URL = '/ngComponentOutlet'; - afterEach(verifyNoBrowserErrors); - - describe('ng-component-outlet-example', () => { - it('should render simple', () => { - browser.get(URL); - waitForElement('ng-component-outlet-simple-example'); - expect(element.all(by.css('hello-world')).getText()).toEqual(['Hello World!']); - }); - }); -}); diff --git a/adev/src/content/api-examples/common/ngComponentOutlet/ts/module.ts b/adev/src/content/api-examples/common/ngComponentOutlet/ts/module.ts deleted file mode 100644 index c9f6ab3b94b9..000000000000 --- a/adev/src/content/api-examples/common/ngComponentOutlet/ts/module.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - Component, - Injectable, - Injector, - Input, - NgModule, - OnInit, - TemplateRef, - ViewChild, - ViewContainerRef, -} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -// #docregion SimpleExample -@Component({ - selector: 'hello-world', - template: 'Hello World!', - standalone: false, -}) -export class HelloWorld {} - -@Component({ - selector: 'ng-component-outlet-simple-example', - template: ` - - `, - standalone: false, -}) -export class NgComponentOutletSimpleExample { - // This field is necessary to expose HelloWorld to the template. - HelloWorld = HelloWorld; -} -// #enddocregion - -// #docregion CompleteExample -@Injectable() -export class Greeter { - suffix = '!'; -} - -@Component({ - selector: 'complete-component', - template: ` - {{ label }}: - - - {{ greeter.suffix }} - `, - standalone: false, -}) -export class CompleteComponent { - @Input() label!: string; - - constructor(public greeter: Greeter) {} -} - -@Component({ - selector: 'ng-component-outlet-complete-example', - template: ` - Ahoj - Svet - - `, - standalone: false, -}) -export class NgComponentOutletCompleteExample implements OnInit { - // This field is necessary to expose CompleteComponent to the template. - CompleteComponent = CompleteComponent; - - myInputs = {'label': 'Complete'}; - - myInjector: Injector; - @ViewChild('ahoj', {static: true}) ahojTemplateRef!: TemplateRef; - @ViewChild('svet', {static: true}) svetTemplateRef!: TemplateRef; - myContent?: any[][]; - - constructor( - injector: Injector, - private vcr: ViewContainerRef, - ) { - this.myInjector = Injector.create({ - providers: [{provide: Greeter, deps: []}], - parent: injector, - }); - } - - ngOnInit() { - // Create the projectable content from the templates - this.myContent = [ - this.vcr.createEmbeddedView(this.ahojTemplateRef).rootNodes, - this.vcr.createEmbeddedView(this.svetTemplateRef).rootNodes, - ]; - } -} -// #enddocregion - -@Component({ - selector: 'example-app', - template: ` - -
- - `, - standalone: false, -}) -export class AppComponent {} - -@NgModule({ - imports: [BrowserModule], - declarations: [ - AppComponent, - NgComponentOutletSimpleExample, - NgComponentOutletCompleteExample, - HelloWorld, - CompleteComponent, - ], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/common/ngIf/ts/e2e_test/ngIf_spec.ts b/adev/src/content/api-examples/common/ngIf/ts/e2e_test/ngIf_spec.ts deleted file mode 100644 index 5df495d07fc1..000000000000 --- a/adev/src/content/api-examples/common/ngIf/ts/e2e_test/ngIf_spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, ExpectedConditions} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('ngIf', () => { - const URL = '/ngIf'; - afterEach(verifyNoBrowserErrors); - - describe('ng-if-simple', () => { - let comp = 'ng-if-simple'; - it('should hide/show content', () => { - browser.get(URL); - waitForElement(comp); - expect(element.all(by.css(comp)).get(0).getText()).toEqual('hide show = true\nText to show'); - element(by.css(comp + ' button')).click(); - expect(element.all(by.css(comp)).get(0).getText()).toEqual('show show = false'); - }); - }); - - describe('ng-if-else', () => { - let comp = 'ng-if-else'; - it('should hide/show content', () => { - browser.get(URL); - waitForElement(comp); - expect(element.all(by.css(comp)).get(0).getText()).toEqual('hide show = true\nText to show'); - element(by.css(comp + ' button')).click(); - expect(element.all(by.css(comp)).get(0).getText()).toEqual( - 'show show = false\nAlternate text while primary text is hidden', - ); - }); - }); - - describe('ng-if-then-else', () => { - let comp = 'ng-if-then-else'; - - it('should hide/show content', () => { - browser.get(URL); - waitForElement(comp); - expect(element.all(by.css(comp)).get(0).getText()).toEqual( - 'hideSwitch Primary show = true\nPrimary text to show', - ); - element - .all(by.css(comp + ' button')) - .get(1) - .click(); - expect(element.all(by.css(comp)).get(0).getText()).toEqual( - 'hideSwitch Primary show = true\nSecondary text to show', - ); - element - .all(by.css(comp + ' button')) - .get(0) - .click(); - expect(element.all(by.css(comp)).get(0).getText()).toEqual( - 'showSwitch Primary show = false\nAlternate text while primary text is hidden', - ); - }); - }); - - describe('ng-if-as', () => { - let comp = 'ng-if-as'; - it('should hide/show content', () => { - browser.get(URL); - waitForElement(comp); - expect(element.all(by.css(comp)).get(0).getText()).toEqual( - 'Next User\nWaiting... (user is null)', - ); - element(by.css(comp + ' button')).click(); - expect(element.all(by.css(comp)).get(0).getText()).toEqual('Next User\nHello Smith, John!'); - }); - }); -}); diff --git a/adev/src/content/api-examples/common/ngIf/ts/module.ts b/adev/src/content/api-examples/common/ngIf/ts/module.ts deleted file mode 100644 index c0a6fe9028ee..000000000000 --- a/adev/src/content/api-examples/common/ngIf/ts/module.ts +++ /dev/null @@ -1,128 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule, OnInit, TemplateRef, ViewChild} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {Subject} from 'rxjs'; - -// #docregion NgIfSimple -@Component({ - selector: 'ng-if-simple', - template: ` - - show = {{ show }} -
-
Text to show
- `, - standalone: false, -}) -export class NgIfSimple { - show = true; -} -// #enddocregion - -// #docregion NgIfElse -@Component({ - selector: 'ng-if-else', - template: ` - - show = {{ show }} -
-
Text to show
- Alternate text while primary text is hidden - `, - standalone: false, -}) -export class NgIfElse { - show = true; -} -// #enddocregion - -// #docregion NgIfThenElse -@Component({ - selector: 'ng-if-then-else', - template: ` - - - show = {{ show }} -
-
this is ignored
- Primary text to show - Secondary text to show - Alternate text while primary text is hidden - `, - standalone: false, -}) -export class NgIfThenElse implements OnInit { - thenBlock: TemplateRef | null = null; - show = true; - - @ViewChild('primaryBlock', {static: true}) primaryBlock: TemplateRef | null = null; - @ViewChild('secondaryBlock', {static: true}) secondaryBlock: TemplateRef | null = null; - - switchPrimary() { - this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock; - } - - ngOnInit() { - this.thenBlock = this.primaryBlock; - } -} -// #enddocregion - -// #docregion NgIfAs -@Component({ - selector: 'ng-if-as', - template: ` - -
-
- Hello {{ user.last }}, {{ user.first }}! -
- Waiting... (user is {{ user | json }}) - `, - standalone: false, -}) -export class NgIfAs { - userObservable = new Subject<{first: string; last: string}>(); - first = ['John', 'Mike', 'Mary', 'Bob']; - firstIndex = 0; - last = ['Smith', 'Novotny', 'Angular']; - lastIndex = 0; - - nextUser() { - let first = this.first[this.firstIndex++]; - if (this.firstIndex >= this.first.length) this.firstIndex = 0; - let last = this.last[this.lastIndex++]; - if (this.lastIndex >= this.last.length) this.lastIndex = 0; - this.userObservable.next({first, last}); - } -} -// #enddocregion - -@Component({ - selector: 'example-app', - template: ` - -
- -
- -
- -
- `, - standalone: false, -}) -export class AppComponent {} - -@NgModule({ - imports: [BrowserModule], - declarations: [AppComponent, NgIfSimple, NgIfElse, NgIfThenElse, NgIfAs], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts b/adev/src/content/api-examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts deleted file mode 100644 index f1c55ef1f9fc..000000000000 --- a/adev/src/content/api-examples/common/ngTemplateOutlet/ts/e2e_test/ngTemplateOutlet_spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, ExpectedConditions} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('ngTemplateOutlet', () => { - const URL = '/ngTemplateOutlet'; - afterEach(verifyNoBrowserErrors); - - describe('ng-template-outlet-example', () => { - it('should render', () => { - browser.get(URL); - waitForElement('ng-template-outlet-example'); - expect(element.all(by.css('ng-template-outlet-example span')).getText()).toEqual([ - 'Hello', - 'Hello World!', - 'Ahoj Svet!', - ]); - }); - }); -}); diff --git a/adev/src/content/api-examples/common/ngTemplateOutlet/ts/module.ts b/adev/src/content/api-examples/common/ngTemplateOutlet/ts/module.ts deleted file mode 100644 index 019184e8bb3b..000000000000 --- a/adev/src/content/api-examples/common/ngTemplateOutlet/ts/module.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -// #docregion NgTemplateOutlet -@Component({ - selector: 'ng-template-outlet-example', - template: ` - -
- -
- -
- - Hello - - Hello {{ name }}! - - - Ahoj {{ person }}! - - `, - standalone: false, -}) -export class NgTemplateOutletExample { - myContext = {$implicit: 'World', localSk: 'Svet'}; -} -// #enddocregion - -@Component({ - selector: 'example-app', - template: ` - - `, - standalone: false, -}) -export class AppComponent {} - -@NgModule({ - imports: [BrowserModule], - declarations: [AppComponent, NgTemplateOutletExample], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/common/pipes/ts/async_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/async_pipe.ts deleted file mode 100644 index f4fb5bcf4042..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/async_pipe.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; -import {Observable, Observer} from 'rxjs'; - -// #docregion AsyncPipePromise -@Component({ - selector: 'async-promise-pipe', - template: ` -
- promise|async - : - - Wait for it... {{ greeting | async }} -
- `, - standalone: false, -}) -export class AsyncPromisePipeComponent { - greeting: Promise | null = null; - arrived: boolean = false; - - private resolve: Function | null = null; - - constructor() { - this.reset(); - } - - reset() { - this.arrived = false; - this.greeting = new Promise((resolve, reject) => { - this.resolve = resolve; - }); - } - - clicked() { - if (this.arrived) { - this.reset(); - } else { - this.resolve!('hi there!'); - this.arrived = true; - } - } -} -// #enddocregion - -// #docregion AsyncPipeObservable -@Component({ - selector: 'async-observable-pipe', - template: '
observable|async: Time: {{ time | async }}
', - standalone: false, -}) -export class AsyncObservablePipeComponent { - time = new Observable((observer: Observer) => { - setInterval(() => observer.next(new Date().toString()), 1000); - }); -} -// #enddocregion - -// For some reason protractor hangs on setInterval. So we will run outside of angular zone so that -// protractor will not see us. Also we want to have this outside the docregion so as not to confuse -// the reader. -function setInterval(fn: Function, delay: number) { - const zone = (window as any)['Zone'].current; - let rootZone = zone; - while (rootZone.parent) { - rootZone = rootZone.parent; - } - rootZone.run(() => { - window.setInterval(function (this: unknown) { - zone.run(fn, this, arguments as any); - }, delay); - }); -} diff --git a/adev/src/content/api-examples/common/pipes/ts/currency_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/currency_pipe.ts deleted file mode 100644 index 4f899627c7cd..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/currency_pipe.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {registerLocaleData} from '@angular/common'; -import {Component} from '@angular/core'; -// we need to import data for the french locale -import localeFr from './locale-fr'; - -// registering french data -registerLocaleData(localeFr); - -// #docregion CurrencyPipe -@Component({ - selector: 'currency-pipe', - template: ` -
- -

A: {{ a | currency }}

- - -

A: {{ a | currency : 'CAD' }}

- - -

A: {{ a | currency : 'CAD' : 'code' }}

- - -

B: {{ b | currency : 'CAD' : 'symbol' : '4.2-2' }}

- - -

B: {{ b | currency : 'CAD' : 'symbol-narrow' : '4.2-2' }}

- - -

B: {{ b | currency : 'CAD' : 'symbol' : '4.2-2' : 'fr' }}

- - -

B: {{ b | currency : 'CLP' }}

-
- `, - standalone: false, -}) -export class CurrencyPipeComponent { - a: number = 0.259; - b: number = 1.3495; -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/date_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/date_pipe.ts deleted file mode 100644 index 21774bcc4274..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/date_pipe.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {registerLocaleData} from '@angular/common'; -import {Component} from '@angular/core'; -// we need to import data for the french locale -import localeFr from './locale-fr'; - -// registering french data -registerLocaleData(localeFr); - -@Component({ - selector: 'date-pipe', - template: ` -
- -

Today is {{ today | date }}

- - -

Or if you prefer, {{ today | date : 'fullDate' }}

- - -

The time is {{ today | date : 'shortTime' }}

- - -

The full date/time is {{ today | date : 'full' }}

- - -

The full date/time in french is: {{ today | date : 'full' : '' : 'fr' }}

- - -

The custom date is {{ today | date : 'yyyy-MM-dd HH:mm a z' : '+0900' }}

- - -

- The custom date with fixed timezone is - {{ fixedTimezone | date : 'yyyy-MM-dd HH:mm a z' : '+0900' }} -

-
- `, - standalone: false, -}) -export class DatePipeComponent { - today = Date.now(); - fixedTimezone = '2015-06-15T09:03:01+0900'; -} -@Component({ - selector: 'deprecated-date-pipe', - template: ` -
- -

Today is {{ today | date }}

- - -

Or if you prefer, {{ today | date : 'fullDate' }}

- - -

The time is {{ today | date : 'shortTime' }}

- - -

The custom date is {{ today | date : 'yyyy-MM-dd HH:mm a' }}

-
- `, - standalone: false, -}) -export class DeprecatedDatePipeComponent { - today = Date.now(); -} diff --git a/adev/src/content/api-examples/common/pipes/ts/e2e_test/pipe_spec.ts b/adev/src/content/api-examples/common/pipes/ts/e2e_test/pipe_spec.ts deleted file mode 100644 index d448bf3d2d5e..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/e2e_test/pipe_spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, ExpectedConditions} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('pipe', () => { - afterEach(verifyNoBrowserErrors); - const URL = '/pipes'; - - describe('async', () => { - it('should resolve and display promise', () => { - browser.get(URL); - waitForElement('async-promise-pipe'); - expect(element.all(by.css('async-promise-pipe span')).get(0).getText()).toEqual( - 'Wait for it...', - ); - element(by.css('async-promise-pipe button')).click(); - expect(element.all(by.css('async-promise-pipe span')).get(0).getText()).toEqual( - 'Wait for it... hi there!', - ); - }); - }); - - describe('lowercase/uppercase', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('lowerupper-pipe'); - element(by.css('lowerupper-pipe input')).sendKeys('Hello World!'); - expect(element.all(by.css('lowerupper-pipe pre')).get(0).getText()).toEqual("'hello world!'"); - expect(element.all(by.css('lowerupper-pipe pre')).get(1).getText()).toEqual("'HELLO WORLD!'"); - }); - }); - - describe('titlecase', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('titlecase-pipe'); - expect(element.all(by.css('titlecase-pipe p')).get(0).getText()).toEqual('Some String'); - expect(element.all(by.css('titlecase-pipe p')).get(1).getText()).toEqual( - 'This Is Mixed Case', - ); - expect(element.all(by.css('titlecase-pipe p')).get(2).getText()).toEqual( - "It's Non-trivial Question", - ); - expect(element.all(by.css('titlecase-pipe p')).get(3).getText()).toEqual('One,two,three'); - expect(element.all(by.css('titlecase-pipe p')).get(4).getText()).toEqual('True|false'); - expect(element.all(by.css('titlecase-pipe p')).get(5).getText()).toEqual('Foo-vs-bar'); - }); - }); - - describe('keyvalue', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('keyvalue-pipe'); - expect(element.all(by.css('keyvalue-pipe div')).get(0).getText()).toEqual('1:bar'); - expect(element.all(by.css('keyvalue-pipe div')).get(1).getText()).toEqual('2:foo'); - expect(element.all(by.css('keyvalue-pipe div')).get(2).getText()).toEqual('1:bar'); - expect(element.all(by.css('keyvalue-pipe div')).get(3).getText()).toEqual('2:foo'); - }); - }); - - describe('number', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('number-pipe'); - const examples = element.all(by.css('number-pipe p')); - expect(examples.get(0).getText()).toEqual('No specified formatting: 3.142'); - expect(examples.get(1).getText()).toEqual('With digitsInfo parameter specified: 0,003.14159'); - expect(examples.get(2).getText()).toEqual( - 'With digitsInfo and locale parameters specified: 0\u202f003,14159', - ); - }); - }); - - describe('percent', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('percent-pipe'); - const examples = element.all(by.css('percent-pipe p')); - expect(examples.get(0).getText()).toEqual('A: 26%'); - expect(examples.get(1).getText()).toEqual('B: 0,134.950%'); - expect(examples.get(2).getText()).toEqual('B: 0\u202f134,950 %'); - }); - }); - - describe('currency', () => { - it('should work properly', () => { - browser.get(URL); - waitForElement('currency-pipe'); - const examples = element.all(by.css('currency-pipe p')); - expect(examples.get(0).getText()).toEqual('A: $0.26'); - expect(examples.get(1).getText()).toEqual('A: CA$0.26'); - expect(examples.get(2).getText()).toEqual('A: CAD0.26'); - expect(examples.get(3).getText()).toEqual('B: CA$0,001.35'); - expect(examples.get(4).getText()).toEqual('B: $0,001.35'); - expect(examples.get(5).getText()).toEqual('B: 0\u202f001,35 $CA'); - expect(examples.get(6).getText()).toEqual('B: CLP1'); - }); - }); -}); diff --git a/adev/src/content/api-examples/common/pipes/ts/i18n_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/i18n_pipe.ts deleted file mode 100644 index 847cb9ef5071..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/i18n_pipe.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion I18nPluralPipeComponent -@Component({ - selector: 'i18n-plural-pipe', - template: ` -
{{ messages.length | i18nPlural : messageMapping }}
- `, - standalone: false, -}) -export class I18nPluralPipeComponent { - messages: any[] = ['Message 1']; - messageMapping: {[k: string]: string} = { - '=0': 'No messages.', - '=1': 'One message.', - 'other': '# messages.', - }; -} -// #enddocregion - -// #docregion I18nSelectPipeComponent -@Component({ - selector: 'i18n-select-pipe', - template: ` -
{{ gender | i18nSelect : inviteMap }}
- `, - standalone: false, -}) -export class I18nSelectPipeComponent { - gender: string = 'male'; - inviteMap: any = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'}; -} -//#enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/json_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/json_pipe.ts deleted file mode 100644 index bd21cc5d999f..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/json_pipe.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion JsonPipe -@Component({ - selector: 'json-pipe', - template: ` -
-

Without JSON pipe:

-
{{ object }}
-

With JSON pipe:

-
{{ object | json }}
-
- `, - standalone: false, -}) -export class JsonPipeComponent { - object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}}; -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/keyvalue_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/keyvalue_pipe.ts deleted file mode 100644 index 05d45f1345fc..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/keyvalue_pipe.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion KeyValuePipe -@Component({ - selector: 'keyvalue-pipe', - template: ` - -

Object

-
{{ item.key }}:{{ item.value }}
-

Map

-
{{ item.key }}:{{ item.value }}
-

Natural order

-
{{ item.key }}:{{ item.value }}
-
- `, - standalone: false, -}) -export class KeyValuePipeComponent { - object: {[key: number]: string} = {2: 'foo', 1: 'bar'}; - map = new Map([ - [2, 'foo'], - [1, 'bar'], - ]); -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/locale-fr.ts b/adev/src/content/api-examples/common/pipes/ts/locale-fr.ts deleted file mode 100644 index 0b9bd67975c4..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/locale-fr.ts +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// THIS CODE IS GENERATED - DO NOT MODIFY -// See angular/tools/gulp-tasks/cldr/extract.js - -const u = undefined; - -function plural(n: number): number { - let i = Math.floor(Math.abs(n)); - if (i === 0 || i === 1) return 1; - return 5; -} - -export default [ - 'fr', - [['AM', 'PM'], u, u], - u, - [ - ['D', 'L', 'M', 'M', 'J', 'V', 'S'], - ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], - ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], - ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'], - ], - u, - [ - ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'], - [ - 'janv.', - 'févr.', - 'mars', - 'avr.', - 'mai', - 'juin', - 'juil.', - 'août', - 'sept.', - 'oct.', - 'nov.', - 'déc.', - ], - [ - 'janvier', - 'février', - 'mars', - 'avril', - 'mai', - 'juin', - 'juillet', - 'août', - 'septembre', - 'octobre', - 'novembre', - 'décembre', - ], - ], - u, - [['av. J.-C.', 'ap. J.-C.'], u, ['avant Jésus-Christ', 'après Jésus-Christ']], - 1, - [6, 0], - ['dd/MM/y', 'd MMM y', 'd MMMM y', 'EEEE d MMMM y'], - ['HH:mm', 'HH:mm:ss', 'HH:mm:ss z', 'HH:mm:ss zzzz'], - ['{1} {0}', "{1} 'à' {0}", u, u], - [',', '\u202f', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'], - ['#,##0.###', '#,##0 %', '#,##0.00 ¤', '#E0'], - 'EUR', - '€', - 'euro', - { - 'ARS': ['$AR', '$'], - 'AUD': ['$AU', '$'], - 'BEF': ['FB'], - 'BMD': ['$BM', '$'], - 'BND': ['$BN', '$'], - 'BZD': ['$BZ', '$'], - 'CAD': ['$CA', '$'], - 'CLP': ['$CL', '$'], - 'CNY': [u, '¥'], - 'COP': ['$CO', '$'], - 'CYP': ['£CY'], - 'EGP': [u, '£E'], - 'FJD': ['$FJ', '$'], - 'FKP': ['£FK', '£'], - 'FRF': ['F'], - 'GBP': ['£GB', '£'], - 'GIP': ['£GI', '£'], - 'HKD': [u, '$'], - 'IEP': ['£IE'], - 'ILP': ['£IL'], - 'ITL': ['₤IT'], - 'JPY': [u, '¥'], - 'KMF': [u, 'FC'], - 'LBP': ['£LB', '£L'], - 'MTP': ['£MT'], - 'MXN': ['$MX', '$'], - 'NAD': ['$NA', '$'], - 'NIO': [u, '$C'], - 'NZD': ['$NZ', '$'], - 'RHD': ['$RH'], - 'RON': [u, 'L'], - 'RWF': [u, 'FR'], - 'SBD': ['$SB', '$'], - 'SGD': ['$SG', '$'], - 'SRD': ['$SR', '$'], - 'TOP': [u, '$T'], - 'TTD': ['$TT', '$'], - 'TWD': [u, 'NT$'], - 'USD': ['$US', '$'], - 'UYU': ['$UY', '$'], - 'WST': ['$WS'], - 'XCD': [u, '$'], - 'XPF': ['FCFP'], - 'ZMW': [u, 'Kw'], - }, - 'ltr', - plural, -]; diff --git a/adev/src/content/api-examples/common/pipes/ts/lowerupper_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/lowerupper_pipe.ts deleted file mode 100644 index 34b5891f23b4..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/lowerupper_pipe.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion LowerUpperPipe -@Component({ - selector: 'lowerupper-pipe', - template: ` -
- - -

In lowercase:

-
'{{ value | lowercase }}'
-

In uppercase:

-
'{{ value | uppercase }}'
-
- `, - standalone: false, -}) -export class LowerUpperPipeComponent { - value: string = ''; - change(value: string) { - this.value = value; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/module.ts b/adev/src/content/api-examples/common/pipes/ts/module.ts deleted file mode 100644 index 446dd1b49955..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/module.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -import {AsyncObservablePipeComponent, AsyncPromisePipeComponent} from './async_pipe'; -import {CurrencyPipeComponent} from './currency_pipe'; -import {DatePipeComponent, DeprecatedDatePipeComponent} from './date_pipe'; -import {I18nPluralPipeComponent, I18nSelectPipeComponent} from './i18n_pipe'; -import {JsonPipeComponent} from './json_pipe'; -import {KeyValuePipeComponent} from './keyvalue_pipe'; -import {LowerUpperPipeComponent} from './lowerupper_pipe'; -import {NumberPipeComponent} from './number_pipe'; -import {PercentPipeComponent} from './percent_pipe'; -import {SlicePipeListComponent, SlicePipeStringComponent} from './slice_pipe'; -import {TitleCasePipeComponent} from './titlecase_pipe'; - -@Component({ - selector: 'example-app', - template: ` -

Pipe Example

- -

async

- - - -

date

- - -

json

- - -

- lower - , - upper -

- - -

titlecase

- - -

number

- - - - -

slice

- - - -

i18n

- - - -

keyvalue

- - `, - standalone: false, -}) -export class AppComponent {} - -@NgModule({ - declarations: [ - AsyncPromisePipeComponent, - AsyncObservablePipeComponent, - AppComponent, - JsonPipeComponent, - DatePipeComponent, - DeprecatedDatePipeComponent, - LowerUpperPipeComponent, - TitleCasePipeComponent, - NumberPipeComponent, - PercentPipeComponent, - CurrencyPipeComponent, - SlicePipeStringComponent, - SlicePipeListComponent, - I18nPluralPipeComponent, - I18nSelectPipeComponent, - KeyValuePipeComponent, - ], - imports: [BrowserModule], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/common/pipes/ts/number_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/number_pipe.ts deleted file mode 100644 index 79b871782d5e..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/number_pipe.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {registerLocaleData} from '@angular/common'; -import {Component} from '@angular/core'; -// we need to import data for the french locale -import localeFr from './locale-fr'; - -registerLocaleData(localeFr, 'fr'); - -// #docregion NumberPipe -@Component({ - selector: 'number-pipe', - template: ` -
-

- No specified formatting: - {{ pi | number }} - -

- -

- With digitsInfo parameter specified: - {{ pi | number : '4.1-5' }} - -

- -

- With digitsInfo and locale parameters specified: - {{ pi | number : '4.1-5' : 'fr' }} - -

-
- `, - standalone: false, -}) -export class NumberPipeComponent { - pi: number = 3.14159265359; -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/percent_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/percent_pipe.ts deleted file mode 100644 index daf978f9adfa..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/percent_pipe.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {registerLocaleData} from '@angular/common'; -import {Component} from '@angular/core'; -// we need to import data for the french locale -import localeFr from './locale-fr'; - -// registering french data -registerLocaleData(localeFr); - -// #docregion PercentPipe -@Component({ - selector: 'percent-pipe', - template: ` -
- -

A: {{ a | percent }}

- - -

B: {{ b | percent : '4.3-5' }}

- - -

B: {{ b | percent : '4.3-5' : 'fr' }}

-
- `, - standalone: false, -}) -export class PercentPipeComponent { - a: number = 0.259; - b: number = 1.3495; -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/slice_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/slice_pipe.ts deleted file mode 100644 index 201f97a5ae22..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/slice_pipe.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion SlicePipe_string -@Component({ - selector: 'slice-string-pipe', - template: ` -
-

{{ str }}[0:4]: '{{ str | slice : 0 : 4 }}' - output is expected to be 'abcd'

-

{{ str }}[4:0]: '{{ str | slice : 4 : 0 }}' - output is expected to be ''

-

{{ str }}[-4]: '{{ str | slice : -4 }}' - output is expected to be 'ghij'

-

{{ str }}[-4:-2]: '{{ str | slice : -4 : -2 }}' - output is expected to be 'gh'

-

{{ str }}[-100]: '{{ str | slice : -100 }}' - output is expected to be 'abcdefghij'

-

{{ str }}[100]: '{{ str | slice : 100 }}' - output is expected to be ''

-
- `, - standalone: false, -}) -export class SlicePipeStringComponent { - str: string = 'abcdefghij'; -} -// #enddocregion - -// #docregion SlicePipe_list -@Component({ - selector: 'slice-list-pipe', - template: ` -
    -
  • {{ i }}
  • -
- `, - standalone: false, -}) -export class SlicePipeListComponent { - collection: string[] = ['a', 'b', 'c', 'd']; -} -// #enddocregion diff --git a/adev/src/content/api-examples/common/pipes/ts/titlecase_pipe.ts b/adev/src/content/api-examples/common/pipes/ts/titlecase_pipe.ts deleted file mode 100644 index b2616e284a0f..000000000000 --- a/adev/src/content/api-examples/common/pipes/ts/titlecase_pipe.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -// #docregion TitleCasePipe -@Component({ - selector: 'titlecase-pipe', - template: ` -
-

{{ 'some string' | titlecase }}

- -

{{ 'tHIs is mIXeD CaSe' | titlecase }}

- -

{{ "it's non-trivial question" | titlecase }}

- -

{{ 'one,two,three' | titlecase }}

- -

{{ 'true|false' | titlecase }}

- -

{{ 'foo-vs-bar' | titlecase }}

- -
- `, - standalone: false, -}) -export class TitleCasePipeComponent {} -// #enddocregion diff --git a/adev/src/content/api-examples/common/start-server.js b/adev/src/content/api-examples/common/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/common/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/common/test_module.ts b/adev/src/content/api-examples/common/test_module.ts deleted file mode 100644 index 2ad20032052a..000000000000 --- a/adev/src/content/api-examples/common/test_module.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {RouterModule} from '@angular/router'; - -import * as locationExample from './location/ts/module'; -import * as ngComponentOutletExample from './ngComponentOutlet/ts/module'; -import * as ngIfExample from './ngIf/ts/module'; -import * as ngTemplateOutletExample from './ngTemplateOutlet/ts/module'; -import * as pipesExample from './pipes/ts/module'; - -@Component({ - selector: 'example-app:not(y)', - template: '', - standalone: false, -}) -export class TestsAppComponent {} - -@NgModule({ - imports: [ - locationExample.AppModule, - ngComponentOutletExample.AppModule, - ngIfExample.AppModule, - ngTemplateOutletExample.AppModule, - pipesExample.AppModule, - - // Router configuration so that the individual e2e tests can load their - // app components. - RouterModule.forRoot([ - {path: 'location', component: locationExample.AppComponent}, - {path: 'ngComponentOutlet', component: ngComponentOutletExample.AppComponent}, - {path: 'ngIf', component: ngIfExample.AppComponent}, - {path: 'ngTemplateOutlet', component: ngTemplateOutletExample.AppComponent}, - {path: 'pipes', component: pipesExample.AppComponent}, - ]), - ], - declarations: [TestsAppComponent], - bootstrap: [TestsAppComponent], -}) -export class TestsAppModule {} diff --git a/adev/src/content/api-examples/core/animation/ts/dsl/animation_example.ts b/adev/src/content/api-examples/core/animation/ts/dsl/animation_example.ts deleted file mode 100644 index 3612681158b2..000000000000 --- a/adev/src/content/api-examples/core/animation/ts/dsl/animation_example.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {animate, state, style, transition, trigger} from '@angular/animations'; -import {Component, NgModule} from '@angular/core'; -import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; - -@Component({ - selector: 'example-app', - styles: [ - ` - .toggle-container { - background-color: white; - border: 10px solid black; - width: 200px; - text-align: center; - line-height: 100px; - font-size: 50px; - box-sizing: border-box; - overflow: hidden; - } - `, - ], - animations: [ - trigger('openClose', [ - state('collapsed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})), - state('expanded', style({height: '*', borderColor: 'green', color: 'green'})), - transition('collapsed <=> expanded', [animate(500, style({height: '250px'})), animate(500)]), - ]), - ], - template: ` - - -
-
Look at this box
- `, - standalone: false, -}) -export class MyExpandoCmp { - // TODO(issue/24571): remove '!'. - stateExpression!: string; - constructor() { - this.collapse(); - } - expand() { - this.stateExpression = 'expanded'; - } - collapse() { - this.stateExpression = 'collapsed'; - } -} - -@NgModule({ - imports: [BrowserAnimationsModule], - declarations: [MyExpandoCmp], - bootstrap: [MyExpandoCmp], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts b/adev/src/content/api-examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts deleted file mode 100644 index 0ccfaf2d4db5..000000000000 --- a/adev/src/content/api-examples/core/animation/ts/dsl/e2e_test/animation_example_spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {$, browser, by, element, ExpectedConditions} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -function waitForElement(selector: string) { - const EC = ExpectedConditions; - // Waits for the element with id 'abc' to be present on the dom. - browser.wait(EC.presenceOf($(selector)), 20000); -} - -describe('animation example', () => { - afterEach(verifyNoBrowserErrors); - - describe('index view', () => { - const URL = '/animation/dsl/'; - - it('should list out the current collection of items', () => { - browser.get(URL); - waitForElement('.toggle-container'); - expect(element.all(by.css('.toggle-container')).get(0).getText()).toEqual('Look at this box'); - }); - }); -}); diff --git a/adev/src/content/api-examples/core/animation/ts/dsl/module.ts b/adev/src/content/api-examples/core/animation/ts/dsl/module.ts deleted file mode 100644 index 39a299db24de..000000000000 --- a/adev/src/content/api-examples/core/animation/ts/dsl/module.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -export {AppModule, MyExpandoCmp as AppComponent} from './animation_example'; diff --git a/adev/src/content/api-examples/core/debug/ts/debug_element/debug_element.ts b/adev/src/content/api-examples/core/debug/ts/debug_element/debug_element.ts deleted file mode 100644 index fa8da0c082ab..000000000000 --- a/adev/src/content/api-examples/core/debug/ts/debug_element/debug_element.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DebugElement} from '@angular/core'; - -let debugElement: DebugElement = undefined!; -let predicate: any; - -debugElement.query(predicate); diff --git a/adev/src/content/api-examples/core/di/ts/contentChild/content_child_example.ts b/adev/src/content/api-examples/core/di/ts/contentChild/content_child_example.ts deleted file mode 100644 index 8284471f4dbc..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChild/content_child_example.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component, ContentChild, Directive, Input} from '@angular/core'; - -@Directive({ - selector: 'pane', - standalone: false, -}) -export class Pane { - @Input() id!: string; -} - -@Component({ - selector: 'tab', - template: ` -
pane: {{ pane?.id }}
- `, - standalone: false, -}) -export class Tab { - @ContentChild(Pane) pane!: Pane; -} - -@Component({ - selector: 'example-app', - template: ` - - - - - - - `, - standalone: false, -}) -export class ContentChildComp { - shouldShow = true; - - toggle() { - this.shouldShow = !this.shouldShow; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/contentChild/content_child_howto.ts b/adev/src/content/api-examples/core/di/ts/contentChild/content_child_howto.ts deleted file mode 100644 index 4027a8f4eed6..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChild/content_child_howto.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion HowTo -import {AfterContentInit, ContentChild, Directive} from '@angular/core'; - -@Directive({ - selector: 'child-directive', - standalone: false, -}) -class ChildDirective {} - -@Directive({ - selector: 'someDir', - standalone: false, -}) -class SomeDir implements AfterContentInit { - @ContentChild(ChildDirective) contentChild!: ChildDirective; - - ngAfterContentInit() { - // contentChild is set - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts b/adev/src/content/api-examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts deleted file mode 100644 index 3a1a4515d4e3..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChild/e2e_test/content_child_spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('contentChild example', () => { - afterEach(verifyNoBrowserErrors); - let button: ElementFinder; - let result: ElementFinder; - - beforeEach(() => { - browser.get('/di/contentChild'); - button = element(by.css('button')); - result = element(by.css('div')); - }); - - it('should query content child', () => { - expect(result.getText()).toEqual('pane: 1'); - - button.click(); - - expect(result.getText()).toEqual('pane: 2'); - }); -}); diff --git a/adev/src/content/api-examples/core/di/ts/contentChild/module.ts b/adev/src/content/api-examples/core/di/ts/contentChild/module.ts deleted file mode 100644 index 6c1ee19db726..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChild/module.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {ContentChildComp, Pane, Tab} from './content_child_example'; - -@NgModule({ - imports: [BrowserModule], - declarations: [ContentChildComp, Pane, Tab], - bootstrap: [ContentChildComp], -}) -export class AppModule {} - -export {ContentChildComp as AppComponent}; diff --git a/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_example.ts b/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_example.ts deleted file mode 100644 index 65ba6520efd3..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_example.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component, ContentChildren, Directive, Input, QueryList} from '@angular/core'; - -@Directive({ - selector: 'pane', - standalone: false, -}) -export class Pane { - @Input() id!: string; -} - -@Component({ - selector: 'tab', - template: ` -
Top level panes: {{ serializedPanes }}
-
Arbitrary nested panes: {{ serializedNestedPanes }}
- `, - standalone: false, -}) -export class Tab { - @ContentChildren(Pane) topLevelPanes!: QueryList; - @ContentChildren(Pane, {descendants: true}) arbitraryNestedPanes!: QueryList; - - get serializedPanes(): string { - return this.topLevelPanes ? this.topLevelPanes.map((p) => p.id).join(', ') : ''; - } - get serializedNestedPanes(): string { - return this.arbitraryNestedPanes ? this.arbitraryNestedPanes.map((p) => p.id).join(', ') : ''; - } -} - -@Component({ - selector: 'example-app', - template: ` - - - - - - - - - - - - - `, - standalone: false, -}) -export class ContentChildrenComp { - shouldShow = false; - - show() { - this.shouldShow = true; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_howto.ts b/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_howto.ts deleted file mode 100644 index 7d52fcb60478..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChildren/content_children_howto.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion HowTo -import {AfterContentInit, ContentChildren, Directive, QueryList} from '@angular/core'; - -@Directive({ - selector: 'child-directive', - standalone: false, -}) -class ChildDirective {} - -@Directive({ - selector: 'someDir', - standalone: false, -}) -class SomeDir implements AfterContentInit { - @ContentChildren(ChildDirective) contentChildren!: QueryList; - - ngAfterContentInit() { - // contentChildren is set - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts b/adev/src/content/api-examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts deleted file mode 100644 index 45e68efdf658..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChildren/e2e_test/content_children_spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('contentChildren example', () => { - afterEach(verifyNoBrowserErrors); - let button: ElementFinder; - let resultTopLevel: ElementFinder; - let resultNested: ElementFinder; - - beforeEach(() => { - browser.get('/di/contentChildren'); - button = element(by.css('button')); - resultTopLevel = element(by.css('.top-level')); - resultNested = element(by.css('.nested')); - }); - - it('should query content children', () => { - expect(resultTopLevel.getText()).toEqual('Top level panes: 1, 2'); - - button.click(); - - expect(resultTopLevel.getText()).toEqual('Top level panes: 1, 2, 3'); - }); - - it('should query nested content children', () => { - expect(resultNested.getText()).toEqual('Arbitrary nested panes: 1, 2'); - - button.click(); - - expect(resultNested.getText()).toEqual('Arbitrary nested panes: 1, 2, 3, 3_1, 3_2'); - }); -}); diff --git a/adev/src/content/api-examples/core/di/ts/contentChildren/module.ts b/adev/src/content/api-examples/core/di/ts/contentChildren/module.ts deleted file mode 100644 index acc805640fb2..000000000000 --- a/adev/src/content/api-examples/core/di/ts/contentChildren/module.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {ContentChildrenComp, Pane, Tab} from './content_children_example'; - -@NgModule({ - imports: [BrowserModule], - declarations: [ContentChildrenComp, Pane, Tab], - bootstrap: [ContentChildrenComp], -}) -export class AppModule {} - -export {ContentChildrenComp as AppComponent}; diff --git a/adev/src/content/api-examples/core/di/ts/forward_ref/forward_ref_spec.ts b/adev/src/content/api-examples/core/di/ts/forward_ref/forward_ref_spec.ts deleted file mode 100644 index 20f9d879dbcf..000000000000 --- a/adev/src/content/api-examples/core/di/ts/forward_ref/forward_ref_spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {forwardRef, Inject, Injectable, Injector, resolveForwardRef} from '@angular/core'; - -{ - describe('forwardRef examples', () => { - it('ForwardRefFn example works', () => { - // #docregion forward_ref_fn - const ref = forwardRef(() => Lock); - // #enddocregion - expect(ref).not.toBeNull(); - - class Lock {} - }); - - it('can be used to inject a class defined later', () => { - // #docregion forward_ref - @Injectable() - class Door { - lock: Lock; - - // Door attempts to inject Lock, despite it not being defined yet. - // forwardRef makes this possible. - constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { - this.lock = lock; - } - } - - // Only at this point Lock is defined. - class Lock {} - - const injector = Injector.create({ - providers: [ - {provide: Lock, deps: []}, - {provide: Door, deps: [Lock]}, - ], - }); - - expect(injector.get(Door) instanceof Door).toBe(true); - expect(injector.get(Door).lock instanceof Lock).toBe(true); - // #enddocregion - }); - - it('can be unwrapped', () => { - // #docregion resolve_forward_ref - const ref = forwardRef(() => 'refValue'); - expect(resolveForwardRef(ref as any)).toEqual('refValue'); - expect(resolveForwardRef('regularValue')).toEqual('regularValue'); - // #enddocregion - }); - }); -} diff --git a/adev/src/content/api-examples/core/di/ts/injector_spec.ts b/adev/src/content/api-examples/core/di/ts/injector_spec.ts deleted file mode 100644 index 96d548b62c32..000000000000 --- a/adev/src/content/api-examples/core/di/ts/injector_spec.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - inject, - InjectFlags, - InjectionToken, - InjectOptions, - Injector, - ProviderToken, - ɵsetCurrentInjector as setCurrentInjector, - ɵsetInjectorProfilerContext, -} from '@angular/core'; - -class MockRootScopeInjector implements Injector { - constructor(readonly parent: Injector) {} - - get( - token: ProviderToken, - defaultValue?: any, - flags: InjectFlags | InjectOptions = InjectFlags.Default, - ): T { - if ((token as any).ɵprov && (token as any).ɵprov.providedIn === 'root') { - const old = setCurrentInjector(this); - const previousInjectorProfilerContext = ɵsetInjectorProfilerContext({ - injector: this, - token: null, - }); - try { - return (token as any).ɵprov.factory(); - } finally { - setCurrentInjector(old); - ɵsetInjectorProfilerContext(previousInjectorProfilerContext); - } - } - return this.parent.get(token, defaultValue, flags); - } -} - -{ - describe('injector metadata examples', () => { - it('works', () => { - // #docregion Injector - const injector: Injector = Injector.create({ - providers: [{provide: 'validToken', useValue: 'Value'}], - }); - expect(injector.get('validToken')).toEqual('Value'); - expect(() => injector.get('invalidToken')).toThrowError(); - expect(injector.get('invalidToken', 'notFound')).toEqual('notFound'); - // #enddocregion - }); - - it('injects injector', () => { - // #docregion injectInjector - const injector = Injector.create({providers: []}); - expect(injector.get(Injector)).toBe(injector); - // #enddocregion - }); - - it('should infer type', () => { - // #docregion InjectionToken - const BASE_URL = new InjectionToken('BaseUrl'); - const injector = Injector.create({ - providers: [{provide: BASE_URL, useValue: 'http://localhost'}], - }); - const url = injector.get(BASE_URL); - // Note: since `BASE_URL` is `InjectionToken` - // `url` is correctly inferred to be `string` - expect(url).toBe('http://localhost'); - // #enddocregion - }); - - it('injects a tree-shakeable InjectionToken', () => { - class MyDep {} - const injector = new MockRootScopeInjector( - Injector.create({providers: [{provide: MyDep, deps: []}]}), - ); - - // #docregion ShakableInjectionToken - class MyService { - constructor(readonly myDep: MyDep) {} - } - - const MY_SERVICE_TOKEN = new InjectionToken('Manually constructed MyService', { - providedIn: 'root', - factory: () => new MyService(inject(MyDep)), - }); - - const instance = injector.get(MY_SERVICE_TOKEN); - expect(instance instanceof MyService).toBeTruthy(); - expect(instance.myDep instanceof MyDep).toBeTruthy(); - // #enddocregion - }); - }); -} diff --git a/adev/src/content/api-examples/core/di/ts/metadata_spec.ts b/adev/src/content/api-examples/core/di/ts/metadata_spec.ts deleted file mode 100644 index 5fe993ee65c5..000000000000 --- a/adev/src/content/api-examples/core/di/ts/metadata_spec.ts +++ /dev/null @@ -1,190 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - Component, - Directive, - Host, - Injectable, - Injector, - Optional, - Self, - SkipSelf, -} from '@angular/core'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; - -{ - describe('di metadata examples', () => { - describe('Inject', () => { - it('works without decorator', () => { - // #docregion InjectWithoutDecorator - class Engine {} - - @Injectable() - class Car { - constructor(public engine: Engine) {} // same as constructor(@Inject(Engine) engine:Engine) - } - - const injector = Injector.create({ - providers: [ - {provide: Engine, deps: []}, - {provide: Car, deps: [Engine]}, - ], - }); - expect(injector.get(Car).engine instanceof Engine).toBe(true); - // #enddocregion - }); - }); - - describe('Optional', () => { - it('works', () => { - // #docregion Optional - class Engine {} - - @Injectable() - class Car { - constructor(@Optional() public engine: Engine) {} - } - - const injector = Injector.create({ - providers: [{provide: Car, deps: [[new Optional(), Engine]]}], - }); - expect(injector.get(Car).engine).toBeNull(); - // #enddocregion - }); - }); - - describe('Injectable', () => { - it('works', () => { - // #docregion Injectable - @Injectable() - class UsefulService {} - - @Injectable() - class NeedsService { - constructor(public service: UsefulService) {} - } - - const injector = Injector.create({ - providers: [ - {provide: NeedsService, deps: [UsefulService]}, - {provide: UsefulService, deps: []}, - ], - }); - expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true); - // #enddocregion - }); - }); - - describe('Self', () => { - it('works', () => { - // #docregion Self - class Dependency {} - - @Injectable() - class NeedsDependency { - constructor(@Self() public dependency: Dependency) {} - } - - let inj = Injector.create({ - providers: [ - {provide: Dependency, deps: []}, - {provide: NeedsDependency, deps: [[new Self(), Dependency]]}, - ], - }); - const nd = inj.get(NeedsDependency); - - expect(nd.dependency instanceof Dependency).toBe(true); - - const child = Injector.create({ - providers: [{provide: NeedsDependency, deps: [[new Self(), Dependency]]}], - parent: inj, - }); - expect(() => child.get(NeedsDependency)).toThrowError(); - // #enddocregion - }); - }); - - describe('SkipSelf', () => { - it('works', () => { - // #docregion SkipSelf - class Dependency {} - - @Injectable() - class NeedsDependency { - constructor(@SkipSelf() public dependency: Dependency) {} - } - - const parent = Injector.create({providers: [{provide: Dependency, deps: []}]}); - const child = Injector.create({ - providers: [{provide: NeedsDependency, deps: [Dependency]}], - parent, - }); - expect(child.get(NeedsDependency).dependency instanceof Dependency).toBe(true); - - const inj = Injector.create({ - providers: [{provide: NeedsDependency, deps: [[new Self(), Dependency]]}], - }); - expect(() => inj.get(NeedsDependency)).toThrowError(); - // #enddocregion - }); - }); - - describe('Host', () => { - it('works', () => { - // #docregion Host - class OtherService {} - class HostService {} - - @Directive({ - selector: 'child-directive', - standalone: false, - }) - class ChildDirective { - logs: string[] = []; - - constructor(@Optional() @Host() os: OtherService, @Optional() @Host() hs: HostService) { - // os is null: true - this.logs.push(`os is null: ${os === null}`); - // hs is an instance of HostService: true - this.logs.push(`hs is an instance of HostService: ${hs instanceof HostService}`); - } - } - - @Component({ - selector: 'parent-cmp', - viewProviders: [HostService], - template: '', - standalone: false, - }) - class ParentCmp {} - - @Component({ - selector: 'app', - viewProviders: [OtherService], - template: '', - standalone: false, - }) - class App {} - // #enddocregion - - TestBed.configureTestingModule({ - declarations: [App, ParentCmp, ChildDirective], - }); - - let cmp: ComponentFixture = undefined!; - expect(() => (cmp = TestBed.createComponent(App))).not.toThrow(); - - expect(cmp.debugElement.children[0].children[0].injector.get(ChildDirective).logs).toEqual([ - 'os is null: true', - 'hs is an instance of HostService: true', - ]); - }); - }); - }); -} diff --git a/adev/src/content/api-examples/core/di/ts/provider_spec.ts b/adev/src/content/api-examples/core/di/ts/provider_spec.ts deleted file mode 100644 index f377a410dff6..000000000000 --- a/adev/src/content/api-examples/core/di/ts/provider_spec.ts +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Injectable, InjectionToken, Injector, Optional} from '@angular/core'; - -{ - describe('Provider examples', () => { - describe('TypeProvider', () => { - it('works', () => { - // #docregion TypeProvider - @Injectable() - class Greeting { - salutation = 'Hello'; - } - - const injector = Injector.create({providers: [{provide: Greeting, useClass: Greeting}]}); - - expect(injector.get(Greeting).salutation).toBe('Hello'); - // #enddocregion - }); - }); - - describe('ValueProvider', () => { - it('works', () => { - // #docregion ValueProvider - const injector = Injector.create({providers: [{provide: String, useValue: 'Hello'}]}); - - expect(injector.get(String)).toEqual('Hello'); - // #enddocregion - }); - }); - - describe('MultiProviderAspect', () => { - it('works', () => { - // #docregion MultiProviderAspect - const locale = new InjectionToken('locale'); - const injector = Injector.create({ - providers: [ - {provide: locale, multi: true, useValue: 'en'}, - {provide: locale, multi: true, useValue: 'sk'}, - ], - }); - - const locales: string[] = injector.get(locale); - expect(locales).toEqual(['en', 'sk']); - // #enddocregion - }); - }); - - describe('ClassProvider', () => { - it('works', () => { - // #docregion ClassProvider - abstract class Shape { - name!: string; - } - - class Square extends Shape { - override name = 'square'; - } - - const injector = Injector.create({providers: [{provide: Shape, useValue: new Square()}]}); - - const shape: Shape = injector.get(Shape); - expect(shape.name).toEqual('square'); - expect(shape instanceof Square).toBe(true); - // #enddocregion - }); - - it('is different then useExisting', () => { - // #docregion ClassProviderDifference - class Greeting { - salutation = 'Hello'; - } - - class FormalGreeting extends Greeting { - override salutation = 'Greetings'; - } - - const injector = Injector.create({ - providers: [ - {provide: FormalGreeting, useClass: FormalGreeting}, - {provide: Greeting, useClass: FormalGreeting}, - ], - }); - - // The injector returns different instances. - // See: {provide: ?, useExisting: ?} if you want the same instance. - expect(injector.get(FormalGreeting)).not.toBe(injector.get(Greeting)); - // #enddocregion - }); - }); - - describe('StaticClassProvider', () => { - it('works', () => { - // #docregion StaticClassProvider - abstract class Shape { - name!: string; - } - - class Square extends Shape { - override name = 'square'; - } - - const injector = Injector.create({ - providers: [{provide: Shape, useClass: Square, deps: []}], - }); - - const shape: Shape = injector.get(Shape); - expect(shape.name).toEqual('square'); - expect(shape instanceof Square).toBe(true); - // #enddocregion - }); - - it('is different then useExisting', () => { - // #docregion StaticClassProviderDifference - class Greeting { - salutation = 'Hello'; - } - - class FormalGreeting extends Greeting { - override salutation = 'Greetings'; - } - - const injector = Injector.create({ - providers: [ - {provide: FormalGreeting, useClass: FormalGreeting, deps: []}, - {provide: Greeting, useClass: FormalGreeting, deps: []}, - ], - }); - - // The injector returns different instances. - // See: {provide: ?, useExisting: ?} if you want the same instance. - expect(injector.get(FormalGreeting)).not.toBe(injector.get(Greeting)); - // #enddocregion - }); - }); - - describe('ConstructorProvider', () => { - it('works', () => { - // #docregion ConstructorProvider - class Square { - name = 'square'; - } - - const injector = Injector.create({providers: [{provide: Square, deps: []}]}); - - const shape: Square = injector.get(Square); - expect(shape.name).toEqual('square'); - expect(shape instanceof Square).toBe(true); - // #enddocregion - }); - }); - - describe('ExistingProvider', () => { - it('works', () => { - // #docregion ExistingProvider - class Greeting { - salutation = 'Hello'; - } - - class FormalGreeting extends Greeting { - override salutation = 'Greetings'; - } - - const injector = Injector.create({ - providers: [ - {provide: FormalGreeting, deps: []}, - {provide: Greeting, useExisting: FormalGreeting}, - ], - }); - - expect(injector.get(Greeting).salutation).toEqual('Greetings'); - expect(injector.get(FormalGreeting).salutation).toEqual('Greetings'); - expect(injector.get(FormalGreeting)).toBe(injector.get(Greeting)); - // #enddocregion - }); - }); - - describe('FactoryProvider', () => { - it('works', () => { - // #docregion FactoryProvider - const Location = new InjectionToken('location'); - const Hash = new InjectionToken('hash'); - - const injector = Injector.create({ - providers: [ - {provide: Location, useValue: 'https://angular.io/#someLocation'}, - { - provide: Hash, - useFactory: (location: string) => location.split('#')[1], - deps: [Location], - }, - ], - }); - - expect(injector.get(Hash)).toEqual('someLocation'); - // #enddocregion - }); - - it('supports optional dependencies', () => { - // #docregion FactoryProviderOptionalDeps - const Location = new InjectionToken('location'); - const Hash = new InjectionToken('hash'); - - const injector = Injector.create({ - providers: [ - { - provide: Hash, - useFactory: (location: string) => `Hash for: ${location}`, - // use a nested array to define metadata for dependencies. - deps: [[new Optional(), Location]], - }, - ], - }); - - expect(injector.get(Hash)).toEqual('Hash for: null'); - // #enddocregion - }); - }); - }); -} diff --git a/adev/src/content/api-examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts b/adev/src/content/api-examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts deleted file mode 100644 index d87f32534ffb..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChild/e2e_test/view_child_spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('viewChild example', () => { - afterEach(verifyNoBrowserErrors); - let button: ElementFinder; - let result: ElementFinder; - - beforeEach(() => { - browser.get('/di/viewChild'); - button = element(by.css('button')); - result = element(by.css('div')); - }); - - it('should query view child', () => { - expect(result.getText()).toEqual('Selected: 1'); - - button.click(); - - expect(result.getText()).toEqual('Selected: 2'); - }); -}); diff --git a/adev/src/content/api-examples/core/di/ts/viewChild/module.ts b/adev/src/content/api-examples/core/di/ts/viewChild/module.ts deleted file mode 100644 index c778282ee178..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChild/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -import {Pane, ViewChildComp} from './view_child_example'; - -@NgModule({ - imports: [BrowserModule], - declarations: [ViewChildComp, Pane], - bootstrap: [ViewChildComp], -}) -export class AppModule {} - -export {ViewChildComp as AppComponent}; diff --git a/adev/src/content/api-examples/core/di/ts/viewChild/view_child_example.ts b/adev/src/content/api-examples/core/di/ts/viewChild/view_child_example.ts deleted file mode 100644 index 4b92e4782940..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChild/view_child_example.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component, Directive, Input, ViewChild} from '@angular/core'; - -@Directive({ - selector: 'pane', - standalone: false, -}) -export class Pane { - @Input() id!: string; -} - -@Component({ - selector: 'example-app', - template: ` - - - - - -
Selected: {{ selectedPane }}
- `, - standalone: false, -}) -export class ViewChildComp { - @ViewChild(Pane) - set pane(v: Pane) { - setTimeout(() => { - this.selectedPane = v.id; - }, 0); - } - selectedPane: string = ''; - shouldShow = true; - toggle() { - this.shouldShow = !this.shouldShow; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/viewChild/view_child_howto.ts b/adev/src/content/api-examples/core/di/ts/viewChild/view_child_howto.ts deleted file mode 100644 index 959fee8d9de4..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChild/view_child_howto.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion HowTo -import {AfterViewInit, Component, Directive, ViewChild} from '@angular/core'; - -@Directive({ - selector: 'child-directive', - standalone: false, -}) -class ChildDirective {} - -@Component({ - selector: 'someCmp', - templateUrl: 'someCmp.html', - standalone: false, -}) -class SomeCmp implements AfterViewInit { - @ViewChild(ChildDirective) child!: ChildDirective; - - ngAfterViewInit() { - // child is set - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts b/adev/src/content/api-examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts deleted file mode 100644 index de21bc2dab02..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChildren/e2e_test/view_children_spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('viewChildren example', () => { - afterEach(verifyNoBrowserErrors); - let button: ElementFinder; - let result: ElementFinder; - - beforeEach(() => { - browser.get('/di/viewChildren'); - button = element(by.css('button')); - result = element(by.css('div')); - }); - - it('should query view children', () => { - expect(result.getText()).toEqual('panes: 1, 2'); - - button.click(); - - expect(result.getText()).toEqual('panes: 1, 2, 3'); - }); -}); diff --git a/adev/src/content/api-examples/core/di/ts/viewChildren/module.ts b/adev/src/content/api-examples/core/di/ts/viewChildren/module.ts deleted file mode 100644 index 2956ed99afc2..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChildren/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -import {Pane, ViewChildrenComp} from './view_children_example'; - -@NgModule({ - imports: [BrowserModule], - declarations: [ViewChildrenComp, Pane], - bootstrap: [ViewChildrenComp], -}) -export class AppModule {} - -export {ViewChildrenComp as AppComponent}; diff --git a/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_example.ts b/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_example.ts deleted file mode 100644 index 06456ea1d3e0..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_example.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {AfterViewInit, Component, Directive, Input, QueryList, ViewChildren} from '@angular/core'; - -@Directive({ - selector: 'pane', - standalone: false, -}) -export class Pane { - @Input() id!: string; -} - -@Component({ - selector: 'example-app', - template: ` - - - - - - -
panes: {{ serializedPanes }}
- `, - standalone: false, -}) -export class ViewChildrenComp implements AfterViewInit { - @ViewChildren(Pane) panes!: QueryList; - serializedPanes: string = ''; - - shouldShow = false; - - show() { - this.shouldShow = true; - } - - ngAfterViewInit() { - this.calculateSerializedPanes(); - this.panes.changes.subscribe((r) => { - this.calculateSerializedPanes(); - }); - } - - calculateSerializedPanes() { - setTimeout(() => { - this.serializedPanes = this.panes.map((p) => p.id).join(', '); - }, 0); - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_howto.ts b/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_howto.ts deleted file mode 100644 index 0f3b0a6e94ba..000000000000 --- a/adev/src/content/api-examples/core/di/ts/viewChildren/view_children_howto.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion HowTo -import {AfterViewInit, Component, Directive, QueryList, ViewChildren} from '@angular/core'; - -@Directive({ - selector: 'child-directive', - standalone: false, -}) -class ChildDirective {} - -@Component({ - selector: 'someCmp', - templateUrl: 'someCmp.html', - standalone: false, -}) -class SomeCmp implements AfterViewInit { - @ViewChildren(ChildDirective) viewChildren!: QueryList; - - ngAfterViewInit() { - // viewChildren is set - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/core/main.ts b/adev/src/content/api-examples/core/main.ts deleted file mode 100644 index 765eac5625c0..000000000000 --- a/adev/src/content/api-examples/core/main.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; -import 'zone.js/lib/zone-spec/task-tracking'; - -// okd - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {TestsAppModule} from './test_module'; - -platformBrowserDynamic().bootstrapModule(TestsAppModule); diff --git a/adev/src/content/api-examples/core/start-server.js b/adev/src/content/api-examples/core/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/core/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/core/test_module.ts b/adev/src/content/api-examples/core/test_module.ts deleted file mode 100644 index cc0d87f74602..000000000000 --- a/adev/src/content/api-examples/core/test_module.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {RouterModule} from '@angular/router'; - -import * as animationDslExample from './animation/ts/dsl/module'; -import * as diContentChildExample from './di/ts/contentChild/module'; -import * as diContentChildrenExample from './di/ts/contentChildren/module'; -import * as diViewChildExample from './di/ts/viewChild/module'; -import * as diViewChildrenExample from './di/ts/viewChildren/module'; -import * as testabilityWhenStableExample from './testability/ts/whenStable/module'; - -@Component({ - selector: 'example-app', - template: '', - standalone: false, -}) -export class TestsAppComponent {} - -@NgModule({ - imports: [ - animationDslExample.AppModule, - diContentChildExample.AppModule, - diContentChildrenExample.AppModule, - diViewChildExample.AppModule, - diViewChildrenExample.AppModule, - testabilityWhenStableExample.AppModule, - - // Router configuration so that the individual e2e tests can load their - // app components. - RouterModule.forRoot([ - {path: 'animation/dsl', component: animationDslExample.AppComponent}, - {path: 'di/contentChild', component: diContentChildExample.AppComponent}, - {path: 'di/contentChildren', component: diContentChildrenExample.AppComponent}, - {path: 'di/viewChild', component: diViewChildExample.AppComponent}, - {path: 'di/viewChildren', component: diViewChildrenExample.AppComponent}, - {path: 'testability/whenStable', component: testabilityWhenStableExample.AppComponent}, - ]), - ], - declarations: [TestsAppComponent], - bootstrap: [TestsAppComponent], -}) -export class TestsAppModule {} diff --git a/adev/src/content/api-examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts b/adev/src/content/api-examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts deleted file mode 100644 index 4bb31de89d47..000000000000 --- a/adev/src/content/api-examples/core/testability/ts/whenStable/e2e_test/testability_example_spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -// Declare the global "window" and "document" constant since we don't want to add the "dom" -// TypeScript lib for the e2e specs that execute code in the browser and reference such -// global constants. -declare const window: any; -declare const document: any; - -describe('testability example', () => { - afterEach(verifyNoBrowserErrors); - - describe('using task tracking', () => { - const URL = '/testability/whenStable/'; - - it('times out with a list of tasks', (done) => { - browser.get(URL); - browser.ignoreSynchronization = true; - - // Script that runs in the browser and calls whenStable with a timeout. - let waitWithResultScript = function (done: any) { - let rootEl = document.querySelector('example-app'); - let testability = window.getAngularTestability(rootEl); - testability.whenStable(() => { - done(); - }, 1000); - }; - - element(by.css('.start-button')).click(); - - browser.driver.executeAsyncScript(waitWithResultScript).then(() => { - expect(element(by.css('.status')).getText()).not.toContain('done'); - done(); - }); - }); - - afterAll(() => { - browser.ignoreSynchronization = false; - }); - }); -}); diff --git a/adev/src/content/api-examples/core/testability/ts/whenStable/module.ts b/adev/src/content/api-examples/core/testability/ts/whenStable/module.ts deleted file mode 100644 index 35894ea5ccf2..000000000000 --- a/adev/src/content/api-examples/core/testability/ts/whenStable/module.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -export {AppModule, StableTestCmp as AppComponent} from './testability_example'; diff --git a/adev/src/content/api-examples/core/testability/ts/whenStable/testability_example.ts b/adev/src/content/api-examples/core/testability/ts/whenStable/testability_example.ts deleted file mode 100644 index 188de28984fa..000000000000 --- a/adev/src/content/api-examples/core/testability/ts/whenStable/testability_example.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -@Component({ - selector: 'example-app', - template: ` - -
Status: {{ status }}
- `, - standalone: false, -}) -export class StableTestCmp { - status = 'none'; - start() { - this.status = 'running'; - setTimeout(() => { - this.status = 'done'; - }, 5000); - } -} - -@NgModule({imports: [BrowserModule], declarations: [StableTestCmp], bootstrap: [StableTestCmp]}) -export class AppModule {} diff --git a/adev/src/content/api-examples/core/testing/ts/example_spec.ts b/adev/src/content/api-examples/core/testing/ts/example_spec.ts deleted file mode 100644 index 5151e20f499e..000000000000 --- a/adev/src/content/api-examples/core/testing/ts/example_spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// Import the "fake_async" example that registers tests which are shown as examples. These need -// to be valid tests, so we run them here. Note that we need to add this layer of abstraction here -// because the "jasmine_node_test" rule only picks up test files with the "_spec.ts" file suffix. -import './fake_async'; diff --git a/adev/src/content/api-examples/core/testing/ts/fake_async.ts b/adev/src/content/api-examples/core/testing/ts/fake_async.ts deleted file mode 100644 index 7c853bab709d..000000000000 --- a/adev/src/content/api-examples/core/testing/ts/fake_async.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {discardPeriodicTasks, fakeAsync, tick} from '@angular/core/testing'; - -// #docregion basic -describe('this test', () => { - it( - 'looks async but is synchronous', - fakeAsync((): void => { - let flag = false; - setTimeout(() => { - flag = true; - }, 100); - expect(flag).toBe(false); - tick(50); - expect(flag).toBe(false); - tick(50); - expect(flag).toBe(true); - }), - ); -}); -// #enddocregion - -describe('this test', () => { - it( - 'aborts a periodic timer', - fakeAsync((): void => { - // This timer is scheduled but doesn't need to complete for the - // test to pass (maybe it's a timeout for some operation). - // Leaving it will cause the test to fail... - setInterval(() => {}, 100); - - // Unless we clean it up first. - discardPeriodicTasks(); - }), - ); -}); diff --git a/adev/src/content/api-examples/core/ts/.gitkeep b/adev/src/content/api-examples/core/ts/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/adev/src/content/api-examples/core/ts/bootstrap/bootstrap.ts b/adev/src/content/api-examples/core/ts/bootstrap/bootstrap.ts deleted file mode 100644 index 2f0a19ee57a2..000000000000 --- a/adev/src/content/api-examples/core/ts/bootstrap/bootstrap.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -@Component({ - selector: 'app-root', - template: 'Hello {{ name }}!', - standalone: false, -}) -class MyApp { - name: string = 'World'; -} - -@NgModule({imports: [BrowserModule], bootstrap: [MyApp]}) -class AppModule {} - -export function main() { - platformBrowserDynamic().bootstrapModule(AppModule); -} diff --git a/adev/src/content/api-examples/core/ts/change_detect/change-detection.ts b/adev/src/content/api-examples/core/ts/change_detect/change-detection.ts deleted file mode 100644 index ac7dd5a7a766..000000000000 --- a/adev/src/content/api-examples/core/ts/change_detect/change-detection.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -/* tslint:disable:no-console */ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - Input, - NgModule, -} from '@angular/core'; -import {FormsModule} from '@angular/forms'; - -// #docregion mark-for-check -@Component({ - selector: 'app-root', - template: ` - Number of ticks: {{ numberOfTicks }} - `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -class AppComponent { - numberOfTicks = 0; - - constructor(private ref: ChangeDetectorRef) { - setInterval(() => { - this.numberOfTicks++; - // require view to be updated - this.ref.markForCheck(); - }, 1000); - } -} -// #enddocregion mark-for-check - -// #docregion detach -class DataListProvider { - // in a real application the returned data will be different every time - get data() { - return [1, 2, 3, 4, 5]; - } -} - -@Component({ - selector: 'giant-list', - template: ` -
  • Data {{ d }}
  • - `, - standalone: false, -}) -class GiantList { - constructor( - private ref: ChangeDetectorRef, - public dataProvider: DataListProvider, - ) { - ref.detach(); - setInterval(() => { - this.ref.detectChanges(); - }, 5000); - } -} - -@Component({ - selector: 'app', - providers: [DataListProvider], - template: ` - - `, - standalone: false, -}) -class App {} -// #enddocregion detach - -// #docregion reattach -class DataProvider { - data = 1; - constructor() { - setInterval(() => { - this.data = 2; - }, 500); - } -} - -@Component({ - selector: 'live-data', - inputs: ['live'], - template: 'Data: {{dataProvider.data}}', - standalone: false, -}) -class LiveData { - constructor( - private ref: ChangeDetectorRef, - public dataProvider: DataProvider, - ) {} - - @Input() - set live(value: boolean) { - if (value) { - this.ref.reattach(); - } else { - this.ref.detach(); - } - } -} - -@Component({ - selector: 'app', - providers: [DataProvider], - template: ` - Live Update: - - - `, - standalone: false, -}) -class App1 { - live = true; -} -// #enddocregion reattach - -@NgModule({declarations: [AppComponent, GiantList, App, LiveData, App1], imports: [FormsModule]}) -class CoreExamplesModule {} diff --git a/adev/src/content/api-examples/core/ts/metadata/directives.ts b/adev/src/content/api-examples/core/ts/metadata/directives.ts deleted file mode 100644 index 6c2b95012737..000000000000 --- a/adev/src/content/api-examples/core/ts/metadata/directives.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -/* tslint:disable:no-console */ -import {Component, Directive, EventEmitter, NgModule} from '@angular/core'; - -// #docregion component-input -@Component({ - selector: 'app-bank-account', - inputs: ['bankName', 'id: account-id'], - template: ` - Bank Name: {{ bankName }} Account Id: {{ id }} - `, - standalone: false, -}) -export class BankAccountComponent { - bankName: string | null = null; - id: string | null = null; - - // this property is not bound, and won't be automatically updated by Angular - normalizedBankName: string | null = null; -} - -@Component({ - selector: 'app-my-input', - template: ` - - `, - standalone: false, -}) -export class MyInputComponent {} -// #enddocregion component-input - -// #docregion component-output-interval -@Directive({ - selector: 'app-interval-dir', - outputs: ['everySecond', 'fiveSecs: everyFiveSeconds'], - standalone: false, -}) -export class IntervalDirComponent { - everySecond = new EventEmitter(); - fiveSecs = new EventEmitter(); - - constructor() { - setInterval(() => this.everySecond.emit('event'), 1000); - setInterval(() => this.fiveSecs.emit('event'), 5000); - } -} - -@Component({ - selector: 'app-my-output', - template: ` - - `, - standalone: false, -}) -export class MyOutputComponent { - onEverySecond() { - console.log('second'); - } - onEveryFiveSeconds() { - console.log('five seconds'); - } -} -// #enddocregion component-output-interval - -@NgModule({ - declarations: [BankAccountComponent, MyInputComponent, IntervalDirComponent, MyOutputComponent], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/core/ts/metadata/encapsulation.ts b/adev/src/content/api-examples/core/ts/metadata/encapsulation.ts deleted file mode 100644 index 910d3924709e..000000000000 --- a/adev/src/content/api-examples/core/ts/metadata/encapsulation.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, ViewEncapsulation} from '@angular/core'; - -// #docregion longform -@Component({ - selector: 'app-root', - template: ` -

    Hello World!

    - Shadow DOM Rocks! - `, - styles: [ - ` - :host { - display: block; - border: 1px solid black; - } - h1 { - color: blue; - } - .red { - background-color: red; - } - `, - ], - encapsulation: ViewEncapsulation.ShadowDom, - standalone: false, -}) -class MyApp {} -// #enddocregion diff --git a/adev/src/content/api-examples/core/ts/metadata/lifecycle_hooks_spec.ts b/adev/src/content/api-examples/core/ts/metadata/lifecycle_hooks_spec.ts deleted file mode 100644 index ebb2b728cfb6..000000000000 --- a/adev/src/content/api-examples/core/ts/metadata/lifecycle_hooks_spec.ts +++ /dev/null @@ -1,221 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - AfterContentChecked, - AfterContentInit, - AfterViewChecked, - AfterViewInit, - Component, - DoCheck, - Input, - OnChanges, - OnDestroy, - OnInit, - SimpleChanges, - Type, -} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; - -(function () { - describe('lifecycle hooks examples', () => { - it('should work with ngOnInit', () => { - // #docregion OnInit - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements OnInit { - ngOnInit() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngOnInit', []]]); - }); - - it('should work with ngDoCheck', () => { - // #docregion DoCheck - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements DoCheck { - ngDoCheck() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngDoCheck', []]]); - }); - - it('should work with ngAfterContentChecked', () => { - // #docregion AfterContentChecked - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements AfterContentChecked { - ngAfterContentChecked() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentChecked', []]]); - }); - - it('should work with ngAfterContentInit', () => { - // #docregion AfterContentInit - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements AfterContentInit { - ngAfterContentInit() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterContentInit', []]]); - }); - - it('should work with ngAfterViewChecked', () => { - // #docregion AfterViewChecked - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements AfterViewChecked { - ngAfterViewChecked() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewChecked', []]]); - }); - - it('should work with ngAfterViewInit', () => { - // #docregion AfterViewInit - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements AfterViewInit { - ngAfterViewInit() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngAfterViewInit', []]]); - }); - - it('should work with ngOnDestroy', () => { - // #docregion OnDestroy - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements OnDestroy { - ngOnDestroy() { - // ... - } - } - // #enddocregion - - expect(createAndLogComponent(MyComponent)).toEqual([['ngOnDestroy', []]]); - }); - - it('should work with ngOnChanges', () => { - // #docregion OnChanges - @Component({ - selector: 'my-cmp', - template: ` - ... - `, - standalone: false, - }) - class MyComponent implements OnChanges { - @Input() prop: number = 0; - - ngOnChanges(changes: SimpleChanges) { - // changes.prop contains the old and the new value... - } - } - // #enddocregion - - const log = createAndLogComponent(MyComponent, ['prop']); - expect(log.length).toBe(1); - expect(log[0][0]).toBe('ngOnChanges'); - const changes: SimpleChanges = log[0][1][0]; - expect(changes['prop'].currentValue).toBe(true); - }); - }); - - function createAndLogComponent(clazz: Type, inputs: string[] = []): any[] { - const log: any[] = []; - createLoggingSpiesFromProto(clazz, log); - - const inputBindings = inputs.map((input) => `[${input}] = true`).join(' '); - - @Component({ - template: ` - - `, - standalone: false, - }) - class ParentComponent {} - - const fixture = TestBed.configureTestingModule({ - declarations: [ParentComponent, clazz], - }).createComponent(ParentComponent); - fixture.detectChanges(); - fixture.destroy(); - return log; - } - - function createLoggingSpiesFromProto(clazz: Type, log: any[]) { - const proto = clazz.prototype; - // For ES2015+ classes, members are not enumerable in the prototype. - Object.getOwnPropertyNames(proto).forEach((method) => { - if (method === 'constructor') { - return; - } - - proto[method] = (...args: any[]) => { - log.push([method, args]); - }; - }); - } -})(); diff --git a/adev/src/content/api-examples/core/ts/metadata/metadata.ts b/adev/src/content/api-examples/core/ts/metadata/metadata.ts deleted file mode 100644 index 102cf9d71da2..000000000000 --- a/adev/src/content/api-examples/core/ts/metadata/metadata.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Attribute, Component, Directive, Pipe} from '@angular/core'; - -class CustomDirective {} - -@Component({ - selector: 'greet', - template: 'Hello {{name}}!', - standalone: false, -}) -class Greet { - name: string = 'World'; -} - -// #docregion attributeFactory -@Component({ - selector: 'page', - template: 'Title: {{title}}', - standalone: false, -}) -class Page { - title: string; - constructor(@Attribute('title') title: string) { - this.title = title; - } -} -// #enddocregion - -// #docregion attributeMetadata -@Directive({ - selector: 'input', - standalone: false, -}) -class InputAttrDirective { - constructor(@Attribute('type') type: string) { - // type would be 'text' in this example - } -} -// #enddocregion - -@Directive({ - selector: 'input', - standalone: false, -}) -class InputDirective { - constructor() { - // Add some logic. - } -} - -@Pipe({ - name: 'lowercase', - standalone: false, -}) -class Lowercase { - transform(v: string, args: any[]) { - return v.toLowerCase(); - } -} diff --git a/adev/src/content/api-examples/core/ts/pipes/pipeTransFormEx_module.ts b/adev/src/content/api-examples/core/ts/pipes/pipeTransFormEx_module.ts deleted file mode 100644 index 6bef0d0a9a1c..000000000000 --- a/adev/src/content/api-examples/core/ts/pipes/pipeTransFormEx_module.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {TruncatePipe as SimpleTruncatePipe} from './simple_truncate'; -import {TruncatePipe} from './truncate'; - -@NgModule({declarations: [SimpleTruncatePipe, TruncatePipe]}) -export class TruncateModule {} diff --git a/adev/src/content/api-examples/core/ts/pipes/simple_truncate.ts b/adev/src/content/api-examples/core/ts/pipes/simple_truncate.ts deleted file mode 100644 index 52665e9d83f2..000000000000 --- a/adev/src/content/api-examples/core/ts/pipes/simple_truncate.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// #docregion -import {Pipe, PipeTransform} from '@angular/core'; - -@Pipe({ - name: 'truncate', - standalone: false, -}) -export class TruncatePipe implements PipeTransform { - transform(value: string) { - return value.split(' ').slice(0, 2).join(' ') + '...'; - } -} diff --git a/adev/src/content/api-examples/core/ts/pipes/truncate.ts b/adev/src/content/api-examples/core/ts/pipes/truncate.ts deleted file mode 100644 index 10bd278d1886..000000000000 --- a/adev/src/content/api-examples/core/ts/pipes/truncate.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// #docregion -import {Pipe, PipeTransform} from '@angular/core'; - -@Pipe({ - name: 'truncate', - standalone: false, -}) -export class TruncatePipe implements PipeTransform { - transform(value: string, length: number, symbol: string) { - return value.split(' ').slice(0, length).join(' ') + symbol; - } -} diff --git a/adev/src/content/api-examples/core/ts/platform/platform.ts b/adev/src/content/api-examples/core/ts/platform/platform.ts deleted file mode 100644 index 2d3916a41e63..000000000000 --- a/adev/src/content/api-examples/core/ts/platform/platform.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {ApplicationRef, Component, DoBootstrap, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; - -@Component({ - selector: 'app-root', - template: ` -

    Component One

    - `, - standalone: false, -}) -export class ComponentOne {} - -@Component({ - selector: 'app-root', - template: ` -

    Component Two

    - `, - standalone: false, -}) -export class ComponentTwo {} - -@Component({ - selector: 'app-root', - template: ` -

    Component Three

    - `, - standalone: false, -}) -export class ComponentThree {} - -@Component({ - selector: 'app-root', - template: ` -

    Component Four

    - `, - standalone: false, -}) -export class ComponentFour {} - -@NgModule({imports: [BrowserModule], declarations: [ComponentOne, ComponentTwo]}) -export class AppModule implements DoBootstrap { - // #docregion componentSelector - ngDoBootstrap(appRef: ApplicationRef) { - this.fetchDataFromApi().then((componentName: string) => { - if (componentName === 'ComponentOne') { - appRef.bootstrap(ComponentOne); - } else { - appRef.bootstrap(ComponentTwo); - } - }); - } - // #enddocregion - - fetchDataFromApi(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve('ComponentTwo'); - }, 2000); - }); - } -} - -@NgModule({imports: [BrowserModule], declarations: [ComponentThree]}) -export class AppModuleTwo implements DoBootstrap { - // #docregion cssSelector - ngDoBootstrap(appRef: ApplicationRef) { - appRef.bootstrap(ComponentThree, '#root-element'); - } - // #enddocregion cssSelector -} - -@NgModule({imports: [BrowserModule], declarations: [ComponentFour]}) -export class AppModuleThree implements DoBootstrap { - // #docregion domNode - ngDoBootstrap(appRef: ApplicationRef) { - const element = document.querySelector('#root-element'); - appRef.bootstrap(ComponentFour, element); - } - // #enddocregion domNode -} diff --git a/adev/src/content/api-examples/core/ts/prod_mode/my_component.ts b/adev/src/content/api-examples/core/ts/prod_mode/my_component.ts deleted file mode 100644 index 948e8d25e2be..000000000000 --- a/adev/src/content/api-examples/core/ts/prod_mode/my_component.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component} from '@angular/core'; - -@Component({ - selector: 'my-component', - template: '

    My Component

    ', - standalone: false, -}) -export class MyComponent {} diff --git a/adev/src/content/api-examples/core/ts/prod_mode/prod_mode_example.ts b/adev/src/content/api-examples/core/ts/prod_mode/prod_mode_example.ts deleted file mode 100644 index a062b71f45bf..000000000000 --- a/adev/src/content/api-examples/core/ts/prod_mode/prod_mode_example.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {enableProdMode, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {MyComponent} from './my_component'; - -enableProdMode(); - -@NgModule({imports: [BrowserModule], declarations: [MyComponent], bootstrap: [MyComponent]}) -export class AppModule {} - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/adev/src/content/api-examples/forms/BUILD.bazel b/adev/src/content/api-examples/forms/BUILD.bazel deleted file mode 100644 index e56085266223..000000000000 --- a/adev/src/content/api-examples/forms/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("//tools:defaults.bzl", "esbuild", "http_server", "ng_module", "protractor_web_test_suite", "ts_library") - -package(default_visibility = ["//visibility:public"]) - -ng_module( - name = "forms_examples", - srcs = glob( - ["**/*.ts"], - exclude = ["**/*_spec.ts"], - ), - deps = [ - "//packages/core", - "//packages/forms", - "//packages/platform-browser", - "//packages/platform-browser-dynamic", - "//packages/router", - "//packages/zone.js/lib", - "@npm//rxjs", - ], -) - -ts_library( - name = "forms_e2e_tests_lib", - testonly = True, - srcs = glob(["**/e2e_test/*_spec.ts"]), - tsconfig = "//packages/examples:tsconfig-e2e.json", - deps = [ - "//packages/examples/test-utils", - "//packages/private/testing", - "@npm//@types/jasminewd2", - "@npm//protractor", - ], -) - -esbuild( - name = "app_bundle", - entry_point = ":main.ts", - deps = [":forms_examples"], -) - -http_server( - name = "devserver", - srcs = ["//packages/examples:index.html"], - additional_root_paths = ["angular/packages/examples"], - deps = [":app_bundle"], -) - -protractor_web_test_suite( - name = "protractor_tests", - on_prepare = ":start-server.js", - server = ":devserver", - deps = [ - ":forms_e2e_tests_lib", - "@npm//selenium-webdriver", - ], -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/forms/main.ts b/adev/src/content/api-examples/forms/main.ts deleted file mode 100644 index 0c83c8112df0..000000000000 --- a/adev/src/content/api-examples/forms/main.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {TestsAppModule} from './test_module'; - -platformBrowserDynamic().bootstrapModule(TestsAppModule); diff --git a/adev/src/content/api-examples/forms/start-server.js b/adev/src/content/api-examples/forms/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/forms/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/forms/test_module.ts b/adev/src/content/api-examples/forms/test_module.ts deleted file mode 100644 index 5b992ce02300..000000000000 --- a/adev/src/content/api-examples/forms/test_module.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {RouterModule} from '@angular/router'; - -import * as formBuilderExample from './ts/formBuilder/module'; -import * as nestedFormArrayExample from './ts/nestedFormArray/module'; -import * as nestedFormGroupExample from './ts/nestedFormGroup/module'; -import * as ngModelGroupExample from './ts/ngModelGroup/module'; -import * as radioButtonsExample from './ts/radioButtons/module'; -import * as reactiveRadioButtonsExample from './ts/reactiveRadioButtons/module'; -import * as reactiveSelectControlExample from './ts/reactiveSelectControl/module'; -import * as selectControlExample from './ts/selectControl/module'; -import * as simpleFormExample from './ts/simpleForm/module'; -import * as simpleFormControlExample from './ts/simpleFormControl/module'; -import * as simpleFormGroupExample from './ts/simpleFormGroup/module'; -import * as simpleNgModelExample from './ts/simpleNgModel/module'; - -@Component({ - selector: 'example-app', - template: '', - standalone: false, -}) -export class TestsAppComponent {} - -@NgModule({ - imports: [ - formBuilderExample.AppModule, - nestedFormArrayExample.AppModule, - nestedFormGroupExample.AppModule, - ngModelGroupExample.AppModule, - radioButtonsExample.AppModule, - reactiveRadioButtonsExample.AppModule, - reactiveSelectControlExample.AppModule, - selectControlExample.AppModule, - simpleFormExample.AppModule, - simpleFormControlExample.AppModule, - simpleFormGroupExample.AppModule, - simpleNgModelExample.AppModule, - - // Router configuration so that the individual e2e tests can load their - // app components. - RouterModule.forRoot([ - {path: 'formBuilder', component: formBuilderExample.AppComponent}, - {path: 'nestedFormArray', component: nestedFormArrayExample.AppComponent}, - {path: 'nestedFormGroup', component: nestedFormGroupExample.AppComponent}, - {path: 'ngModelGroup', component: ngModelGroupExample.AppComponent}, - {path: 'radioButtons', component: radioButtonsExample.AppComponent}, - {path: 'reactiveRadioButtons', component: reactiveRadioButtonsExample.AppComponent}, - {path: 'reactiveSelectControl', component: reactiveSelectControlExample.AppComponent}, - {path: 'selectControl', component: selectControlExample.AppComponent}, - {path: 'simpleForm', component: simpleFormExample.AppComponent}, - {path: 'simpleFormControl', component: simpleFormControlExample.AppComponent}, - {path: 'simpleFormGroup', component: simpleFormGroupExample.AppComponent}, - {path: 'simpleNgModel', component: simpleNgModelExample.AppComponent}, - ]), - ], - declarations: [TestsAppComponent], - bootstrap: [TestsAppComponent], -}) -export class TestsAppModule {} diff --git a/adev/src/content/api-examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts b/adev/src/content/api-examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts deleted file mode 100644 index 6a837d1705a7..000000000000 --- a/adev/src/content/api-examples/forms/ts/formBuilder/e2e_test/form_builder_spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('formBuilder example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - let paragraphs: ElementArrayFinder; - - beforeEach(() => { - browser.get('/formBuilder'); - inputs = element.all(by.css('input')); - paragraphs = element.all(by.css('p')); - }); - - it('should populate the UI with initial values', () => { - expect(inputs.get(0).getAttribute('value')).toEqual('Nancy'); - expect(inputs.get(1).getAttribute('value')).toEqual('Drew'); - }); - - it('should update the validation status', () => { - expect(paragraphs.get(1).getText()).toEqual('Validation status: VALID'); - - inputs.get(0).click(); - inputs.get(0).clear(); - inputs.get(0).sendKeys('a'); - expect(paragraphs.get(1).getText()).toEqual('Validation status: INVALID'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/formBuilder/form_builder_example.ts b/adev/src/content/api-examples/forms/ts/formBuilder/form_builder_example.ts deleted file mode 100644 index c60a9f720c64..000000000000 --- a/adev/src/content/api-examples/forms/ts/formBuilder/form_builder_example.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion disabled-control -import {Component, Inject} from '@angular/core'; -import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; -// #enddocregion disabled-control - -@Component({ - selector: 'example-app', - template: ` -
    -
    - - -
    - - -
    - -

    Value: {{ form.value | json }}

    -

    Validation status: {{ form.status }}

    - `, - standalone: false, -}) -export class FormBuilderComp { - form: FormGroup; - - constructor(@Inject(FormBuilder) formBuilder: FormBuilder) { - this.form = formBuilder.group( - { - name: formBuilder.group({ - first: ['Nancy', Validators.minLength(2)], - last: 'Drew', - }), - email: '', - }, - {updateOn: 'change'}, - ); - } -} - -// #docregion disabled-control -@Component({ - selector: 'app-disabled-form-control', - template: ` - - `, - standalone: false, -}) -export class DisabledFormControlComponent { - control: FormControl; - - constructor(private formBuilder: FormBuilder) { - this.control = formBuilder.control({value: 'my val', disabled: true}); - } -} -// #enddocregion disabled-control diff --git a/adev/src/content/api-examples/forms/ts/formBuilder/module.ts b/adev/src/content/api-examples/forms/ts/formBuilder/module.ts deleted file mode 100644 index 6330713e9a57..000000000000 --- a/adev/src/content/api-examples/forms/ts/formBuilder/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {DisabledFormControlComponent, FormBuilderComp} from './form_builder_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [FormBuilderComp, DisabledFormControlComponent], - bootstrap: [FormBuilderComp], -}) -export class AppModule {} - -export {FormBuilderComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts b/adev/src/content/api-examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts deleted file mode 100644 index 304081add69c..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormArray/e2e_test/nested_form_array_spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('nestedFormArray example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - let buttons: ElementArrayFinder; - - beforeEach(() => { - browser.get('/nestedFormArray'); - inputs = element.all(by.css('input')); - buttons = element.all(by.css('button')); - }); - - it('should populate the UI with initial values', () => { - expect(inputs.get(0).getAttribute('value')).toEqual('SF'); - expect(inputs.get(1).getAttribute('value')).toEqual('NY'); - }); - - it('should add inputs programmatically', () => { - expect(inputs.count()).toBe(2); - - buttons.get(1).click(); - inputs = element.all(by.css('input')); - - expect(inputs.count()).toBe(3); - }); - - it('should set the value programmatically', () => { - buttons.get(2).click(); - expect(inputs.get(0).getAttribute('value')).toEqual('LA'); - expect(inputs.get(1).getAttribute('value')).toEqual('MTV'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/nestedFormArray/module.ts b/adev/src/content/api-examples/forms/ts/nestedFormArray/module.ts deleted file mode 100644 index 166d2f90c8aa..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormArray/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {NestedFormArray} from './nested_form_array_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [NestedFormArray], - bootstrap: [NestedFormArray], -}) -export class AppModule {} - -export {NestedFormArray as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/nestedFormArray/nested_form_array_example.ts b/adev/src/content/api-examples/forms/ts/nestedFormArray/nested_form_array_example.ts deleted file mode 100644 index 9b5459db2ae8..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormArray/nested_form_array_example.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* tslint:disable:no-console */ -// #docregion Component -import {Component} from '@angular/core'; -import {FormArray, FormControl, FormGroup} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    -
    -
    - -
    -
    - -
    - - - - `, - standalone: false, -}) -export class NestedFormArray { - form = new FormGroup({ - cities: new FormArray([new FormControl('SF'), new FormControl('NY')]), - }); - - get cities(): FormArray { - return this.form.get('cities') as FormArray; - } - - addCity() { - this.cities.push(new FormControl()); - } - - onSubmit() { - console.log(this.cities.value); // ['SF', 'NY'] - console.log(this.form.value); // { cities: ['SF', 'NY'] } - } - - setPreset() { - this.cities.patchValue(['LA', 'MTV']); - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts b/adev/src/content/api-examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts deleted file mode 100644 index 037106c194b6..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormGroup/e2e_test/nested_form_group_spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('nestedFormGroup example', () => { - afterEach(verifyNoBrowserErrors); - let firstInput: ElementFinder; - let lastInput: ElementFinder; - let button: ElementFinder; - - beforeEach(() => { - browser.get('/nestedFormGroup'); - firstInput = element(by.css('[formControlName="first"]')); - lastInput = element(by.css('[formControlName="last"]')); - button = element(by.css('button:not([type="submit"])')); - }); - - it('should populate the UI with initial values', () => { - expect(firstInput.getAttribute('value')).toEqual('Nancy'); - expect(lastInput.getAttribute('value')).toEqual('Drew'); - }); - - it('should show the error when name is invalid', () => { - firstInput.click(); - firstInput.clear(); - firstInput.sendKeys('a'); - - expect(element(by.css('p')).getText()).toEqual('Name is invalid.'); - }); - - it('should set the value programmatically', () => { - button.click(); - expect(firstInput.getAttribute('value')).toEqual('Bess'); - expect(lastInput.getAttribute('value')).toEqual('Marvin'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/nestedFormGroup/module.ts b/adev/src/content/api-examples/forms/ts/nestedFormGroup/module.ts deleted file mode 100644 index 4e6c4e9d47e2..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormGroup/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {NestedFormGroupComp} from './nested_form_group_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [NestedFormGroupComp], - bootstrap: [NestedFormGroupComp], -}) -export class AppModule {} - -export {NestedFormGroupComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/nestedFormGroup/nested_form_group_example.ts b/adev/src/content/api-examples/forms/ts/nestedFormGroup/nested_form_group_example.ts deleted file mode 100644 index dec365ea760d..000000000000 --- a/adev/src/content/api-examples/forms/ts/nestedFormGroup/nested_form_group_example.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* tslint:disable:no-console */ -// #docregion Component -import {Component} from '@angular/core'; -import {FormControl, FormGroup, Validators} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    -

    Name is invalid.

    - -
    - - -
    - - -
    - - - `, - standalone: false, -}) -export class NestedFormGroupComp { - form = new FormGroup({ - name: new FormGroup({ - first: new FormControl('Nancy', Validators.minLength(2)), - last: new FormControl('Drew', Validators.required), - }), - email: new FormControl(), - }); - - get first(): any { - return this.form.get('name.first'); - } - - get name(): any { - return this.form.get('name'); - } - - onSubmit() { - console.log(this.first.value); // 'Nancy' - console.log(this.name.value); // {first: 'Nancy', last: 'Drew'} - console.log(this.form.value); // {name: {first: 'Nancy', last: 'Drew'}, email: ''} - console.log(this.form.status); // VALID - } - - setPreset() { - this.name.setValue({first: 'Bess', last: 'Marvin'}); - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts b/adev/src/content/api-examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts deleted file mode 100644 index 2dddce99af70..000000000000 --- a/adev/src/content/api-examples/forms/ts/ngModelGroup/e2e_test/ng_model_group_spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('ngModelGroup example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - let buttons: ElementArrayFinder; - - beforeEach(() => { - browser.get('/ngModelGroup'); - inputs = element.all(by.css('input')); - buttons = element.all(by.css('button')); - }); - - it('should populate the UI with initial values', () => { - expect(inputs.get(0).getAttribute('value')).toEqual('Nancy'); - expect(inputs.get(1).getAttribute('value')).toEqual('J'); - expect(inputs.get(2).getAttribute('value')).toEqual('Drew'); - }); - - it('should show the error when name is invalid', () => { - inputs.get(0).click(); - inputs.get(0).clear(); - inputs.get(0).sendKeys('a'); - - expect(element(by.css('p')).getText()).toEqual('Name is invalid.'); - }); - - it('should set the value when changing the domain model', () => { - buttons.get(1).click(); - expect(inputs.get(0).getAttribute('value')).toEqual('Bess'); - expect(inputs.get(1).getAttribute('value')).toEqual('S'); - expect(inputs.get(2).getAttribute('value')).toEqual('Marvin'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/ngModelGroup/module.ts b/adev/src/content/api-examples/forms/ts/ngModelGroup/module.ts deleted file mode 100644 index 2e538c902f1f..000000000000 --- a/adev/src/content/api-examples/forms/ts/ngModelGroup/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {NgModelGroupComp} from './ng_model_group_example'; - -@NgModule({ - imports: [BrowserModule, FormsModule], - declarations: [NgModelGroupComp], - bootstrap: [NgModelGroupComp], -}) -export class AppModule {} - -export {NgModelGroupComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/ngModelGroup/ng_model_group_example.ts b/adev/src/content/api-examples/forms/ts/ngModelGroup/ng_model_group_example.ts deleted file mode 100644 index 9e272d574e2d..000000000000 --- a/adev/src/content/api-examples/forms/ts/ngModelGroup/ng_model_group_example.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* tslint:disable:no-console */ -// #docregion Component -import {Component} from '@angular/core'; -import {NgForm} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    -

    Name is invalid.

    - -
    - - - -
    - - - -
    - - - `, - standalone: false, -}) -export class NgModelGroupComp { - name = {first: 'Nancy', middle: 'J', last: 'Drew'}; - - onSubmit(f: NgForm) { - console.log(f.value); // {name: {first: 'Nancy', middle: 'J', last: 'Drew'}, email: ''} - console.log(f.valid); // true - } - - setValue() { - this.name = {first: 'Bess', middle: 'S', last: 'Marvin'}; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts b/adev/src/content/api-examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts deleted file mode 100644 index 427f80d297b5..000000000000 --- a/adev/src/content/api-examples/forms/ts/radioButtons/e2e_test/radio_button_spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('radioButtons example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - let paragraphs: ElementArrayFinder; - - beforeEach(() => { - browser.get('/radioButtons'); - inputs = element.all(by.css('input')); - paragraphs = element.all(by.css('p')); - }); - - it('should populate the UI with initial values', () => { - expect(inputs.get(0).getAttribute('checked')).toEqual(null); - expect(inputs.get(1).getAttribute('checked')).toEqual('true'); - expect(inputs.get(2).getAttribute('checked')).toEqual(null); - - expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "lamb" }'); - expect(paragraphs.get(1).getText()).toEqual('myFood value: lamb'); - }); - - it('update model and other buttons as the UI value changes', () => { - inputs.get(0).click(); - - expect(inputs.get(0).getAttribute('checked')).toEqual('true'); - expect(inputs.get(1).getAttribute('checked')).toEqual(null); - expect(inputs.get(2).getAttribute('checked')).toEqual(null); - - expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "beef" }'); - expect(paragraphs.get(1).getText()).toEqual('myFood value: beef'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/radioButtons/module.ts b/adev/src/content/api-examples/forms/ts/radioButtons/module.ts deleted file mode 100644 index 212184b089c3..000000000000 --- a/adev/src/content/api-examples/forms/ts/radioButtons/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {RadioButtonComp} from './radio_button_example'; - -@NgModule({ - imports: [BrowserModule, FormsModule], - declarations: [RadioButtonComp], - bootstrap: [RadioButtonComp], -}) -export class AppModule {} - -export {RadioButtonComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/radioButtons/radio_button_example.ts b/adev/src/content/api-examples/forms/ts/radioButtons/radio_button_example.ts deleted file mode 100644 index a5470e6ece27..000000000000 --- a/adev/src/content/api-examples/forms/ts/radioButtons/radio_button_example.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -import {Component} from '@angular/core'; - -@Component({ - selector: 'example-app', - template: ` -
    - - Beef - - Lamb - - Fish -
    - -

    Form value: {{ f.value | json }}

    - -

    myFood value: {{ myFood }}

    - - `, - standalone: false, -}) -export class RadioButtonComp { - myFood = 'lamb'; -} diff --git a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts b/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts deleted file mode 100644 index 18296723f5bb..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/e2e_test/reactive_radio_button_spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('radioButtons example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - - beforeEach(() => { - browser.get('/reactiveRadioButtons'); - inputs = element.all(by.css('input')); - }); - - it('should populate the UI with initial values', () => { - expect(inputs.get(0).getAttribute('checked')).toEqual(null); - expect(inputs.get(1).getAttribute('checked')).toEqual('true'); - expect(inputs.get(2).getAttribute('checked')).toEqual(null); - - expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "lamb" }'); - }); - - it('update model and other buttons as the UI value changes', () => { - inputs.get(0).click(); - - expect(inputs.get(0).getAttribute('checked')).toEqual('true'); - expect(inputs.get(1).getAttribute('checked')).toEqual(null); - expect(inputs.get(2).getAttribute('checked')).toEqual(null); - - expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "beef" }'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/module.ts b/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/module.ts deleted file mode 100644 index 84402087f63f..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {ReactiveRadioButtonComp} from './reactive_radio_button_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [ReactiveRadioButtonComp], - bootstrap: [ReactiveRadioButtonComp], -}) -export class AppModule {} - -export {ReactiveRadioButtonComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts b/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts deleted file mode 100644 index 973759ee79ef..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Reactive -import {Component} from '@angular/core'; -import {FormControl, FormGroup} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    - - Beef - - Lamb - - Fish -
    - -

    Form value: {{ form.value | json }}

    - - `, - standalone: false, -}) -export class ReactiveRadioButtonComp { - form = new FormGroup({ - food: new FormControl('lamb'), - }); -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts b/adev/src/content/api-examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts deleted file mode 100644 index d66bc0de0a80..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/e2e_test/reactive_select_control_spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('reactiveSelectControl example', () => { - afterEach(verifyNoBrowserErrors); - let select: ElementFinder; - let options: ElementArrayFinder; - let p: ElementFinder; - - beforeEach(() => { - browser.get('/reactiveSelectControl'); - select = element(by.css('select')); - options = element.all(by.css('option')); - p = element(by.css('p')); - }); - - it('should populate the initial selection', () => { - expect(select.getAttribute('value')).toEqual('3: Object'); - expect(options.get(3).getAttribute('selected')).toBe('true'); - }); - - it('should update the model when the value changes in the UI', () => { - select.click(); - options.get(0).click(); - - expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/module.ts b/adev/src/content/api-examples/forms/ts/reactiveSelectControl/module.ts deleted file mode 100644 index c260cb7ba1ba..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {ReactiveSelectComp} from './reactive_select_control_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [ReactiveSelectComp], - bootstrap: [ReactiveSelectComp], -}) -export class AppModule {} - -export {ReactiveSelectComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/reactive_select_control_example.ts b/adev/src/content/api-examples/forms/ts/reactiveSelectControl/reactive_select_control_example.ts deleted file mode 100644 index 83b2281ba73f..000000000000 --- a/adev/src/content/api-examples/forms/ts/reactiveSelectControl/reactive_select_control_example.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component} from '@angular/core'; -import {FormControl, FormGroup} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    - -
    - -

    Form value: {{ form.value | json }}

    - - `, - standalone: false, -}) -export class ReactiveSelectComp { - states = [ - {name: 'Arizona', abbrev: 'AZ'}, - {name: 'California', abbrev: 'CA'}, - {name: 'Colorado', abbrev: 'CO'}, - {name: 'New York', abbrev: 'NY'}, - {name: 'Pennsylvania', abbrev: 'PA'}, - ]; - - form = new FormGroup({ - state: new FormControl(this.states[3]), - }); -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/selectControl/e2e_test/select_control_spec.ts b/adev/src/content/api-examples/forms/ts/selectControl/e2e_test/select_control_spec.ts deleted file mode 100644 index 193c24cef358..000000000000 --- a/adev/src/content/api-examples/forms/ts/selectControl/e2e_test/select_control_spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('selectControl example', () => { - afterEach(verifyNoBrowserErrors); - let select: ElementFinder; - let options: ElementArrayFinder; - let p: ElementFinder; - - beforeEach(() => { - browser.get('/selectControl'); - select = element(by.css('select')); - options = element.all(by.css('option')); - p = element(by.css('p')); - }); - - it('should initially select the placeholder option', () => { - expect(options.get(0).getAttribute('selected')).toBe('true'); - }); - - it('should update the model when the value changes in the UI', () => { - select.click(); - options.get(1).click(); - - expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/selectControl/module.ts b/adev/src/content/api-examples/forms/ts/selectControl/module.ts deleted file mode 100644 index 18367160cc7c..000000000000 --- a/adev/src/content/api-examples/forms/ts/selectControl/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {SelectControlComp} from './select_control_example'; - -@NgModule({ - imports: [BrowserModule, FormsModule], - declarations: [SelectControlComp], - bootstrap: [SelectControlComp], -}) -export class AppModule {} - -export {SelectControlComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/selectControl/select_control_example.ts b/adev/src/content/api-examples/forms/ts/selectControl/select_control_example.ts deleted file mode 100644 index 09c412a26775..000000000000 --- a/adev/src/content/api-examples/forms/ts/selectControl/select_control_example.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component} from '@angular/core'; - -@Component({ - selector: 'example-app', - template: ` -
    - -
    - -

    Form value: {{ f.value | json }}

    - - `, - standalone: false, -}) -export class SelectControlComp { - states = [ - {name: 'Arizona', abbrev: 'AZ'}, - {name: 'California', abbrev: 'CA'}, - {name: 'Colorado', abbrev: 'CO'}, - {name: 'New York', abbrev: 'NY'}, - {name: 'Pennsylvania', abbrev: 'PA'}, - ]; -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts b/adev/src/content/api-examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts deleted file mode 100644 index 21857d5137d8..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleForm/e2e_test/simple_form_spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('simpleForm example', () => { - afterEach(verifyNoBrowserErrors); - let inputs: ElementArrayFinder; - let paragraphs: ElementArrayFinder; - - beforeEach(() => { - browser.get('/simpleForm'); - inputs = element.all(by.css('input')); - paragraphs = element.all(by.css('p')); - }); - - it('should update the domain model as you type', () => { - inputs.get(0).click(); - inputs.get(0).sendKeys('Nancy'); - - inputs.get(1).click(); - inputs.get(1).sendKeys('Drew'); - - expect(paragraphs.get(0).getText()).toEqual('First name value: Nancy'); - expect(paragraphs.get(2).getText()).toEqual('Form value: { "first": "Nancy", "last": "Drew" }'); - }); - - it('should report the validity correctly', () => { - expect(paragraphs.get(1).getText()).toEqual('First name valid: false'); - expect(paragraphs.get(3).getText()).toEqual('Form valid: false'); - inputs.get(0).click(); - inputs.get(0).sendKeys('a'); - - expect(paragraphs.get(1).getText()).toEqual('First name valid: true'); - expect(paragraphs.get(3).getText()).toEqual('Form valid: true'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/simpleForm/module.ts b/adev/src/content/api-examples/forms/ts/simpleForm/module.ts deleted file mode 100644 index b5b9b6473f8c..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleForm/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {SimpleFormComp} from './simple_form_example'; - -@NgModule({ - imports: [BrowserModule, FormsModule], - declarations: [SimpleFormComp], - bootstrap: [SimpleFormComp], -}) -export class AppModule {} - -export {SimpleFormComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/simpleForm/simple_form_example.ts b/adev/src/content/api-examples/forms/ts/simpleForm/simple_form_example.ts deleted file mode 100644 index 81bfed6312ba..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleForm/simple_form_example.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* tslint:disable:no-console */ -// #docregion Component -import {Component} from '@angular/core'; -import {NgForm} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    - - - -
    - -

    First name value: {{ first.value }}

    -

    First name valid: {{ first.valid }}

    -

    Form value: {{ f.value | json }}

    -

    Form valid: {{ f.valid }}

    - `, - standalone: false, -}) -export class SimpleFormComp { - onSubmit(f: NgForm) { - console.log(f.value); // { first: '', last: '' } - console.log(f.valid); // false - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts b/adev/src/content/api-examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts deleted file mode 100644 index 879ec120a3da..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormControl/e2e_test/simple_form_control_spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('simpleFormControl example', () => { - afterEach(verifyNoBrowserErrors); - - describe('index view', () => { - let input: ElementFinder; - let valueP: ElementFinder; - let statusP: ElementFinder; - - beforeEach(() => { - browser.get('/simpleFormControl'); - input = element(by.css('input')); - valueP = element(by.css('p:first-of-type')); - statusP = element(by.css('p:last-of-type')); - }); - - it('should populate the form control value in the DOM', () => { - expect(input.getAttribute('value')).toEqual('value'); - expect(valueP.getText()).toEqual('Value: value'); - }); - - it('should update the value as user types', () => { - input.click(); - input.sendKeys('s!'); - - expect(valueP.getText()).toEqual('Value: values!'); - }); - - it('should show the correct validity state', () => { - expect(statusP.getText()).toEqual('Validation status: VALID'); - - input.click(); - input.clear(); - input.sendKeys('a'); - expect(statusP.getText()).toEqual('Validation status: INVALID'); - }); - - it('should set the value programmatically', () => { - element(by.css('button')).click(); - expect(input.getAttribute('value')).toEqual('new value'); - }); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/simpleFormControl/module.ts b/adev/src/content/api-examples/forms/ts/simpleFormControl/module.ts deleted file mode 100644 index 32716278f187..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormControl/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {SimpleFormControl} from './simple_form_control_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [SimpleFormControl], - bootstrap: [SimpleFormControl], -}) -export class AppModule {} - -export {SimpleFormControl as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/simpleFormControl/simple_form_control_example.ts b/adev/src/content/api-examples/forms/ts/simpleFormControl/simple_form_control_example.ts deleted file mode 100644 index d5a0ebdcd8ad..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormControl/simple_form_control_example.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component} from '@angular/core'; -import {FormControl, Validators} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` - - -

    Value: {{ control.value }}

    -

    Validation status: {{ control.status }}

    - - - `, - standalone: false, -}) -export class SimpleFormControl { - control: FormControl = new FormControl('value', Validators.minLength(2)); - - setValue() { - this.control.setValue('new value'); - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts b/adev/src/content/api-examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts deleted file mode 100644 index 05a1c99125d2..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormGroup/e2e_test/simple_form_group_spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('formControlName example', () => { - afterEach(verifyNoBrowserErrors); - - describe('index view', () => { - let firstInput: ElementFinder; - let lastInput: ElementFinder; - - beforeEach(() => { - browser.get('/simpleFormGroup'); - firstInput = element(by.css('[formControlName="first"]')); - lastInput = element(by.css('[formControlName="last"]')); - }); - - it('should populate the form control values in the DOM', () => { - expect(firstInput.getAttribute('value')).toEqual('Nancy'); - expect(lastInput.getAttribute('value')).toEqual('Drew'); - }); - - it('should show the error when the form is invalid', () => { - firstInput.click(); - firstInput.clear(); - firstInput.sendKeys('a'); - - expect(element(by.css('div')).getText()).toEqual('Name is too short.'); - }); - - it('should set the value programmatically', () => { - element(by.css('button:not([type="submit"])')).click(); - expect(firstInput.getAttribute('value')).toEqual('Carson'); - expect(lastInput.getAttribute('value')).toEqual('Drew'); - }); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/simpleFormGroup/module.ts b/adev/src/content/api-examples/forms/ts/simpleFormGroup/module.ts deleted file mode 100644 index 9745325a5eb6..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormGroup/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {ReactiveFormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {SimpleFormGroup} from './simple_form_group_example'; - -@NgModule({ - imports: [BrowserModule, ReactiveFormsModule], - declarations: [SimpleFormGroup], - bootstrap: [SimpleFormGroup], -}) -export class AppModule {} - -export {SimpleFormGroup as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/simpleFormGroup/simple_form_group_example.ts b/adev/src/content/api-examples/forms/ts/simpleFormGroup/simple_form_group_example.ts deleted file mode 100644 index 95f9b43bd1e1..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleFormGroup/simple_form_group_example.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -/* tslint:disable:no-console */ -// #docregion Component -import {Component} from '@angular/core'; -import {FormControl, FormGroup, Validators} from '@angular/forms'; - -@Component({ - selector: 'example-app', - template: ` -
    -
    Name is too short.
    - - - - - -
    - - `, - standalone: false, -}) -export class SimpleFormGroup { - form = new FormGroup({ - first: new FormControl('Nancy', Validators.minLength(2)), - last: new FormControl('Drew'), - }); - - get first(): any { - return this.form.get('first'); - } - - onSubmit(): void { - console.log(this.form.value); // {first: 'Nancy', last: 'Drew'} - } - - setValue() { - this.form.setValue({first: 'Carson', last: 'Drew'}); - } -} - -// #enddocregion diff --git a/adev/src/content/api-examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts b/adev/src/content/api-examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts deleted file mode 100644 index 92f90660e2d8..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleNgModel/e2e_test/simple_ng_model_spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../packages/examples/test-utils/index'; - -describe('simpleNgModel example', () => { - afterEach(verifyNoBrowserErrors); - let input: ElementFinder; - let paragraphs: ElementArrayFinder; - let button: ElementFinder; - - beforeEach(() => { - browser.get('/simpleNgModel'); - input = element(by.css('input')); - paragraphs = element.all(by.css('p')); - button = element(by.css('button')); - }); - - it('should update the domain model as you type', () => { - input.click(); - input.sendKeys('Carson'); - - expect(paragraphs.get(0).getText()).toEqual('Value: Carson'); - }); - - it('should report the validity correctly', () => { - expect(paragraphs.get(1).getText()).toEqual('Valid: false'); - input.click(); - input.sendKeys('a'); - - expect(paragraphs.get(1).getText()).toEqual('Valid: true'); - }); - - it('should set the value by changing the domain model', () => { - button.click(); - expect(input.getAttribute('value')).toEqual('Nancy'); - }); -}); diff --git a/adev/src/content/api-examples/forms/ts/simpleNgModel/module.ts b/adev/src/content/api-examples/forms/ts/simpleNgModel/module.ts deleted file mode 100644 index 6c89425f6fd1..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleNgModel/module.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {NgModule} from '@angular/core'; -import {FormsModule} from '@angular/forms'; -import {BrowserModule} from '@angular/platform-browser'; -import {SimpleNgModelComp} from './simple_ng_model_example'; - -@NgModule({ - imports: [BrowserModule, FormsModule], - declarations: [SimpleNgModelComp], - bootstrap: [SimpleNgModelComp], -}) -export class AppModule {} - -export {SimpleNgModelComp as AppComponent}; diff --git a/adev/src/content/api-examples/forms/ts/simpleNgModel/simple_ng_model_example.ts b/adev/src/content/api-examples/forms/ts/simpleNgModel/simple_ng_model_example.ts deleted file mode 100644 index 2e17d76b8a03..000000000000 --- a/adev/src/content/api-examples/forms/ts/simpleNgModel/simple_ng_model_example.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion Component -import {Component} from '@angular/core'; - -@Component({ - selector: 'example-app', - template: ` - - -

    Value: {{ name }}

    -

    Valid: {{ ctrl.valid }}

    - - - `, - standalone: false, -}) -export class SimpleNgModelComp { - name: string = ''; - - setValue() { - this.name = 'Nancy'; - } -} -// #enddocregion diff --git a/adev/src/content/api-examples/http/ts/.gitkeep b/adev/src/content/api-examples/http/ts/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/adev/src/content/api-examples/platform-browser/BUILD.bazel b/adev/src/content/api-examples/platform-browser/BUILD.bazel deleted file mode 100644 index f594500164f8..000000000000 --- a/adev/src/content/api-examples/platform-browser/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("//tools:defaults.bzl", "ng_module") - -package(default_visibility = ["//visibility:public"]) - -ng_module( - name = "platform_browser_examples", - srcs = glob(["**/*.ts"]), - deps = [ - "//packages/compiler", - "//packages/core", - "//packages/platform-browser", - "//packages/platform-browser-dynamic", - ], -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/platform-browser/dom/debug/ts/by/by.ts b/adev/src/content/api-examples/platform-browser/dom/debug/ts/by/by.ts deleted file mode 100644 index f7a62f4e60d8..000000000000 --- a/adev/src/content/api-examples/platform-browser/dom/debug/ts/by/by.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {DebugElement} from '@angular/core'; -import {By} from '@angular/platform-browser'; - -let debugElement: DebugElement = undefined!; -class MyDirective {} - -// #docregion by_all -debugElement.query(By.all()); -// #enddocregion - -// #docregion by_css -debugElement.query(By.css('[attribute]')); -// #enddocregion - -// #docregion by_directive -debugElement.query(By.directive(MyDirective)); -// #enddocregion diff --git a/adev/src/content/api-examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts b/adev/src/content/api-examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts deleted file mode 100644 index 831fc5c3076d..000000000000 --- a/adev/src/content/api-examples/platform-browser/dom/debug/ts/debug_element_view_listener/providers.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -@Component({ - selector: 'my-component', - template: 'text', - standalone: false, -}) -class MyAppComponent {} -@NgModule({imports: [BrowserModule], bootstrap: [MyAppComponent]}) -class AppModule {} -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/adev/src/content/api-examples/router/activated-route/BUILD.bazel b/adev/src/content/api-examples/router/activated-route/BUILD.bazel deleted file mode 100644 index 84c44956acb0..000000000000 --- a/adev/src/content/api-examples/router/activated-route/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools:defaults.bzl", "esbuild", "http_server", "ng_module") - -package(default_visibility = ["//visibility:public"]) - -ng_module( - name = "router_activated_route_examples", - srcs = glob( - ["**/*.ts"], - ), - deps = [ - "//packages/core", - "//packages/platform-browser", - "//packages/platform-browser-dynamic", - "//packages/router", - "//packages/zone.js/lib", - "@npm//rxjs", - ], -) - -esbuild( - name = "app_bundle", - entry_point = ":main.ts", - deps = [":router_activated_route_examples"], -) - -http_server( - name = "devserver", - srcs = ["//packages/examples:index.html"], - additional_root_paths = ["angular/packages/examples"], - deps = [":app_bundle"], -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/router/activated-route/main.ts b/adev/src/content/api-examples/router/activated-route/main.ts deleted file mode 100644 index d48b7c36918c..000000000000 --- a/adev/src/content/api-examples/router/activated-route/main.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {AppModule} from './module'; - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/adev/src/content/api-examples/router/activated-route/module.ts b/adev/src/content/api-examples/router/activated-route/module.ts deleted file mode 100644 index a068768dc008..000000000000 --- a/adev/src/content/api-examples/router/activated-route/module.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// #docregion activated-route -import {Component, NgModule} from '@angular/core'; -// #enddocregion activated-route -import {BrowserModule} from '@angular/platform-browser'; -// #docregion activated-route -import {ActivatedRoute, RouterModule} from '@angular/router'; -import {Observable} from 'rxjs'; -import {map} from 'rxjs/operators'; -// #enddocregion activated-route - -// #docregion activated-route - -@Component({ - // #enddocregion activated-route - selector: 'example-app', - template: '...', - standalone: false, -}) -export class ActivatedRouteComponent { - constructor(route: ActivatedRoute) { - const id: Observable = route.params.pipe(map((p) => p['id'])); - const url: Observable = route.url.pipe(map((segments) => segments.join(''))); - // route.data includes both `data` and `resolve` - const user = route.data.pipe(map((d) => d['user'])); - } -} -// #enddocregion activated-route - -@NgModule({ - imports: [BrowserModule, RouterModule.forRoot([])], - declarations: [ActivatedRouteComponent], - bootstrap: [ActivatedRouteComponent], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/router/route_functional_guards.ts b/adev/src/content/api-examples/router/route_functional_guards.ts deleted file mode 100644 index a0b4db629284..000000000000 --- a/adev/src/content/api-examples/router/route_functional_guards.ts +++ /dev/null @@ -1,179 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Component, inject, Injectable} from '@angular/core'; -import {bootstrapApplication} from '@angular/platform-browser'; -import { - ActivatedRoute, - ActivatedRouteSnapshot, - CanActivateChildFn, - CanActivateFn, - CanMatchFn, - provideRouter, - ResolveFn, - Route, - RouterStateSnapshot, - UrlSegment, -} from '@angular/router'; - -@Component({ - template: '', - standalone: false, -}) -export class App {} - -@Component({ - template: '', - standalone: false, -}) -export class TeamComponent {} - -// #docregion CanActivateFn -@Injectable() -class UserToken {} - -@Injectable() -class PermissionsService { - canActivate(currentUser: UserToken, userId: string): boolean { - return true; - } - canMatch(currentUser: UserToken): boolean { - return true; - } -} - -const canActivateTeam: CanActivateFn = ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot, -) => { - return inject(PermissionsService).canActivate(inject(UserToken), route.params['id']); -}; -// #enddocregion - -// #docregion CanActivateFnInRoute -bootstrapApplication(App, { - providers: [ - provideRouter([ - { - path: 'team/:id', - component: TeamComponent, - canActivate: [canActivateTeam], - }, - ]), - ], -}); -// #enddocregion - -// #docregion CanActivateChildFn -const canActivateChildExample: CanActivateChildFn = ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot, -) => { - return inject(PermissionsService).canActivate(inject(UserToken), route.params['id']); -}; - -bootstrapApplication(App, { - providers: [ - provideRouter([ - { - path: 'team/:id', - component: TeamComponent, - canActivateChild: [canActivateChildExample], - children: [], - }, - ]), - ], -}); -// #enddocregion - -// #docregion CanDeactivateFn -@Component({ - template: '', - standalone: false, -}) -export class UserComponent { - hasUnsavedChanges = true; -} - -bootstrapApplication(App, { - providers: [ - provideRouter([ - { - path: 'user/:id', - component: UserComponent, - canDeactivate: [(component: UserComponent) => !component.hasUnsavedChanges], - }, - ]), - ], -}); -// #enddocregion - -// #docregion CanMatchFn -const canMatchTeam: CanMatchFn = (route: Route, segments: UrlSegment[]) => { - return inject(PermissionsService).canMatch(inject(UserToken)); -}; - -bootstrapApplication(App, { - providers: [ - provideRouter([ - { - path: 'team/:id', - component: TeamComponent, - canMatch: [canMatchTeam], - }, - ]), - ], -}); -// #enddocregion - -// #docregion ResolveDataUse -@Component({ - template: '', - standalone: false, -}) -export class HeroDetailComponent { - constructor(private activatedRoute: ActivatedRoute) {} - - ngOnInit() { - this.activatedRoute.data.subscribe(({hero}) => { - // do something with your resolved data ... - }); - } -} -// #enddocregion - -// #docregion ResolveFn -interface Hero { - name: string; -} -@Injectable() -export class HeroService { - getHero(id: string) { - return {name: `Superman-${id}`}; - } -} - -export const heroResolver: ResolveFn = ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot, -) => { - return inject(HeroService).getHero(route.paramMap.get('id')!); -}; - -bootstrapApplication(App, { - providers: [ - provideRouter([ - { - path: 'detail/:id', - component: HeroDetailComponent, - resolve: {hero: heroResolver}, - }, - ]), - ], -}); -// #enddocregion diff --git a/adev/src/content/api-examples/router/testing/BUILD.bazel b/adev/src/content/api-examples/router/testing/BUILD.bazel deleted file mode 100644 index bbaf09459fdb..000000000000 --- a/adev/src/content/api-examples/router/testing/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("//tools:defaults.bzl", "jasmine_node_test", "karma_web_test_suite", "ts_library") - -package(default_visibility = ["//visibility:public"]) - -ts_library( - name = "test_lib", - testonly = True, - srcs = glob(["**/*.spec.ts"]), - deps = [ - "//packages/common", - "//packages/core", - "//packages/core/testing", - "//packages/router", - "//packages/router/testing", - ], -) - -jasmine_node_test( - name = "test", - bootstrap = ["//tools/testing:node"], - deps = [ - ":test_lib", - ], -) - -karma_web_test_suite( - name = "test_web", - deps = [ - ":test_lib", - ], -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/router/testing/test/router_testing_harness_examples.spec.ts b/adev/src/content/api-examples/router/testing/test/router_testing_harness_examples.spec.ts deleted file mode 100644 index 309ec85e091c..000000000000 --- a/adev/src/content/api-examples/router/testing/test/router_testing_harness_examples.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {AsyncPipe} from '@angular/common'; -import {Component, inject} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; -import {ActivatedRoute, CanActivateFn, provideRouter, Router} from '@angular/router'; -import {RouterTestingHarness} from '@angular/router/testing'; - -describe('navigate for test examples', () => { - // #docregion RoutedComponent - it('navigates to routed component', async () => { - @Component({standalone: true, template: 'hello {{name}}'}) - class TestCmp { - name = 'world'; - } - - TestBed.configureTestingModule({ - providers: [provideRouter([{path: '', component: TestCmp}])], - }); - - const harness = await RouterTestingHarness.create(); - const activatedComponent = await harness.navigateByUrl('/', TestCmp); - expect(activatedComponent).toBeInstanceOf(TestCmp); - expect(harness.routeNativeElement?.innerHTML).toContain('hello world'); - }); - // #enddocregion - - it('testing a guard', async () => { - @Component({standalone: true, template: ''}) - class AdminComponent {} - @Component({standalone: true, template: ''}) - class LoginComponent {} - - // #docregion Guard - let isLoggedIn = false; - const isLoggedInGuard: CanActivateFn = () => { - return isLoggedIn ? true : inject(Router).parseUrl('/login'); - }; - - TestBed.configureTestingModule({ - providers: [ - provideRouter([ - {path: 'admin', canActivate: [isLoggedInGuard], component: AdminComponent}, - {path: 'login', component: LoginComponent}, - ]), - ], - }); - - const harness = await RouterTestingHarness.create('/admin'); - expect(TestBed.inject(Router).url).toEqual('/login'); - isLoggedIn = true; - await harness.navigateByUrl('/admin'); - expect(TestBed.inject(Router).url).toEqual('/admin'); - // #enddocregion - }); - - it('test a ActivatedRoute', async () => { - // #docregion ActivatedRoute - @Component({ - standalone: true, - imports: [AsyncPipe], - template: ` - search: {{ (route.queryParams | async)?.query }} - `, - }) - class SearchCmp { - constructor( - readonly route: ActivatedRoute, - readonly router: Router, - ) {} - - async searchFor(thing: string) { - await this.router.navigate([], {queryParams: {query: thing}}); - } - } - - TestBed.configureTestingModule({ - providers: [provideRouter([{path: 'search', component: SearchCmp}])], - }); - - const harness = await RouterTestingHarness.create(); - const activatedComponent = await harness.navigateByUrl('/search', SearchCmp); - await activatedComponent.searchFor('books'); - harness.detectChanges(); - expect(TestBed.inject(Router).url).toEqual('/search?query=books'); - expect(harness.routeNativeElement?.innerHTML).toContain('books'); - // #enddocregion - }); -}); diff --git a/adev/src/content/api-examples/router/utils/functional_guards.ts b/adev/src/content/api-examples/router/utils/functional_guards.ts deleted file mode 100644 index 5ea493e4b1df..000000000000 --- a/adev/src/content/api-examples/router/utils/functional_guards.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {Injectable} from '@angular/core'; -import {mapToCanActivate, mapToResolve, Route} from '@angular/router'; - -// #docregion CanActivate -@Injectable({providedIn: 'root'}) -export class AdminGuard { - canActivate() { - return true; - } -} - -const route: Route = { - path: 'admin', - canActivate: mapToCanActivate([AdminGuard]), -}; -// #enddocregion - -// #docregion Resolve -@Injectable({providedIn: 'root'}) -export class ResolveUser { - resolve() { - return {name: 'Bob'}; - } -} - -const userRoute: Route = { - path: 'user', - resolve: { - user: mapToResolve(ResolveUser), - }, -}; -// #enddocregion diff --git a/adev/src/content/api-examples/service-worker/push/e2e_test/push_spec.ts b/adev/src/content/api-examples/service-worker/push/e2e_test/push_spec.ts deleted file mode 100644 index 04f8e2bbb649..000000000000 --- a/adev/src/content/api-examples/service-worker/push/e2e_test/push_spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; -import {verifyNoBrowserErrors} from '../../../../../../../packages/examples/test-utils/index'; - -describe('SW `SwPush` example', () => { - const pageUrl = '/push'; - const appElem = element(by.css('example-app')); - - afterEach(verifyNoBrowserErrors); - - it('should be enabled', () => { - browser.get(pageUrl); - expect(appElem.getText()).toBe('SW enabled: true'); - }); -}); diff --git a/adev/src/content/api-examples/service-worker/push/main.ts b/adev/src/content/api-examples/service-worker/push/main.ts deleted file mode 100644 index d48b7c36918c..000000000000 --- a/adev/src/content/api-examples/service-worker/push/main.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {AppModule} from './module'; - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/adev/src/content/api-examples/service-worker/push/module.ts b/adev/src/content/api-examples/service-worker/push/module.ts deleted file mode 100644 index 0eb9e59466b3..000000000000 --- a/adev/src/content/api-examples/service-worker/push/module.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// tslint:disable: no-duplicate-imports -import {Component, NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {ServiceWorkerModule} from '@angular/service-worker'; -// #docregion inject-sw-push -import {SwPush} from '@angular/service-worker'; -// #enddocregion inject-sw-push -// tslint:enable: no-duplicate-imports - -const PUBLIC_VAPID_KEY_OF_SERVER = '...'; - -@Component({ - selector: 'example-app', - template: 'SW enabled: {{ swPush.isEnabled }}', - standalone: false, -}) -// #docregion inject-sw-push -export class AppComponent { - constructor(readonly swPush: SwPush) {} - // #enddocregion inject-sw-push - - // #docregion subscribe-to-push - private async subscribeToPush() { - try { - const sub = await this.swPush.requestSubscription({ - serverPublicKey: PUBLIC_VAPID_KEY_OF_SERVER, - }); - // TODO: Send to server. - } catch (err) { - console.error('Could not subscribe due to:', err); - } - } - // #enddocregion subscribe-to-push - - private subscribeToNotificationClicks() { - // #docregion subscribe-to-notification-clicks - this.swPush.notificationClicks.subscribe(({action, notification}) => { - // TODO: Do something in response to notification click. - }); - // #enddocregion subscribe-to-notification-clicks - } - // #docregion inject-sw-push -} -// #enddocregion inject-sw-push - -@NgModule({ - bootstrap: [AppComponent], - declarations: [AppComponent], - imports: [BrowserModule, ServiceWorkerModule.register('ngsw-worker.js')], -}) -export class AppModule {} diff --git a/adev/src/content/api-examples/service-worker/push/ngsw-worker.js b/adev/src/content/api-examples/service-worker/push/ngsw-worker.js deleted file mode 100644 index b4e14c9506fc..000000000000 --- a/adev/src/content/api-examples/service-worker/push/ngsw-worker.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// Mock `ngsw-worker.js` used for testing the examples. -// Immediately takes over and unregisters itself. -self.addEventListener('install', (evt) => evt.waitUntil(self.skipWaiting())); -self.addEventListener('activate', (evt) => - evt.waitUntil(self.clients.claim().then(() => self.registration.unregister())), -); diff --git a/adev/src/content/api-examples/service-worker/push/start-server.js b/adev/src/content/api-examples/service-worker/push/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/service-worker/push/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/service-worker/registration-options/e2e_test/registration-options_spec.ts b/adev/src/content/api-examples/service-worker/registration-options/e2e_test/registration-options_spec.ts deleted file mode 100644 index a1844bd60cfc..000000000000 --- a/adev/src/content/api-examples/service-worker/registration-options/e2e_test/registration-options_spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; -import {verifyNoBrowserErrors} from '../../../../../../../packages/examples/test-utils/index'; - -describe('SW `SwRegistrationOptions` example', () => { - const pageUrl = '/registration-options'; - const appElem = element(by.css('example-app')); - - afterEach(verifyNoBrowserErrors); - - it('not register the SW by default', () => { - browser.get(pageUrl); - expect(appElem.getText()).toBe('SW enabled: false'); - }); - - it('register the SW when navigating to `?sw=true`', () => { - browser.get(`${pageUrl}?sw=true`); - expect(appElem.getText()).toBe('SW enabled: true'); - }); -}); diff --git a/adev/src/content/api-examples/service-worker/registration-options/main.ts b/adev/src/content/api-examples/service-worker/registration-options/main.ts deleted file mode 100644 index d48b7c36918c..000000000000 --- a/adev/src/content/api-examples/service-worker/registration-options/main.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import 'zone.js/lib/browser/rollup-main'; - -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; - -import {AppModule} from './module'; - -platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/adev/src/content/api-examples/service-worker/registration-options/module.ts b/adev/src/content/api-examples/service-worker/registration-options/module.ts deleted file mode 100644 index 74e252439e4d..000000000000 --- a/adev/src/content/api-examples/service-worker/registration-options/module.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// tslint:disable: no-duplicate-imports -import {Component} from '@angular/core'; -// #docregion registration-options -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {ServiceWorkerModule, SwRegistrationOptions} from '@angular/service-worker'; -// #enddocregion registration-options -import {SwUpdate} from '@angular/service-worker'; -// tslint:enable: no-duplicate-imports - -@Component({ - selector: 'example-app', - template: 'SW enabled: {{ swu.isEnabled }}', - standalone: false, -}) -export class AppComponent { - constructor(readonly swu: SwUpdate) {} -} -// #docregion registration-options - -@NgModule({ - // #enddocregion registration-options - bootstrap: [AppComponent], - declarations: [AppComponent], - // #docregion registration-options - imports: [BrowserModule, ServiceWorkerModule.register('ngsw-worker.js')], - providers: [ - { - provide: SwRegistrationOptions, - useFactory: () => ({enabled: location.search.includes('sw=true')}), - }, - ], -}) -export class AppModule {} -// #enddocregion registration-options diff --git a/adev/src/content/api-examples/service-worker/registration-options/ngsw-worker.js b/adev/src/content/api-examples/service-worker/registration-options/ngsw-worker.js deleted file mode 100644 index b4e14c9506fc..000000000000 --- a/adev/src/content/api-examples/service-worker/registration-options/ngsw-worker.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// Mock `ngsw-worker.js` used for testing the examples. -// Immediately takes over and unregisters itself. -self.addEventListener('install', (evt) => evt.waitUntil(self.skipWaiting())); -self.addEventListener('activate', (evt) => - evt.waitUntil(self.clients.claim().then(() => self.registration.unregister())), -); diff --git a/adev/src/content/api-examples/service-worker/registration-options/start-server.js b/adev/src/content/api-examples/service-worker/registration-options/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/service-worker/registration-options/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/test-utils/index.ts b/adev/src/content/api-examples/test-utils/index.ts deleted file mode 100644 index 727375002a42..000000000000 --- a/adev/src/content/api-examples/test-utils/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -/* tslint:disable:no-console */ -import {logging, WebDriver} from 'selenium-webdriver'; - -declare var browser: WebDriver; -declare var expect: any; - -// TODO (juliemr): remove this method once this becomes a protractor plugin -export async function verifyNoBrowserErrors() { - const browserLog = await browser.manage().logs().get('browser'); - const collectedErrors: any[] = []; - - browserLog.forEach((logEntry) => { - const msg = logEntry.message; - - console.log('>> ' + msg, logEntry); - - if (logEntry.level.value >= logging.Level.INFO.value) { - collectedErrors.push(msg); - } - }); - - expect(collectedErrors).toEqual([]); -} diff --git a/adev/src/content/api-examples/testing/ts/testing.ts b/adev/src/content/api-examples/testing/ts/testing.ts deleted file mode 100644 index da8d2015493b..000000000000 --- a/adev/src/content/api-examples/testing/ts/testing.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -let db: any; -class MyService {} -class MyMockService implements MyService {} - -describe('some component', () => { - it('does something', () => { - // This is a test. - }); -}); - -// tslint:disable-next-line:ban -fdescribe('some component', () => { - it('has a test', () => { - // This test will run. - }); -}); -describe('another component', () => { - it('also has a test', () => { - throw 'This test will not run.'; - }); -}); - -xdescribe('some component', () => { - it('has a test', () => { - throw 'This test will not run.'; - }); -}); -describe('another component', () => { - it('also has a test', () => { - // This test will run. - }); -}); - -describe('some component', () => { - // tslint:disable-next-line:ban - fit('has a test', () => { - // This test will run. - }); - it('has another test', () => { - throw 'This test will not run.'; - }); -}); - -describe('some component', () => { - xit('has a test', () => { - throw 'This test will not run.'; - }); - it('has another test', () => { - // This test will run. - }); -}); - -describe('some component', () => { - beforeEach(() => { - db.connect(); - }); - it('uses the db', () => { - // Database is connected. - }); -}); - -describe('some component', () => { - afterEach((done: Function) => { - db.reset().then((_: any) => done()); - }); - it('uses the db', () => { - // This test can leave the database in a dirty state. - // The afterEach will ensure it gets reset. - }); -}); diff --git a/adev/src/content/api-examples/upgrade/index.html b/adev/src/content/api-examples/upgrade/index.html deleted file mode 100644 index 9c9843d9a5b9..000000000000 --- a/adev/src/content/api-examples/upgrade/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Angular Upgrade Examples - - - - - - - Loading... - - - - - - - diff --git a/adev/src/content/api-examples/upgrade/start-server.js b/adev/src/content/api-examples/upgrade/start-server.js deleted file mode 100644 index 07d52c5eb3fa..000000000000 --- a/adev/src/content/api-examples/upgrade/start-server.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -const protractorUtils = require('@bazel/protractor/protractor-utils'); -const protractor = require('protractor'); - -module.exports = async function (config) { - const {port} = await protractorUtils.runServer(config.workspace, config.server, '--port', []); - const serverUrl = `http://localhost:${port}`; - - protractor.browser.baseUrl = serverUrl; -}; diff --git a/adev/src/content/api-examples/upgrade/static/ts/full/e2e_test/static_full_spec.ts b/adev/src/content/api-examples/upgrade/static/ts/full/e2e_test/static_full_spec.ts deleted file mode 100644 index 2dfdef042441..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/full/e2e_test/static_full_spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -function loadPage() { - browser.rootEl = 'example-app'; - browser.get('/'); -} - -describe('upgrade/static (full)', () => { - beforeEach(loadPage); - afterEach(verifyNoBrowserErrors); - - it('should render the `ng2-heroes` component', () => { - expect(element(by.css('h1')).getText()).toEqual('Heroes'); - expect(element.all(by.css('p')).get(0).getText()).toEqual('There are 3 heroes.'); - }); - - it('should render 3 ng1-hero components', () => { - const heroComponents = element.all(by.css('ng1-hero')); - expect(heroComponents.count()).toEqual(3); - }); - - it('should add a new hero when the "Add Hero" button is pressed', () => { - const addHeroButton = element.all(by.css('button')).last(); - expect(addHeroButton.getText()).toEqual('Add Hero'); - addHeroButton.click(); - const heroComponents = element.all(by.css('ng1-hero')); - expect(heroComponents.last().element(by.css('h2')).getText()).toEqual('Kamala Khan'); - }); - - it('should remove a hero when the "Remove" button is pressed', () => { - let firstHero = element.all(by.css('ng1-hero')).get(0); - expect(firstHero.element(by.css('h2')).getText()).toEqual('Superman'); - - const removeHeroButton = firstHero.element(by.css('button')); - expect(removeHeroButton.getText()).toEqual('Remove'); - removeHeroButton.click(); - - const heroComponents = element.all(by.css('ng1-hero')); - expect(heroComponents.count()).toEqual(2); - - firstHero = element.all(by.css('ng1-hero')).get(0); - expect(firstHero.element(by.css('h2')).getText()).toEqual('Wonder Woman'); - }); -}); diff --git a/adev/src/content/api-examples/upgrade/static/ts/full/module.spec.ts b/adev/src/content/api-examples/upgrade/static/ts/full/module.spec.ts deleted file mode 100644 index cb4ff8898780..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/full/module.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docregion angular-setup -import {TestBed} from '@angular/core/testing'; -import { - createAngularJSTestingModule, - createAngularTestingModule, -} from '@angular/upgrade/static/testing'; - -import {HeroesService, ng1AppModule, Ng2AppModule} from './module'; - -const {module, inject} = (window as any).angular.mock; - -// #enddocregion angular-setup -describe('HeroesService (from Angular)', () => { - // #docregion angular-setup - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [createAngularTestingModule([ng1AppModule.name]), Ng2AppModule], - }); - }); - // #enddocregion angular-setup - - // #docregion angular-spec - it('should have access to the HeroesService', () => { - const heroesService = TestBed.inject(HeroesService); - expect(heroesService).toBeDefined(); - }); - // #enddocregion angular-spec -}); - -describe('HeroesService (from AngularJS)', () => { - // #docregion angularjs-setup - beforeEach(module(createAngularJSTestingModule([Ng2AppModule]))); - beforeEach(module(ng1AppModule.name)); - // #enddocregion angularjs-setup - - // #docregion angularjs-spec - it('should have access to the HeroesService', inject((heroesService: HeroesService) => { - expect(heroesService).toBeDefined(); - })); - // #enddocregion angularjs-spec -}); diff --git a/adev/src/content/api-examples/upgrade/static/ts/full/module.ts b/adev/src/content/api-examples/upgrade/static/ts/full/module.ts deleted file mode 100644 index 13c0b09f184b..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/full/module.ts +++ /dev/null @@ -1,202 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ -// #docplaster -import { - Component, - Directive, - ElementRef, - EventEmitter, - Injectable, - Injector, - Input, - NgModule, - Output, -} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import { - downgradeComponent, - downgradeInjectable, - UpgradeComponent, - UpgradeModule, -} from '@angular/upgrade/static'; - -declare var angular: ng.IAngularStatic; - -export interface Hero { - name: string; - description: string; -} - -// #docregion ng1-text-formatter-service -export class TextFormatter { - titleCase(value: string) { - return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()); - } -} - -// #enddocregion -// #docregion ng2-heroes -// This Angular component will be "downgraded" to be used in AngularJS -@Component({ - selector: 'ng2-heroes', - // This template uses the upgraded `ng1-hero` component - // Note that because its element is compiled by Angular we must use camelCased attribute names - template: ` -
    - -
    - - Super Hero - -
    - - `, - standalone: false, -}) -export class Ng2HeroesComponent { - @Input() heroes!: Hero[]; - @Output() addHero = new EventEmitter(); - @Output() removeHero = new EventEmitter(); -} -// #enddocregion - -// #docregion ng2-heroes-service -// This Angular service will be "downgraded" to be used in AngularJS -@Injectable() -export class HeroesService { - heroes: Hero[] = [ - {name: 'superman', description: 'The man of steel'}, - {name: 'wonder woman', description: 'Princess of the Amazons'}, - {name: 'thor', description: 'The hammer-wielding god'}, - ]; - - // #docregion use-ng1-upgraded-service - constructor(textFormatter: TextFormatter) { - // Change all the hero names to title case, using the "upgraded" AngularJS service - this.heroes.forEach((hero: Hero) => (hero.name = textFormatter.titleCase(hero.name))); - } - // #enddocregion - - addHero() { - this.heroes = this.heroes.concat([ - {name: 'Kamala Khan', description: 'Epic shape-shifting healer'}, - ]); - } - - removeHero(hero: Hero) { - this.heroes = this.heroes.filter((item: Hero) => item !== hero); - } -} -// #enddocregion - -// #docregion ng1-hero-wrapper -// This Angular directive will act as an interface to the "upgraded" AngularJS component -@Directive({ - selector: 'ng1-hero', - standalone: false, -}) -export class Ng1HeroComponentWrapper extends UpgradeComponent { - // The names of the input and output properties here must match the names of the - // `<` and `&` bindings in the AngularJS component that is being wrapped - @Input() hero!: Hero; - @Output() onRemove: EventEmitter = new EventEmitter(); - - constructor(elementRef: ElementRef, injector: Injector) { - // We must pass the name of the directive as used by AngularJS to the super - super('ng1Hero', elementRef, injector); - } -} -// #enddocregion - -// #docregion ng2-module -// This NgModule represents the Angular pieces of the application -@NgModule({ - declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper], - providers: [ - HeroesService, - // #docregion upgrade-ng1-service - // Register an Angular provider whose value is the "upgraded" AngularJS service - {provide: TextFormatter, useFactory: (i: any) => i.get('textFormatter'), deps: ['$injector']}, - // #enddocregion - ], - // We must import `UpgradeModule` to get access to the AngularJS core services - imports: [BrowserModule, UpgradeModule], -}) -// #docregion bootstrap-ng1 -export class Ng2AppModule { - // #enddocregion ng2-module - constructor(private upgrade: UpgradeModule) {} - - ngDoBootstrap() { - // We bootstrap the AngularJS app. - this.upgrade.bootstrap(document.body, [ng1AppModule.name]); - } - // #docregion ng2-module -} -// #enddocregion bootstrap-ng1 -// #enddocregion ng2-module - -// This Angular 1 module represents the AngularJS pieces of the application -export const ng1AppModule: ng.IModule = angular.module('ng1AppModule', []); - -// #docregion ng1-hero -// This AngularJS component will be "upgraded" to be used in Angular -ng1AppModule.component('ng1Hero', { - bindings: {hero: '<', onRemove: '&'}, - transclude: true, - template: `
    -

    {{ $ctrl.hero.name }}

    -

    {{ $ctrl.hero.description }}

    - `, -}); -// #enddocregion - -// #docregion ng1-text-formatter-service -// This AngularJS service will be "upgraded" to be used in Angular -ng1AppModule.service('textFormatter', [TextFormatter]); -// #enddocregion - -// #docregion downgrade-ng2-heroes-service -// Register an AngularJS service, whose value is the "downgraded" Angular injectable. -ng1AppModule.factory('heroesService', downgradeInjectable(HeroesService) as any); -// #enddocregion - -// #docregion ng2-heroes-wrapper -// This directive will act as the interface to the "downgraded" Angular component -ng1AppModule.directive('ng2Heroes', downgradeComponent({component: Ng2HeroesComponent})); -// #enddocregion - -// #docregion example-app -// This is our top level application component -ng1AppModule.component('exampleApp', { - // We inject the "downgraded" HeroesService into this AngularJS component - // (We don't need the `HeroesService` type for AngularJS DI - it just helps with TypeScript - // compilation) - controller: [ - 'heroesService', - function (heroesService: HeroesService) { - this.heroesService = heroesService; - }, - ], - // This template makes use of the downgraded `ng2-heroes` component - // Note that because its element is compiled by AngularJS we must use kebab-case attributes - // for inputs and outputs - template: ` - -

    Heroes

    -

    There are {{ $ctrl.heroesService.heroes.length }} heroes.

    -
    `, -}); -// #enddocregion - -// #docregion bootstrap-ng2 -// We bootstrap the Angular module as we would do in a normal Angular app. -// (We are using the dynamic browser platform as this example has not been compiled AOT.) -platformBrowserDynamic().bootstrapModule(Ng2AppModule); -// #enddocregion diff --git a/adev/src/content/api-examples/upgrade/static/ts/full/styles.css b/adev/src/content/api-examples/upgrade/static/ts/full/styles.css deleted file mode 100644 index f3785c1c229d..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/full/styles.css +++ /dev/null @@ -1,17 +0,0 @@ -ng2-heroes { - border: solid black 2px; - display: block; - padding: 5px; -} - -ng1-hero { - border: solid green 2px; - margin-top: 5px; - padding: 5px; - display: block; -} - -.title { - background-color: blue; - color: white; -} diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/BUILD.bazel b/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/BUILD.bazel deleted file mode 100644 index 34f8e0297944..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("//packages/examples/upgrade:upgrade_example.bzl", "create_upgrade_example_targets") - -package(default_visibility = ["//visibility:public"]) - -create_upgrade_example_targets( - name = "lite-multi-shared", - srcs = glob( - ["**/*.ts"], - exclude = ["**/*_spec.ts"], - ), - e2e_srcs = glob(["e2e_test/*_spec.ts"]), - entry_point = ":module.ts", -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts b/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts deleted file mode 100644 index 506a141dd3a5..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/e2e_test/static_lite_multi_shared_spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('upgrade/static (lite with multiple downgraded modules and shared root module)', () => { - const compA = element(by.css('ng2-a')); - const compB = element(by.css('ng2-b')); - const compC = element(by.css('ng2-c')); - - beforeEach(() => browser.get('/')); - afterEach(verifyNoBrowserErrors); - - it('should share the same injectable instance across downgraded modules A and B', () => { - expect(compA.getText()).toBe('Component A (Service ID: 2)'); - expect(compB.getText()).toBe('Component B (Service ID: 2)'); - }); - - it('should use a different injectable instance on downgraded module C', () => { - expect(compC.getText()).toBe('Component C (Service ID: 1)'); - }); -}); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/module.ts b/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/module.ts deleted file mode 100644 index b2e3a8545b89..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite-multi-shared/module.ts +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { - Compiler, - Component, - getPlatform, - Injectable, - Injector, - NgModule, - StaticProvider, -} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {downgradeComponent, downgradeModule} from '@angular/upgrade/static'; - -declare var angular: ng.IAngularStatic; - -// An Angular service provided in root. Each instance of the service will get a new ID. -@Injectable({providedIn: 'root'}) -export class Ng2Service { - static nextId = 1; - id = Ng2Service.nextId++; -} - -// An Angular module that will act as "root" for all downgraded modules, so that injectables -// provided in root will be available to all. -@NgModule({ - imports: [BrowserModule], -}) -export class Ng2RootModule { - ngDoBootstrap() {} -} - -// An Angular module that declares an Angular component, -// which in turn uses an Angular service from the root module. -@Component({ - selector: 'ng2A', - template: 'Component A (Service ID: {{ service.id }})', - standalone: false, -}) -export class Ng2AComponent { - constructor(public service: Ng2Service) {} -} - -@NgModule({ - declarations: [Ng2AComponent], -}) -export class Ng2AModule { - ngDoBootstrap() {} -} - -// Another Angular module that declares an Angular component, which uses the same service. -@Component({ - selector: 'ng2B', - template: 'Component B (Service ID: {{ service.id }})', - standalone: false, -}) -export class Ng2BComponent { - constructor(public service: Ng2Service) {} -} - -@NgModule({ - declarations: [Ng2BComponent], -}) -export class Ng2BModule { - ngDoBootstrap() {} -} - -// A third Angular module that declares an Angular component, which uses the same service. -@Component({ - selector: 'ng2C', - template: 'Component C (Service ID: {{ service.id }})', - standalone: false, -}) -export class Ng2CComponent { - constructor(public service: Ng2Service) {} -} - -@NgModule({ - imports: [BrowserModule], - declarations: [Ng2CComponent], -}) -export class Ng2CModule { - ngDoBootstrap() {} -} - -// The downgraded Angular modules. Modules A and B share a common root module. Module C does not. -// #docregion shared-root-module -let rootInjectorPromise: Promise | null = null; -const getRootInjector = (extraProviders: StaticProvider[]) => { - if (!rootInjectorPromise) { - rootInjectorPromise = platformBrowserDynamic(extraProviders) - .bootstrapModule(Ng2RootModule) - .then((moduleRef) => moduleRef.injector); - } - return rootInjectorPromise; -}; - -const downgradedNg2AModule = downgradeModule(async (extraProviders: StaticProvider[]) => { - const rootInjector = await getRootInjector(extraProviders); - const moduleAFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2AModule); - return moduleAFactory.create(rootInjector); -}); -const downgradedNg2BModule = downgradeModule(async (extraProviders: StaticProvider[]) => { - const rootInjector = await getRootInjector(extraProviders); - const moduleBFactory = await rootInjector.get(Compiler).compileModuleAsync(Ng2BModule); - return moduleBFactory.create(rootInjector); -}); -// #enddocregion shared-root-module - -const downgradedNg2CModule = downgradeModule((extraProviders: StaticProvider[]) => - (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2CModule), -); - -// The AngularJS app including downgraded modules and components. -// #docregion shared-root-module -const appModule = angular - .module('exampleAppModule', [downgradedNg2AModule, downgradedNg2BModule, downgradedNg2CModule]) - // #enddocregion shared-root-module - .component('exampleApp', {template: ' | | '}) - .directive( - 'ng2A', - downgradeComponent({ - component: Ng2AComponent, - // Since there is more than one downgraded Angular module, - // specify which module this component belongs to. - downgradedModule: downgradedNg2AModule, - propagateDigest: false, - }), - ) - .directive( - 'ng2B', - downgradeComponent({ - component: Ng2BComponent, - // Since there is more than one downgraded Angular module, - // specify which module this component belongs to. - downgradedModule: downgradedNg2BModule, - propagateDigest: false, - }), - ) - .directive( - 'ng2C', - downgradeComponent({ - component: Ng2CComponent, - // Since there is more than one downgraded Angular module, - // specify which module this component belongs to. - downgradedModule: downgradedNg2CModule, - propagateDigest: false, - }), - ); - -// Bootstrap the AngularJS app. -angular.bootstrap(document.body, [appModule.name]); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts b/adev/src/content/api-examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts deleted file mode 100644 index 65f0808071d2..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite-multi/e2e_test/static_lite_multi_spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -describe('upgrade/static (lite with multiple downgraded modules)', () => { - const navButtons = element.all(by.css('nav button')); - const mainContent = element(by.css('main')); - - beforeEach(() => browser.get('/')); - afterEach(verifyNoBrowserErrors); - - it('should correctly bootstrap multiple downgraded modules', () => { - navButtons.get(1).click(); - expect(mainContent.getText()).toBe('Component B'); - - navButtons.get(0).click(); - expect(mainContent.getText()).toBe('Component A | ng1(ng2)'); - }); -}); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite-multi/module.ts b/adev/src/content/api-examples/upgrade/static/ts/lite-multi/module.ts deleted file mode 100644 index aae301f21c80..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite-multi/module.ts +++ /dev/null @@ -1,144 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docplaster -import { - Component, - Directive, - ElementRef, - getPlatform, - Injectable, - Injector, - NgModule, - StaticProvider, -} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import { - downgradeComponent, - downgradeInjectable, - downgradeModule, - UpgradeComponent, -} from '@angular/upgrade/static'; - -declare var angular: ng.IAngularStatic; - -// An Angular module that declares an Angular service and a component, -// which in turn uses an upgraded AngularJS component. -@Component({ - selector: 'ng2A', - template: 'Component A | ', - standalone: false, -}) -export class Ng2AComponent {} - -@Directive({ - selector: 'ng1A', - standalone: false, -}) -export class Ng1AComponentFacade extends UpgradeComponent { - constructor(elementRef: ElementRef, injector: Injector) { - super('ng1A', elementRef, injector); - } -} - -@Injectable() -export class Ng2AService { - getValue() { - return 'ng2'; - } -} - -@NgModule({ - imports: [BrowserModule], - providers: [Ng2AService], - declarations: [Ng1AComponentFacade, Ng2AComponent], -}) -export class Ng2AModule { - ngDoBootstrap() {} -} - -// Another Angular module that declares an Angular component. -@Component({ - selector: 'ng2B', - template: 'Component B', - standalone: false, -}) -export class Ng2BComponent {} - -@NgModule({ - imports: [BrowserModule], - declarations: [Ng2BComponent], -}) -export class Ng2BModule { - ngDoBootstrap() {} -} - -// The downgraded Angular modules. -const downgradedNg2AModule = downgradeModule((extraProviders: StaticProvider[]) => - (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2AModule), -); - -const downgradedNg2BModule = downgradeModule((extraProviders: StaticProvider[]) => - (getPlatform() || platformBrowserDynamic(extraProviders)).bootstrapModule(Ng2BModule), -); - -// The AngularJS app including downgraded modules, components and injectables. -const appModule = angular - .module('exampleAppModule', [downgradedNg2AModule, downgradedNg2BModule]) - .component('exampleApp', { - template: ` - -
    -
    - - -
    - `, - controller: class ExampleAppController { - page = 'A'; - }, - }) - .component('ng1A', { - template: 'ng1({{ $ctrl.value }})', - controller: [ - 'ng2AService', - class Ng1AController { - value = this.ng2AService.getValue(); - constructor(private ng2AService: Ng2AService) {} - }, - ], - }) - .directive( - 'ng2A', - downgradeComponent({ - component: Ng2AComponent, - // Since there is more than one downgraded Angular module, - // specify which module this component belongs to. - downgradedModule: downgradedNg2AModule, - propagateDigest: false, - }), - ) - .directive( - 'ng2B', - downgradeComponent({ - component: Ng2BComponent, - // Since there is more than one downgraded Angular module, - // specify which module this component belongs to. - downgradedModule: downgradedNg2BModule, - propagateDigest: false, - }), - ) - .factory('ng2AService', downgradeInjectable(Ng2AService, downgradedNg2AModule)); - -// Bootstrap the AngularJS app. -angular.bootstrap(document.body, [appModule.name]); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite/BUILD.bazel b/adev/src/content/api-examples/upgrade/static/ts/lite/BUILD.bazel deleted file mode 100644 index 666041cb7ebd..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("//packages/examples/upgrade:upgrade_example.bzl", "create_upgrade_example_targets") - -package(default_visibility = ["//visibility:public"]) - -create_upgrade_example_targets( - name = "lite", - srcs = glob( - ["**/*.ts"], - exclude = ["e2e_test/*"], - ), - assets = ["styles.css"], - e2e_srcs = glob(["e2e_test/*.ts"]), - entry_point = ":module.ts", -) - -filegroup( - name = "files_for_docgen", - srcs = glob([ - "**/*.ts", - ]), -) diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts b/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts deleted file mode 100644 index c3b56d7674e4..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/e2e_util.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {by, ElementFinder} from 'protractor'; - -declare global { - namespace jasmine { - interface Matchers { - toBeAHero(): Promise; - toHaveName(exectedName: string): Promise; - } - } -} - -const isTitleCased = (text: string) => - text.split(/\s+/).every((word) => word[0] === word[0].toUpperCase()); - -export function addCustomMatchers() { - jasmine.addMatchers({ - toBeAHero: () => ({ - compare(actualNg1Hero: ElementFinder | undefined) { - const getText = (selector: string) => actualNg1Hero!.element(by.css(selector)).getText(); - const result = { - message: 'Expected undefined to be an `ng1Hero` ElementFinder.', - pass: - !!actualNg1Hero && - Promise.all(['.title', 'h2', 'p'].map(getText) as PromiseLike[]).then( - ([actualTitle, actualName, actualDescription]) => { - const pass = - actualTitle === 'Super Hero' && - isTitleCased(actualName) && - actualDescription.length > 0; - - const actualHero = `Hero(${actualTitle}, ${actualName}, ${actualDescription})`; - result.message = `Expected ${actualHero}'${pass ? ' not' : ''} to be a real hero.`; - - return pass; - }, - ), - }; - return result; - }, - }), - toHaveName: () => ({ - compare(actualNg1Hero: ElementFinder | undefined, expectedName: string) { - const result = { - message: 'Expected undefined to be an `ng1Hero` ElementFinder.', - pass: - !!actualNg1Hero && - actualNg1Hero - .element(by.css('h2')) - .getText() - .then((actualName) => { - const pass = actualName === expectedName; - result.message = `Expected Hero(${actualName})${ - pass ? ' not' : '' - } to have name '${expectedName}'.`; - return pass; - }), - }; - return result; - }, - }), - } as any); -} diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts b/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts deleted file mode 100644 index 6e71c72d9759..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite/e2e_test/static_lite_spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import {browser, by, element, ElementArrayFinder, ElementFinder} from 'protractor'; - -import {verifyNoBrowserErrors} from '../../../../../../../../../packages/examples/test-utils/index'; - -import {addCustomMatchers} from './e2e_util'; - -function loadPage() { - browser.rootEl = 'example-app'; - browser.get('/'); -} - -describe('upgrade/static (lite)', () => { - let showHideBtn: ElementFinder; - let ng2Heroes: ElementFinder; - let ng2HeroesHeader: ElementFinder; - let ng2HeroesExtra: ElementFinder; - let ng2HeroesAddBtn: ElementFinder; - let ng1Heroes: ElementArrayFinder; - - const expectHeroes = (isShown: boolean, ng1HeroCount = 3, statusMessage = 'Ready') => { - // Verify the show/hide button text. - expect(showHideBtn.getText()).toBe(isShown ? 'Hide heroes' : 'Show heroes'); - - // Verify the `` component. - expect(ng2Heroes.isPresent()).toBe(isShown); - if (isShown) { - expect(ng2HeroesHeader.getText()).toBe('Heroes'); - expect(ng2HeroesExtra.getText()).toBe(`Status: ${statusMessage}`); - } - - // Verify the `` components. - expect(ng1Heroes.count()).toBe(isShown ? ng1HeroCount : 0); - if (isShown) { - ng1Heroes.each((ng1Hero) => expect(ng1Hero).toBeAHero()); - } - }; - - beforeEach(() => { - showHideBtn = element(by.binding('toggleBtnText')); - - ng2Heroes = element(by.css('.ng2-heroes')); - ng2HeroesHeader = ng2Heroes.element(by.css('h1')); - ng2HeroesExtra = ng2Heroes.element(by.css('.extra')); - ng2HeroesAddBtn = ng2Heroes.element(by.buttonText('Add Hero')); - - ng1Heroes = element.all(by.css('.ng1-hero')); - }); - beforeEach(addCustomMatchers); - beforeEach(loadPage); - afterEach(verifyNoBrowserErrors); - - it('should initially not render the heroes', () => expectHeroes(false)); - - it('should toggle the heroes when clicking the "show/hide" button', () => { - showHideBtn.click(); - expectHeroes(true); - - showHideBtn.click(); - expectHeroes(false); - }); - - it('should add a new hero when clicking the "add" button', () => { - showHideBtn.click(); - ng2HeroesAddBtn.click(); - - expectHeroes(true, 4, 'Added hero Kamala Khan'); - expect(ng1Heroes.last()).toHaveName('Kamala Khan'); - }); - - it('should remove a hero when clicking its "remove" button', () => { - showHideBtn.click(); - - const firstHero = ng1Heroes.first(); - expect(firstHero).toHaveName('Superman'); - - const removeBtn = firstHero.element(by.buttonText('Remove')); - removeBtn.click(); - - expectHeroes(true, 2, 'Removed hero Superman'); - expect(ng1Heroes.first()).not.toHaveName('Superman'); - }); -}); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite/module.ts b/adev/src/content/api-examples/upgrade/static/ts/lite/module.ts deleted file mode 100644 index 401ea8528939..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite/module.ts +++ /dev/null @@ -1,223 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -// #docplaster -import { - Component, - Directive, - ElementRef, - EventEmitter, - Inject, - Injectable, - Injector, - Input, - NgModule, - Output, - StaticProvider, -} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -// #docregion basic-how-to -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -// #enddocregion -/* tslint:disable: no-duplicate-imports */ -// #docregion basic-how-to -import {downgradeComponent, downgradeModule, UpgradeComponent} from '@angular/upgrade/static'; - -// #enddocregion -/* tslint:enable: no-duplicate-imports */ - -declare var angular: ng.IAngularStatic; - -interface Hero { - name: string; - description: string; -} - -// This Angular service will use an "upgraded" AngularJS service. -@Injectable() -class HeroesService { - heroes: Hero[] = [ - {name: 'superman', description: 'The man of steel'}, - {name: 'wonder woman', description: 'Princess of the Amazons'}, - {name: 'thor', description: 'The hammer-wielding god'}, - ]; - - constructor(@Inject('titleCase') titleCase: (v: string) => string) { - // Change all the hero names to title case, using the "upgraded" AngularJS service. - this.heroes.forEach((hero: Hero) => (hero.name = titleCase(hero.name))); - } - - addHero() { - const newHero: Hero = {name: 'Kamala Khan', description: 'Epic shape-shifting healer'}; - this.heroes = this.heroes.concat([newHero]); - return newHero; - } - - removeHero(hero: Hero) { - this.heroes = this.heroes.filter((item: Hero) => item !== hero); - } -} - -// This Angular component will be "downgraded" to be used in AngularJS. -@Component({ - selector: 'ng2-heroes', - // This template uses the "upgraded" `ng1-hero` component - // (Note that because its element is compiled by Angular we must use camelCased attribute names.) - template: ` -
    -
    - -
    - - Super Hero - -
    - -
    - `, - standalone: false, -}) -class Ng2HeroesComponent { - @Output() private addHero = new EventEmitter(); - @Output() private removeHero = new EventEmitter(); - - constructor( - @Inject('$rootScope') private $rootScope: ng.IRootScopeService, - public heroesService: HeroesService, - ) {} - - onAddHero() { - const newHero = this.heroesService.addHero(); - this.addHero.emit(newHero); - - // When a new instance of an "upgraded" component - such as `ng1Hero` - is created, we want to - // run a `$digest` to initialize its bindings. Here, the component will be created by `ngFor` - // asynchronously, thus we have to schedule the `$digest` to also happen asynchronously. - this.$rootScope.$applyAsync(); - } - - onRemoveHero(hero: Hero) { - this.heroesService.removeHero(hero); - this.removeHero.emit(hero); - } -} - -// This Angular directive will act as an interface to the "upgraded" AngularJS component. -@Directive({ - selector: 'ng1-hero', - standalone: false, -}) -class Ng1HeroComponentWrapper extends UpgradeComponent { - // The names of the input and output properties here must match the names of the - // `<` and `&` bindings in the AngularJS component that is being wrapped. - @Input() hero!: Hero; - @Output() onRemove: EventEmitter = new EventEmitter(); - - constructor(elementRef: ElementRef, injector: Injector) { - // We must pass the name of the directive as used by AngularJS to the super. - super('ng1Hero', elementRef, injector); - } -} - -// This Angular module represents the Angular pieces of the application. -@NgModule({ - imports: [BrowserModule], - declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper], - providers: [ - HeroesService, - // Register an Angular provider whose value is the "upgraded" AngularJS service. - {provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']}, - ], - // Note that there are no `bootstrap` components, since the "downgraded" component - // will be instantiated by ngUpgrade. -}) -class MyLazyAngularModule { - // Empty placeholder method to prevent the `Compiler` from complaining. - ngDoBootstrap() {} -} - -// #docregion basic-how-to - -// The function that will bootstrap the Angular module (when/if necessary). -// (This would be omitted if we provided an `NgModuleFactory` directly.) -const ng2BootstrapFn = (extraProviders: StaticProvider[]) => - platformBrowserDynamic(extraProviders).bootstrapModule(MyLazyAngularModule); -// #enddocregion -// (We are using the dynamic browser platform, as this example has not been compiled AOT.) - -// #docregion basic-how-to - -// This AngularJS module represents the AngularJS pieces of the application. -const myMainAngularJsModule = angular.module('myMainAngularJsModule', [ - // We declare a dependency on the "downgraded" Angular module. - downgradeModule(ng2BootstrapFn), - // or - // downgradeModule(MyLazyAngularModuleFactory) -]); -// #enddocregion - -// This AngularJS component will be "upgraded" to be used in Angular. -myMainAngularJsModule.component('ng1Hero', { - bindings: {hero: '<', onRemove: '&'}, - transclude: true, - template: ` -
    -
    -

    {{ $ctrl.hero.name }}

    -

    {{ $ctrl.hero.description }}

    - -
    - `, -}); - -// This AngularJS service will be "upgraded" to be used in Angular. -myMainAngularJsModule.factory( - 'titleCase', - () => (value: string) => value.replace(/(^|\s)[a-z]/g, (m) => m.toUpperCase()), -); - -// This directive will act as the interface to the "downgraded" Angular component. -myMainAngularJsModule.directive( - 'ng2Heroes', - downgradeComponent({ - component: Ng2HeroesComponent, - // Optionally, disable `$digest` propagation to avoid unnecessary change detection. - // (Change detection is still run when the inputs of a "downgraded" component change.) - propagateDigest: false, - }), -); - -// This is our top level application component. -myMainAngularJsModule.component('exampleApp', { - // This template makes use of the "downgraded" `ng2-heroes` component, - // but loads it lazily only when/if the user clicks the button. - // (Note that because its element is compiled by AngularJS, - // we must use kebab-case attributes for inputs and outputs.) - template: ` - - - -

    Heroes

    -

    Status: {{ $ctrl.statusMessage }}

    -
    - `, - controller: function () { - this.showHeroes = false; - this.statusMessage = 'Ready'; - - this.setStatusMessage = (msg: string) => (this.statusMessage = msg); - this.toggleHeroes = () => (this.showHeroes = !this.showHeroes); - this.toggleBtnText = () => `${this.showHeroes ? 'Hide' : 'Show'} heroes`; - }, -}); - -// We bootstrap the Angular module as we would do in a normal Angular app. -angular.bootstrap(document.body, [myMainAngularJsModule.name]); diff --git a/adev/src/content/api-examples/upgrade/static/ts/lite/styles.css b/adev/src/content/api-examples/upgrade/static/ts/lite/styles.css deleted file mode 100644 index f3785c1c229d..000000000000 --- a/adev/src/content/api-examples/upgrade/static/ts/lite/styles.css +++ /dev/null @@ -1,17 +0,0 @@ -ng2-heroes { - border: solid black 2px; - display: block; - padding: 5px; -} - -ng1-hero { - border: solid green 2px; - margin-top: 5px; - padding: 5px; - display: block; -} - -.title { - background-color: blue; - color: white; -} diff --git a/adev/src/content/api-examples/upgrade/tsconfig-build.json b/adev/src/content/api-examples/upgrade/tsconfig-build.json deleted file mode 100644 index eb79d48482c3..000000000000 --- a/adev/src/content/api-examples/upgrade/tsconfig-build.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "importHelpers": true, - "lib": ["dom", "es2015"], - "types": ["angular"] - } -} diff --git a/adev/src/content/api-examples/upgrade/upgrade_example.bzl b/adev/src/content/api-examples/upgrade/upgrade_example.bzl deleted file mode 100644 index 33934a3ecce7..000000000000 --- a/adev/src/content/api-examples/upgrade/upgrade_example.bzl +++ /dev/null @@ -1,66 +0,0 @@ -load("//tools:defaults.bzl", "esbuild", "http_server", "ng_module", "protractor_web_test_suite", "ts_library") - -""" - Macro that can be used to create the Bazel targets for an "upgrade" example. Since the - upgrade examples bootstrap their application manually, and we cannot serve all examples, - we need to define the devserver for each example. This macro reduces code duplication - for defining these targets. -""" - -def create_upgrade_example_targets(name, srcs, e2e_srcs, entry_point, assets = []): - ng_module( - name = "%s_sources" % name, - srcs = srcs, - deps = [ - "@npm//@types/angular", - "@npm//@types/jasmine", - "//packages/core", - "//packages/platform-browser", - "//packages/platform-browser-dynamic", - "//packages/upgrade/static", - "//packages/core/testing", - "//packages/upgrade/static/testing", - ], - tsconfig = "//packages/examples/upgrade:tsconfig-build.json", - ) - - ts_library( - name = "%s_e2e_lib" % name, - srcs = e2e_srcs, - testonly = True, - deps = [ - "@npm//@types/jasminewd2", - "@npm//protractor", - "//packages/examples/test-utils", - "//packages/private/testing", - ], - tsconfig = "//packages/examples:tsconfig-e2e.json", - ) - - esbuild( - name = "app_bundle", - entry_point = entry_point, - deps = [":%s_sources" % name], - ) - - http_server( - name = "devserver", - additional_root_paths = ["angular/packages/examples/upgrade"], - srcs = [ - "//packages/examples/upgrade:index.html", - "//packages/zone.js/bundles:zone.umd.js", - "@npm//:node_modules/angular-1.8/angular.js", - "@npm//:node_modules/reflect-metadata/Reflect.js", - ] + assets, - deps = [":app_bundle"], - ) - - protractor_web_test_suite( - name = "%s_protractor" % name, - on_prepare = "//packages/examples/upgrade:start-server.js", - server = ":devserver", - deps = [ - ":%s_e2e_lib" % name, - "@npm//selenium-webdriver", - ], - ) diff --git a/adev/src/content/best-practices/style-guide.md b/adev/src/content/best-practices/style-guide.md index 4d80ceaf410b..184899457993 100644 --- a/adev/src/content/best-practices/style-guide.md +++ b/adev/src/content/best-practices/style-guide.md @@ -293,8 +293,8 @@ Provides a consistent way to quickly identify and reference pipes. | Symbol name | File name | |:--- |:--- | -| @Pipe({ standalone: true, name: 'ellipsis' })
    export class EllipsisPipe implements PipeTransform { }
    | ellipsis.pipe.ts | -| @Pipe({ standalone: true, name: 'initCaps' })
    export class InitCapsPipe implements PipeTransform { }
    | init-caps.pipe.ts | +| @Pipe({ name: 'ellipsis' })
    export class EllipsisPipe implements PipeTransform { }
    | ellipsis.pipe.ts | +| @Pipe({ name: 'initCaps' })
    export class InitCapsPipe implements PipeTransform { }
    | init-caps.pipe.ts | ### Unit test file names diff --git a/adev/src/content/cli/help/serve.json b/adev/src/content/cli/help/serve.json index 4fd0b971b2f9..0cf82f713239 100644 --- a/adev/src/content/cli/help/serve.json +++ b/adev/src/content/cli/help/serve.json @@ -98,7 +98,7 @@ { "name": "prebundle", "type": "boolean", - "description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other Webpack-based builders." + "description": "Enable and control the Vite-based development server's prebundling capabilities. To enable prebundling, the Angular CLI cache must also be enabled. This option has no effect when using the 'browser' or other webpack-based builders." }, { "name": "project", @@ -149,4 +149,4 @@ "description": "Rebuild on change." } ] -} \ No newline at end of file +} diff --git a/adev/src/content/cli/index.md b/adev/src/content/cli/index.md index 97c07112f778..76cc1778f2a4 100644 --- a/adev/src/content/cli/index.md +++ b/adev/src/content/cli/index.md @@ -42,7 +42,7 @@ ng serve In your browser, open http://localhost:4200/ to see the new application run. When you use the [ng serve](cli/serve) command to build an application and serve it locally, the server automatically rebuilds the application and reloads the page when you change any of the source files. -
    +
    When you run `ng new my-first-project` a new folder, named `my-first-project`, will be created in the current working directory. Since you want to be able to create files inside that folder, make sure you have sufficient rights in the current working directory before running the command. diff --git a/adev/src/content/ecosystem/rxjs-interop/BUILD.bazel b/adev/src/content/ecosystem/rxjs-interop/BUILD.bazel new file mode 100644 index 000000000000..73d85c91898d --- /dev/null +++ b/adev/src/content/ecosystem/rxjs-interop/BUILD.bazel @@ -0,0 +1,9 @@ +load("//adev/shared-docs:index.bzl", "generate_guides") + +generate_guides( + name = "rxjs-interop", + srcs = glob([ + "*.md", + ]), + visibility = ["//adev:__subpackages__"], +) diff --git a/adev/src/content/ecosystem/rxjs-interop/output-interop.md b/adev/src/content/ecosystem/rxjs-interop/output-interop.md new file mode 100644 index 000000000000..2007e832da9d --- /dev/null +++ b/adev/src/content/ecosystem/rxjs-interop/output-interop.md @@ -0,0 +1,50 @@ +# RxJS interop with component and directive outputs + +Tip: This guide assumes you're familiar with [component and directive outputs](guide/components/outputs). + +The `@angular/rxjs-interop` package offers two APIs related to component and directive outputs. + +## Creating an output based on an RxJs Observable + +The `outputFromObservable` lets you create a component or directive output that emits based on an RxJS observable: + + +import {Directive} from '@angular/core'; +import {outputFromObservable} from '@angular/core/rxjs-interop'; + +@Directive({/*...*/}) +class Draggable { + pointerMoves$: Observable = listenToPointerMoves(); + + // Whenever `pointerMoves$` emits, the `pointerMove` event fires. + pointerMove = outputFromObservable(this.pointerMoves$); +} + + +The `outputFromObservable` function has special meaning to the Angular compiler. **You may only call `outputFromObservable` in component and directive property initializers.** + +When you `subscribe` to the output, Angular automatically forwards the subscription to the underlying observable. Angular stops forwarding values when the component or directive is destroyed. + +HELPFUL: Consider using `output()` directly if you can emit values imperatively. + +## Creating an RxJS Observable from a component or directive output + +The `outputToObservable` function lets you create an RxJS observable from a component output. + + +import {outputToObservable} from '@angular/core/rxjs-interop'; + +@Component(/*...*/) +class CustomSlider { + valueChange = output(); +} + +// Instance reference to `CustomSlider`. +const slider: CustomSlider = createSlider(); + +outputToObservable(slider.valueChange) // Observable + .pipe(...) + .subscribe(...); + + +HELPFUL: Consider using the `subscribe` method on `OutputRef` directly if it meets your needs. diff --git a/adev/src/content/guide/signals/rxjs-interop.md b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md similarity index 83% rename from adev/src/content/guide/signals/rxjs-interop.md rename to adev/src/content/ecosystem/rxjs-interop/signals-interop.md index 66132bc615ee..69c3533ba6f8 100644 --- a/adev/src/content/guide/signals/rxjs-interop.md +++ b/adev/src/content/ecosystem/rxjs-interop/signals-interop.md @@ -1,10 +1,10 @@ -# RxJS Interop +# RxJS interop with Angular signals IMPORTANT: The RxJS Interop package is available for [developer preview](reference/releases#developer-preview). It's ready for you to try, but it might change before it is stable. -Angular's `@angular/core/rxjs-interop` package provides useful utilities to integrate [Angular Signals](guide/signals) with RxJS Observables. +The `@angular/rxjs-interop` package offers APIs that help you integrate RxJS and Angular signals. -## `toSignal` +## Create a signal from an RxJs Observable with `toSignal` Use the `toSignal` function to create a signal which tracks the value of an Observable. It behaves similarly to the `async` pipe in templates, but is more flexible and can be used anywhere in an application. @@ -63,12 +63,13 @@ If an Observable used in `toSignal` produces an error, that error is thrown when If an Observable used in `toSignal` completes, the signal continues to return the most recently emitted value before completion. -## `toObservable` +## Create an RxJS Observale from a signal with `toObservable` Use the `toObservable` utility to create an `Observable` which tracks the value of a signal. The signal's value is monitored with an `effect` which emits the value to the Observable when it changes. ```ts import { Component, signal } from '@angular/core'; +import { toObservable } from '@angular/core/rxjs-interop'; @Component(...) export class SearchResults { @@ -103,29 +104,3 @@ mySignal.set(3); ``` Here, only the last value (3) will be logged. - -### `outputFromObservable` - -`outputFromObservable(...)` declares an Angular output that emits values based on an RxJS observable. - -```ts -class MyDir { - nameChange$ = new Observable(/* ... */); - nameChange = outputFromObservable(this.nameChange$); // OutputRef -} -``` - -See more details in the [output() API guide](/guide/components/output-fn). - -### `outputToObservable` - -`outputToObservable(...)` converts an Angular output to an observable. -This allows you to integrate Angular outputs conveniently into RxJS streams. - -```ts -outputToObservable(myComp.instance.onNameChange) - .pipe(...) - .subscribe(...) -``` - -See more details in the [output() API guide](/guide/components/output-fn). diff --git a/adev/src/content/examples/accessibility/src/app/app.component.ts b/adev/src/content/examples/accessibility/src/app/app.component.ts index 2d7cf55c7071..292783fc0e95 100755 --- a/adev/src/content/examples/accessibility/src/app/app.component.ts +++ b/adev/src/content/examples/accessibility/src/app/app.component.ts @@ -2,7 +2,6 @@ import {Component} from '@angular/core'; import {ExampleProgressbarComponent} from './progress-bar.component'; @Component({ - standalone: true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], diff --git a/adev/src/content/examples/accessibility/src/app/progress-bar.component.ts b/adev/src/content/examples/accessibility/src/app/progress-bar.component.ts index a135654080dd..deaea6d5c74d 100755 --- a/adev/src/content/examples/accessibility/src/app/progress-bar.component.ts +++ b/adev/src/content/examples/accessibility/src/app/progress-bar.component.ts @@ -6,7 +6,6 @@ import {Component, Input} from '@angular/core'; * Example progressbar component. */ @Component({ - standalone: true, selector: 'app-example-progressbar', template: '
    ', styleUrls: ['./progress-bar.component.css'], diff --git a/adev/src/content/examples/angular-compiler-options/src/app/app.component.ts b/adev/src/content/examples/angular-compiler-options/src/app/app.component.ts index d2e081e6e4de..e0309856e876 100644 --- a/adev/src/content/examples/angular-compiler-options/src/app/app.component.ts +++ b/adev/src/content/examples/angular-compiler-options/src/app/app.component.ts @@ -1,7 +1,6 @@ import {Component} from '@angular/core'; @Component({ - standalone: true, selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], diff --git a/adev/src/content/examples/animations/src/app/about.component.ts b/adev/src/content/examples/animations/src/app/about.component.ts index 209cd8b37306..43f171e65c05 100755 --- a/adev/src/content/examples/animations/src/app/about.component.ts +++ b/adev/src/content/examples/animations/src/app/about.component.ts @@ -1,7 +1,6 @@ import {Component} from '@angular/core'; @Component({ - standalone: true, selector: 'app-about', templateUrl: './about.component.html', styleUrls: ['./about.component.css'], diff --git a/adev/src/content/examples/animations/src/app/app.component.ts b/adev/src/content/examples/animations/src/app/app.component.ts index a222b280f7e0..b7dd0efe9009 100755 --- a/adev/src/content/examples/animations/src/app/app.component.ts +++ b/adev/src/content/examples/animations/src/app/app.component.ts @@ -16,7 +16,6 @@ import {slideInAnimation} from './animations'; // #docregion decorator, toggle-app-animations, define @Component({ - standalone: true, selector: 'app-root', templateUrl: 'app.component.html', styleUrls: ['app.component.css'], diff --git a/adev/src/content/examples/animations/src/app/hero-list-auto-page.component.ts b/adev/src/content/examples/animations/src/app/hero-list-auto-page.component.ts index 9506f826cf06..351a092a8ade 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-auto-page.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-auto-page.component.ts @@ -3,7 +3,6 @@ import {HEROES} from './mock-heroes'; import {HeroListAutoComponent} from './hero-list-auto.component'; @Component({ - standalone: true, selector: 'app-hero-list-auto-page', template: `
    diff --git a/adev/src/content/examples/animations/src/app/hero-list-auto.component.ts b/adev/src/content/examples/animations/src/app/hero-list-auto.component.ts index 28812a01c2be..f2454e2c39c2 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-auto.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-auto.component.ts @@ -5,7 +5,6 @@ import {Hero} from './hero'; import {NgFor} from '@angular/common'; @Component({ - standalone: true, selector: 'app-hero-list-auto', templateUrl: 'hero-list-auto.component.html', styleUrls: ['./hero-list-page.component.css'], diff --git a/adev/src/content/examples/animations/src/app/hero-list-enter-leave-page.component.ts b/adev/src/content/examples/animations/src/app/hero-list-enter-leave-page.component.ts index 6a3d4d349efb..4ec12d92982f 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-enter-leave-page.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-enter-leave-page.component.ts @@ -3,7 +3,6 @@ import {HEROES} from './mock-heroes'; import {HeroListEnterLeaveComponent} from './hero-list-enter-leave.component'; @Component({ - standalone: true, selector: 'app-hero-list-enter-leave-page', template: `
    diff --git a/adev/src/content/examples/animations/src/app/hero-list-enter-leave.component.ts b/adev/src/content/examples/animations/src/app/hero-list-enter-leave.component.ts index 8609c0eec8d8..6a3df81e006f 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-enter-leave.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-enter-leave.component.ts @@ -5,7 +5,6 @@ import {Hero} from './hero'; import {NgFor} from '@angular/common'; @Component({ - standalone: true, selector: 'app-hero-list-enter-leave', template: `
      diff --git a/adev/src/content/examples/animations/src/app/hero-list-group-page.component.ts b/adev/src/content/examples/animations/src/app/hero-list-group-page.component.ts index a705c46d7c6e..89197a7cf35f 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-group-page.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-group-page.component.ts @@ -3,7 +3,6 @@ import {HEROES} from './mock-heroes'; import {HeroListGroupsComponent} from './hero-list-groups.component'; @Component({ - standalone: true, selector: 'app-hero-list-groups-page', template: `
      diff --git a/adev/src/content/examples/animations/src/app/hero-list-groups.component.ts b/adev/src/content/examples/animations/src/app/hero-list-groups.component.ts index 162243087d0a..6d82cb7bcd6b 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-groups.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-groups.component.ts @@ -5,7 +5,6 @@ import {Hero} from './hero'; import {NgFor} from '@angular/common'; @Component({ - standalone: true, selector: 'app-hero-list-groups', template: `
        diff --git a/adev/src/content/examples/animations/src/app/hero-list-page.component.ts b/adev/src/content/examples/animations/src/app/hero-list-page.component.ts index efe45b704ae3..04bf7c06eea3 100755 --- a/adev/src/content/examples/animations/src/app/hero-list-page.component.ts +++ b/adev/src/content/examples/animations/src/app/hero-list-page.component.ts @@ -9,7 +9,6 @@ import {NgFor} from '@angular/common'; // #docregion filter-animations @Component({ // #enddocregion filter-animations - standalone: true, imports: [NgFor], selector: 'app-hero-list-page', templateUrl: 'hero-list-page.component.html', diff --git a/adev/src/content/examples/animations/src/app/home.component.ts b/adev/src/content/examples/animations/src/app/home.component.ts index 2c51056ea2de..1dc6980f1649 100755 --- a/adev/src/content/examples/animations/src/app/home.component.ts +++ b/adev/src/content/examples/animations/src/app/home.component.ts @@ -1,7 +1,6 @@ import {Component} from '@angular/core'; @Component({ - standalone: true, selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'], diff --git a/adev/src/content/examples/animations/src/app/insert-remove.component.ts b/adev/src/content/examples/animations/src/app/insert-remove.component.ts index 9b7fa5c90548..b1b690fe3f66 100755 --- a/adev/src/content/examples/animations/src/app/insert-remove.component.ts +++ b/adev/src/content/examples/animations/src/app/insert-remove.component.ts @@ -4,7 +4,6 @@ import {trigger, transition, animate, style} from '@angular/animations'; import {NgIf} from '@angular/common'; @Component({ - standalone: true, selector: 'app-insert-remove', imports: [NgIf], animations: [ diff --git a/adev/src/content/examples/animations/src/app/open-close-page.component.ts b/adev/src/content/examples/animations/src/app/open-close-page.component.ts index d3e4224715be..8dae2fb20a00 100755 --- a/adev/src/content/examples/animations/src/app/open-close-page.component.ts +++ b/adev/src/content/examples/animations/src/app/open-close-page.component.ts @@ -2,7 +2,6 @@ import {Component} from '@angular/core'; import {OpenCloseComponent} from './open-close.component'; @Component({ - standalone: true, selector: 'app-open-close-page', template: `
        diff --git a/adev/src/content/examples/animations/src/app/open-close.component.1.ts b/adev/src/content/examples/animations/src/app/open-close.component.1.ts index 6543329a8c4c..940d4b577eee 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.1.ts +++ b/adev/src/content/examples/animations/src/app/open-close.component.1.ts @@ -10,7 +10,6 @@ import { } from '@angular/animations'; @Component({ - standalone: true, selector: 'app-open-close', animations: [ // #docregion trigger diff --git a/adev/src/content/examples/animations/src/app/open-close.component.2.ts b/adev/src/content/examples/animations/src/app/open-close.component.2.ts index db79d2226ea9..68ccda26ff4f 100644 --- a/adev/src/content/examples/animations/src/app/open-close.component.2.ts +++ b/adev/src/content/examples/animations/src/app/open-close.component.2.ts @@ -2,7 +2,6 @@ import {Component} from '@angular/core'; import {trigger, transition, state, animate, style} from '@angular/animations'; @Component({ - standalone: true, selector: 'app-open-close-boolean', // #docregion trigger-boolean animations: [ diff --git a/adev/src/content/examples/animations/src/app/open-close.component.3.ts b/adev/src/content/examples/animations/src/app/open-close.component.3.ts index 5f1bbffd8e41..9a8719f4455f 100755 --- a/adev/src/content/examples/animations/src/app/open-close.component.3.ts +++ b/adev/src/content/examples/animations/src/app/open-close.component.3.ts @@ -5,7 +5,6 @@ import {transition, trigger, useAnimation, AnimationEvent} from '@angular/animat import {transitionAnimation} from './animations'; @Component({ - standalone: true, selector: 'app-open-close-reusable', animations: [ trigger('openClose', [ diff --git a/adev/src/content/examples/animations/src/app/open-close.component.4.ts b/adev/src/content/examples/animations/src/app/open-close.component.4.ts index 39b4ad15d054..ef428acae4b6 100755 --- a/adev/src/content/examples/animations/src/app/open-close.component.4.ts +++ b/adev/src/content/examples/animations/src/app/open-close.component.4.ts @@ -6,7 +6,6 @@ import {trigger, transition, state, animate, style} from '@angular/animations'; // #docregion toggle-animation @Component({ // #enddocregion toggle-animation - standalone: true, selector: 'app-open-close-toggle', templateUrl: 'open-close.component.4.html', styleUrls: ['open-close.component.css'], diff --git a/adev/src/content/examples/animations/src/app/open-close.component.ts b/adev/src/content/examples/animations/src/app/open-close.component.ts index 18d1d15ff4e7..0908f89dffae 100755 --- a/adev/src/content/examples/animations/src/app/open-close.component.ts +++ b/adev/src/content/examples/animations/src/app/open-close.component.ts @@ -4,7 +4,6 @@ import {trigger, transition, state, animate, style, AnimationEvent} from '@angul // #docregion component, events1 @Component({ - standalone: true, selector: 'app-open-close', // #docregion trigger-wildcard1, trigger-transition animations: [ diff --git a/adev/src/content/examples/animations/src/app/querying.component.ts b/adev/src/content/examples/animations/src/app/querying.component.ts index b000dfa20a74..eff51228d586 100755 --- a/adev/src/content/examples/animations/src/app/querying.component.ts +++ b/adev/src/content/examples/animations/src/app/querying.component.ts @@ -14,7 +14,6 @@ import {HEROES} from './mock-heroes'; import {NgIf} from '@angular/common'; @Component({ - standalone: true, selector: 'app-querying', template: `