Resources
+Here are some links to help you get started:
+ + + + +Next Steps
+What do you want to do next with your app?
+ + + +ng generate component xyz+
ng add @angular/material+
ng add @angular/pwa+
ng add _____+
ng test+
ng build --prod+
diff --git a/.bazelrc b/.bazelrc index 37f7b117fb..1f07b2fb6d 100644 --- a/.bazelrc +++ b/.bazelrc @@ -74,8 +74,8 @@ test --test_output=errors # Trick bazel into treating BUILD files under integration/bazel as being regular files # This lets us glob() up all the files inside this integration test to make them inputs to tests # (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it) -build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e -query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e +build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e +query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e ################################ # Temporary Settings for Ivy # @@ -136,15 +136,6 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com # retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times. test:saucelabs --flaky_test_attempts=1 -############################### -# NodeJS rules settings -# These settings are required for rules_nodejs -############################### - -# Turn on managed directories feature in Bazel -# This allows us to avoid installing a second copy of node_modules -common --experimental_allow_incremental_repository_updates - #################################################### # User bazel configuration # NOTE: This needs to be the *last* entry in the config. diff --git a/.bazelversion b/.bazelversion index 44d4d72062..a57c344a29 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1,3 +1,3 @@ -3.2.0 +3.6.0 # [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117] # When updating the Bazel version you also need to update the RBE toolchains version in package.bzl diff --git a/.circleci/bazel.linux.rc b/.circleci/bazel.linux.rc index 9ecef0e1c6..9bbc339b20 100644 --- a/.circleci/bazel.linux.rc +++ b/.circleci/bazel.linux.rc @@ -14,8 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache # Bazel doesn't calculate the memory ceiling correctly when running under Docker. # Limit Bazel to consuming resources that fit in CircleCI "xlarge" class # https://circleci.com/docs/2.0/configuration-reference/#resource_class -build --local_cpu_resources=8 -build --local_ram_resources=14336 +build --local_cpu_resources=20 +build --local_ram_resources=32768 # All build executed remotely should be done using our RBE configuration. build:remote --google_default_credentials diff --git a/.circleci/bazel.windows.rc b/.circleci/bazel.windows.rc index ce3e53392c..309b1cba13 100644 --- a/.circleci/bazel.windows.rc +++ b/.circleci/bazel.windows.rc @@ -6,13 +6,9 @@ # 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=C:/Users/circleci/bazel_repository_cache - # Manually set the local resources used in windows CI runs -build --local_ram_resources=13500 -build --local_cpu_resources=4 +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 diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b1267df96..ed6f77d700 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,9 +3,9 @@ # Note: YAML anchors allow an object to be re-used, reducing duplication. # The ampersand declares an alias for an object, then later the `<<: *name` # syntax dereferences it. -# See http://blog.daemonl.com/2016/02/yaml.html +# See https://blog.daemonl.com/2016/02/yaml.html # To validate changes, use an online parser, eg. -# http://yaml-online-parser.appspot.com/ +# https://yaml-online-parser.appspot.com/ # CircleCI configuration version # Version 2.1 allows for extra config reuse features @@ -22,18 +22,16 @@ version: 2.1 # **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match. # **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks. # See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI. -var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} +var_3: &cache_key v1-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} # We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache # folder will contain all previously used versions and ultimately cause the cache restoring to # be slower due to its growing size. -var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }} -var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }} -var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }} +var_4: &cache_key_fallback v1-angular-node-12-{{ checksum ".bazelversion" }} # Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the # cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable. -var_5: &components_repo_unit_tests_cache_key v7-angular-components-f428c00465dfcf8a020237f22532480eedbd2cb6 -var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components- +var_5: &components_repo_unit_tests_cache_key v1-angular-components-09e68db8ed5b1253f2fe38ff954ef0df019fc25a +var_6: &components_repo_unit_tests_cache_key_fallback v1-angular-components- # Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and # `build-ivy-npm-packages`. @@ -80,7 +78,7 @@ executors: windows-executor: working_directory: ~/ng - resource_class: windows.medium + resource_class: windows.2xlarge # CircleCI windows VMs do have the GitBash shell available: # https://github.com/CircleCI-Public/windows-preview-docs#shells # But in this specific case we really should not use it because Bazel must not be ran from @@ -183,23 +181,12 @@ commands: setup_win: description: Setup windows node environment steps: - # Use the Linux workspace directly, as it already has checkout, rebased and node modules. - - custom_attach_workspace + - checkout # Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM. - run: ./.circleci/windows-env.ps1 - run: node --version - run: yarn --version - - restore_cache: - keys: - - *cache_key_win - - *cache_key_win_fallback - # Reinstall to get windows binaries. - run: yarn install --frozen-lockfile --non-interactive - # Install @bazel/bazelisk globally and use that for the first run. - # Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894 - # NB: the issue was for @bazel/bazel but the same problem applies to @bazel/bazelisk - - run: yarn global add @bazel/bazelisk@$env:BAZELISK_VERSION - - run: bazelisk info notify_webhook_on_fail: description: Notify a webhook about failure @@ -233,7 +220,7 @@ jobs: git config user.name "angular-ci" git config user.email "angular-ci" # Rebase PR on top of target branch. - node tools/rebase-pr.js + node .circleci/rebase-pr.js else echo "This build is not over a PR, nothing to do." fi @@ -273,6 +260,7 @@ jobs: - run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check - run: yarn -s ts-circular-deps:check - run: yarn -s ng-dev pullapprove verify + - run: yarn -s ng-dev ngbot verify - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE test: @@ -377,10 +365,6 @@ jobs: test_aio: executor: default-executor - parameters: - debugArtifactsDir: - type: string - default: aio/dist/size-debug-artifacts steps: - custom_attach_workspace - init_environment @@ -399,15 +383,6 @@ jobs: - run: yarn --cwd aio test-a11y-score-localhost # Check the bundle sizes. - run: yarn --cwd aio payload-size - # When `payload-size` check fails, copy the files that were checked into `debugArtifactsDir`. - - run: - when: on_fail - name: Prepare JS bundles to be stored as artifacts - command: node aio/scripts/prepare-size-debug-artifacts aio << parameters.debugArtifactsDir >> - # Store files in `debugArtifactsDir` (if any) as artifacts for debugging purposes. - - store_artifacts: - path: << parameters.debugArtifactsDir >> - destination: aio # Run unit tests for Firebase redirects - run: yarn --cwd aio redirects-test @@ -423,9 +398,6 @@ jobs: test_aio_local: parameters: - debugArtifactsDir: - type: string - default: aio/dist/size-debug-artifacts viewengine: type: boolean default: false @@ -444,15 +416,6 @@ jobs: - run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE # Check the bundle sizes. - run: yarn --cwd aio payload-size aio-local<<# parameters.viewengine >>-viewengine< parameters.viewengine >> - # When `payload-size` check fails, copy the files that were checked into `debugArtifactsDir`. - - run: - when: on_fail - name: Prepare JS bundles to be stored as artifacts - command: node aio/scripts/prepare-size-debug-artifacts aio-local<<# parameters.viewengine >>-viewengine< parameters.viewengine >> << parameters.debugArtifactsDir >> - # Store files in `debugArtifactsDir` (if any) as artifacts for debugging purposes. - - store_artifacts: - path: << parameters.debugArtifactsDir >> - destination: aio test_aio_tools: executor: default-executor @@ -590,7 +553,8 @@ jobs: # Publish `zone.js` package. - run: name: Create artifacts for zone.js package - command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $ZONEJS_PACKAGES_DIR $ZONEJS_PACKAGES_ARCHIVES_DIR + # Need to remove the zone.js.tgz before archive + command: rm -rf $ZONEJS_PACKAGES_DIR/archive && ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $ZONEJS_PACKAGES_DIR $ZONEJS_PACKAGES_ARCHIVES_DIR - store_artifacts: path: *zonejs_packages_archives_dir destination: zone.js @@ -678,9 +642,23 @@ jobs: name: Starting Saucelabs tunnel service command: ./tools/saucelabs/sauce-service.sh run background: true - - run: yarn tsc -p packages - - run: yarn tsc -p modules + # add module umd tsc compile option so the test can work + # properly in the legacy browsers + - run: yarn tsc -p packages --module UMD + - run: yarn tsc -p modules --module UMD - run: yarn bazel build //packages/zone.js:npm_package + # Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling + # specific tests which are reliant on such generated fixtures is not an option as SystemJS + # in the Saucelabs legacy job always fetches referenced files, even if the imports would be + # guarded by an check to skip in the Saucelabs legacy job. We should be good running such + # test in all supported browsers on Saucelabs anyway until this job can be removed. + - run: + name: Preparing Bazel-generated fixtures required in legacy tests + command: | + yarn bazel build //packages/core/test:downleveled_es5_fixture + # Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`. + cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \ + dist/all/@angular/core/test/reflection/es5_downleveled_inheritance_fixture.js - run: # Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready. name: Waiting for Saucelabs tunnel to connect @@ -750,6 +728,7 @@ jobs: steps: - custom_attach_workspace - init_environment + - install_java # Install - run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive # Run zone.js tools tests @@ -758,8 +737,13 @@ jobs: - run: yarn bazel build //packages/zone.js:npm_package && cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ && cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ && - yarn --cwd packages/zone.js electrontest - - run: yarn --cwd packages/zone.js jesttest + cp dist/bin/packages/zone.js/npm_package/bundles/zone.umd.js ./packages/zone.js/build/test/closure/zone.js + - run: yarn --cwd packages/zone.js jest:test + - run: yarn --cwd packages/zone.js jest:nodetest + - run: yarn --cwd packages/zone.js electrontest + - run: yarn --cwd packages/zone.js closuretest + - run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive + - run: yarn --cwd packages/zone.js/test/typings test # Windows jobs # Docs: https://circleci.com/docs/2.0/hello-world-windows/ @@ -768,25 +752,26 @@ jobs: steps: - setup_win - run: - # Ran into a command parsing problem where `-browser:chromium-local` was converted to - # `-browser: chromium-local` (a space was added) in https://circleci.com/gh/angular/angular/357511. - # Probably a powershell command parsing thing. There's no problem using a yarn script though. - command: yarn circleci-win-ve - no_output_timeout: 45m - # Save bazel repository cache to use on subsequent runs. - # We don't save node_modules because it's faster to use the linux workspace and reinstall. - - save_cache: - key: *cache_key_win - paths: - - "C:/Users/circleci/bazel_repository_cache" + name: Build all windows CI targets + command: yarn bazel build --build_tag_filters=-ivy-only,-no-windows //packages/compiler-cli/... //tools/ts-api-guardian/... + no_output_timeout: 15m + - run: + name: Test all windows CI targets + command: yarn bazel test --build_tag_filters=-no-windows --test_tag_filters="-ivy-only,-no-windows,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/... + no_output_timeout: 15m test_ivy_aot_win: executor: windows-executor steps: - setup_win - run: - command: yarn circleci-win-ivy - no_output_timeout: 45m + name: Build all windows CI targets + command: yarn bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-no-windows,-fixme-ivy-aot //packages/compiler-cli/... //tools/ts-api-guardian/... + no_output_timeout: 15m + - run: + name: Test all windows CI targets + command: yarn bazel test --config=ivy --build_tag_filters=-no-windows --test_tag_filters="-no-ivy-aot,-no-windows,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/... + no_output_timeout: 15m workflows: @@ -870,26 +855,25 @@ workflows: - build-npm-packages - build-ivy-npm-packages - legacy-unit-tests-saucelabs - - components-repo-unit-tests: - requires: - - build-npm-packages + # Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in + # rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very + # difficult and time consuming to resolve and involve patching @angular/bazel in components repo such + # as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to + # 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes + # 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot + # 3) update angular/angular to the landed components commit and re-enable these tests + # - components-repo-unit-tests: + # requires: + # - build-npm-packages - test_zonejs: requires: - setup - # Windows Jobs - # These are very slow so we run them on non-PRs only for now. - # TODO: remove the filter when CircleCI makes Windows FS faster. - # The Windows jobs are only run after their non-windows counterparts finish successfully. - # This isn't strictly necessary as there is no artifact dependency, but helps economize - # CI resources by not attempting to build when we know should fail. - test_win: - <<: *skip_on_pull_requests requires: - - test + - setup - test_ivy_aot_win: - <<: *skip_on_pull_requests requires: - - test_ivy_aot + - setup monitoring: jobs: diff --git a/.circleci/env.sh b/.circleci/env.sh index 88ad302440..93b4130587 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo" setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git" setPublicVar COMPONENTS_REPO_BRANCH "master" # **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`. -setPublicVar COMPONENTS_REPO_COMMIT "f428c00465dfcf8a020237f22532480eedbd2cb6" +setPublicVar COMPONENTS_REPO_COMMIT "09e68db8ed5b1253f2fe38ff954ef0df019fc25a" #################################################################################################### diff --git a/.circleci/rebase-pr.js b/.circleci/rebase-pr.js new file mode 100644 index 0000000000..085ab4398d --- /dev/null +++ b/.circleci/rebase-pr.js @@ -0,0 +1,207 @@ +/** + * @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.io/license + */ + +/** + * Rebases the current branch on top of the GitHub PR target branch. + * + * **Context:** + * Since a GitHub PR is not necessarily up to date with its target branch, it is useful to rebase + * prior to testing it on CI to ensure more up to date test results. + * + * **NOTE:** + * This script cannot use external dependencies or be compiled because it needs to run before the + * environment is setup. + * Use only features supported by the NodeJS versions used in the environment. + */ +// tslint:disable:no-console +const {execSync} = require('child_process'); + + +/** A regex to select a ref that matches our semver refs. */ +const semverRegex = /^(\d+)\.(\d+)\.x$/; + +// Run +_main().catch(err => { + console.log('Failed to rebase on top of target branch.\n'); + console.error(err); + process.exitCode = 1; +}); + +// Helpers +async function _main() { + const refs = await getRefsAndShasForChange(); + + // Log known refs and shas + console.log(`--------------------------------`); + console.log(` Target Branch: ${refs.base.ref}`); + console.log(` Latest Commit for Target Branch: ${refs.target.latestSha}`); + console.log(` Latest Commit for PR: ${refs.base.latestSha}`); + console.log(` First Common Ancestor SHA: ${refs.commonAncestorSha}`); + console.log(`--------------------------------`); + console.log(); + + // Get the count of commits between the latest commit from origin and the common ancestor SHA. + const commitCount = + exec(`git rev-list --count origin/${refs.base.ref}...${refs.commonAncestorSha}`); + console.log(`Checking ${commitCount} commits for changes in the CircleCI config file.`); + + // Check if the files changed between the latest commit from origin and the common ancestor SHA + // includes the CircleCI config. + const circleCIConfigChanged = exec(`git diff --name-only origin/${refs.base.ref} ${ + refs.commonAncestorSha} -- .circleci/config.yml`); + + if (!!circleCIConfigChanged) { + throw Error(` + CircleCI config on ${refs.base.ref} has been modified since commit + ${refs.commonAncestorSha.slice(0, 7)}, which this PR is based on. + + Please rebase the PR on ${refs.base.ref} after fetching from upstream. + + Rebase instructions for PR Author, please run the following commands: + + git fetch upstream ${refs.base.ref}; + git checkout ${refs.target.ref}; + git rebase upstream/${refs.base.ref}; + git push --force-with-lease; + `); + } else { + console.log('No change found in the CircleCI config file, continuing.'); + } + console.log(); + + // Rebase the PR. + exec(`git rebase origin/${refs.base.ref}`); + console.log(`Rebased current branch onto ${refs.base.ref}.`); +} + + + +/** + * Sort a list of fullpath refs into a list and then provide the first entry. + * + * The sort order will first find master ref, and then any semver ref, followed + * by the rest of the refs in the order provided. + * + * Branches are sorted in this order as work is primarily done on master, and + * otherwise on a semver branch. If neither of those were to match, the most + * likely correct branch will be the first one encountered in the list. + */ +function getRefFromBranchList(gitOutput) { + const branches = gitOutput.split('\n').map(b => b.split('/').slice(1).join('').trim()); + return branches.sort((a, b) => { + if (a === 'master') { + return -1; + } + if (b === 'master') { + return 1; + } + const aIsSemver = semverRegex.test(a); + const bIsSemver = semverRegex.test(b); + if (aIsSemver && bIsSemver) { + const [, aMajor, aMinor] = a.match(semverRegex); + const [, bMajor, bMinor] = b.match(semverRegex); + return parseInt(bMajor, 10) - parseInt(aMajor, 10) || + parseInt(aMinor, 10) - parseInt(bMinor, 10) || 0; + } + if (aIsSemver) { + return -1; + } + if (bIsSemver) { + return 1; + } + return 0; + })[0]; +} + +/** + * Get the full sha of the ref provided. + * + * example: 1bc0c1a6c01ede7168f22fa9b3508ba51f1f464e + */ +function getShaFromRef(ref) { + return exec(`git rev-parse ${ref}`); +} + +/** + * Get the list of branches which contain the provided sha, sorted in descending order + * by committerdate. + * + * example: + * upstream/master + * upstream/9.0.x + * upstream/test + * upstream/1.1.x + */ +function getBranchListForSha(sha, remote) { + return exec(`git branch -r '${remote}/*' --sort=-committerdate --contains ${sha}`); +} + +/** Get the common ancestor sha of the two provided shas. */ +function getCommonAncestorSha(sha1, sha2) { + return exec(`git merge-base ${sha1} ${sha2}`); +} + +/** + * Adds the remote to git, if it doesn't already exist. Returns a boolean indicating + * whether the remote was added by the command. + */ +function addAndFetchRemote(owner, name) { + const remoteName = `${owner}_${name}`; + exec(`git remote add ${remoteName} https://github.com/${owner}/${name}.git`, false); + exec(`git fetch ${remoteName}`); + return remoteName; +} + + +/** Get the ref and latest shas for the provided sha on a specific remote. */ +function getRefAndShas(sha, owner, name) { + const remoteName = addAndFetchRemote(owner, name); + + // Get the ref on the remote for the sha provided. + const branches = getBranchListForSha(sha, remoteName); + const ref = getRefFromBranchList(branches); + + // Get the latest sha on the discovered remote ref. + const latestSha = getShaFromRef(`${remoteName}/${ref}`); + + return {remote: remoteName, ref, latestSha, sha}; +} + + +/** Gets the refs and shas for the base and target of the current environment. */ +function getRefsAndShasForChange() { + const base = getRefAndShas( + process.env['CI_GIT_BASE_REVISION'], process.env['CI_REPO_OWNER'], + process.env['CI_REPO_NAME']); + const target = getRefAndShas( + process.env['CI_GIT_REVISION'], process.env['CI_PR_USERNAME'], process.env['CI_PR_REPONAME']); + const commonAncestorSha = getCommonAncestorSha(base.sha, target.sha); + return { + base, + target, + commonAncestorSha, + }; +} + + +/** + * Synchronously executes the command. + * + * Return the trimmed stdout as a string, with an added attribute of the exit code. + */ +function exec(command, allowStderr = true) { + let output = new String(); + output.code = 0; + try { + output += execSync(command, {stdio: ['pipe', 'pipe', 'pipe']}).toString().trim(); + } catch (err) { + allowStderr && console.error(err.stderr.toString()); + output.code = err.status; + } + return output; +} diff --git a/.circleci/windows-env.ps1 b/.circleci/windows-env.ps1 index ece28bb8bf..014c2fcf97 100644 --- a/.circleci/windows-env.ps1 +++ b/.circleci/windows-env.ps1 @@ -2,8 +2,8 @@ # https://docs.bazel.build/versions/master/install-windows.html # https://docs.bazel.build/versions/master/windows.html # Install MSYS2 and packages -choco install msys2 --version 20180531.0.0 --no-progress --package-parameters "/NoUpdate" -C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils git" +choco install msys2 --version 20200903.0.0 --no-progress --package-parameters "/NoUpdate" +C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils" # Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile. # https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85) @@ -41,7 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc #################################################################################################### # Install specific version of node. #################################################################################################### -choco install nodejs --version 12.14.1 --no-progress +nvm install 12.14.1 +nvm use 12.14.1 # These Bazel prereqs aren't needed because the CircleCI image already includes them. # choco install yarn --version 1.16.0 --no-progress diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md index 39b3eb6862..4785c8fb39 100644 --- a/.github/ISSUE_TEMPLATE/1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/1-bug-report.md @@ -1,5 +1,5 @@ --- -name: "\U0001F41EBug report" +name: "\U0001F41E Bug report" about: Report a bug in the Angular Framework --- diff --git a/aio/content/examples/accessibility/src/app/app.component.ts b/aio/content/examples/accessibility/src/app/app.component.ts index dd635acc9c..d9e6d230c9 100755 --- a/aio/content/examples/accessibility/src/app/app.component.ts +++ b/aio/content/examples/accessibility/src/app/app.component.ts @@ -7,4 +7,8 @@ import { Component } from '@angular/core'; }) export class AppComponent { progress = 0; + + setProgress($event: Event) { + this.progress = +($event.target as HTMLInputElement).value; + } } diff --git a/aio/content/examples/accessibility/src/app/progress-bar.component.ts b/aio/content/examples/accessibility/src/app/progress-bar.component.ts index fe1f5d8c73..f8ae4662d4 100755 --- a/aio/content/examples/accessibility/src/app/progress-bar.component.ts +++ b/aio/content/examples/accessibility/src/app/progress-bar.component.ts @@ -1,3 +1,4 @@ +// tslint:disable: no-host-metadata-property // #docregion progressbar-component import { Component, Input } from '@angular/core'; diff --git a/aio/content/examples/ajs-quick-reference/e2e/src/app.e2e-spec.ts b/aio/content/examples/ajs-quick-reference/e2e/src/app.e2e-spec.ts index 0eef85ffde..7a8e7e6218 100644 --- a/aio/content/examples/ajs-quick-reference/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/ajs-quick-reference/e2e/src/app.e2e-spec.ts @@ -1,20 +1,16 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('AngularJS to Angular Quick Reference Tests', function () { +describe('AngularJS to Angular Quick Reference Tests', () => { - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it('should display no poster images after bootstrap', async () => { + await testImagesAreDisplayed(false); }); - it('should display no poster images after bootstrap', function () { - testImagesAreDisplayed(false); - }); - - it('should display proper movie data', function () { + it('should display proper movie data', async () => { // We check only a few samples - let expectedSamples: any[] = [ + const expectedSamples: any[] = [ {row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true}, {row: 0, column: 2, value: 'Celeritas'}, {row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format @@ -25,20 +21,19 @@ describe('AngularJS to Angular Quick Reference Tests', function () { ]; // Go through the samples - let movieRows = getMovieRows(); - for (let i = 0; i < expectedSamples.length; i++) { - let sample = expectedSamples[i]; - let tableCell = movieRows.get(sample.row) + const movieRows = getMovieRows(); + for (const sample of expectedSamples) { + const tableCell = movieRows.get(sample.row) .all(by.tagName('td')).get(sample.column); // Check the cell or its nested element - let elementToCheck = sample.element + const elementToCheck = sample.element ? tableCell.element(by.tagName(sample.element)) : tableCell; // Check element attribute or text - let valueToCheck = sample.attr - ? elementToCheck.getAttribute(sample.attr) - : elementToCheck.getText(); + const valueToCheck = sample.attr + ? await elementToCheck.getAttribute(sample.attr) + : await elementToCheck.getText(); // Test for equals/contains/match if (sample.contains) { @@ -51,65 +46,64 @@ describe('AngularJS to Angular Quick Reference Tests', function () { } }); - it('should display images after Show Poster', function () { - testPosterButtonClick('Show Poster', true); + it('should display images after Show Poster', async () => { + await testPosterButtonClick('Show Poster', true); }); - it('should hide images after Hide Poster', function () { - testPosterButtonClick('Hide Poster', false); + it('should hide images after Hide Poster', async () => { + await testPosterButtonClick('Hide Poster', false); }); - it('should display no movie when no favorite hero is specified', function () { - testFavoriteHero(null, 'Please enter your favorite hero.'); + it('should display no movie when no favorite hero is specified', async () => { + await testFavoriteHero(null, 'Please enter your favorite hero.'); }); - it('should display no movie for Magneta', function () { - testFavoriteHero('Magneta', 'No movie, sorry!'); + it('should display no movie for Magneta', async () => { + await testFavoriteHero('Magneta', 'No movie, sorry!'); }); - it('should display a movie for Dr Nice', function () { - testFavoriteHero('Dr Nice', 'Excellent choice!'); + it('should display a movie for Dr Nice', async () => { + await testFavoriteHero('Dr Nice', 'Excellent choice!'); }); - function testImagesAreDisplayed(isDisplayed: boolean) { - let expectedMovieCount = 3; + async function testImagesAreDisplayed(isDisplayed: boolean) { + const expectedMovieCount = 3; - let movieRows = getMovieRows(); - expect(movieRows.count()).toBe(expectedMovieCount); + const movieRows = getMovieRows(); + expect(await movieRows.count()).toBe(expectedMovieCount); for (let i = 0; i < expectedMovieCount; i++) { - let movieImage = movieRows.get(i).element(by.css('td > img')); - expect(movieImage.isDisplayed()).toBe(isDisplayed); + const movieImage = movieRows.get(i).element(by.css('td > img')); + expect(await movieImage.isDisplayed()).toBe(isDisplayed); } } - function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) { - let posterButton = element(by.css('app-movie-list tr > th > button')); - expect(posterButton.getText()).toBe(expectedButtonText); + async function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) { + const posterButton = element(by.css('app-movie-list tr > th > button')); + expect(await posterButton.getText()).toBe(expectedButtonText); - posterButton.click().then(function () { - testImagesAreDisplayed(isDisplayed); - }); + await posterButton.click(); + await testImagesAreDisplayed(isDisplayed); } function getMovieRows() { return element.all(by.css('app-movie-list tbody > tr')); } - function testFavoriteHero(heroName: string, expectedLabel: string) { - let movieListComp = element(by.tagName('app-movie-list')); - let heroInput = movieListComp.element(by.tagName('input')); - let favoriteHeroLabel = movieListComp.element(by.tagName('h3')); - let resultLabel = movieListComp.element(by.css('span > p')); + async function testFavoriteHero(heroName: string, expectedLabel: string) { + const movieListComp = element(by.tagName('app-movie-list')); + const heroInput = movieListComp.element(by.tagName('input')); + const favoriteHeroLabel = movieListComp.element(by.tagName('h3')); + const resultLabel = movieListComp.element(by.css('span > p')); - heroInput.clear().then(function () { - heroInput.sendKeys(heroName || ''); - expect(resultLabel.getText()).toBe(expectedLabel); - if (heroName) { - expect(favoriteHeroLabel.isDisplayed()).toBe(true); - expect(favoriteHeroLabel.getText()).toContain(heroName); - } else { - expect(favoriteHeroLabel.isDisplayed()).toBe(false); - } - }); + await heroInput.clear(); + await heroInput.sendKeys(heroName || ''); + + expect(await resultLabel.getText()).toBe(expectedLabel); + if (heroName) { + expect(await favoriteHeroLabel.isDisplayed()).toBe(true); + expect(await favoriteHeroLabel.getText()).toContain(heroName); + } else { + expect(await favoriteHeroLabel.isDisplayed()).toBe(false); + } } }); diff --git a/aio/content/examples/ajs-quick-reference/src/app/app-routing.module.ts b/aio/content/examples/ajs-quick-reference/src/app/app-routing.module.ts index a7cbe8a74d..e00e4e4c62 100644 --- a/aio/content/examples/ajs-quick-reference/src/app/app-routing.module.ts +++ b/aio/content/examples/ajs-quick-reference/src/app/app-routing.module.ts @@ -1,6 +1,6 @@ // #docregion import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; import { MovieListComponent } from './movie-list.component'; diff --git a/aio/content/examples/ajs-quick-reference/src/app/app.module.1.ts b/aio/content/examples/ajs-quick-reference/src/app/app.module.1.ts index 5b24020186..530cd295e7 100644 --- a/aio/content/examples/ajs-quick-reference/src/app/app.module.1.ts +++ b/aio/content/examples/ajs-quick-reference/src/app/app.module.1.ts @@ -1,8 +1,8 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; @NgModule({ imports: [ BrowserModule ], diff --git a/aio/content/examples/ajs-quick-reference/src/app/app.module.ts b/aio/content/examples/ajs-quick-reference/src/app/app.module.ts index 1dc46ad17c..46ae679ee0 100644 --- a/aio/content/examples/ajs-quick-reference/src/app/app.module.ts +++ b/aio/content/examples/ajs-quick-reference/src/app/app.module.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { MovieListComponent } from './movie-list.component'; import { AppRoutingModule } from './app-routing.module'; diff --git a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts index 49e4dcaf0b..f19b62f7ee 100644 --- a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts @@ -1,6 +1,4 @@ -'use strict'; // necessary for es6 output in node - -import { browser, ExpectedConditions as EC } from 'protractor'; +import { browser } from 'protractor'; import { logging } from 'selenium-webdriver'; import * as openClose from './open-close.po'; import * as statusSlider from './status-slider.po'; @@ -20,9 +18,7 @@ describe('Animation Tests', () => { const filterHref = getLinkById('heroes'); const heroGroupsHref = getLinkById('hero-groups'); - beforeAll(() => { - browser.get(''); - }); + beforeAll(() => browser.get('')); describe('Open/Close Component', () => { const closedHeight = '100px'; @@ -30,7 +26,7 @@ describe('Animation Tests', () => { beforeAll(async () => { await openCloseHref.click(); - sleepFor(); + await sleepFor(); }); it('should be open', async () => { @@ -86,7 +82,7 @@ describe('Animation Tests', () => { beforeAll(async () => { await statusSliderHref.click(); - sleepFor(2000); + await sleepFor(2000); }); it('should be inactive with a blue background', async () => { @@ -127,7 +123,7 @@ describe('Animation Tests', () => { describe('Toggle Animations Component', () => { beforeAll(async () => { await toggleHref.click(); - sleepFor(); + await sleepFor(); }); it('should disabled animations on the child element', async () => { @@ -145,7 +141,7 @@ describe('Animation Tests', () => { describe('Enter/Leave Component', () => { beforeAll(async () => { await enterLeaveHref.click(); - sleepFor(100); + await sleepFor(100); }); it('should attach a flyInOut trigger to the list of items', async () => { @@ -171,7 +167,7 @@ describe('Animation Tests', () => { describe('Auto Calculation Component', () => { beforeAll(async () => { await autoHref.click(); - sleepFor(0); + await sleepFor(0); }); it('should attach a shrinkOut trigger to the list of items', async () => { @@ -195,7 +191,7 @@ describe('Animation Tests', () => { describe('Filter/Stagger Component', () => { beforeAll(async () => { await filterHref.click(); - sleepFor(); + await sleepFor(); }); it('should attach a filterAnimations trigger to the list container', async () => { @@ -222,7 +218,7 @@ describe('Animation Tests', () => { describe('Hero Groups Component', () => { beforeAll(async () => { await heroGroupsHref.click(); - sleepFor(300); + await sleepFor(300); }); it('should attach a flyInOut trigger to the list of items', async () => { @@ -247,5 +243,3 @@ describe('Animation Tests', () => { }); }); }); - - diff --git a/aio/content/examples/animations/src/app/animations.ts b/aio/content/examples/animations/src/app/animations.ts index d525647354..8b6abd5ec7 100644 --- a/aio/content/examples/animations/src/app/animations.ts +++ b/aio/content/examples/animations/src/app/animations.ts @@ -32,15 +32,15 @@ export const slideInAnimation = // #enddocregion style-view // #docregion query query(':enter', [ - style({ left: '-100%'}) + style({ left: '-100%' }) ]), query(':leave', animateChild()), group([ query(':leave', [ - animate('300ms ease-out', style({ left: '100%'})) + animate('300ms ease-out', style({ left: '100%' })) ]), query(':enter', [ - animate('300ms ease-out', style({ left: '0%'})) + animate('300ms ease-out', style({ left: '0%' })) ]) ]), query(':enter', animateChild()), @@ -56,15 +56,15 @@ export const slideInAnimation = }) ]), query(':enter', [ - style({ left: '-100%'}) + style({ left: '-100%' }) ]), query(':leave', animateChild()), group([ query(':leave', [ - animate('200ms ease-out', style({ left: '100%'})) + animate('200ms ease-out', style({ left: '100%' })) ]), query(':enter', [ - animate('300ms ease-out', style({ left: '0%'})) + animate('300ms ease-out', style({ left: '0%' })) ]) ]), query(':enter', animateChild()), diff --git a/aio/content/examples/animations/src/app/app.component.html b/aio/content/examples/animations/src/app/app.component.html index 1deca03101..78d1655313 100644 --- a/aio/content/examples/animations/src/app/app.component.html +++ b/aio/content/examples/animations/src/app/app.component.html @@ -17,7 +17,7 @@ Toggle All Animations -
One-Two |
// Setup -let clicks$ = fromEvent(buttonEl, ‘click’); +const clicks$ = fromEvent(buttonEl, ‘click’); // Begin listening -let subscription = clicks$ +const subscription = clicks$ .subscribe(e => console.log(‘Clicked’, e)) // Stop listening subscription.unsubscribe();diff --git a/aio/content/guide/component-interaction.md b/aio/content/guide/component-interaction.md index d35b9848d1..2e3ab831f7 100644 --- a/aio/content/guide/component-interaction.md +++ b/aio/content/guide/component-interaction.md @@ -41,7 +41,7 @@ in which two or more components share information. **See the
`
).
+For example:
+
+如果你想让你的模板跨越多行,你可以使用反引号( `` ` `` )。例如:
+
+app.component.html
file. It is not needed for this example.
-
-删除 app.component.html
文件,这个范例中不再需要它了。
-
-Then modify the app.component.ts
file by
-changing the template and the body of the component.
-
-然后,到 `app.component.ts` 文件中修改组件的模板和代码。
-
-When you're done, it should look like this:
-
-修改完之后,它应该是这样的:
-
-\`
).
-The backtick (\`
)—which is *not* the same character as a single
-quote (`'`)—allows you to compose a string over several lines, which makes the
-HTML more readable.
-
-模板是包在 ECMAScript 2015 反引号 (\`
) 中的一个多行字符串。
-反引号 (\`
) — 注意,不是单引号 (') — 允许把一个字符串写在多行上,
-使 HTML 模板更容易阅读。
-
-main.ts
), Angular looks for a `app.component.ts
and delete or comment out one of the elements from the heroes array.
-The browser should refresh automatically and the message should disappear.
-
-试一下。因为这个数组中有四个条目,所以消息应该显示出来。
-回到 `app.component.ts`,从英雄数组中删除或注释掉一个元素。
-浏览器应该自动刷新,消息应该会消失。
-
-## Summary
-
-## 小结
-
-Now you know how to use:
-
-现在你知道了如何使用:
-
-* **Interpolation** with double curly braces to display a component property.
-
- 带有双花括号的**插值 (interpolation) **来显示一个组件属性。
-
-* **ngFor** to display an array of items.
-
- 用 **ngFor** 显示数组。
-
-* A TypeScript class to shape the **model data** for your component and display properties of that model.
-
- 用一个 TypeScript 类来为你的组件描述**模型数据**并显示模型的属性。
-
-* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
-
- 用 **ngIf** 根据一个布尔表达式有条件地显示一段 HTML。
-
-Here's the final code:
-
-下面是最终的代码:
-
-+* [Domain](#domain): A domain NgModule is organized around a feature, business domain, or user experience. - Feature Module + [领域模块](#domain):领域模块围绕特性、业务领域或用户体验进行组织。 - 特性模块 +* [Routed](#routed): The top component of the NgModule acts as the destination of a [router](guide/glossary#router "Definition of router") navigation route. - | + [带路由的模块](#routed):模块的顶层组件充当[路由器](guide/glossary#router "路由器的定义")访问这部分路由时的目的地。 -+* [Routing](#routing): A routing NgModule provides the routing configuration for another NgModule. - Guidelines + [路由配置模块](#routing):路由配置模块为另一个模块提供路由配置。 - 指导原则 +* [Service](#service): A service NgModule provides utility services such as data access and messaging. - | + [服务模块](#service):服务模块提供实用服务,比如数据访问和消息传递。 -
---|---|
+* [Shared](#shared): A shared NgModule makes a set of components, directives, and pipes available to other NgModules. - Domain + [共享模块](#shared):共享模块可以为其它的模块提供组件,指令和管道的集合。 - 领域 +The following table summarizes the key characteristics of each category. - | - -- - Domain feature modules deliver a user experience dedicated to a particular application domain like editing a customer or placing an order. - - 领域特性模块用来给用户提供应用程序领域中特有的用户体验,比如编辑客户信息或下订单等。 - - They typically have a top component that acts as the feature root and private, supporting sub-components descend from it. - - 它们通常会有一个顶层组件来充当该特性的根组件,并且通常是私有的。用来支持它的各级子组件。 - - Domain feature modules consist mostly of declarations. Only the top component is exported. - - 领域特性模块大部分由 `declarations` 组成,只有顶层组件会被导出。 - - Domain feature modules rarely have providers. When they do, the lifetime of the provided services should be the same as the lifetime of the module. - - 领域特性模块很少会有服务提供者。如果有,那么这些服务的生命周期必须和该模块的生命周期完全相同。 - - Domain feature modules are typically imported exactly once by a larger feature module. - - 领域特性模块通常会由更高一级的特性模块导入且只导入一次。 - - They might be imported by the root `AppModule` of a small application that lacks routing. - - 对于缺少路由的小型应用,它们可能只会被根模块 `AppModule` 导入一次。 - - | - -
- - Routed - - 路由 - - | - -- - Routed feature modules are domain feature modules whose top components are the targets of router navigation routes. - - 带路由的特性模块是一种特殊的领域特性模块,但它的顶层组件会作为路由导航时的目标组件。 - - All lazy-loaded modules are routed feature modules by definition. - - 根据这个定义,所有惰性加载的模块都是路由特性模块。 - - Routed feature modules don’t export anything because their components never appear in the template of an external component. - - 带路由的特性模块不会导出任何东西,因为它们的组件永远不会出现在外部组件的模板中。 - - A lazy-loaded routed feature module should not be imported by any module. Doing so would trigger an eager load, defeating the purpose of lazy loading.That means you won’t see them mentioned among the `AppModule` imports. An eager loaded routed feature module must be imported by another module so that the compiler learns about its components. - - 惰性加载的路由特性模块不应该被任何模块导入。如果那样做就会导致它被急性加载,破坏了惰性加载的设计用途。 - 也就是说你应该永远不会看到它们在 `AppModule` 的 `imports` 中被引用。 - 急性加载的路由特性模块必须被其它模块导入,以便编译器能了解它所包含的组件。 - - Routed feature modules rarely have providers for reasons explained in [Lazy Loading Feature Modules](/guide/lazy-loading-ngmodules). When they do, the lifetime of the provided services should be the same as the lifetime of the module. Don't provide application-wide singleton services in a routed feature module or in a module that the routed module imports. - - 路由特性模块很少会有服务提供者,原因参见[惰性加载的特性模块](/guide/lazy-loading-ngmodules)中的解释。如果那样做,那么它所提供的服务的生命周期必须与该模块的生命周期完全相同。不要在路由特性模块或被路由特性模块所导入的模块中提供全应用级的单例服务。 - - | - -
- - Routing - - 路由 - - | - -
-
- A routing module provides routing configuration for another module and separates routing concerns from its companion module.
-
- 路由模块为其它模块提供路由配置,并且把路由这个关注点从它的配套模块中分离出来。
-
- A routing module typically does the following:
-
- 路由模块通常会做这些:
-
-
|
-
-
- - Service - - 服务 - - | - -- - Service modules provide utility services such as data access and messaging. Ideally, they consist entirely of providers and have no declarations. Angular's `HttpClientModule` is a good example of a service module. - - 服务模块提供了一些工具服务,比如数据访问和消息。理论上,它们应该是完全由服务提供者组成的,不应该有可声明对象。Angular 的 `HttpClientModule` 就是一个服务模块的好例子。 - - The root `AppModule` is the only module that should import service modules. - - 根模块 `AppModule` 是唯一的可以导入服务模块的模块。 - - | - -
- - Widget - - 窗口部件 - - | - -- - A widget module makes components, directives, and pipes available to external modules. Many third-party UI component libraries are widget modules. - - 窗口部件模块为外部模块提供组件、指令和管道。很多第三方 UI 组件库都是窗口部件模块。 - - A widget module should consist entirely of declarations, most of them exported. - - 窗口部件模块应该完全由可声明对象组成,它们中的大部分都应该被导出。 - - A widget module should rarely have providers. - - 窗口部件模块很少会有服务提供者。 - - Import widget modules in any module whose component templates need the widgets. - - 如果任何模块的组件模板中需要用到这些窗口部件,就请导入相应的窗口部件模块。 - - | - -
- - Feature Module - - 特性模块 - + NgModule | - Declarations - - 声明 `declarations` - | - Providers - - 提供者 `providers` - | - Exports - - 导出什么 - | - Imported by - - 被谁导入 - + | +|||||
---|---|---|---|---|---|---|---|---|---|
+ 模块 | ++ 可声明对象 + | + ++ 提供者 + | + ++ 导出 + | + ++ 被谁导入 + | |||||
- - Domain - - 领域 - - | - -- - Yes - - 有 - - | - -- - Rare - - 罕见 - - | - -- - Top component - - 顶层组件 - - | - -- - Feature, AppModule - - 特性模块,AppModule - - | - +Domain | +Yes | +Rare | +Top component | +Another domain, AppModule | +
领域模块 | +是 | +罕见 | +顶级组件 | +其它领域模块、根模块 | |||||
- - Routed - - 路由 - - | - -- - Yes - - 有 - - | - -- - Rare - - 罕见 - - | - -- - No - - 无 - - | - -- - None - - 无 - - | - +Routed | +Yes | +Rare | +No | +None | +
带路由的模块 | +是 | +罕见 | +否 | +无 | |||||
- - Routing - - 路由 - - | - -- - No - - 无 - - | - -- - Yes (Guards) - - 有(守卫) - - | - -- - RouterModule - - | - -- - Feature (for routing) - - 特性(供路由使用) - - | - +Routing | +No | +Yes (Guards) | +RouterModule | +Another domain (for routing) | +
路由定义模块 | +否 | +是 (路由守卫) | +RouterModule | +其它领域模块(为获取路由定义) | |||||
- - Service - - 服务 - - | - -- - No - - 无 - - | - -- - Yes - - 有 - - | - -- - No - - 无 - - | - -- - AppModule - - | - +Service | +No | +Yes | +No | +AppModule | +
服务模块 | +否 | +是 | +否 | +AppModule | |||||
Widget | +Yes | +Rare | +Yes | +Another domain | +|||||
小部件模块 | +是 | +罕见 | +是 | +其它领域模块 | +- - Widget - - 窗口部件 - - | - -- - Yes - - 有 - - | - -- - Rare - - 罕见 - - | - -- - Yes - - 有 - - | - -- - Feature - - 特性 - - | - +
Shared | +Yes | +No | +Yes | +Another domain | +|||||
共享模块 | +是 | +否 | +是 | +其它领域模块 |
ContactModule
in contact.module.ts
has a routing NgModule named ContactRoutingModule
in contact-routing.module.ts
.
+
+路由定义模块的名字应该和其伴生模块的名字平行,但使用 `Routing` 后缀。例如, `contact.module.ts` 中的 `ContactModule` 有一个位于 `contact-routing.module.ts` 中的名为 `ContactRoutingModule` 的路由定义模块。
+
+Import a routing NgModule only into its companion NgModule.
+If the companion NgModule is the root AppModule
, the AppRoutingModule
adds router configuration to its imports with RouterModule.forRoot(routes)
.
+All other routing NgModules are children that import RouterModule.forChild(routes)
.
+
+路由定义模块只能导入它的伴生模块中。如果伴生模块是根模块 `AppModule` ,那么 `AppRoutingModule` 就会通过其导入表中的 `RouterModule.forRoot(routes)` 来添加路由器配置。所有其他的子路由定义模块都会导入 `RouterModule.forChild(routes)`。
+
+In your routing NgModule, re-export the RouterModule
as a convenience so that components of the companion NgModule have access to router directives such as RouterLink
and RouterOutlet
.
+
+在路由定义模块中,要重新导出 `RouterModule`,以便其伴生模块中的组件可以访问路由器指令,比如 `RouterLink` 和 `RouterOutlet` 。
+
+Don't use declarations in a routing NgModule.
+Components, directives, and pipes are the responsibility of the companion domain NgModule, not the routing NgModule.
+
+不要在路由定义模块中使用可声明对象。组件、指令和管道都是伴生领域模块的责任,而不是路由定义模块的。
+
+{@a service}
+
+## Service NgModules
+
+## 服务模块
+
+Use a service NgModule to provide a utility service such as data access or messaging.
+Ideal service NgModules consist entirely of providers and have no declarations.
+Angular's `HttpClientModule` is a good example of a service NgModule.
+
+使用服务模块来提供实用工具服务,比如数据访问或消息传递。理想的服务模块完全由提供者组成,没有可声明对象。 Angular 的 `HttpClientModule` 是服务模块的一个典范。
+
+Use only the root `AppModule` to import service NgModules.
+
+只能使用根模块 `AppModule` 来导入各种服务模块。
+
+{@a widget}
+
+## Widget NgModules
+
+## 小部件模块
+
+Use a widget NgModule to make a component, directive, or pipe available to external NgModules.
+Import widget NgModules into any NgModules that need the widgets in their templates.
+Many third-party UI component libraries are provided as widget NgModules.
+
+使用小部件模块可以把组件、指令或管道提供给外部模块使用。把小部件模块导入到任何需要在模板使用这些小部件的模块中。很多第三方 UI 组件库都是作为小部件模块提供的。
+
+A widget NgModule should consist entirely of declarations, most of them exported.
+It would rarely have providers.
+
+小部件模块应该完全由可声明对象组成,其中大部分都是导出的。服务提供者非常罕见。
+
+{@a shared}
+
+## Shared NgModules
+
+## 共享模块
+
+Put commonly used directives, pipes, and components into one NgModule, typically named `SharedModule`, and then import just that NgModule wherever you need it in other parts of your app.
+You can import the shared NgModule in your domain NgModules, including [lazy-loaded NgModules](guide/lazy-loading-ngmodules "Lazy-loading an NgModule").
+One example is `SharedModule` in the ` element so that you can use `special` to apply CSS styles. + +Angular 会将 `special` 类应用到 `
` 元素,以便你可以通过 `special` 来应用 CSS 样式。
+
+## Bind values between components
+
+## 在组件之间绑定值
+
+To set the model property of a custom component, place the target, here `childItem`, between square brackets `[]` followed by an equal sign and the property.
+Here, the property is `parentItem`.
+
+要设置自定义组件的模型属性,请将目标属性(此处为 `childItem`)放在方括号 `[]` 中,其后跟着等号与源属性。在这里,这个源属性是 `parentItem` 。
+
+ Here are some links to help you get started: What do you want to do next with your app?Resources
+ Next Steps
+ ng generate component xyz
+ ng add @angular/material
+ ng add @angular/pwa
+ ng add _____
+ ng test
+ ng build --prod
+ Trusted Types tests
+
+
+
+ Hello from iframe
`;
+ replace = `Hello from second outerHTML`;
+ safeHtml: SafeHtml;
+ safeResourceUrl: SafeResourceUrl;
+
+ constructor(sanitizer: DomSanitizer) {
+ this.safeHtml = sanitizer.bypassSecurityTrustHtml(
+ `Hello from bound SafeHtml`);
+ this.safeResourceUrl = sanitizer.bypassSecurityTrustResourceUrl(
+ `data:text/html,Hello from object
`);
+ }
+}
diff --git a/aio/content/examples/styleguide/src/03-02/app/app.module.ts b/integration/trusted-types/src/app/app.module.ts
similarity index 58%
rename from aio/content/examples/styleguide/src/03-02/app/app.module.ts
rename to integration/trusted-types/src/app/app.module.ts
index 2db4012ebf..2c3ba2995c 100644
--- a/aio/content/examples/styleguide/src/03-02/app/app.module.ts
+++ b/integration/trusted-types/src/app/app.module.ts
@@ -1,17 +1,18 @@
-import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
-import { RouterModule } from '@angular/router';
+import { NgModule } from '@angular/core';
+import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
- imports: [
- BrowserModule,
- RouterModule.forChild([{ path: '03-02', component: AppComponent }])
- ],
declarations: [
AppComponent
],
- exports: [ AppComponent ]
+ imports: [
+ BrowserModule,
+ AppRoutingModule
+ ],
+ providers: [],
+ bootstrap: [AppComponent]
})
-export class AppModule {}
+export class AppModule { }
diff --git a/integration/trusted-types/src/assets/.gitkeep b/integration/trusted-types/src/assets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/integration/trusted-types/src/environments/environment.prod.ts b/integration/trusted-types/src/environments/environment.prod.ts
new file mode 100644
index 0000000000..3612073bc3
--- /dev/null
+++ b/integration/trusted-types/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/integration/trusted-types/src/environments/environment.ts b/integration/trusted-types/src/environments/environment.ts
new file mode 100644
index 0000000000..7b4f817adb
--- /dev/null
+++ b/integration/trusted-types/src/environments/environment.ts
@@ -0,0 +1,16 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/integration/trusted-types/src/favicon.ico b/integration/trusted-types/src/favicon.ico
new file mode 100644
index 0000000000..997406ad22
Binary files /dev/null and b/integration/trusted-types/src/favicon.ico differ
diff --git a/integration/trusted-types/src/index.html b/integration/trusted-types/src/index.html
new file mode 100644
index 0000000000..21b3e70df9
--- /dev/null
+++ b/integration/trusted-types/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Input App
-
`
-})
-export class HelloCmp {
- greeting: string;
- lastKey: string = '(none)';
-
- constructor(service: GreetingService) {
- this.greeting = service.greeting;
- }
-
- changeGreeting(): void {
- this.greeting = 'howdy';
- }
-
- onKeyDown(event: KeyboardEvent): void {
- this.lastKey = String.fromCharCode(event.keyCode);
- }
-}
diff --git a/modules/playground/src/web_workers/kitchen_sink/loader.js b/modules/playground/src/web_workers/kitchen_sink/loader.js
deleted file mode 100644
index 9dd1900c0b..0000000000
--- a/modules/playground/src/web_workers/kitchen_sink/loader.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.io/license
- */
-
-importScripts('angular/modules/playground/src/web_workers/worker-configure.js');
-
-System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}});
-
-System.import('./background_index.js')
- .catch(error => console.error('error loading background', error));
diff --git a/modules/playground/src/web_workers/message_broker/BUILD.bazel b/modules/playground/src/web_workers/message_broker/BUILD.bazel
deleted file mode 100644
index 9dd6417a30..0000000000
--- a/modules/playground/src/web_workers/message_broker/BUILD.bazel
+++ /dev/null
@@ -1,38 +0,0 @@
-load("//tools:defaults.bzl", "ng_module", "ts_devserver")
-
-package(default_visibility = ["//modules/playground:__subpackages__"])
-
-ng_module(
- name = "message_broker",
- srcs = glob(["**/*.ts"]),
- tsconfig = "//modules/playground:tsconfig-build.json",
- deps = [
- "//packages/core",
- "//packages/platform-webworker",
- "//packages/platform-webworker-dynamic",
- ],
-)
-
-ts_devserver(
- name = "devserver",
- bootstrap = [
- "@npm//:node_modules/systemjs/dist/system.js",
- "//packages/zone.js/bundles:zone.umd.js",
- "//packages/zone.js/bundles:long-stack-trace-zone.umd.js",
- "@npm//:node_modules/reflect-metadata/Reflect.js",
- ],
- entry_module = "angular/modules/playground/src/web_workers/message_broker/index",
- port = 4200,
- scripts = [
- "@npm//:node_modules/tslib/tslib.js",
- "//tools/rxjs:rxjs_umd_modules",
- ],
- static_files = [
- "index.html",
- "loader.js",
- "//modules/playground/src/web_workers:worker-config",
- "//modules/playground:systemjs-config.js",
- "//modules/playground:systemjs-rxjs-operators.js",
- ],
- deps = [":message_broker"],
-)
diff --git a/modules/playground/src/web_workers/message_broker/background_index.ts b/modules/playground/src/web_workers/message_broker/background_index.ts
deleted file mode 100644
index f9f8e033c7..0000000000
--- a/modules/playground/src/web_workers/message_broker/background_index.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.io/license
- */
-
-import {NgModule} from '@angular/core';
-import {WorkerAppModule} from '@angular/platform-webworker';
-import {platformWorkerAppDynamic} from '@angular/platform-webworker-dynamic';
-
-import {App} from './index_common';
-
-@NgModule({imports: [WorkerAppModule], bootstrap: [App], declarations: [App]})
-export class ExampleModule {
-}
-
-platformWorkerAppDynamic().bootstrapModule(ExampleModule);
diff --git a/modules/playground/src/web_workers/message_broker/index.html b/modules/playground/src/web_workers/message_broker/index.html
deleted file mode 100644
index f1aab56501..0000000000
--- a/modules/playground/src/web_workers/message_broker/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- WebWorker MessageBroker Test
'})
-export class App {
- constructor(private _serviceBrokerFactory: ServiceMessageBrokerFactory) {
- const broker = _serviceBrokerFactory.createMessageBroker(ECHO_CHANNEL, false);
- broker.registerMethod(
- 'echo', [SerializerTypes.PRIMITIVE], this._echo, SerializerTypes.PRIMITIVE);
- }
-
- private _echo(val: string) {
- return new Promise((res) => res(val));
- }
-}
diff --git a/modules/playground/src/web_workers/message_broker/loader.js b/modules/playground/src/web_workers/message_broker/loader.js
deleted file mode 100644
index 9dd1900c0b..0000000000
--- a/modules/playground/src/web_workers/message_broker/loader.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.io/license
- */
-
-importScripts('angular/modules/playground/src/web_workers/worker-configure.js');
-
-System.config({packages: {'angular/modules/playground/src/web_workers': {defaultExtension: 'js'}}});
-
-System.import('./background_index.js')
- .catch(error => console.error('error loading background', error));
diff --git a/modules/playground/src/web_workers/router/BUILD.bazel b/modules/playground/src/web_workers/router/BUILD.bazel
deleted file mode 100644
index e771007426..0000000000
--- a/modules/playground/src/web_workers/router/BUILD.bazel
+++ /dev/null
@@ -1,41 +0,0 @@
-load("//tools:defaults.bzl", "ng_module", "ts_devserver")
-
-package(default_visibility = ["//modules/playground:__subpackages__"])
-
-ng_module(
- name = "router",
- srcs = glob(["**/*.ts"]),
- assets = ["app.html"],
- tsconfig = "//modules/playground:tsconfig-build.json",
- deps = [
- "//packages/core",
- "//packages/platform-webworker",
- "//packages/platform-webworker-dynamic",
- "//packages/router",
- "@npm//rxjs",
- ],
-)
-
-ts_devserver(
- name = "devserver",
- bootstrap = [
- "@npm//:node_modules/systemjs/dist/system.js",
- "//packages/zone.js/bundles:zone.umd.js",
- "//packages/zone.js/bundles:long-stack-trace-zone.umd.js",
- "@npm//:node_modules/reflect-metadata/Reflect.js",
- ],
- entry_module = "angular/modules/playground/src/web_workers/router/index",
- port = 4200,
- scripts = [
- "@npm//:node_modules/tslib/tslib.js",
- "//tools/rxjs:rxjs_umd_modules",
- ],
- static_files = [
- "index.html",
- "loader.js",
- "//modules/playground/src/web_workers:worker-config",
- "//modules/playground:systemjs-config.js",
- "//modules/playground:systemjs-rxjs-operators.js",
- ],
- deps = [":router"],
-)
diff --git a/modules/playground/src/web_workers/router/app.html b/modules/playground/src/web_workers/router/app.html
deleted file mode 100644
index d79243c194..0000000000
--- a/modules/playground/src/web_workers/router/app.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-About
'})
-export class About {
-}
diff --git a/modules/playground/src/web_workers/router/components/contact.ts b/modules/playground/src/web_workers/router/components/contact.ts
deleted file mode 100644
index 46ba8e7cdc..0000000000
--- a/modules/playground/src/web_workers/router/components/contact.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.io/license
- */
-
-import {Component} from '@angular/core';
-@Component({selector: 'contact', template: 'Contact
'})
-export class Contact {
-}
diff --git a/modules/playground/src/web_workers/router/components/start.ts b/modules/playground/src/web_workers/router/components/start.ts
deleted file mode 100644
index d80dab1618..0000000000
--- a/modules/playground/src/web_workers/router/components/start.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.io/license
- */
-
-import {Component} from '@angular/core';
-@Component({selector: 'start', template: 'Start
'})
-export class Start {
-}
diff --git a/modules/playground/src/web_workers/router/index.html b/modules/playground/src/web_workers/router/index.html
deleted file mode 100644
index 90ce028dae..0000000000
--- a/modules/playground/src/web_workers/router/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- todos
-
-
-
-
-
-