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<> - # 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.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 -
+
diff --git a/aio/content/examples/animations/src/app/app.component.ts b/aio/content/examples/animations/src/app/app.component.ts index 4a0b559939..ea5d52cba8 100644 --- a/aio/content/examples/animations/src/app/app.component.ts +++ b/aio/content/examples/animations/src/app/app.component.ts @@ -34,7 +34,7 @@ export class AppComponent { // #docregion prepare-router-outlet prepareRoute(outlet: RouterOutlet) { - return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; + return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation; } // #enddocregion prepare-router-outlet diff --git a/aio/content/examples/animations/src/app/hero-list-page.component.ts b/aio/content/examples/animations/src/app/hero-list-page.component.ts index 746c195d19..0963f5d8e2 100644 --- a/aio/content/examples/animations/src/app/hero-list-page.component.ts +++ b/aio/content/examples/animations/src/app/hero-list-page.component.ts @@ -1,4 +1,6 @@ +// tslint:disable: variable-name // #docplaster +// #docregion import { Component, HostBinding, OnInit } from '@angular/core'; import { trigger, transition, animate, style, query, stagger } from '@angular/animations'; import { HEROES } from './mock-heroes'; @@ -52,13 +54,11 @@ export class HeroListPageComponent implements OnInit { @HostBinding('@pageAnimations') public animatePage = true; - _heroes = []; // #docregion filter-animations heroTotal = -1; // #enddocregion filter-animations - get heroes() { - return this._heroes; - } + get heroes() { return this._heroes; } + private _heroes = []; ngOnInit() { this._heroes = HEROES; diff --git a/aio/content/examples/animations/src/app/open-close.component.ts b/aio/content/examples/animations/src/app/open-close.component.ts index f5ca38e47f..c3b5bcf633 100644 --- a/aio/content/examples/animations/src/app/open-close.component.ts +++ b/aio/content/examples/animations/src/app/open-close.component.ts @@ -8,8 +8,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang // #docregion trigger, trigger-wildcard1, trigger-transition animations: [ trigger('openClose', [ -// #enddocregion events1 -// #docregion state1, events1 +// #docregion state1 // ... // #enddocregion events1 state('open', style({ @@ -34,8 +33,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang transition('closed => open', [ animate('0.5s') ]), -// #enddocregion trigger, component -// #enddocregion transition2 +// #enddocregion transition2, trigger, component // #docregion trigger-wildcard1 transition('* => closed', [ animate('1s') @@ -70,7 +68,9 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang }) // #docregion events export class OpenCloseComponent { -// #enddocregion events1, events +// #enddocregion events1, events, component + @Input() logging = false; +// #docregion component isOpen = true; toggle() { @@ -78,9 +78,8 @@ export class OpenCloseComponent { } // #enddocregion component - @Input() logging = false; // #docregion events1, events - onAnimationEvent ( event: AnimationEvent ) { + onAnimationEvent( event: AnimationEvent ) { // #enddocregion events1, events if (!this.logging) { return; diff --git a/aio/content/examples/architecture/e2e/src/app.e2e-spec.ts b/aio/content/examples/architecture/e2e/src/app.e2e-spec.ts index d6e9316e8e..4be469461f 100644 --- a/aio/content/examples/architecture/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/architecture/e2e/src/app.e2e-spec.ts @@ -1,5 +1,3 @@ -'use strict'; // necessary for es6 output in node - import { protractor, browser, element, by, ElementFinder } from 'protractor'; const nameSuffix = 'X'; @@ -16,12 +14,12 @@ describe('Architecture', () => { beforeAll(() => browser.get('')); - it(`has title '${expectedTitle}'`, () => { - expect(browser.getTitle()).toEqual(expectedTitle); + it(`has title '${expectedTitle}'`, async () => { + expect(await browser.getTitle()).toEqual(expectedTitle); }); - it(`has h2 '${expectedH2}'`, () => { - let h2 = element.all(by.css('h2')).map((elt: any) => elt.getText()); + it(`has h2 '${expectedH2}'`, async () => { + const h2 = await element.all(by.css('h2')).map((elt: any) => elt.getText()); expect(h2).toEqual(expectedH2); }); @@ -33,45 +31,45 @@ function heroTests() { const targetHero: Hero = { id: 2, name: 'Dr Nice' }; - it('has the right number of heroes', () => { - let page = getPageElts(); - expect(page.heroes.count()).toEqual(3); + it('has the right number of heroes', async () => { + const page = getPageElts(); + expect(await page.heroes.count()).toEqual(3); }); - it('has no hero details initially', function () { - let page = getPageElts(); - expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail'); + it('has no hero details initially', async () => { + const page = getPageElts(); + expect(await page.heroDetail.isPresent()).toBeFalsy('no hero detail'); }); it('shows selected hero details', async () => { await element(by.cssContainingText('li', targetHero.name)).click(); - let page = getPageElts(); - let hero = await heroFromDetail(page.heroDetail); + const page = getPageElts(); + const hero = await heroFromDetail(page.heroDetail); expect(hero.id).toEqual(targetHero.id); expect(hero.name).toEqual(targetHero.name); }); it(`shows updated hero name in details`, async () => { - let input = element.all(by.css('input')).first(); - input.sendKeys(nameSuffix); - let page = getPageElts(); - let hero = await heroFromDetail(page.heroDetail); - let newName = targetHero.name + nameSuffix; + const input = element.all(by.css('input')).first(); + await input.sendKeys(nameSuffix); + const page = getPageElts(); + const hero = await heroFromDetail(page.heroDetail); + const newName = targetHero.name + nameSuffix; expect(hero.id).toEqual(targetHero.id); expect(hero.name).toEqual(newName); }); } function salesTaxTests() { - it('has no sales tax initially', function () { - let page = getPageElts(); - expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info'); + it('has no sales tax initially', async () => { + const page = getPageElts(); + expect(await page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info'); }); - it('shows sales tax', async function () { - let page = getPageElts(); - page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER); - expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00'); + it('shows sales tax', async () => { + const page = getPageElts(); + await page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER); + expect(await page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00'); }); } @@ -88,13 +86,11 @@ function getPageElts() { async function heroFromDetail(detail: ElementFinder): Promise { // Get hero id from the first
- // let _id = await detail.all(by.css('div')).first().getText(); - let _id = await detail.all(by.css('div')).first().getText(); + const id = await detail.all(by.css('div')).first().getText(); // Get name from the h2 - // let _name = await detail.element(by.css('h4')).getText(); - let _name = await detail.element(by.css('h4')).getText(); + const name = await detail.element(by.css('h4')).getText(); return { - id: +_id.substr(_id.indexOf(' ') + 1), - name: _name.substr(0, _name.lastIndexOf(' ')) + id: +id.substr(id.indexOf(' ') + 1), + name: name.substr(0, name.lastIndexOf(' ')), }; } diff --git a/aio/content/examples/architecture/src/app/app.module.ts b/aio/content/examples/architecture/src/app/app.module.ts index f6e64beecd..fdd2da883b 100644 --- a/aio/content/examples/architecture/src/app/app.module.ts +++ b/aio/content/examples/architecture/src/app/app.module.ts @@ -1,15 +1,15 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; // #docregion imports -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; // #enddocregion imports import { HeroDetailComponent } from './hero-detail.component'; -import { HeroListComponent } from './hero-list.component'; -import { SalesTaxComponent } from './sales-tax.component'; -import { HeroService } from './hero.service'; -import { BackendService } from './backend.service'; -import { Logger } from './logger.service'; +import { HeroListComponent } from './hero-list.component'; +import { SalesTaxComponent } from './sales-tax.component'; +import { HeroService } from './hero.service'; +import { BackendService } from './backend.service'; +import { Logger } from './logger.service'; @NgModule({ imports: [ diff --git a/aio/content/examples/architecture/src/app/backend.service.ts b/aio/content/examples/architecture/src/app/backend.service.ts index 145be4cad5..3385f9e4fc 100644 --- a/aio/content/examples/architecture/src/app/backend.service.ts +++ b/aio/content/examples/architecture/src/app/backend.service.ts @@ -18,7 +18,7 @@ export class BackendService { // TODO: get from the database return Promise.resolve(HEROES); } - let err = new Error('Cannot get object of this type'); + const err = new Error('Cannot get object of this type'); this.logger.error(err); throw err; } diff --git a/aio/content/examples/architecture/src/app/hero-list.component.ts b/aio/content/examples/architecture/src/app/hero-list.component.ts index 4dc0b8e2f3..ba764d8371 100644 --- a/aio/content/examples/architecture/src/app/hero-list.component.ts +++ b/aio/content/examples/architecture/src/app/hero-list.component.ts @@ -1,7 +1,7 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; -import { Hero } from './hero'; -import { HeroService } from './hero.service'; +import { Hero } from './hero'; +import { HeroService } from './hero.service'; // #docregion metadata, providers @Component({ diff --git a/aio/content/examples/architecture/src/app/mini-app.ts b/aio/content/examples/architecture/src/app/mini-app.ts index 85689b6304..dfc22c461e 100644 --- a/aio/content/examples/architecture/src/app/mini-app.ts +++ b/aio/content/examples/architecture/src/app/mini-app.ts @@ -22,7 +22,7 @@ export class AppComponent { } // #docregion module -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; // #docregion import-browser-module import { BrowserModule } from '@angular/platform-browser'; // #enddocregion import-browser-module diff --git a/aio/content/examples/architecture/src/app/sales-tax.component.ts b/aio/content/examples/architecture/src/app/sales-tax.component.ts index 2d33398f4c..9909c28ab8 100644 --- a/aio/content/examples/architecture/src/app/sales-tax.component.ts +++ b/aio/content/examples/architecture/src/app/sales-tax.component.ts @@ -1,7 +1,7 @@ -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { SalesTaxService } from './sales-tax.service'; -import { TaxRateService } from './tax-rate.service'; +import { TaxRateService } from './tax-rate.service'; @Component({ selector: 'app-sales-tax', diff --git a/aio/content/examples/architecture/src/app/sales-tax.service.ts b/aio/content/examples/architecture/src/app/sales-tax.service.ts index d859dc1595..8e92096dd6 100644 --- a/aio/content/examples/architecture/src/app/sales-tax.service.ts +++ b/aio/content/examples/architecture/src/app/sales-tax.service.ts @@ -7,7 +7,7 @@ export class SalesTaxService { constructor(private rateService: TaxRateService) { } getVAT(value: string | number) { - let amount = (typeof value === 'string') ? + const amount = (typeof value === 'string') ? parseFloat(value) : value; return (amount || 0) * this.rateService.getRate('VAT'); } diff --git a/aio/content/examples/attribute-binding/e2e/src/app.e2e-spec.ts b/aio/content/examples/attribute-binding/e2e/src/app.e2e-spec.ts index 95281656a9..cf08ae5bba 100644 --- a/aio/content/examples/attribute-binding/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/attribute-binding/e2e/src/app.e2e-spec.ts @@ -1,36 +1,35 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Attribute binding example', function () { +describe('Attribute binding example', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display Property Binding with Angular', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings'); }); - it('should display Property Binding with Angular', function () { - expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings'); + it('should display a table', async () => { + expect(await element.all(by.css('table')).isPresent()).toBe(true); }); - it('should display a table', function() { - expect(element.all(by.css('table')).isPresent()).toBe(true); + it('should display an Aria button', async () => { + expect(await element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria'); }); - it('should display an Aria button', function () { - expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria'); + it('should display a blue background on div', async () => { + const div = element.all(by.css('div')).get(1); + expect(await div.getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)'); }); - it('should display a blue background on div', function () { - expect(element.all(by.css('div')).get(1).getCssValue('background-color')).toEqual('rgba(25, 118, 210, 1)'); + it('should display a blue div with a red border', async () => { + const div = element.all(by.css('div')).get(1); + expect(await div.getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)'); }); - it('should display a blue div with a red border', function () { - expect(element.all(by.css('div')).get(1).getCssValue('border')).toEqual('2px solid rgb(212, 30, 46)'); - }); - - it('should display a div with many classes', function () { - expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('special'); - expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance'); + it('should display a div with many classes', async () => { + const div = element.all(by.css('div')).get(1); + expect(await div.getAttribute('class')).toContain('special'); + expect(await div.getAttribute('class')).toContain('clearance'); }); }); diff --git a/aio/content/examples/attribute-binding/src/app/app.component.html b/aio/content/examples/attribute-binding/src/app/app.component.html index 69f82857fd..7514b871c1 100644 --- a/aio/content/examples/attribute-binding/src/app/app.component.html +++ b/aio/content/examples/attribute-binding/src/app/app.component.html @@ -3,8 +3,10 @@

Attribute binding

+ +

Basic specificity

- -
Some text.
+ +
Some text.
- -
Some text.
+ +
Some text.

Source specificity

- +Some text. - +Some text.

Dynamic vs static

- -
Some text.
+ +
Some text.
- -
Some text.
+ +
Some text.
diff --git a/aio/content/examples/attribute-binding/src/app/app.component.ts b/aio/content/examples/attribute-binding/src/app/app.component.ts index ab35bb09a0..57c9f28718 100644 --- a/aio/content/examples/attribute-binding/src/app/app.component.ts +++ b/aio/content/examples/attribute-binding/src/app/app.component.ts @@ -9,7 +9,7 @@ export class AppComponent { actionName = 'Go for it'; isSpecial = true; canSave = true; - classExpr = 'special clearance'; - styleExpr = 'color: red'; + classExpression = 'special clearance'; + styleExpression = 'color: red'; color = 'blue'; } diff --git a/aio/content/examples/attribute-binding/src/app/comp-with-host-binding.component.ts b/aio/content/examples/attribute-binding/src/app/comp-with-host-binding.component.ts index f41c3d4278..b604e44454 100644 --- a/aio/content/examples/attribute-binding/src/app/comp-with-host-binding.component.ts +++ b/aio/content/examples/attribute-binding/src/app/comp-with-host-binding.component.ts @@ -1,16 +1,19 @@ -import { Component } from '@angular/core'; +import { Component, HostBinding } from '@angular/core'; @Component({ selector: 'comp-with-host-binding', template: 'I am a component!', - host: { - '[class.special]': 'isSpecial', - '[style.color]': 'color', - '[style.width]': 'width' - } }) export class CompWithHostBindingComponent { + @HostBinding('class.special') isSpecial = false; + + @HostBinding('style.color') color = 'green'; + + // #docregion hostbinding + @HostBinding('style.width') width = '200px'; + // #enddocregion hostbinding + } diff --git a/aio/content/examples/attribute-directives/e2e/src/app.e2e-spec.ts b/aio/content/examples/attribute-directives/e2e/src/app.e2e-spec.ts index 019469c338..771543102c 100644 --- a/aio/content/examples/attribute-directives/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/attribute-directives/e2e/src/app.e2e-spec.ts @@ -1,32 +1,28 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; describe('Attribute directives', () => { - let _title = 'My First Attribute Directive'; + const title = 'My First Attribute Directive'; - beforeAll(() => { - browser.get(''); + beforeAll(() => browser.get('')); + + it(`should display correct title: ${title}`, async () => { + expect(await element(by.css('h1')).getText()).toEqual(title); }); - it(`should display correct title: ${_title}`, () => { - expect(element(by.css('h1')).getText()).toEqual(_title); - }); - - it('should be able to select green highlight', () => { + it('should be able to select green highlight', async () => { const highlightedEle = element(by.cssContainingText('p', 'Highlight me!')); const lightGreen = 'rgba(144, 238, 144, 1)'; const getBgColor = () => highlightedEle.getCssValue('background-color'); - expect(highlightedEle.getCssValue('background-color')).not.toEqual(lightGreen); + expect(await highlightedEle.getCssValue('background-color')).not.toEqual(lightGreen); const greenRb = element.all(by.css('input')).get(0); - greenRb.click(); - browser.actions().mouseMove(highlightedEle).perform(); + await greenRb.click(); + await browser.actions().mouseMove(highlightedEle).perform(); // Wait for up to 4s for the background color to be updated, // to account for slow environments (e.g. CI). - browser.wait(() => highlightedEle.getCssValue('background-color').then(c => c === lightGreen), 4000); + await browser.wait(async () => await getBgColor() === lightGreen, 4000); }); }); diff --git a/aio/content/examples/attribute-directives/src/app/app.component.html b/aio/content/examples/attribute-directives/src/app/app.component.html index c361f11287..c8eb07c6a6 100644 --- a/aio/content/examples/attribute-directives/src/app/app.component.html +++ b/aio/content/examples/attribute-directives/src/app/app.component.html @@ -1,7 +1,7 @@

My First Attribute Directive

-

Pick a highlight color

+

Pick a highlight color

Green Yellow @@ -17,8 +17,22 @@

-
-

Mouse over the following lines to see fixed highlights

+
+

Mouse over the following lines to see fixed highlights

Highlighted in yellow

Highlighted in orange

+ +
+ +

ngNonBindable

+ +

Use ngNonBindable to stop evaluation.

+

This should not evaluate: {{ 1 + 1 }}

+ + + +

ngNonBindable with a directive

+
This should not evaluate: {{ 1 +1 }}, but will highlight yellow. +
+ diff --git a/aio/content/examples/attribute-directives/src/app/dummy.module.1.ts b/aio/content/examples/attribute-directives/src/app/dummy.module.1.ts index 14f0216f05..6354889bba 100644 --- a/aio/content/examples/attribute-directives/src/app/dummy.module.1.ts +++ b/aio/content/examples/attribute-directives/src/app/dummy.module.1.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component.1'; +import { AppComponent } from './app.component.1'; import { HighlightDirective as HLD1 } from './highlight.directive.1'; import { HighlightDirective as HLD2 } from './highlight.directive.2'; import { HighlightDirective as HLD3 } from './highlight.directive.3'; diff --git a/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts b/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts index c9bc11e472..ca8c6bc1f5 100644 --- a/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/binding-syntax/e2e/src/app.e2e-spec.ts @@ -3,74 +3,72 @@ import { logging } from 'selenium-webdriver'; describe('Binding syntax e2e tests', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + + // helper function used to test what's logged to the console + async function logChecker(contents) { + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false); + expect(messages.length).toBeGreaterThan(0); + } + + + it('should display Binding syntax', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Binding syntax'); }); - - // helper function used to test what's logged to the console - async function logChecker(button, contents) { - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false); - expect(message.length).toBeGreaterThan(0); - } - - - it('should display Binding syntax', function () { - expect(element(by.css('h1')).getText()).toEqual('Binding syntax'); + it('should display Save button', async () => { + expect(await element.all(by.css('button')).get(0).getText()).toBe('Save'); }); - it('should display Save button', function () { - expect(element.all(by.css('button')).get(0).getText()).toBe('Save'); + it('should display HTML attributes and DOM properties', async () => { + expect(await element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties'); }); - it('should display HTML attributes and DOM properties', function () { - expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties'); + it('should display 1. Use the inspector...', async () => { + expect(await element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector'); }); - it('should display 1. Use the inspector...', function () { - expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector'); - }); - - it('should display Disabled property vs. attribute', function () { - expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute'); + it('should display Disabled property vs. attribute', async () => { + expect(await element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute'); }); it('should log a message including Sarah', async () => { - let attributeButton = element.all(by.css('button')).get(1); + const attributeButton = element.all(by.css('button')).get(1); await attributeButton.click(); const contents = 'Sarah'; - logChecker(attributeButton, contents); + await logChecker(contents); }); it('should log a message including Sarah for DOM property', async () => { - let DOMPropertyButton = element.all(by.css('button')).get(2); + const DOMPropertyButton = element.all(by.css('button')).get(2); await DOMPropertyButton.click(); const contents = 'Sarah'; - logChecker(DOMPropertyButton, contents); + await logChecker(contents); }); it('should log a message including Sally for DOM property', async () => { - let DOMPropertyButton = element.all(by.css('button')).get(2); - let input = element(by.css('input')); - input.sendKeys('Sally'); + const DOMPropertyButton = element.all(by.css('button')).get(2); + const input = element(by.css('input')); + await input.sendKeys('Sally'); await DOMPropertyButton.click(); const contents = 'Sally'; - logChecker(DOMPropertyButton, contents); + await logChecker(contents); }); it('should log a message that Test Button works', async () => { - let testButton = element.all(by.css('button')).get(3); + const testButton = element.all(by.css('button')).get(3); await testButton.click(); const contents = 'Test'; - logChecker(testButton, contents); + await logChecker(contents); }); it('should toggle Test Button disabled', async () => { - let toggleButton = element.all(by.css('button')).get(4); + const toggleButton = element.all(by.css('button')).get(4); await toggleButton.click(); const contents = 'true'; - logChecker(toggleButton, contents); + await logChecker(contents); }); }); diff --git a/aio/content/examples/binding-syntax/src/app/app.component.ts b/aio/content/examples/binding-syntax/src/app/app.component.ts index bed9bd2a75..0b804d8e84 100644 --- a/aio/content/examples/binding-syntax/src/app/app.component.ts +++ b/aio/content/examples/binding-syntax/src/app/app.component.ts @@ -26,7 +26,7 @@ export class AppComponent { toggleDisabled(): any { - let testButton = document.getElementById('testButton'); + const testButton = document.getElementById('testButton') as HTMLInputElement; testButton.disabled = !testButton.disabled; console.warn(testButton.disabled); } diff --git a/aio/content/examples/bootstrapping/e2e/src/app.e2e-spec.ts b/aio/content/examples/bootstrapping/e2e/src/app.e2e-spec.ts index 676d41469c..78e8b1ee46 100644 --- a/aio/content/examples/bootstrapping/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/bootstrapping/e2e/src/app.e2e-spec.ts @@ -7,8 +7,8 @@ describe('feature-modules App', () => { page = new AppPage(); }); - it('should display message saying app works', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('app works!'); + it('should display message saying app works', async () => { + await page.navigateTo(); + expect(await page.getTitleText()).toEqual('app works!'); }); }); diff --git a/aio/content/examples/bootstrapping/src/app/app.module.ts b/aio/content/examples/bootstrapping/src/app/app.module.ts index 9b8a4fcaef..3059978a82 100644 --- a/aio/content/examples/bootstrapping/src/app/app.module.ts +++ b/aio/content/examples/bootstrapping/src/app/app.module.ts @@ -21,11 +21,13 @@ import { ItemDirective } from './item.directive'; ItemDirective ], // #enddocregion declarations + // #docregion imports imports: [ BrowserModule, FormsModule, HttpClientModule ], + // #enddocregion imports providers: [], bootstrap: [AppComponent] }) diff --git a/aio/content/examples/built-in-directives/e2e/src/app.e2e-spec.ts b/aio/content/examples/built-in-directives/e2e/src/app.e2e-spec.ts index 261afac3d4..d25537cfdf 100644 --- a/aio/content/examples/built-in-directives/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/built-in-directives/e2e/src/app.e2e-spec.ts @@ -1,78 +1,73 @@ -'use strict'; - import { browser, element, by } from 'protractor'; -describe('Built-in Directives', function () { +describe('Built-in Directives', () => { - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('should have title Built-in Directives', function () { - let title = element.all(by.css('h1')).get(0); - expect(title.getText()).toEqual('Built-in Directives'); + it('should have title Built-in Directives', async () => { + const title = element.all(by.css('h1')).get(0); + expect(await title.getText()).toEqual('Built-in Directives'); }); it('should change first Teapot header', async () => { - let firstLabel = element.all(by.css('p')).get(0); - let firstInput = element.all(by.css('input')).get(0); + const firstLabel = element.all(by.css('p')).get(0); + const firstInput = element.all(by.css('input')).get(0); - expect(firstLabel.getText()).toEqual('Current item name: Teapot'); - firstInput.sendKeys('abc'); - expect(firstLabel.getText()).toEqual('Current item name: Teapotabc'); + expect(await firstLabel.getText()).toEqual('Current item name: Teapot'); + await firstInput.sendKeys('abc'); + expect(await firstLabel.getText()).toEqual('Current item name: Teapotabc'); }); - it('should modify sentence when modified checkbox checked', function () { - let modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1); - let modifiedSentence = element.all(by.css('div')).get(1); + it('should modify sentence when modified checkbox checked', async () => { + const modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1); + const modifiedSentence = element.all(by.css('div')).get(1); - modifiedChkbxLabel.click(); - expect(modifiedSentence.getText()).toContain('modified'); + await modifiedChkbxLabel.click(); + expect(await modifiedSentence.getText()).toContain('modified'); }); - it('should modify sentence when normal checkbox checked', function () { - let normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4); - let normalSentence = element.all(by.css('div')).get(7); + it('should modify sentence when normal checkbox checked', async () => { + const normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4); + const normalSentence = element.all(by.css('div')).get(7); - normalChkbxLabel.click(); - expect(normalSentence.getText()).toContain('normal weight and, extra large'); + await normalChkbxLabel.click(); + expect(await normalSentence.getText()).toContain('normal weight and, extra large'); }); - it('should toggle app-item-detail', function () { - let toggleButton = element.all(by.css('button')).get(3); - let toggledDiv = element.all(by.css('app-item-detail')).get(0); + it('should toggle app-item-detail', async () => { + const toggleButton = element.all(by.css('button')).get(3); + const toggledDiv = element.all(by.css('app-item-detail')).get(0); - toggleButton.click(); - expect(toggledDiv.isDisplayed()).toBe(true); + await toggleButton.click(); + expect(await toggledDiv.isDisplayed()).toBe(true); }); - it('should hide app-item-detail', function () { - let hiddenMessage = element.all(by.css('p')).get(11); - let hiddenDiv = element.all(by.css('app-item-detail')).get(2); + it('should hide app-item-detail', async () => { + const hiddenMessage = element.all(by.css('p')).get(11); + const hiddenDiv = element.all(by.css('app-item-detail')).get(2); - expect(hiddenMessage.getText()).toContain('in the DOM'); - expect(hiddenDiv.isDisplayed()).toBe(true); + expect(await hiddenMessage.getText()).toContain('in the DOM'); + expect(await hiddenDiv.isDisplayed()).toBe(true); }); - it('should have 10 lists each containing the string Teapot', function () { - let listDiv = element.all(by.cssContainingText('.box', 'Teapot')); - expect(listDiv.count()).toBe(10); + it('should have 10 lists each containing the string Teapot', async () => { + const listDiv = element.all(by.cssContainingText('.box', 'Teapot')); + expect(await listDiv.count()).toBe(10); }); - it('should switch case', function () { - let tvRadioButton = element.all(by.css('input[type="radio"]')).get(3); - let tvDiv = element(by.css('app-lost-item')); + it('should switch case', async () => { + const tvRadioButton = element.all(by.css('input[type="radio"]')).get(3); + const tvDiv = element(by.css('app-lost-item')); - let fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4); - let fishbowlDiv = element(by.css('app-unknown-item')); + const fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4); + const fishbowlDiv = element(by.css('app-unknown-item')); - tvRadioButton.click(); - expect(tvDiv.getText()).toContain('Television'); - fishbowlRadioButton.click(); - expect(fishbowlDiv.getText()).toContain('mysterious'); + await tvRadioButton.click(); + expect(await tvDiv.getText()).toContain('Television'); + await fishbowlRadioButton.click(); + expect(await fishbowlDiv.getText()).toContain('mysterious'); }); - }); diff --git a/aio/content/examples/built-in-directives/src/app/app.component.ts b/aio/content/examples/built-in-directives/src/app/app.component.ts index 36b75f60b7..a2035effa5 100644 --- a/aio/content/examples/built-in-directives/src/app/app.component.ts +++ b/aio/content/examples/built-in-directives/src/app/app.component.ts @@ -30,6 +30,14 @@ export class AppComponent implements OnInit { itemsWithTrackByCountReset = 0; itemIdIncrement = 1; + // #docregion setClasses + currentClasses: {}; + // #enddocregion setClasses + + // #docregion setStyles + currentStyles: {}; + // #enddocregion setStyles + ngOnInit() { this.resetItems(); this.setCurrentClasses(); @@ -41,20 +49,18 @@ export class AppComponent implements OnInit { this.currentItem.name = name.toUpperCase(); } -// #docregion setClasses - currentClasses: {}; + // #docregion setClasses setCurrentClasses() { // CSS classes: added/removed per current state of component properties this.currentClasses = { - 'saveable': this.canSave, - 'modified': !this.isUnchanged, - 'special': this.isSpecial + saveable: this.canSave, + modified: !this.isUnchanged, + special: this.isSpecial }; } // #enddocregion setClasses // #docregion setStyles - currentStyles: {}; setCurrentStyles() { // CSS styles: set per current state of component properties this.currentStyles = { @@ -70,11 +76,7 @@ export class AppComponent implements OnInit { } giveNullCustomerValue() { - !(this.nullCustomer = null) ? (this.nullCustomer = 'Kelly') : (this.nullCustomer = null); - } - - resetNullItem() { - this.nullCustomer = null; + this.nullCustomer = 'Kelly'; } resetItems() { @@ -84,7 +86,7 @@ export class AppComponent implements OnInit { } resetList() { - this.resetItems() + this.resetItems(); this.itemsWithTrackByCountReset = 0; this.itemsNoTrackByCount = ++this.itemsNoTrackByCount; } @@ -107,7 +109,7 @@ export class AppComponent implements OnInit { trackByItems(index: number, item: Item): number { return item.id; } // #enddocregion trackByItems - trackById(index: number, item: any): number { return item['id']; } + trackById(index: number, item: any): number { return item.id; } } diff --git a/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts b/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts index 79c9f38d1c..03e4a2e5e3 100644 --- a/aio/content/examples/built-in-template-functions/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/built-in-template-functions/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('Built Template Functions Example', function () { - beforeAll(function () { - browser.get(''); +describe('Built Template Functions Example', () => { + beforeAll(() => browser.get('')); + + it('should have title Built-in Template Functions', async () => { + const title = element.all(by.css('h1')).get(0); + expect(await title.getText()).toEqual('Built-in Template Functions'); }); - it('should have title Built-in Template Functions', function () { - let title = element.all(by.css('h1')).get(0); - expect(title.getText()).toEqual('Built-in Template Functions'); - }); - - it('should display $any( ) in h2', function () { - let header = element(by.css('h2')); - expect(header.getText()).toContain('$any( )'); + it('should display $any( ) in h2', async () => { + const header = element(by.css('h2')); + expect(await header.getText()).toContain('$any( )'); }); }); diff --git a/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts b/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..ac89f882d0 --- /dev/null +++ b/aio/content/examples/comparing-observables/e2e/src/app.e2e-spec.ts @@ -0,0 +1,9 @@ +/* + * This example project is special in that it is not a cli app. To run tests appropriate for this + * project, the test command is overwritten in `aio/content/examples/observables/example-config.json`. + * + * This is an empty placeholder file to ensure that `aio/tools/examples/run-example-e2e.js` runs + * tests for this project. + * + * TODO: Fix our infrastructure/tooling, so that this hack is not necessary. + */ diff --git a/aio/content/examples/comparing-observables/example-config.json b/aio/content/examples/comparing-observables/example-config.json new file mode 100644 index 0000000000..3aa2059b4d --- /dev/null +++ b/aio/content/examples/comparing-observables/example-config.json @@ -0,0 +1,12 @@ +{ + "tests": [ + { + "cmd": "yarn", + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] + } + ] +} diff --git a/aio/content/examples/comparing-observables/src/observables.spec.ts b/aio/content/examples/comparing-observables/src/observables.spec.ts new file mode 100644 index 0000000000..e27ee9ced1 --- /dev/null +++ b/aio/content/examples/comparing-observables/src/observables.spec.ts @@ -0,0 +1,26 @@ +import { docRegionChain, docRegionObservable, docRegionUnsubscribe } from './observables'; + +describe('observables', () => { + it('should print 2', (doneFn: DoneFn) => { + const consoleLogSpy = spyOn(console, 'log'); + const observable = docRegionObservable(console); + observable.subscribe(() => { + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith(2); + doneFn(); + }); + }); + + it('should close the subscription', () => { + const subscription = docRegionUnsubscribe(); + expect(subscription.closed).toBeTruthy(); + }); + + it('should chain an observable', (doneFn: DoneFn) => { + const observable = docRegionChain(); + observable.subscribe(value => { + expect(value).toBe(4); + doneFn(); + }); + }); +}); diff --git a/aio/content/examples/comparing-observables/src/observables.ts b/aio/content/examples/comparing-observables/src/observables.ts index f0e8c1a138..ffa1777279 100644 --- a/aio/content/examples/comparing-observables/src/observables.ts +++ b/aio/content/examples/comparing-observables/src/observables.ts @@ -1,40 +1,72 @@ -import { map } from 'rxjs/operators'; +// #docplaster + import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; -// #docregion observable +export function docRegionObservable(console: Console) { + // #docregion observable -// declare a publishing operation -const observable = new Observable(observer => { - // Subscriber fn... -}); + // declare a publishing operation + const observable = new Observable(observer => { + // Subscriber fn... + // #enddocregion observable + // The below code is used for unit testing only + observer.next(2); + // #docregion observable + }); -// initiate execution -observable.subscribe(() => { - // observer handles notifications -}); + // initiate execution + observable.subscribe(value => { + // observer handles notifications + // #enddocregion observable + // The below code is used for unit testing only + console.log(value); + // #docregion observable + }); -// #enddocregion observable + // #enddocregion observable + return observable; +} -// #docregion unsubscribe +export function docRegionUnsubscribe() { + const observable = new Observable(() => { + // Subscriber fn... + }); + // #docregion unsubscribe -const subscription = observable.subscribe(() => { - // observer handles notifications -}); + const subscription = observable.subscribe(() => { + // observer handles notifications + }); -subscription.unsubscribe(); + subscription.unsubscribe(); -// #enddocregion unsubscribe + // #enddocregion unsubscribe + return subscription; +} -// #docregion error +export function docRegionError() { + const observable = new Observable(() => { + // Subscriber fn... + }); -observable.subscribe(() => { - throw Error('my error'); -}); + // #docregion error + observable.subscribe(() => { + throw new Error('my error'); + }); + // #enddocregion error +} -// #enddocregion error +export function docRegionChain() { + let observable = new Observable(observer => { + // Subscriber fn... + observer.next(2); + }); -// #docregion chain + observable = + // #docregion chain -observable.pipe(map(v => 2 * v)); + observable.pipe(map(v => 2 * v)); -// #enddocregion chain + // #enddocregion chain + return observable; +} diff --git a/aio/content/examples/comparing-observables/src/promises.spec.ts b/aio/content/examples/comparing-observables/src/promises.spec.ts new file mode 100644 index 0000000000..9d10092f1d --- /dev/null +++ b/aio/content/examples/comparing-observables/src/promises.spec.ts @@ -0,0 +1,23 @@ +import { docRegionError, docRegionPromise } from './promises'; + +describe('promises', () => { + it('should print 2', (doneFn: DoneFn) => { + const consoleLogSpy = spyOn(console, 'log'); + const pr = docRegionPromise(console, 2); + pr.then((value) => { + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith(2); + expect(value).toBe(4); + doneFn(); + }); + }); + + it('should throw an error', (doneFn: DoneFn) => { + const promise = docRegionError(); + promise + .then(() => { + throw new Error('Promise should be rejected.'); + }, + () => doneFn()); + }); +}); diff --git a/aio/content/examples/comparing-observables/src/promises.ts b/aio/content/examples/comparing-observables/src/promises.ts index 0ad68d99da..d24bba12ae 100644 --- a/aio/content/examples/comparing-observables/src/promises.ts +++ b/aio/content/examples/comparing-observables/src/promises.ts @@ -1,25 +1,44 @@ -// #docregion promise -// initiate execution -const promise = new Promise((resolve, reject) => { - // Executer fn... -}); +// #docplaster -promise.then(value => { - // handle result here -}); +export function docRegionPromise(console: Console, inputValue: number) { + // #docregion promise + // initiate execution + let promise = new Promise((resolve, reject) => { + // Executer fn... + // #enddocregion promise + // The below is used in the unit tests. + resolve(inputValue); + // #docregion promise + }); + // #enddocregion promise + promise = + // #docregion promise + promise.then(value => { + // handle result here + // #enddocregion promise + // The below is used in the unit tests. + console.log(value); + return value; + // #docregion promise + }); + // #enddocregion promise + promise = + // #docregion chain + promise.then(v => 2 * v); + // #enddocregion chain -// #enddocregion promise + return promise; +} -// #docregion chain +export function docRegionError() { + let promise = Promise.resolve(); + promise = + // #docregion error -promise.then(v => 2 * v); + promise.then(() => { + throw new Error('my error'); + }); -// #enddocregion chain - -// #docregion error - -promise.then(() => { - throw Error('my error'); -}); - -// #enddocregion error + // #enddocregion error + return promise; +} diff --git a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts index abc8ecf765..6f2c27990a 100644 --- a/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/component-interaction/e2e/src/app.e2e-spec.ts @@ -1,229 +1,236 @@ -'use strict'; // necessary for es6 output in node +import { browser, by, element } from 'protractor'; -import { browser, element, by } from 'protractor'; +describe('Component Communication Cookbook Tests', () => { -describe('Component Communication Cookbook Tests', function () { + beforeEach(() => browser.get(browser.baseUrl)); - // Note: '?e2e' which app can read to know it is running in protractor - // e.g. `if (!/e2e/.test(location.search)) { ...` - beforeAll(function () { - browser.get('?e2e'); - }); - - describe('Parent-to-child communication', function() { + describe('Parent-to-child communication', () => { // #docregion parent-to-child // ... - let _heroNames = ['Dr IQ', 'Magneta', 'Bombasto']; - let _masterName = 'Master'; + const heroNames = ['Dr IQ', 'Magneta', 'Bombasto']; + const masterName = 'Master'; - it('should pass properties to children properly', function () { - let parent = element.all(by.tagName('app-hero-parent')).get(0); - let heroes = parent.all(by.tagName('app-hero-child')); + it('should pass properties to children properly', async () => { + const parent = element(by.tagName('app-hero-parent')); + const heroes = parent.all(by.tagName('app-hero-child')); - for (let i = 0; i < _heroNames.length; i++) { - let childTitle = heroes.get(i).element(by.tagName('h3')).getText(); - let childDetail = heroes.get(i).element(by.tagName('p')).getText(); - expect(childTitle).toEqual(_heroNames[i] + ' says:'); - expect(childDetail).toContain(_masterName); + for (let i = 0; i < heroNames.length; i++) { + const childTitle = await heroes.get(i).element(by.tagName('h3')).getText(); + const childDetail = await heroes.get(i).element(by.tagName('p')).getText(); + expect(childTitle).toEqual(heroNames[i] + ' says:'); + expect(childDetail).toContain(masterName); } }); // ... // #enddocregion parent-to-child }); - describe('Parent-to-child communication with setter', function() { + describe('Parent-to-child communication with setter', () => { // #docregion parent-to-child-setter // ... - it('should display trimmed, non-empty names', function () { - let _nonEmptyNameIndex = 0; - let _nonEmptyName = '"Dr IQ"'; - let parent = element.all(by.tagName('app-name-parent')).get(0); - let hero = parent.all(by.tagName('app-name-child')).get(_nonEmptyNameIndex); + it('should display trimmed, non-empty names', async () => { + const nonEmptyNameIndex = 0; + const nonEmptyName = '"Dr IQ"'; + const parent = element(by.tagName('app-name-parent')); + const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex); - let displayName = hero.element(by.tagName('h3')).getText(); - expect(displayName).toEqual(_nonEmptyName); + const displayName = await hero.element(by.tagName('h3')).getText(); + expect(displayName).toEqual(nonEmptyName); }); - it('should replace empty name with default name', function () { - let _emptyNameIndex = 1; - let _defaultName = '""'; - let parent = element.all(by.tagName('app-name-parent')).get(0); - let hero = parent.all(by.tagName('app-name-child')).get(_emptyNameIndex); + it('should replace empty name with default name', async () => { + const emptyNameIndex = 1; + const defaultName = '""'; + const parent = element(by.tagName('app-name-parent')); + const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex); - let displayName = hero.element(by.tagName('h3')).getText(); - expect(displayName).toEqual(_defaultName); + const displayName = await hero.element(by.tagName('h3')).getText(); + expect(displayName).toEqual(defaultName); }); // ... // #enddocregion parent-to-child-setter }); - describe('Parent-to-child communication with ngOnChanges', function() { + describe('Parent-to-child communication with ngOnChanges', () => { // #docregion parent-to-child-onchanges // ... // Test must all execute in this exact order - it('should set expected initial values', function () { - let actual = getActual(); + it('should set expected initial values', async () => { + const actual = await getActual(); - let initialLabel = 'Version 1.23'; - let initialLog = 'Initial value of major set to 1, Initial value of minor set to 23'; + const initialLabel = 'Version 1.23'; + const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23'; expect(actual.label).toBe(initialLabel); expect(actual.count).toBe(1); - expect(actual.logs.get(0).getText()).toBe(initialLog); + expect(await actual.logs.get(0).getText()).toBe(initialLog); }); - it('should set expected values after clicking \'Minor\' twice', function () { - let repoTag = element(by.tagName('app-version-parent')); - let newMinorButton = repoTag.all(by.tagName('button')).get(0); + it('should set expected values after clicking \'Minor\' twice', async () => { + const repoTag = element(by.tagName('app-version-parent')); + const newMinorButton = repoTag.all(by.tagName('button')).get(0); - newMinorButton.click().then(function() { - newMinorButton.click().then(function() { - let actual = getActual(); + await newMinorButton.click(); + await newMinorButton.click(); - let labelAfter2Minor = 'Version 1.25'; - let logAfter2Minor = 'minor changed from 24 to 25'; + const actual = await getActual(); - expect(actual.label).toBe(labelAfter2Minor); - expect(actual.count).toBe(3); - expect(actual.logs.get(2).getText()).toBe(logAfter2Minor); - }); - }); + const labelAfter2Minor = 'Version 1.25'; + const logAfter2Minor = 'minor changed from 24 to 25'; + + expect(actual.label).toBe(labelAfter2Minor); + expect(actual.count).toBe(3); + expect(await actual.logs.get(2).getText()).toBe(logAfter2Minor); }); - it('should set expected values after clicking \'Major\' once', function () { - let repoTag = element(by.tagName('app-version-parent')); - let newMajorButton = repoTag.all(by.tagName('button')).get(1); + it('should set expected values after clicking \'Major\' once', async () => { + const repoTag = element(by.tagName('app-version-parent')); + const newMajorButton = repoTag.all(by.tagName('button')).get(1); - newMajorButton.click().then(function() { - let actual = getActual(); + await newMajorButton.click(); + const actual = await getActual(); - let labelAfterMajor = 'Version 2.0'; - let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0'; + const labelAfterMajor = 'Version 2.0'; + const logAfterMajor = 'major changed from 1 to 2, minor changed from 23 to 0'; - expect(actual.label).toBe(labelAfterMajor); - expect(actual.count).toBe(4); - expect(actual.logs.get(3).getText()).toBe(logAfterMajor); - }); + expect(actual.label).toBe(labelAfterMajor); + expect(actual.count).toBe(2); + expect(await actual.logs.get(1).getText()).toBe(logAfterMajor); }); - function getActual() { - let versionTag = element(by.tagName('app-version-child')); - let label = versionTag.element(by.tagName('h3')).getText(); - let ul = versionTag.element((by.tagName('ul'))); - let logs = ul.all(by.tagName('li')); + async function getActual() { + const versionTag = element(by.tagName('app-version-child')); + const label = await versionTag.element(by.tagName('h3')).getText(); + const ul = versionTag.element((by.tagName('ul'))); + const logs = ul.all(by.tagName('li')); return { - label: label, - logs: logs, - count: logs.count() + label, + logs, + count: await logs.count(), }; } // ... // #enddocregion parent-to-child-onchanges - }); - describe('Child-to-parent communication', function() { + describe('Child-to-parent communication', () => { // #docregion child-to-parent // ... - it('should not emit the event initially', function () { - let voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 0, Disagree: 0'); + it('should not emit the event initially', async () => { + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); + expect(await voteLabel.getText()).toBe('Agree: 0, Disagree: 0'); }); - it('should process Agree vote', function () { - let agreeButton1 = element.all(by.tagName('app-voter')).get(0) + it('should process Agree vote', async () => { + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); + const agreeButton1 = element.all(by.tagName('app-voter')).get(0) .all(by.tagName('button')).get(0); - agreeButton1.click().then(function() { - let voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 1, Disagree: 0'); - }); + + await agreeButton1.click(); + + expect(await voteLabel.getText()).toBe('Agree: 1, Disagree: 0'); }); - it('should process Disagree vote', function () { - let agreeButton1 = element.all(by.tagName('app-voter')).get(1) + it('should process Disagree vote', async () => { + const voteLabel = element(by.tagName('app-vote-taker')).element(by.tagName('h3')); + const agreeButton1 = element.all(by.tagName('app-voter')).get(1) .all(by.tagName('button')).get(1); - agreeButton1.click().then(function() { - let voteLabel = element(by.tagName('app-vote-taker')) - .element(by.tagName('h3')).getText(); - expect(voteLabel).toBe('Agree: 1, Disagree: 1'); - }); + + await agreeButton1.click(); + + expect(await voteLabel.getText()).toBe('Agree: 0, Disagree: 1'); }); // ... // #enddocregion child-to-parent }); - // Can't run timer tests in protractor because - // interaction w/ zones causes all tests to freeze & timeout. - xdescribe('Parent calls child via local var', function() { - countDownTimerTests('countdown-parent-lv'); + describe('Parent calls child via local var', () => { + countDownTimerTests('app-countdown-parent-lv'); }); - xdescribe('Parent calls ViewChild', function() { - countDownTimerTests('countdown-parent-vc'); + describe('Parent calls ViewChild', () => { + countDownTimerTests('app-countdown-parent-vc'); }); function countDownTimerTests(parentTag: string) { // #docregion countdown-timer-tests // ... - it('timer and parent seconds should match', function () { - let parent = element(by.tagName(parentTag)); - let message = parent.element(by.tagName('app-countdown-timer')).getText(); - browser.sleep(10); // give `seconds` a chance to catchup with `message` - let seconds = parent.element(by.className('seconds')).getText(); - expect(message).toContain(seconds); + // The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent + // the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples + // for more details. + // To allow the tests to complete, we will disable automatically waiting for the Angular app to + // stabilize. + beforeEach(() => browser.waitForAngularEnabled(false)); + afterEach(() => browser.waitForAngularEnabled(true)); + + it('timer and parent seconds should match', async () => { + const parent = element(by.tagName(parentTag)); + const startButton = parent.element(by.buttonText('Start')); + const seconds = parent.element(by.className('seconds')); + const timer = parent.element(by.tagName('app-countdown-timer')); + + await startButton.click(); + + // Wait for `` to be populated with any text. + await browser.wait(() => timer.getText(), 2000); + + expect(await timer.getText()).toContain(await seconds.getText()); }); - it('should stop the countdown', function () { - let parent = element(by.tagName(parentTag)); - let stopButton = parent.all(by.tagName('button')).get(1); + it('should stop the countdown', async () => { + const parent = element(by.tagName(parentTag)); + const startButton = parent.element(by.buttonText('Start')); + const stopButton = parent.element(by.buttonText('Stop')); + const timer = parent.element(by.tagName('app-countdown-timer')); - stopButton.click().then(function() { - let message = parent.element(by.tagName('app-countdown-timer')).getText(); - expect(message).toContain('Holding'); - }); + await startButton.click(); + expect(await timer.getText()).not.toContain('Holding'); + + await stopButton.click(); + expect(await timer.getText()).toContain('Holding'); }); // ... // #enddocregion countdown-timer-tests } - - describe('Parent and children communicate via a service', function() { + describe('Parent and children communicate via a service', () => { // #docregion bidirectional-service // ... - it('should announce a mission', function () { - let missionControl = element(by.tagName('app-mission-control')); - let announceButton = missionControl.all(by.tagName('button')).get(0); - announceButton.click().then(function () { - let history = missionControl.all(by.tagName('li')); - expect(history.count()).toBe(1); - expect(history.get(0).getText()).toMatch(/Mission.* announced/); - }); + it('should announce a mission', async () => { + const missionControl = element(by.tagName('app-mission-control')); + const announceButton = missionControl.all(by.tagName('button')).get(0); + const history = missionControl.all(by.tagName('li')); + + await announceButton.click(); + + expect(await history.count()).toBe(1); + expect(await history.get(0).getText()).toMatch(/Mission.* announced/); }); - it('should confirm the mission by Lovell', function () { - testConfirmMission(1, 2, 'Lovell'); + it('should confirm the mission by Lovell', async () => { + await testConfirmMission(1, 'Lovell'); }); - it('should confirm the mission by Haise', function () { - testConfirmMission(3, 3, 'Haise'); + it('should confirm the mission by Haise', async () => { + await testConfirmMission(3, 'Haise'); }); - it('should confirm the mission by Swigert', function () { - testConfirmMission(2, 4, 'Swigert'); + it('should confirm the mission by Swigert', async () => { + await testConfirmMission(2, 'Swigert'); }); - function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) { - let _confirmedLog = ' confirmed the mission'; - let missionControl = element(by.tagName('app-mission-control')); - let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); - confirmButton.click().then(function () { - let history = missionControl.all(by.tagName('li')); - expect(history.count()).toBe(expectedLogCount); - expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog); - }); + async function testConfirmMission(buttonIndex: number, astronaut: string) { + const missionControl = element(by.tagName('app-mission-control')); + const announceButton = missionControl.all(by.tagName('button')).get(0); + const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); + const history = missionControl.all(by.tagName('li')); + + await announceButton.click(); + await confirmButton.click(); + + expect(await history.count()).toBe(2); + expect(await history.get(1).getText()).toBe(`${astronaut} confirmed the mission`); } // ... // #enddocregion bidirectional-service diff --git a/aio/content/examples/component-interaction/example-config.json b/aio/content/examples/component-interaction/example-config.json index 05e262817d..e69de29bb2 100644 --- a/aio/content/examples/component-interaction/example-config.json +++ b/aio/content/examples/component-interaction/example-config.json @@ -1,13 +0,0 @@ -{ - "tests": [ - { - "cmd": "yarn", - "args": [ - "e2e", - "--protractor-config=e2e/protractor-puppeteer.conf.js", - "--no-webdriver-update", - "--port={PORT}" - ] - } - ] -} diff --git a/aio/content/examples/component-interaction/src/app/app.component.html b/aio/content/examples/component-interaction/src/app/app.component.html index 907e0181fa..88d05abdb1 100644 --- a/aio/content/examples/component-interaction/src/app/app.component.html +++ b/aio/content/examples/component-interaction/src/app/app.component.html @@ -30,22 +30,21 @@
Back to Top -
+
Back to Top -
+
Back to Top -
+
Back to Top -
diff --git a/aio/content/examples/component-interaction/src/app/app.module.ts b/aio/content/examples/component-interaction/src/app/app.module.ts index 7a27465ff8..caecee59e5 100644 --- a/aio/content/examples/component-interaction/src/app/app.module.ts +++ b/aio/content/examples/component-interaction/src/app/app.module.ts @@ -1,5 +1,5 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { AstronautComponent } from './astronaut.component'; @@ -15,10 +15,17 @@ import { VersionParentComponent } from './version-parent.component'; import { VoterComponent } from './voter.component'; import { VoteTakerComponent } from './votetaker.component'; -let directives: any[] = [ + +@NgModule({ + imports: [ + BrowserModule, + ], + declarations: [ AppComponent, AstronautComponent, + CountdownLocalVarParentComponent, CountdownTimerComponent, + CountdownViewChildParentComponent, HeroChildComponent, HeroParentComponent, MissionControlComponent, @@ -27,28 +34,8 @@ let directives: any[] = [ VersionChildComponent, VersionParentComponent, VoterComponent, - VoteTakerComponent - ]; - -let schemas: any[] = []; - -// Include Countdown examples -// unless in e2e tests which they break. -if (!/e2e/.test(location.search)) { - console.log('adding countdown timer examples'); - directives.push(CountdownLocalVarParentComponent); - directives.push(CountdownViewChildParentComponent); -} else { - // In e2e test use CUSTOM_ELEMENTS_SCHEMA to suppress unknown element errors - schemas.push(CUSTOM_ELEMENTS_SCHEMA); -} - -@NgModule({ - imports: [ - BrowserModule + VoteTakerComponent, ], - declarations: directives, bootstrap: [ AppComponent ], - schemas: schemas }) export class AppModule { } diff --git a/aio/content/examples/component-interaction/src/app/astronaut.component.ts b/aio/content/examples/component-interaction/src/app/astronaut.component.ts index 9bd4c9d78c..c87edd7b10 100644 --- a/aio/content/examples/component-interaction/src/app/astronaut.component.ts +++ b/aio/content/examples/component-interaction/src/app/astronaut.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnDestroy } from '@angular/core'; import { MissionService } from './mission.service'; -import { Subscription } from 'rxjs'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-astronaut', diff --git a/aio/content/examples/component-interaction/src/app/countdown-parent.component.ts b/aio/content/examples/component-interaction/src/app/countdown-parent.component.ts index 9f4e5bd4df..5c2399d721 100644 --- a/aio/content/examples/component-interaction/src/app/countdown-parent.component.ts +++ b/aio/content/examples/component-interaction/src/app/countdown-parent.component.ts @@ -2,8 +2,8 @@ // #docregion vc import { AfterViewInit, ViewChild } from '@angular/core'; // #docregion lv -import { Component } from '@angular/core'; -import { CountdownTimerComponent } from './countdown-timer.component'; +import { Component } from '@angular/core'; +import { CountdownTimerComponent } from './countdown-timer.component'; // #enddocregion lv // #enddocregion vc diff --git a/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts b/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts index dcc88334d0..cba4de07ac 100644 --- a/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts +++ b/aio/content/examples/component-interaction/src/app/countdown-timer.component.ts @@ -1,19 +1,16 @@ // #docregion -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; @Component({ selector: 'app-countdown-timer', template: '

{{message}}

' }) -export class CountdownTimerComponent implements OnInit, OnDestroy { +export class CountdownTimerComponent implements OnDestroy { intervalId = 0; message = ''; seconds = 11; - clearTimer() { clearInterval(this.intervalId); } - - ngOnInit() { this.start(); } ngOnDestroy() { this.clearTimer(); } start() { this.countDown(); } @@ -22,6 +19,8 @@ export class CountdownTimerComponent implements OnInit, OnDestroy { this.message = `Holding at T-${this.seconds} seconds`; } + private clearTimer() { clearInterval(this.intervalId); } + private countDown() { this.clearTimer(); this.intervalId = window.setInterval(() => { diff --git a/aio/content/examples/component-interaction/src/app/hero-child.component.ts b/aio/content/examples/component-interaction/src/app/hero-child.component.ts index 4a0b8d4229..d36ffdeeb1 100644 --- a/aio/content/examples/component-interaction/src/app/hero-child.component.ts +++ b/aio/content/examples/component-interaction/src/app/hero-child.component.ts @@ -12,6 +12,6 @@ import { Hero } from './hero'; }) export class HeroChildComponent { @Input() hero: Hero; - @Input('master') masterName: string; + @Input('master') masterName: string; // tslint:disable-line: no-input-rename } // #enddocregion diff --git a/aio/content/examples/component-interaction/src/app/mission.service.ts b/aio/content/examples/component-interaction/src/app/mission.service.ts index e8f4a42667..1c450fd88d 100644 --- a/aio/content/examples/component-interaction/src/app/mission.service.ts +++ b/aio/content/examples/component-interaction/src/app/mission.service.ts @@ -1,6 +1,6 @@ // #docregion import { Injectable } from '@angular/core'; -import { Subject } from 'rxjs'; +import { Subject } from 'rxjs'; @Injectable() export class MissionService { diff --git a/aio/content/examples/component-interaction/src/app/missioncontrol.component.ts b/aio/content/examples/component-interaction/src/app/missioncontrol.component.ts index 4c1f539b59..191a619e87 100644 --- a/aio/content/examples/component-interaction/src/app/missioncontrol.component.ts +++ b/aio/content/examples/component-interaction/src/app/missioncontrol.component.ts @@ -1,7 +1,7 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; -import { MissionService } from './mission.service'; +import { MissionService } from './mission.service'; @Component({ selector: 'app-mission-control', @@ -34,7 +34,7 @@ export class MissionControlComponent { } announce() { - let mission = this.missions[this.nextMission++]; + const mission = this.missions[this.nextMission++]; this.missionService.announceMission(mission); this.history.push(`Mission "${mission}" announced`); if (this.nextMission >= this.missions.length) { this.nextMission = 0; } diff --git a/aio/content/examples/component-interaction/src/app/name-child.component.ts b/aio/content/examples/component-interaction/src/app/name-child.component.ts index 8393e21ebd..06e65bb87c 100644 --- a/aio/content/examples/component-interaction/src/app/name-child.component.ts +++ b/aio/content/examples/component-interaction/src/app/name-child.component.ts @@ -1,3 +1,4 @@ +// tslint:disable: variable-name // #docregion import { Component, Input } from '@angular/core'; @@ -6,13 +7,11 @@ import { Component, Input } from '@angular/core'; template: '

"{{name}}"

' }) export class NameChildComponent { - private _name = ''; - @Input() + get name(): string { return this._name; } set name(name: string) { this._name = (name && name.trim()) || ''; } - - get name(): string { return this._name; } + private _name = ''; } // #enddocregion diff --git a/aio/content/examples/component-interaction/src/app/version-child.component.ts b/aio/content/examples/component-interaction/src/app/version-child.component.ts index 1f3da880f4..93ef079d5d 100644 --- a/aio/content/examples/component-interaction/src/app/version-child.component.ts +++ b/aio/content/examples/component-interaction/src/app/version-child.component.ts @@ -1,6 +1,6 @@ /* tslint:disable:forin */ // #docregion -import { Component, Input, OnChanges, SimpleChange } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; @Component({ selector: 'app-version-child', @@ -17,15 +17,15 @@ export class VersionChildComponent implements OnChanges { @Input() minor: number; changeLog: string[] = []; - ngOnChanges(changes: {[propKey: string]: SimpleChange}) { - let log: string[] = []; - for (let propName in changes) { - let changedProp = changes[propName]; - let to = JSON.stringify(changedProp.currentValue); + ngOnChanges(changes: SimpleChanges) { + const log: string[] = []; + for (const propName in changes) { + const changedProp = changes[propName]; + const to = JSON.stringify(changedProp.currentValue); if (changedProp.isFirstChange()) { log.push(`Initial value of ${propName} set to ${to}`); } else { - let from = JSON.stringify(changedProp.previousValue); + const from = JSON.stringify(changedProp.previousValue); log.push(`${propName} changed from ${from} to ${to}`); } } diff --git a/aio/content/examples/component-interaction/src/app/votetaker.component.ts b/aio/content/examples/component-interaction/src/app/votetaker.component.ts index 7b3de1059b..9f1f85e282 100644 --- a/aio/content/examples/component-interaction/src/app/votetaker.component.ts +++ b/aio/content/examples/component-interaction/src/app/votetaker.component.ts @@ -1,5 +1,5 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; @Component({ selector: 'app-vote-taker', diff --git a/aio/content/examples/component-overview/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-overview/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000000..4ddd1bcef1 --- /dev/null +++ b/aio/content/examples/component-overview/e2e/src/app.e2e-spec.ts @@ -0,0 +1,11 @@ +import { browser, element, by } from 'protractor'; + +describe('Component Overview', () => { + + beforeAll(() => browser.get('')); + + it('should display component overview works ', async () => { + expect(await element(by.css('p')).getText()).toEqual('component-overview works!'); + }); + +}); diff --git a/aio/content/examples/component-overview/example-config.json b/aio/content/examples/component-overview/example-config.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/component-overview/src/app/app.component.css b/aio/content/examples/component-overview/src/app/app.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/component-overview/src/app/app.component.html b/aio/content/examples/component-overview/src/app/app.component.html new file mode 100644 index 0000000000..c940e3dc20 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/app.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aio/content/examples/component-overview/src/app/app.component.spec.ts b/aio/content/examples/component-overview/src/app/app.component.spec.ts new file mode 100644 index 0000000000..d27174ee5b --- /dev/null +++ b/aio/content/examples/component-overview/src/app/app.component.spec.ts @@ -0,0 +1,31 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'component-overview'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('component-overview'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement; + expect(compiled.querySelector('.content span').textContent).toContain('component-overview app is running!'); + }); +}); diff --git a/aio/content/examples/component-overview/src/app/app.component.ts b/aio/content/examples/component-overview/src/app/app.component.ts new file mode 100644 index 0000000000..29cfb38dc3 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'component-overview'; +} diff --git a/aio/content/examples/component-overview/src/app/app.module.ts b/aio/content/examples/component-overview/src/app/app.module.ts new file mode 100644 index 0000000000..e22e3e1854 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/app.module.ts @@ -0,0 +1,18 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; + +import { AppComponent } from './app.component'; +import { ComponentOverviewComponent } from './component-overview/component-overview.component'; + +@NgModule({ + declarations: [ + AppComponent, + ComponentOverviewComponent + ], + imports: [ + BrowserModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.1.ts b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.1.ts new file mode 100644 index 0000000000..3e6d5d1fd2 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.1.ts @@ -0,0 +1,14 @@ +// #docplaster +import { Component } from '@angular/core'; + +// #docregion template +@Component({ + selector: 'app-component-overview', + template: '

Hello World!

', +}) +// #enddocregion template + +export class ComponentOverviewComponent { + +} + diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.2.ts b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.2.ts new file mode 100644 index 0000000000..50153f8fbe --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.2.ts @@ -0,0 +1,16 @@ +// #docplaster +import { Component } from '@angular/core'; + +// #docregion templatebacktick +@Component({ + selector: 'app-component-overview', + template: `

Hello World!

+

This template definition spans + multiple lines.

` +}) +// #enddocregion templatebacktick + +export class ComponentOverviewComponent { + +} + diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.3.ts b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.3.ts new file mode 100644 index 0000000000..b1b8b3706c --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.3.ts @@ -0,0 +1,15 @@ +// #docplaster +import { Component } from '@angular/core'; + +// #docregion styles +@Component({ + selector: 'app-component-overview', + template: '

Hello World!

', + styles: ['h1 { font-weight: normal; }'] +}) +// #enddocregion styles + +export class ComponentOverviewComponent { + +} + diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.css b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.html b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.html new file mode 100644 index 0000000000..ffe0904017 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.html @@ -0,0 +1 @@ +

component-overview works!

diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.spec.ts b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.spec.ts new file mode 100644 index 0000000000..e3a0323231 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ComponentOverviewComponent } from './component-overview.component'; + +describe('ComponentOverviewComponent', () => { + let component: ComponentOverviewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ComponentOverviewComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ComponentOverviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.ts b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.ts new file mode 100644 index 0000000000..7e1e3597a1 --- /dev/null +++ b/aio/content/examples/component-overview/src/app/component-overview/component-overview.component.ts @@ -0,0 +1,22 @@ +// #docplaster +// #docregion import +import { Component } from '@angular/core'; +// #enddocregion import + +// #docregion decorator, decorator-skeleton, selector, templateUrl +@Component({ +// #enddocregion decorator-skeleton + selector: 'app-component-overview', +// #enddocregion selector + templateUrl: './component-overview.component.html', +// #enddocregion templateUrl + styleUrls: ['./component-overview.component.css'] +// #docregion decorator-skeleton, selector, templateUrl +}) +// #enddocregion decorator, decorator-skeleton, selector, templateUrl + +// #docregion class +export class ComponentOverviewComponent { + +} +// #enddocregion class diff --git a/aio/content/examples/component-overview/src/index.html b/aio/content/examples/component-overview/src/index.html new file mode 100644 index 0000000000..743fd8d693 --- /dev/null +++ b/aio/content/examples/component-overview/src/index.html @@ -0,0 +1,13 @@ + + + + + ComponentOverview + + + + + + + + diff --git a/aio/content/examples/component-overview/src/main.ts b/aio/content/examples/component-overview/src/main.ts new file mode 100644 index 0000000000..c7b673cf44 --- /dev/null +++ b/aio/content/examples/component-overview/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/aio/content/examples/component-overview/stackblitz.json b/aio/content/examples/component-overview/stackblitz.json new file mode 100644 index 0000000000..865a94123a --- /dev/null +++ b/aio/content/examples/component-overview/stackblitz.json @@ -0,0 +1,8 @@ +{ + "description": "Component Overview", + "files":[ + "!**/*.d.ts", + "!**/*.js" + ], + "tags":["overview", "component"] +} diff --git a/aio/content/examples/component-styles/e2e/src/app.e2e-spec.ts b/aio/content/examples/component-styles/e2e/src/app.e2e-spec.ts index 910f9351d4..34f854693c 100644 --- a/aio/content/examples/component-styles/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/component-styles/e2e/src/app.e2e-spec.ts @@ -1,70 +1,66 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Component Style Tests', function () { +describe('Component Style Tests', () => { - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('scopes component styles to component view', function() { - let componentH1 = element(by.css('app-root > h1')); - let externalH1 = element(by.css('body > h1')); + it('scopes component styles to component view', async () => { + const componentH1 = element(by.css('app-root > h1')); + const externalH1 = element(by.css('body > h1')); // Note: sometimes webdriver returns the fontWeight as "normal", // other times as "400", both of which are equal in CSS terms. - expect(componentH1.getCssValue('fontWeight')).toMatch(/normal|400/); - expect(externalH1.getCssValue('fontWeight')).not.toMatch(/normal|400/); + expect(await componentH1.getCssValue('fontWeight')).toMatch(/normal|400/); + expect(await externalH1.getCssValue('fontWeight')).not.toMatch(/normal|400/); }); - it('allows styling :host element', function() { - let host = element(by.css('app-hero-details')); + it('allows styling :host element', async () => { + const host = element(by.css('app-hero-details')); - expect(host.getCssValue('borderWidth')).toEqual('1px'); + expect(await host.getCssValue('borderWidth')).toEqual('1px'); }); - it('supports :host() in function form', function() { - let host = element(by.css('app-hero-details')); + it('supports :host() in function form', async () => { + const host = element(by.css('app-hero-details')); - host.element(by.buttonText('Activate')).click(); - expect(host.getCssValue('borderWidth')).toEqual('3px'); + await host.element(by.buttonText('Activate')).click(); + expect(await host.getCssValue('borderWidth')).toEqual('3px'); }); - it('allows conditional :host-context() styling', function() { - let h2 = element(by.css('app-hero-details h2')); + it('allows conditional :host-context() styling', async () => { + const h2 = element(by.css('app-hero-details h2')); - expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff + expect(await h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff }); - it('styles both view and content children with /deep/', function() { - let viewH3 = element(by.css('app-hero-team h3')); - let contentH3 = element(by.css('app-hero-controls h3')); + it('styles both view and content children with /deep/', async () => { + const viewH3 = element(by.css('app-hero-team h3')); + const contentH3 = element(by.css('app-hero-controls h3')); - expect(viewH3.getCssValue('fontStyle')).toEqual('italic'); - expect(contentH3.getCssValue('fontStyle')).toEqual('italic'); + expect(await viewH3.getCssValue('fontStyle')).toEqual('italic'); + expect(await contentH3.getCssValue('fontStyle')).toEqual('italic'); }); - it('includes styles loaded with CSS @import', function() { - let host = element(by.css('app-hero-details')); + it('includes styles loaded with CSS @import', async () => { + const host = element(by.css('app-hero-details')); - expect(host.getCssValue('padding')).toEqual('10px'); + expect(await host.getCssValue('padding')).toEqual('10px'); }); - it('processes template inline styles', function() { - let button = element(by.css('app-hero-controls button')); - let externalButton = element(by.css('body > button')); - expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff - expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)'); + it('processes template inline styles', async () => { + const button = element(by.css('app-hero-controls button')); + const externalButton = element(by.css('body > button')); + expect(await button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff + expect(await externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)'); }); - it('processes template s', function() { - let li = element(by.css('app-hero-team li:first-child')); - let externalLi = element(by.css('body > ul li')); + it('processes template s', async () => { + const li = element(by.css('app-hero-team li:first-child')); + const externalLi = element(by.css('body > ul li')); - expect(li.getCssValue('listStyleType')).toEqual('square'); - expect(externalLi.getCssValue('listStyleType')).not.toEqual('square'); + expect(await li.getCssValue('listStyleType')).toEqual('square'); + expect(await externalLi.getCssValue('listStyleType')).not.toEqual('square'); }); }); diff --git a/aio/content/examples/component-styles/src/app/hero-details.component.css b/aio/content/examples/component-styles/src/app/hero-details.component.css index 7c381aa8d2..e7918b5b16 100644 --- a/aio/content/examples/component-styles/src/app/hero-details.component.css +++ b/aio/content/examples/component-styles/src/app/hero-details.component.css @@ -23,7 +23,7 @@ /* #enddocregion hostcontext */ /* #docregion deep */ -:host /deep/ h3 { +:host ::ng-deep h3 { font-style: italic; } /* #enddocregion deep */ diff --git a/aio/content/examples/component-styles/src/app/quest-summary.component.ts b/aio/content/examples/component-styles/src/app/quest-summary.component.ts index 25765ed16d..6d7b580582 100644 --- a/aio/content/examples/component-styles/src/app/quest-summary.component.ts +++ b/aio/content/examples/component-styles/src/app/quest-summary.component.ts @@ -13,8 +13,8 @@ import { Component, ViewEncapsulation } from '@angular/core'; export class QuestSummaryComponent { } // #enddocregion /* - // #docregion encapsulation.native + // #docregion encapsulation.shadow // warning: few browsers support shadow DOM encapsulation at this time - encapsulation: ViewEncapsulation.Native - // #enddocregion encapsulation.native + encapsulation: ViewEncapsulation.ShadowDom + // #enddocregion encapsulation.shadow */ diff --git a/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts b/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts index 4d611f5698..f13d6ad042 100644 --- a/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/dependency-injection-in-action/e2e/src/app.e2e-spec.ts @@ -1,106 +1,95 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Dependency Injection Cookbook', function () { +describe('Dependency Injection Cookbook', () => { - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it('should render Logged in User example', async () => { + const loggedInUser = element(by.cssContainingText('h3', 'Logged in user')); + expect(await loggedInUser.isPresent()).toBe(true); }); - it('should render Logged in User example', function () { - let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0); - expect(loggedInUser).toBeDefined(); + it('"Bombasto" should be the logged in user', async () => { + const loggedInUser = element(by.cssContainingText('div', 'Name: Bombasto')); + expect(await loggedInUser.isPresent()).toBe(true); }); - it('"Bombasto" should be the logged in user', function () { - let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0); - expect(loggedInUser).toBeDefined(); + it('should render sorted heroes', async () => { + const sortedHeroes = element(by.cssContainingText('h3', 'Sorted Heroes')); + expect(await sortedHeroes.isPresent()).toBe(true); + + const sortedHeroElems = element.all(by.css('app-sorted-heroes div')); + const sortedHeroNames = await sortedHeroElems.map(elem => elem.getText()); + expect(sortedHeroNames).toEqual(['Dr Nice', 'Magma', 'RubberMan']); }); - it('should render sorted heroes', function () { - let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0); - expect(sortedHeroes).toBeDefined(); + it('should render Hero of the Month', async () => { + const heroOfTheMonth = element(by.cssContainingText('h3', 'Hero of the Month')); + expect(await heroOfTheMonth.isPresent()).toBe(true); }); - it('Dr Nice should be in sorted heroes', function () { - let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0); - expect(sortedHero).toBeDefined(); + it('should render Hero Bios', async () => { + const heroBios = element(by.cssContainingText('h3', 'Hero Bios')); + expect(await heroBios.isPresent()).toBe(true); }); - it('RubberMan should be in sorted heroes', function () { - let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0); - expect(sortedHero).toBeDefined(); + it('should render Magma\'s description in Hero Bios', async () => { + const magmaBioElem = element.all(by.css('app-hero-bio')).get(1); + const magmaNameElem = magmaBioElem.element(by.css('h4')); + const magmaDescElem = magmaBioElem.element(by.css('textarea')); + + expect(await magmaNameElem.getText()).toBe('Magma'); + expect(await magmaDescElem.getAttribute('value')).toBe('Hero of all trades'); }); - it('Magma should be in sorted heroes', function () { - let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0); - expect(sortedHero).toBeDefined(); + it('should render Magma\'s phone in Hero Bios and Contacts', async () => { + const magmaPhone = element(by.cssContainingText('div', 'Phone #: 555-555-5555')); + expect(await magmaPhone.isPresent()).toBe(true); }); - it('should render Hero of the Month', function () { - let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0); - expect(heroOfTheMonth).toBeDefined(); - }); - - it('should render Hero Bios', function () { - let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0); - expect(heroBios).toBeDefined(); - }); - - it('should render Magma\'s description in Hero Bios', function () { - let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0); - expect(magmaText).toBeDefined(); - }); - - it('should render Magma\'s phone in Hero Bios and Contacts', function () { - let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0); - expect(magmaPhone).toBeDefined(); - }); - - it('should render Hero-of-the-Month runner-ups', function () { - let runnersUp = element(by.id('rups1')).getText(); + it('should render Hero-of-the-Month runner-ups', async () => { + const runnersUp = await element(by.id('rups1')).getText(); expect(runnersUp).toContain('RubberMan, Dr Nice'); }); - it('should render DateLogger log entry in Hero-of-the-Month', function () { - let logs = element.all(by.id('logs')).get(0).getText(); + it('should render DateLogger log entry in Hero-of-the-Month', async () => { + const logs = await element.all(by.id('logs')).get(0).getText(); expect(logs).toContain('INFO: starting up at'); }); - it('should highlight Hero Bios and Contacts container when mouseover', function () { - let target = element(by.css('div[appHighlight="yellow"]')); - let yellow = 'rgba(255, 255, 0, 1)'; + it('should highlight Hero Bios and Contacts container when mouseover', async () => { + const target = element(by.css('div[appHighlight="yellow"]')); + const yellow = 'rgba(255, 255, 0, 1)'; - expect(target.getCssValue('background-color')).not.toEqual(yellow); + expect(await target.getCssValue('background-color')).not.toEqual(yellow); - browser.actions().mouseMove(target).perform(); + await browser.actions().mouseMove(target).perform(); // Wait for up to 2s for the background color to be updated, // to account for slow environments (e.g. CI). - browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000); + await browser.wait(async () => await target.getCssValue('background-color') === yellow, 2000); }); - describe('in Parent Finder', function () { - let cathy1 = element(by.css('alex cathy')); - let craig1 = element(by.css('alex craig')); - let carol1 = element(by.css('alex carol p')); - let carol2 = element(by.css('barry carol p')); + describe('in Parent Finder', () => { + const cathy1 = element(by.css('alex cathy')); + const craig1 = element(by.css('alex craig')); + const carol1 = element(by.css('alex carol p')); + const carol2 = element(by.css('barry carol p')); - it('"Cathy" should find "Alex" via the component class', function () { - expect(cathy1.getText()).toContain('Found Alex via the component'); + it('"Cathy" should find "Alex" via the component class', async () => { + expect(await cathy1.getText()).toContain('Found Alex via the component'); }); - it('"Craig" should not find "Alex" via the base class', function () { - expect(craig1.getText()).toContain('Did not find Alex via the base'); + it('"Craig" should not find "Alex" via the base class', async () => { + expect(await craig1.getText()).toContain('Did not find Alex via the base'); }); - it('"Carol" within "Alex" should have "Alex" parent', function () { - expect(carol1.getText()).toContain('Alex'); + it('"Carol" within "Alex" should have "Alex" parent', async () => { + expect(await carol1.getText()).toContain('Alex'); }); - it('"Carol" within "Barry" should have "Barry" parent', function () { - expect(carol2.getText()).toContain('Barry'); + it('"Carol" within "Barry" should have "Barry" parent', async () => { + expect(await carol2.getText()).toContain('Barry'); }); }); }); diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app-routing.module.ts b/aio/content/examples/dependency-injection-in-action/src/app/app-routing.module.ts index 09a0592d00..52ca1878ab 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app-routing.module.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/app-routing.module.ts @@ -1,5 +1,5 @@ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; const routes: Routes = []; diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts index a1992e0892..3c1f49682f 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/app.component.ts @@ -2,9 +2,9 @@ import { Component } from '@angular/core'; // #docregion import-services -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; import { UserContextService } from './user-context.service'; -import { UserService } from './user.service'; +import { UserService } from './user.service'; @Component({ selector: 'app-root', diff --git a/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts b/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts index 5c48d4f5f5..a2d57cb659 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/app.module.ts @@ -1,26 +1,26 @@ // #docregion -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; -import { HttpClientModule } from '@angular/common/http'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; -// import { AppRoutingModule } from './app-routing.module'; +// import { AppRoutingModule } from './app-routing.module'; import { LocationStrategy, - HashLocationStrategy } from '@angular/common'; -import { NgModule } from '@angular/core'; + HashLocationStrategy } from '@angular/common'; +import { NgModule } from '@angular/core'; -import { HeroData } from './hero-data'; -import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { HeroData } from './hero-data'; +import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; -import { AppComponent } from './app.component'; -import { HeroBioComponent } from './hero-bio.component'; +import { AppComponent } from './app.component'; +import { HeroBioComponent } from './hero-bio.component'; import { HeroBiosComponent, HeroBiosAndContactsComponent } from './hero-bios.component'; -import { HeroOfTheMonthComponent } from './hero-of-the-month.component'; -import { HeroContactComponent } from './hero-contact.component'; +import { HeroOfTheMonthComponent } from './hero-of-the-month.component'; +import { HeroContactComponent } from './hero-contact.component'; import { HeroesBaseComponent, - SortedHeroesComponent } from './sorted-heroes.component'; -import { HighlightDirective } from './highlight.directive'; + SortedHeroesComponent } from './sorted-heroes.component'; +import { HighlightDirective } from './highlight.directive'; import { ParentFinderComponent, AlexComponent, AliceComponent, @@ -30,8 +30,8 @@ import { ParentFinderComponent, CathyComponent, BarryComponent, BethComponent, - BobComponent } from './parent-finder.component'; -import { StorageComponent } from './storage.component'; + BobComponent } from './parent-finder.component'; +import { StorageComponent } from './storage.component'; const declarations = [ AppComponent, @@ -42,11 +42,11 @@ const declarations = [ ParentFinderComponent, ]; -const a_components = [AliceComponent, AlexComponent ]; +const componentListA = [ AliceComponent, AlexComponent ]; -const b_components = [ BarryComponent, BethComponent, BobComponent ]; +const componentListB = [ BarryComponent, BethComponent, BobComponent ]; -const c_components = [ +const componentListC = [ CarolComponent, ChrisComponent, CraigComponent, CathyComponent ]; @@ -61,9 +61,9 @@ const c_components = [ ], declarations: [ declarations, - a_components, - b_components, - c_components, + componentListA, + componentListB, + componentListC, StorageComponent, ], bootstrap: [ AppComponent ], diff --git a/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts index d792018c56..a2a1f02092 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/date-logger.service.ts @@ -1,6 +1,6 @@ /* tslint:disable:one-line*/ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-bio.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-bio.component.ts index 1e573e6acd..1a73a70dfc 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-bio.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-bio.component.ts @@ -1,7 +1,7 @@ // #docregion import { Component, Input, OnInit } from '@angular/core'; -import { HeroCacheService } from './hero-cache.service'; +import { HeroCacheService } from './hero-cache.service'; // #docregion component @Component({ diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-bios.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-bios.component.ts index 07f9a85d6b..ee2fdf88ec 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-bios.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-bios.component.ts @@ -1,9 +1,9 @@ // #docplaster // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; -import { HeroService } from './hero.service'; -import { LoggerService } from './logger.service'; +import { HeroService } from './hero.service'; +import { LoggerService } from './logger.service'; //////// HeroBiosComponent //// // #docregion simple diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-cache.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-cache.service.ts index 6dbc7a0c4f..9e1a24fc88 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-cache.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-cache.service.ts @@ -1,7 +1,7 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; -import { Hero } from './hero'; +import { Hero } from './hero'; import { HeroService } from './hero.service'; // #docregion service diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-contact.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-contact.component.ts index 5681711831..bdf78a3641 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-contact.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-contact.component.ts @@ -3,7 +3,7 @@ import { Component, Host, Optional } from '@angular/core'; import { HeroCacheService } from './hero-cache.service'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; // #docregion component @Component({ diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-data.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-data.ts index 10cdbcaab1..98ca87160e 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-data.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-data.ts @@ -3,7 +3,7 @@ import { Hero } from './hero'; export class HeroData { createDb() { - let heroes = [ + const heroes = [ new Hero(1, 'Windstorm'), new Hero(2, 'Bombasto'), new Hero(3, 'Magneta'), diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts index 4eb3a14060..af92bd4159 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.1.ts @@ -1,8 +1,8 @@ // Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent // Injecting with the MinimalLogger "interface-class" import { Component, NgModule } from '@angular/core'; -import { LoggerService } from './logger.service'; -import { MinimalLogger } from './minimal-logger.service'; +import { LoggerService } from './logger.service'; +import { MinimalLogger } from './minimal-logger.service'; // #docregion @Component({ diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.ts index 2b44cdfe70..9630fe3575 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero-of-the-month.component.ts @@ -10,12 +10,12 @@ export const TITLE = new InjectionToken('title'); import { Component, Inject } from '@angular/core'; import { DateLoggerService } from './date-logger.service'; -import { Hero } from './hero'; -import { HeroService } from './hero.service'; -import { LoggerService } from './logger.service'; -import { MinimalLogger } from './minimal-logger.service'; +import { Hero } from './hero'; +import { HeroService } from './hero.service'; +import { LoggerService } from './logger.service'; +import { MinimalLogger } from './minimal-logger.service'; import { RUNNERS_UP, - runnersUpFactory } from './runners-up'; + runnersUpFactory } from './runners-up'; // #enddocregion hero-of-the-month // #docregion some-hero diff --git a/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts index 369e2be532..2137613971 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/hero.service.ts @@ -1,6 +1,6 @@ // #docregion import { Injectable } from '@angular/core'; -import { Hero } from './hero'; +import { Hero } from './hero'; @Injectable({ providedIn: 'root' diff --git a/aio/content/examples/dependency-injection-in-action/src/app/minimal-logger.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/minimal-logger.service.ts index d87fa594d1..001dfda86e 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/minimal-logger.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/minimal-logger.service.ts @@ -18,5 +18,5 @@ export abstract class MinimalLogger { // #enddocregion minimal-logger-transpiled */ -// See http://stackoverflow.com/questions/43154832/unexpected-token-export-in-angular-app-with-systemjs-and-typescript/ +// See https://stackoverflow.com/questions/43154832/unexpected-token-export-in-angular-app-with-systemjs-and-typescript/ export const _ = 0; diff --git a/aio/content/examples/dependency-injection-in-action/src/app/parent-finder.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/parent-finder.component.ts index 513205e1bd..cf25406ed5 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/parent-finder.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/parent-finder.component.ts @@ -1,5 +1,4 @@ -/* tslint:disable:no-unused-variable component-selector-name one-line check-open-brace */ -/* tslint:disable:*/ +// tslint:disable: component-selector space-before-function-paren // #docplaster // #docregion import { Component, forwardRef, Optional, SkipSelf } from '@angular/core'; @@ -20,8 +19,7 @@ const DifferentParent = Parent; // The `parentType` defaults to `Parent` when omitting the second parameter. // #docregion provide-the-parent export function provideParent -// #enddocregion provide-parent, provide-the-parent -// #docregion provide-parent +// #enddocregion provide-the-parent (component: any, parentType?: any) { return { provide: parentType || Parent, useExisting: forwardRef(() => component) }; } diff --git a/aio/content/examples/dependency-injection-in-action/src/app/runners-up.ts b/aio/content/examples/dependency-injection-in-action/src/app/runners-up.ts index 0ce81ca55c..1c0afb823c 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/runners-up.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/runners-up.ts @@ -2,7 +2,7 @@ // #docregion import { InjectionToken } from '@angular/core'; -import { Hero } from './hero'; +import { Hero } from './hero'; import { HeroService } from './hero.service'; // #docregion runners-up @@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) { .join(', '); // #docregion factory-synopsis }; -}; +} // #enddocregion factory-synopsis diff --git a/aio/content/examples/dependency-injection-in-action/src/app/sorted-heroes.component.ts b/aio/content/examples/dependency-injection-in-action/src/app/sorted-heroes.component.ts index f5100dddef..9f25b2a9a8 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/sorted-heroes.component.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/sorted-heroes.component.ts @@ -2,8 +2,8 @@ // #docregion import { Component, OnInit } from '@angular/core'; -import { Hero } from './hero'; -import { HeroService } from './hero.service'; +import { Hero } from './hero'; +import { HeroService } from './hero.service'; /////// HeroesBaseComponent ///// // #docregion heroes-base, injection diff --git a/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts index f90bbe01bc..df59c4e08a 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/storage.service.ts @@ -16,7 +16,7 @@ export class BrowserStorageService { constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {} get(key: string) { - this.storage.getItem(key); + return this.storage.getItem(key); } set(key: string, value: string) { diff --git a/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts b/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts index 45f24105b3..e6419c5106 100644 --- a/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts +++ b/aio/content/examples/dependency-injection-in-action/src/app/user-context.service.ts @@ -1,9 +1,9 @@ // #docplaster // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; -import { UserService } from './user.service'; +import { UserService } from './user.service'; // #docregion injectables, injectable @Injectable({ @@ -24,7 +24,7 @@ export class UserContextService { // #enddocregion ctor, injectables loadUser(userId: number) { - let user = this.userService.getUserById(userId); + const user = this.userService.getUserById(userId); this.name = user.name; this.role = user.role; diff --git a/aio/content/examples/dependency-injection/e2e/src/app.e2e-spec.ts b/aio/content/examples/dependency-injection/e2e/src/app.e2e-spec.ts index 00bf3a9d0a..622c4d9142 100644 --- a/aio/content/examples/dependency-injection/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/dependency-injection/e2e/src/app.e2e-spec.ts @@ -1,203 +1,189 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by, ElementFinder } from 'protractor'; -describe('Dependency Injection Tests', function () { +describe('Dependency Injection Tests', () => { let expectedMsg: string; let expectedMsgRx: RegExp; - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - describe('Cars:', function() { + describe('Cars:', () => { - it('DI car displays as expected', function () { + it('DI car displays as expected', async () => { expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#di')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#di')).getText()).toEqual(expectedMsg); }); - it('No DI car displays as expected', function () { + it('No DI car displays as expected', async () => { expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#nodi')).getText()).toEqual(expectedMsg); }); - it('Injector car displays as expected', function () { + it('Injector car displays as expected', async () => { expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#injector')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#injector')).getText()).toEqual(expectedMsg); }); - it('Factory car displays as expected', function () { + it('Factory car displays as expected', async () => { expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#factory')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#factory')).getText()).toEqual(expectedMsg); }); - it('Simple car displays as expected', function () { + it('Simple car displays as expected', async () => { expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#simple')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#simple')).getText()).toEqual(expectedMsg); }); - it('Super car displays as expected', function () { + it('Super car displays as expected', async () => { expectedMsg = 'Super car with 12 cylinders and Flintstone tires.'; - expect(element(by.css('#super')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#super')).getText()).toEqual(expectedMsg); }); - it('Test car displays as expected', function () { + it('Test car displays as expected', async () => { expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.'; - expect(element(by.css('#test')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#test')).getText()).toEqual(expectedMsg); }); }); - describe('Other Injections:', function() { - it('DI car displays as expected', function () { + describe('Other Injections:', () => { + it('DI car displays as expected', async () => { expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; - expect(element(by.css('#car')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#car')).getText()).toEqual(expectedMsg); }); - it('Hero displays as expected', function () { + it('Hero displays as expected', async () => { expectedMsg = 'Dr Nice'; - expect(element(by.css('#hero')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#hero')).getText()).toEqual(expectedMsg); }); - it('Optional injection displays as expected', function () { + it('Optional injection displays as expected', async () => { expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!'; - expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#rodent')).getText()).toEqual(expectedMsg); }); }); - describe('Tests:', function() { + describe('Tests:', () => { - it('Tests display as expected', function () { + it('Tests display as expected', async () => { expectedMsgRx = /Tests passed/; - expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx); + expect(await element(by.css('#tests')).getText()).toMatch(expectedMsgRx); }); }); - describe('Provider variations:', function() { + describe('Provider variations:', () => { - it('P1 (class) displays as expected', function () { + it('P1 (class) displays as expected', async () => { expectedMsg = 'Hello from logger provided with Logger class'; - expect(element(by.css('#p1')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p1')).getText()).toEqual(expectedMsg); }); - it('P3 (provide) displays as expected', function () { + it('P3 (provide) displays as expected', async () => { expectedMsg = 'Hello from logger provided with useClass:Logger'; - expect(element(by.css('#p3')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p3')).getText()).toEqual(expectedMsg); }); - it('P4 (useClass:BetterLogger) displays as expected', function () { + it('P4 (useClass:BetterLogger) displays as expected', async () => { expectedMsg = 'Hello from logger provided with useClass:BetterLogger'; - expect(element(by.css('#p4')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p4')).getText()).toEqual(expectedMsg); }); - it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () { + it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', async () => { expectedMsg = 'Message to Bob: Hello from EvenBetterlogger'; - expect(element(by.css('#p5')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p5')).getText()).toEqual(expectedMsg); }); - it('P6a (no alias) displays as expected', function () { + it('P6a (no alias) displays as expected', async () => { expectedMsg = 'Hello OldLogger (but we want NewLogger)'; - expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p6a')).getText()).toEqual(expectedMsg); }); - it('P6b (alias) displays as expected', function () { + it('P6b (alias) displays as expected', async () => { expectedMsg = 'Hello from NewLogger (via aliased OldLogger)'; - expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p6b')).getText()).toEqual(expectedMsg); }); - it('P7 (useValue) displays as expected', function () { + it('P7 (useValue) displays as expected', async () => { expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"'; - expect(element(by.css('#p7')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p7')).getText()).toEqual(expectedMsg); }); - it('P8 (useFactory) displays as expected', function () { + it('P8 (useFactory) displays as expected', async () => { expectedMsg = 'Hero service injected successfully via heroServiceProvider'; - expect(element(by.css('#p8')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p8')).getText()).toEqual(expectedMsg); }); - it('P9 (InjectionToken) displays as expected', function () { + it('P9 (InjectionToken) displays as expected', async () => { expectedMsg = 'APP_CONFIG Application title is Dependency Injection'; - expect(element(by.css('#p9')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p9')).getText()).toEqual(expectedMsg); }); - it('P10 (optional dependency) displays as expected', function () { + it('P10 (optional dependency) displays as expected', async () => { expectedMsg = 'Optional logger was not available'; - expect(element(by.css('#p10')).getText()).toEqual(expectedMsg); + expect(await element(by.css('#p10')).getText()).toEqual(expectedMsg); }); }); - describe('User/Heroes:', function() { - it('User is Bob - unauthorized', function () { + describe('User/Heroes:', () => { + it('User is Bob - unauthorized', async () => { expectedMsgRx = /Bob, is not authorized/; - expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); + expect(await element(by.css('#user')).getText()).toMatch(expectedMsgRx); }); - it('should have button', function () { - expect(element.all(by.cssContainingText('button', 'Next User')) + it('should have button', async () => { + expect(await element.all(by.cssContainingText('button', 'Next User')) .get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed'); }); - it('unauthorized user should have multiple unauthorized heroes', function () { - let heroes = element.all(by.css('#unauthorized app-hero-list div')); - expect(heroes.count()).toBeGreaterThan(0); + it('unauthorized user should have multiple unauthorized heroes', async () => { + const heroes = element.all(by.css('#unauthorized app-hero-list div')); + expect(await heroes.count()).toBeGreaterThan(0); }); - it('unauthorized user should have no secret heroes', function () { - let heroes = element.all(by.css('#unauthorized app-hero-list div')); - expect(heroes.count()).toBeGreaterThan(0); + it('unauthorized user should have no secret heroes', async () => { + const heroes = element.all(by.css('#unauthorized app-hero-list div')); + expect(await heroes.count()).toBeGreaterThan(0); - let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => { - return elem.getText().then((text: string) => { - return /secret/.test(text); - }); + const filteredHeroes = heroes.filter(async elem => /secret/.test(await elem.getText())); + expect(await filteredHeroes.count()).toEqual(0); + }); + + it('unauthorized user should have no authorized heroes listed', async () => { + expect(await element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0); + }); + + describe('after button click', () => { + + beforeAll(async () => { + const buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0); + await buttonEle.click(); }); - expect(filteredHeroes.count()).toEqual(0); - }); - - it('unauthorized user should have no authorized heroes listed', function () { - expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0); - }); - - describe('after button click', function() { - - beforeAll(function (done: any) { - let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0); - buttonEle.click().then(done, done); - }); - - it('User is Alice - authorized', function () { + it('User is Alice - authorized', async () => { expectedMsgRx = /Alice, is authorized/; - expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); + expect(await element(by.css('#user')).getText()).toMatch(expectedMsgRx); }); - it('authorized user should have multiple authorized heroes ', function () { - let heroes = element.all(by.css('#authorized app-hero-list div')); - expect(heroes.count()).toBeGreaterThan(0); + it('authorized user should have multiple authorized heroes ', async () => { + const heroes = element.all(by.css('#authorized app-hero-list div')); + expect(await heroes.count()).toBeGreaterThan(0); }); - it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', function () { - let heroes = element.all(by.css('#tspAuthorized app-hero-list div')); - expect(heroes.count()).toBeGreaterThan(0); + it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', async () => { + const heroes = element.all(by.css('#tspAuthorized app-hero-list div')); + expect(await heroes.count()).toBeGreaterThan(0); }); - it('authorized user should have secret heroes', function () { - let heroes = element.all(by.css('#authorized app-hero-list div')); - expect(heroes.count()).toBeGreaterThan(0); + it('authorized user should have secret heroes', async () => { + const heroes = element.all(by.css('#authorized app-hero-list div')); + expect(await heroes.count()).toBeGreaterThan(0); - let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) { - return elem.getText().then(function(text: string) { - return /secret/.test(text); - }); - }); - - expect(filteredHeroes.count()).toBeGreaterThan(0); + const filteredHeroes = heroes.filter(async elem => /secret/.test(await elem.getText())); + expect(await filteredHeroes.count()).toBeGreaterThan(0); }); - it('authorized user should have no unauthorized heroes listed', function () { - expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0); + it('authorized user should have no unauthorized heroes listed', async () => { + expect(await element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0); }); }); }); diff --git a/aio/content/examples/dependency-injection/src/app/app.component.2.ts b/aio/content/examples/dependency-injection/src/app/app.component.2.ts index c8a2c996b2..1d775e9214 100644 --- a/aio/content/examples/dependency-injection/src/app/app.component.2.ts +++ b/aio/content/examples/dependency-injection/src/app/app.component.2.ts @@ -2,7 +2,7 @@ // #docregion imports import { Component, Inject } from '@angular/core'; -import { APP_CONFIG, AppConfig } from './app.config'; +import { APP_CONFIG, AppConfig } from './app.config'; // #enddocregion imports @Component({ diff --git a/aio/content/examples/dependency-injection/src/app/app.module.ts b/aio/content/examples/dependency-injection/src/app/app.module.ts index 85becdb3b9..981faba079 100644 --- a/aio/content/examples/dependency-injection/src/app/app.module.ts +++ b/aio/content/examples/dependency-injection/src/app/app.module.ts @@ -1,8 +1,8 @@ // #docplaster import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { BrowserModule } from '@angular/platform-browser'; -import { APP_CONFIG, HERO_DI_CONFIG } from './app.config'; +import { APP_CONFIG, HERO_DI_CONFIG } from './app.config'; import { AppComponent } from './app.component'; import { CarComponent } from './car/car.component'; import { HeroesComponent } from './heroes/heroes.component'; diff --git a/aio/content/examples/dependency-injection/src/app/car/car-creations.ts b/aio/content/examples/dependency-injection/src/app/car/car-creations.ts index c758c72951..23011ee626 100644 --- a/aio/content/examples/dependency-injection/src/app/car/car-creations.ts +++ b/aio/content/examples/dependency-injection/src/app/car/car-creations.ts @@ -7,7 +7,7 @@ import { Car, Engine, Tires } from './car'; export function simpleCar() { // #docregion car-ctor-instantiation // Simple car with 4 cylinders and Flintstone tires. - let car = new Car(new Engine(), new Tires()); + const car = new Car(new Engine(), new Tires()); // #enddocregion car-ctor-instantiation car.description = 'Simple'; return car; @@ -16,30 +16,31 @@ export function simpleCar() { ///////// example 2 //////////// // #docregion car-ctor-instantiation-with-param - class Engine2 { - constructor(public cylinders: number) { } - } +class Engine2 { + constructor(public cylinders: number) { } +} // #enddocregion car-ctor-instantiation-with-param + export function superCar() { -// #docregion car-ctor-instantiation-with-param + // #docregion car-ctor-instantiation-with-param // Super car with 12 cylinders and Flintstone tires. - let bigCylinders = 12; - let car = new Car(new Engine2(bigCylinders), new Tires()); -// #enddocregion car-ctor-instantiation-with-param + const bigCylinders = 12; + const car = new Car(new Engine2(bigCylinders), new Tires()); + // #enddocregion car-ctor-instantiation-with-param car.description = 'Super'; return car; } /////////// example 3 ////////// - // #docregion car-ctor-instantiation-with-mocks - class MockEngine extends Engine { cylinders = 8; } - class MockTires extends Tires { make = 'YokoGoodStone'; } +// #docregion car-ctor-instantiation-with-mocks +class MockEngine extends Engine { cylinders = 8; } +class MockTires extends Tires { make = 'YokoGoodStone'; } - // #enddocregion car-ctor-instantiation-with-mocks +// #enddocregion car-ctor-instantiation-with-mocks export function testCar() { // #docregion car-ctor-instantiation-with-mocks // Test car with 8 cylinders and YokoGoodStone tires. - let car = new Car(new MockEngine(), new MockTires()); + const car = new Car(new MockEngine(), new MockTires()); // #enddocregion car-ctor-instantiation-with-mocks car.description = 'Test'; return car; diff --git a/aio/content/examples/dependency-injection/src/app/car/car-factory.ts b/aio/content/examples/dependency-injection/src/app/car/car-factory.ts index 06daafe63b..ed759137e6 100644 --- a/aio/content/examples/dependency-injection/src/app/car/car-factory.ts +++ b/aio/content/examples/dependency-injection/src/app/car/car-factory.ts @@ -4,7 +4,7 @@ import { Engine, Tires, Car } from './car'; // BAD pattern! export class CarFactory { createCar() { - let car = new Car(this.createEngine(), this.createTires()); + const car = new Car(this.createEngine(), this.createTires()); car.description = 'Factory'; return car; } diff --git a/aio/content/examples/dependency-injection/src/app/car/car-injector.ts b/aio/content/examples/dependency-injection/src/app/car/car-injector.ts index 931efefc0a..9611bb0073 100644 --- a/aio/content/examples/dependency-injection/src/app/car/car-injector.ts +++ b/aio/content/examples/dependency-injection/src/app/car/car-injector.ts @@ -1,7 +1,7 @@ import { Injector } from '@angular/core'; import { Car, Engine, Tires } from './car'; -import { Logger } from '../logger.service'; +import { Logger } from '../logger.service'; // #docregion injector export function useInjector() { @@ -26,14 +26,14 @@ export function useInjector() { ] }); // #docregion injector-call - let car = injector.get(Car); + const car = injector.get(Car); // #enddocregion injector-call, injector-create-and-call car.description = 'Injector'; injector = Injector.create({ providers: [{ provide: Logger, deps: [] }] }); - let logger = injector.get(Logger); + const logger = injector.get(Logger); logger.log('Injector car.drive() said: ' + car.drive()); return car; } diff --git a/aio/content/examples/dependency-injection/src/app/car/car.component.ts b/aio/content/examples/dependency-injection/src/app/car/car.component.ts index 481ccd6cee..3463c52250 100644 --- a/aio/content/examples/dependency-injection/src/app/car/car.component.ts +++ b/aio/content/examples/dependency-injection/src/app/car/car.component.ts @@ -1,15 +1,15 @@ // #docregion import { Component } from '@angular/core'; -import { Car, Engine, Tires } from './car'; -import { Car as CarNoDi } from './car-no-di'; -import { CarFactory } from './car-factory'; +import { Car, Engine, Tires } from './car'; +import { Car as CarNoDi } from './car-no-di'; +import { CarFactory } from './car-factory'; import { testCar, simpleCar, - superCar } from './car-creations'; + superCar } from './car-creations'; -import { useInjector } from './car-injector'; +import { useInjector } from './car-injector'; @Component({ @@ -27,9 +27,9 @@ import { useInjector } from './car-injector'; providers: [Car, Engine, Tires] }) export class CarComponent { - factoryCar = (new CarFactory).createCar(); + factoryCar = (new CarFactory()).createCar(); injectorCar = useInjector(); - noDiCar = new CarNoDi; + noDiCar = new CarNoDi(); simpleCar = simpleCar(); superCar = superCar(); testCar = testCar(); diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.1.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.1.ts index 5c216f4d03..995d2e4441 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.1.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.1.ts @@ -1,6 +1,6 @@ // #docregion -import { Component } from '@angular/core'; -import { HEROES } from './mock-heroes'; +import { Component } from '@angular/core'; +import { HEROES } from './mock-heroes'; @Component({ selector: 'app-hero-list', diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.2.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.2.ts index 11f27ed4b1..b126a0361e 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.2.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.2.ts @@ -1,7 +1,7 @@ // #docplaster // #docregion -import { Component } from '@angular/core'; -import { Hero } from './hero'; +import { Component } from '@angular/core'; +import { Hero } from './hero'; // #enddocregion import { HeroService } from './hero.service.1'; /* diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.ts index 4536a04489..4b0df4daa2 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero-list.component.ts @@ -1,7 +1,7 @@ /* tslint:disable:one-line */ // #docregion -import { Component } from '@angular/core'; -import { Hero } from './hero'; +import { Component } from '@angular/core'; +import { Hero } from './hero'; import { HeroService } from './hero.service'; @Component({ diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.1.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.1.ts index 908d75ee65..3ee96adca1 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.1.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.1.ts @@ -1,6 +1,6 @@ // #docregion import { Injectable } from '@angular/core'; -import { HEROES } from './mock-heroes'; +import { HEROES } from './mock-heroes'; @Injectable({ providedIn: 'root', diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.2.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.2.ts index a4c1adcd1b..a9bf656e92 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.2.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.2.ts @@ -1,7 +1,7 @@ // #docregion import { Injectable } from '@angular/core'; -import { HEROES } from './mock-heroes'; -import { Logger } from '../logger.service'; +import { HEROES } from './mock-heroes'; +import { Logger } from '../logger.service'; @Injectable({ providedIn: 'root', diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.provider.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.provider.ts index 6de4ebee90..4f86ce4883 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.provider.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.provider.ts @@ -1,11 +1,11 @@ /* tslint:disable:one-line */ // #docregion import { HeroService } from './hero.service'; -import { Logger } from '../logger.service'; +import { Logger } from '../logger.service'; import { UserService } from '../user.service'; // #docregion factory -let heroServiceFactory = (logger: Logger, userService: UserService) => { +const heroServiceFactory = (logger: Logger, userService: UserService) => { return new HeroService(logger, userService.user.isAuthorized); }; // #enddocregion factory diff --git a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.ts b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.ts index 6c6fc27037..7d02213153 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/hero.service.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/hero.service.ts @@ -1,7 +1,7 @@ // #docregion import { Injectable } from '@angular/core'; -import { HEROES } from './mock-heroes'; -import { Logger } from '../logger.service'; +import { HEROES } from './mock-heroes'; +import { Logger } from '../logger.service'; import { UserService } from '../user.service'; @Injectable({ @@ -17,7 +17,7 @@ export class HeroService { private isAuthorized: boolean) { } getHeroes() { - let auth = this.isAuthorized ? 'authorized ' : 'unauthorized'; + const auth = this.isAuthorized ? 'authorized ' : 'unauthorized'; this.logger.log(`Getting heroes for ${auth} user.`); return HEROES.filter(hero => this.isAuthorized || !hero.isSecret); } diff --git a/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.ts b/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.ts index 97040c09f0..5a3a7da216 100644 --- a/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.ts +++ b/aio/content/examples/dependency-injection/src/app/heroes/heroes.component.ts @@ -1,5 +1,5 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { heroServiceProvider } from './hero.service.provider'; @Component({ diff --git a/aio/content/examples/dependency-injection/src/app/injector.component.ts b/aio/content/examples/dependency-injection/src/app/injector.component.ts index 403a6c427b..f04bf022d5 100644 --- a/aio/content/examples/dependency-injection/src/app/injector.component.ts +++ b/aio/content/examples/dependency-injection/src/app/injector.component.ts @@ -2,11 +2,11 @@ // #docregion import { Component, Injector, OnInit } from '@angular/core'; -import { Car, Engine, Tires } from './car/car'; -import { Hero } from './heroes/hero'; -import { HeroService } from './heroes/hero.service'; -import { heroServiceProvider } from './heroes/hero.service.provider'; -import { Logger } from './logger.service'; +import { Car, Engine, Tires } from './car/car'; +import { Hero } from './heroes/hero'; +import { HeroService } from './heroes/hero.service'; +import { heroServiceProvider } from './heroes/hero.service.provider'; +import { Logger } from './logger.service'; // #docregion injector @Component({ @@ -36,7 +36,7 @@ export class InjectorComponent implements OnInit { } get rodent() { - let rousDontExist = `R.O.U.S.'s? I don't think they exist!`; + const rousDontExist = `R.O.U.S.'s? I don't think they exist!`; return this.injector.get(ROUS, rousDontExist); } } diff --git a/aio/content/examples/dependency-injection/src/app/providers.component.ts b/aio/content/examples/dependency-injection/src/app/providers.component.ts index 1467ec07b6..5b1e770297 100644 --- a/aio/content/examples/dependency-injection/src/app/providers.component.ts +++ b/aio/content/examples/dependency-injection/src/app/providers.component.ts @@ -18,7 +18,7 @@ const template = '{{log}}'; @Component({ selector: 'provider-1', - template: template, + template, // #docregion providers-1, providers-logger providers: [Logger] // #enddocregion providers-1, providers-logger @@ -35,7 +35,7 @@ export class Provider1Component { @Component({ selector: 'provider-3', - template: template, + template, providers: // #docregion providers-3 [{ provide: Logger, useClass: Logger }] @@ -54,7 +54,7 @@ export class BetterLogger extends Logger {} @Component({ selector: 'provider-4', - template: template, + template, providers: // #docregion providers-4 [{ provide: Logger, useClass: BetterLogger }] @@ -76,7 +76,7 @@ export class EvenBetterLogger extends Logger { constructor(private userService: UserService) { super(); } log(message: string) { - let name = this.userService.user.name; + const name = this.userService.user.name; super.log(`Message to ${name}: ${message}`); } } @@ -84,7 +84,7 @@ export class EvenBetterLogger extends Logger { @Component({ selector: 'provider-5', - template: template, + template, providers: // #docregion providers-5 [ UserService, @@ -107,12 +107,12 @@ export class OldLogger { logs: string[] = []; log(message: string) { throw new Error('Should not call the old logger!'); - }; + } } @Component({ selector: 'provider-6a', - template: template, + template, providers: // #docregion providers-6a [ NewLogger, @@ -135,7 +135,7 @@ export class Provider6aComponent { @Component({ selector: 'provider-6b', - template: template, + template, providers: // #docregion providers-6b [ NewLogger, @@ -168,7 +168,7 @@ export const SilentLogger = { @Component({ selector: 'provider-7', - template: template, + template, providers: // #docregion providers-7 [{ provide: Logger, useValue: SilentLogger }] @@ -186,7 +186,7 @@ export class Provider7Component { @Component({ selector: 'provider-8', - template: template, + template, providers: [heroServiceProvider, Logger, UserService] }) export class Provider8Component { @@ -202,10 +202,10 @@ export class Provider8Component { @Component({ selector: 'provider-9', - template: template, + template, /* // #docregion providers-9-interface - // FAIL! Can't use interface as provider token + // Can't use interface as provider token [{ provide: AppConfig, useValue: HERO_DI_CONFIG })] // #enddocregion providers-9-interface */ @@ -217,7 +217,7 @@ export class Provider9Component implements OnInit { log: string; /* // #docregion provider-9-ctor-interface - // FAIL! Can't inject using the interface as the parameter type + // Can't inject using the interface as the parameter type constructor(private config: AppConfig){ } // #enddocregion provider-9-ctor-interface */ @@ -237,11 +237,11 @@ export class Provider9Component implements OnInit { import { Optional } from '@angular/core'; // #enddocregion import-optional -let some_message = 'Hello from the injected logger'; +const someMessage = 'Hello from the injected logger'; @Component({ selector: 'provider-10', - template: template, + template, providers: [{ provide: Logger, useValue: null }] }) export class Provider10Component implements OnInit { @@ -249,7 +249,7 @@ export class Provider10Component implements OnInit { // #docregion provider-10-ctor constructor(@Optional() private logger?: Logger) { if (this.logger) { - this.logger.log(some_message); + this.logger.log(someMessage); } } // #enddocregion provider-10-ctor diff --git a/aio/content/examples/dependency-injection/src/app/test.component.ts b/aio/content/examples/dependency-injection/src/app/test.component.ts index fcc7b5cc1d..9a82764f78 100644 --- a/aio/content/examples/dependency-injection/src/app/test.component.ts +++ b/aio/content/examples/dependency-injection/src/app/test.component.ts @@ -43,7 +43,7 @@ var testResults: {pass: string; message: string}; function expect(actual: any) { return { - toEqual: function(expected: any){ + toEqual: (expected: any) => { testResults = actual === expected ? {pass: 'passed', message: testName} : {pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`}; diff --git a/aio/content/examples/dependency-injection/src/app/tree-shaking/app.module.ts b/aio/content/examples/dependency-injection/src/app/tree-shaking/app.module.ts index 31d79ffc23..a42ee57bfc 100644 --- a/aio/content/examples/dependency-injection/src/app/tree-shaking/app.module.ts +++ b/aio/content/examples/dependency-injection/src/app/tree-shaking/app.module.ts @@ -1,6 +1,6 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { RouterModule } from '@angular/router'; +import { RouterModule } from '@angular/router'; import { ServiceModule } from './service-and-module'; // #docregion diff --git a/aio/content/examples/dependency-injection/src/app/user.service.ts b/aio/content/examples/dependency-injection/src/app/user.service.ts index b665f7c5fa..35de8c92da 100644 --- a/aio/content/examples/dependency-injection/src/app/user.service.ts +++ b/aio/content/examples/dependency-injection/src/app/user.service.ts @@ -8,8 +8,8 @@ export class User { } // TODO: get the user; don't 'new' it. -let alice = new User('Alice', true); -let bob = new User('Bob', false); +const alice = new User('Alice', true); +const bob = new User('Bob', false); @Injectable({ providedIn: 'root' diff --git a/aio/content/examples/displaying-data/e2e/src/app.e2e-spec.ts b/aio/content/examples/displaying-data/e2e/src/app.e2e-spec.ts index 96c52c5d00..6ede202dae 100644 --- a/aio/content/examples/displaying-data/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/displaying-data/e2e/src/app.e2e-spec.ts @@ -1,29 +1,25 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Displaying Data Tests', function () { - let _title = 'Tour of Heroes'; - let _defaultHero = 'Windstorm'; +describe('Displaying Data Tests', () => { + const title = 'Tour of Heroes'; + const defaultHero = 'Windstorm'; - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it(`should display correct title: ${title}`, async () => { + expect(await element(by.css('h1')).getText()).toEqual(title); }); - it('should display correct title: ' + _title, function () { - expect(element(by.css('h1')).getText()).toEqual(_title); + it(`should have correct default hero: ${defaultHero}`, async () => { + expect(await element(by.css('h2')).getText()).toContain(defaultHero); }); - it('should have correct default hero: ' + _defaultHero, function () { - expect(element(by.css('h2')).getText()).toContain(_defaultHero); + it('should have heroes', async () => { + const heroEls = element.all(by.css('li')); + expect(await heroEls.count()).not.toBe(0, 'should have heroes'); }); - it('should have heroes', function () { - let heroEls = element.all(by.css('li')); - expect(heroEls.count()).not.toBe(0, 'should have heroes'); - }); - - it('should display "there are many heroes!"', function () { - expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!'); + it('should display "there are many heroes!"', async () => { + expect(await element(by.css('ul ~ p')).getText()).toContain('There are many heroes!'); }); }); diff --git a/aio/content/examples/displaying-data/src/app/app.module.ts b/aio/content/examples/displaying-data/src/app/app.module.ts index 362f3401fa..691b8c51fc 100644 --- a/aio/content/examples/displaying-data/src/app/app.module.ts +++ b/aio/content/examples/displaying-data/src/app/app.module.ts @@ -1,6 +1,6 @@ // #docregion import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; diff --git a/aio/content/examples/docs-style-guide/e2e/src/app.e2e-spec.ts b/aio/content/examples/docs-style-guide/e2e/src/app.e2e-spec.ts index 68cf02313b..05f5e145a0 100644 --- a/aio/content/examples/docs-style-guide/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/docs-style-guide/e2e/src/app.e2e-spec.ts @@ -1,15 +1,11 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Docs Style Guide', function () { - let _title = 'Authors Style Guide Sample'; +describe('Docs Style Guide', () => { + const title = 'Authors Style Guide Sample'; - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('should display correct title: ' + _title, function () { - expect(element(by.css('h1')).getText()).toEqual(_title); + it(`should display correct title: ${title}`, async () => { + expect(await element(by.css('h1')).getText()).toEqual(title); }); }); diff --git a/aio/content/examples/docs-style-guide/src/app/app.module.ts b/aio/content/examples/docs-style-guide/src/app/app.module.ts index cbcb090547..1297f02c47 100644 --- a/aio/content/examples/docs-style-guide/src/app/app.module.ts +++ b/aio/content/examples/docs-style-guide/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'; // #docregion class @NgModule({ diff --git a/aio/content/examples/dynamic-component-loader/e2e/src/app.e2e-spec.ts b/aio/content/examples/dynamic-component-loader/e2e/src/app.e2e-spec.ts index b04412c28b..f0f7c7297b 100644 --- a/aio/content/examples/dynamic-component-loader/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/dynamic-component-loader/e2e/src/app.e2e-spec.ts @@ -1,21 +1,24 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -/* tslint:disable:quotemark */ -describe('Dynamic Component Loader', function () { +describe('Dynamic Component Loader', () => { - beforeEach(function () { - browser.get(''); - }); + // The tests trigger periodic asynchronous operations (via `setInterval()`), which will prevent + // the app from stabilizing. See https://angular.io/api/core/ApplicationRef#is-stable-examples + // for more details. + // To allow the tests to complete, we will disable automatically waiting for the Angular app to + // stabilize. + beforeAll(() => browser.waitForAngularEnabled(false)); + afterAll(() => browser.waitForAngularEnabled(true)); - it('should load ad banner', function () { - let headline = element(by.xpath("//h4[text()='Featured Hero Profile']")); - let name = element(by.xpath("//h3[text()='Bombasto']")); - let bio = element(by.xpath("//p[text()='Brave as they come']")); + beforeEach(() => browser.get('')); - expect(name).toBeDefined(); - expect(headline).toBeDefined(); - expect(bio).toBeDefined(); + it('should load ad banner', async () => { + const headline = element(by.cssContainingText('h3', 'Featured Hero Profile')); + const name = element(by.cssContainingText('h4', 'Bombasto')); + const bio = element(by.cssContainingText('p', 'Brave as they come')); + + expect(await headline.isPresent()).toBe(true); + expect(await name.isPresent()).toBe(true); + expect(await bio.isPresent()).toBe(true); }); }); diff --git a/aio/content/examples/dynamic-component-loader/src/app/ad-banner.component.ts b/aio/content/examples/dynamic-component-loader/src/app/ad-banner.component.ts index faf03803d7..989bc87b46 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/ad-banner.component.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/ad-banner.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core'; import { AdDirective } from './ad.directive'; -import { AdItem } from './ad-item'; +import { AdItem } from './ad-item'; import { AdComponent } from './ad.component'; @Component({ @@ -11,7 +11,7 @@ import { AdComponent } from './ad.component'; template: `

Advertisements

- +
` // #enddocregion ad-host @@ -43,8 +43,8 @@ export class AdBannerComponent implements OnInit, OnDestroy { const viewContainerRef = this.adHost.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent(componentFactory); - (componentRef.instance).data = adItem.data; + const componentRef = viewContainerRef.createComponent(componentFactory); + componentRef.instance.data = adItem.data; } getAds() { diff --git a/aio/content/examples/dynamic-component-loader/src/app/ad.directive.ts b/aio/content/examples/dynamic-component-loader/src/app/ad.directive.ts index 312e605228..0b47467c88 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/ad.directive.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/ad.directive.ts @@ -1,8 +1,9 @@ +// tslint:disable: directive-selector // #docregion import { Directive, ViewContainerRef } from '@angular/core'; @Directive({ - selector: '[ad-host]', + selector: '[adHost]', }) export class AdDirective { constructor(public viewContainerRef: ViewContainerRef) { } diff --git a/aio/content/examples/dynamic-component-loader/src/app/ad.service.ts b/aio/content/examples/dynamic-component-loader/src/app/ad.service.ts index 91b0758771..8a63dab0bb 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/ad.service.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/ad.service.ts @@ -1,9 +1,9 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; -import { HeroJobAdComponent } from './hero-job-ad.component'; +import { HeroJobAdComponent } from './hero-job-ad.component'; import { HeroProfileComponent } from './hero-profile.component'; -import { AdItem } from './ad-item'; +import { AdItem } from './ad-item'; @Injectable() export class AdService { diff --git a/aio/content/examples/dynamic-component-loader/src/app/app.component.ts b/aio/content/examples/dynamic-component-loader/src/app/app.component.ts index 1a5fa94dbc..4f208000f9 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/app.component.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/app.component.ts @@ -1,8 +1,8 @@ // #docregion import { Component, OnInit } from '@angular/core'; -import { AdService } from './ad.service'; -import { AdItem } from './ad-item'; +import { AdService } from './ad.service'; +import { AdItem } from './ad-item'; @Component({ selector: 'app-root', diff --git a/aio/content/examples/dynamic-component-loader/src/app/app.module.ts b/aio/content/examples/dynamic-component-loader/src/app/app.module.ts index a65d394709..e642c61008 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/app.module.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/app.module.ts @@ -1,12 +1,12 @@ // #docregion -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { AppComponent } from './app.component'; -import { HeroJobAdComponent } from './hero-job-ad.component'; -import { AdBannerComponent } from './ad-banner.component'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { AppComponent } from './app.component'; +import { HeroJobAdComponent } from './hero-job-ad.component'; +import { AdBannerComponent } from './ad-banner.component'; import { HeroProfileComponent } from './hero-profile.component'; -import { AdDirective } from './ad.directive'; -import { AdService } from './ad.service'; +import { AdDirective } from './ad.directive'; +import { AdService } from './ad.service'; @NgModule({ imports: [ BrowserModule ], diff --git a/aio/content/examples/dynamic-component-loader/src/app/hero-job-ad.component.ts b/aio/content/examples/dynamic-component-loader/src/app/hero-job-ad.component.ts index 762b815872..ca1270a276 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/hero-job-ad.component.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/hero-job-ad.component.ts @@ -1,7 +1,7 @@ // #docregion import { Component, Input } from '@angular/core'; -import { AdComponent } from './ad.component'; +import { AdComponent } from './ad.component'; @Component({ template: ` diff --git a/aio/content/examples/dynamic-component-loader/src/app/hero-profile.component.ts b/aio/content/examples/dynamic-component-loader/src/app/hero-profile.component.ts index d543683d8c..1c7cd6d0d4 100644 --- a/aio/content/examples/dynamic-component-loader/src/app/hero-profile.component.ts +++ b/aio/content/examples/dynamic-component-loader/src/app/hero-profile.component.ts @@ -1,7 +1,7 @@ // #docregion -import { Component, Input } from '@angular/core'; +import { Component, Input } from '@angular/core'; -import { AdComponent } from './ad.component'; +import { AdComponent } from './ad.component'; @Component({ template: ` diff --git a/aio/content/examples/dynamic-form/e2e/src/app.e2e-spec.ts b/aio/content/examples/dynamic-form/e2e/src/app.e2e-spec.ts index 49eaba3f9c..4dc15e1b77 100644 --- a/aio/content/examples/dynamic-form/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/dynamic-form/e2e/src/app.e2e-spec.ts @@ -1,29 +1,22 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; /* tslint:disable:quotemark */ -describe('Dynamic Form', function () { +describe('Dynamic Form', () => { - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('should submit form', function () { - let firstNameElement = element.all(by.css('input[id=firstName]')).get(0); - expect(firstNameElement.getAttribute('value')).toEqual('Bombasto'); + it('should submit form', async () => { + const firstNameElement = element.all(by.css('input[id=firstName]')).get(0); + expect(await firstNameElement.getAttribute('value')).toEqual('Bombasto'); - let emailElement = element.all(by.css('input[id=emailAddress]')).get(0); - let email = 'test@test.com'; - emailElement.sendKeys(email); - expect(emailElement.getAttribute('value')).toEqual(email); + const emailElement = element.all(by.css('input[id=emailAddress]')).get(0); + const email = 'test@test.com'; + await emailElement.sendKeys(email); + expect(await emailElement.getAttribute('value')).toEqual(email); - element(by.css('select option[value="solid"]')).click(); - - let saveButton = element.all(by.css('button')).get(0); - saveButton.click().then(function() { - expect(element(by.xpath("//strong[contains(text(),'Saved the following values')]")).isPresent()).toBe(true); - }); + await element(by.css('select option[value="solid"]')).click(); + await element.all(by.css('button')).get(0).click(); + expect(await element(by.cssContainingText('strong', 'Saved the following values')).isPresent()).toBe(true); }); }); diff --git a/aio/content/examples/dynamic-form/src/app/app.component.ts b/aio/content/examples/dynamic-form/src/app/app.component.ts index d8e9dd7e29..64885c44e2 100644 --- a/aio/content/examples/dynamic-form/src/app/app.component.ts +++ b/aio/content/examples/dynamic-form/src/app/app.component.ts @@ -1,9 +1,9 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { QuestionService } from './question.service'; -import { QuestionBase } from './question-base'; -import { Observable } from 'rxjs'; +import { QuestionBase } from './question-base'; +import { Observable } from 'rxjs'; @Component({ selector: 'app-root', diff --git a/aio/content/examples/dynamic-form/src/app/app.module.ts b/aio/content/examples/dynamic-form/src/app/app.module.ts index 7a68e45a92..a943aeb4fc 100644 --- a/aio/content/examples/dynamic-form/src/app/app.module.ts +++ b/aio/content/examples/dynamic-form/src/app/app.module.ts @@ -1,10 +1,10 @@ // #docregion -import { BrowserModule } from '@angular/platform-browser'; -import { ReactiveFormsModule } from '@angular/forms'; -import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; -import { AppComponent } from './app.component'; -import { DynamicFormComponent } from './dynamic-form.component'; +import { AppComponent } from './app.component'; +import { DynamicFormComponent } from './dynamic-form.component'; import { DynamicFormQuestionComponent } from './dynamic-form-question.component'; @NgModule({ diff --git a/aio/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts b/aio/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts index 988e4fb39c..1f4fa86451 100644 --- a/aio/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts +++ b/aio/content/examples/dynamic-form/src/app/dynamic-form-question.component.ts @@ -1,8 +1,8 @@ // #docregion import { Component, Input } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { FormGroup } from '@angular/forms'; -import { QuestionBase } from './question-base'; +import { QuestionBase } from './question-base'; @Component({ selector: 'app-question', diff --git a/aio/content/examples/dynamic-form/src/app/dynamic-form.component.ts b/aio/content/examples/dynamic-form/src/app/dynamic-form.component.ts index cca09063e3..aff48ed5ff 100644 --- a/aio/content/examples/dynamic-form/src/app/dynamic-form.component.ts +++ b/aio/content/examples/dynamic-form/src/app/dynamic-form.component.ts @@ -1,9 +1,9 @@ // #docregion -import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; -import { QuestionBase } from './question-base'; -import { QuestionControlService } from './question-control.service'; +import { QuestionBase } from './question-base'; +import { QuestionControlService } from './question-control.service'; @Component({ selector: 'app-dynamic-form', diff --git a/aio/content/examples/dynamic-form/src/app/question-base.ts b/aio/content/examples/dynamic-form/src/app/question-base.ts index 8edc0dcfdc..b82558438f 100644 --- a/aio/content/examples/dynamic-form/src/app/question-base.ts +++ b/aio/content/examples/dynamic-form/src/app/question-base.ts @@ -10,13 +10,14 @@ export class QuestionBase { options: {key: string, value: string}[]; constructor(options: { - value?: T, - key?: string, - label?: string, - required?: boolean, - order?: number, - controlType?: string, - type?: string + value?: T; + key?: string; + label?: string; + required?: boolean; + order?: number; + controlType?: string; + type?: string; + options?: {key: string, value: string}[]; } = {}) { this.value = options.value; this.key = options.key || ''; @@ -25,5 +26,6 @@ export class QuestionBase { this.order = options.order === undefined ? 1 : options.order; this.controlType = options.controlType || ''; this.type = options.type || ''; + this.options = options.options || []; } } diff --git a/aio/content/examples/dynamic-form/src/app/question-control.service.ts b/aio/content/examples/dynamic-form/src/app/question-control.service.ts index 1ca0727055..8c91b2018b 100644 --- a/aio/content/examples/dynamic-form/src/app/question-control.service.ts +++ b/aio/content/examples/dynamic-form/src/app/question-control.service.ts @@ -1,5 +1,5 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { QuestionBase } from './question-base'; @@ -9,7 +9,7 @@ export class QuestionControlService { constructor() { } toFormGroup(questions: QuestionBase[] ) { - let group: any = {}; + const group: any = {}; questions.forEach(question => { group[question.key] = question.required ? new FormControl(question.value || '', Validators.required) diff --git a/aio/content/examples/dynamic-form/src/app/question-dropdown.ts b/aio/content/examples/dynamic-form/src/app/question-dropdown.ts index 35a9074c74..559badca7d 100644 --- a/aio/content/examples/dynamic-form/src/app/question-dropdown.ts +++ b/aio/content/examples/dynamic-form/src/app/question-dropdown.ts @@ -3,10 +3,4 @@ import { QuestionBase } from './question-base'; export class DropdownQuestion extends QuestionBase { controlType = 'dropdown'; - options: {key: string, value: string}[] = []; - - constructor(options: {} = {}) { - super(options); - this.options = options['options'] || []; - } } diff --git a/aio/content/examples/dynamic-form/src/app/question-textbox.ts b/aio/content/examples/dynamic-form/src/app/question-textbox.ts index aaa7edf267..f6aebe8d14 100644 --- a/aio/content/examples/dynamic-form/src/app/question-textbox.ts +++ b/aio/content/examples/dynamic-form/src/app/question-textbox.ts @@ -3,10 +3,4 @@ import { QuestionBase } from './question-base'; export class TextboxQuestion extends QuestionBase { controlType = 'textbox'; - type: string; - - constructor(options: {} = {}) { - super(options); - this.type = options['type'] || ''; - } } diff --git a/aio/content/examples/dynamic-form/src/app/question.service.ts b/aio/content/examples/dynamic-form/src/app/question.service.ts index 7f80dd78c1..e5dcf0edbe 100644 --- a/aio/content/examples/dynamic-form/src/app/question.service.ts +++ b/aio/content/examples/dynamic-form/src/app/question.service.ts @@ -1,9 +1,9 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { DropdownQuestion } from './question-dropdown'; -import { QuestionBase } from './question-base'; -import { TextboxQuestion } from './question-textbox'; +import { QuestionBase } from './question-base'; +import { TextboxQuestion } from './question-textbox'; import { of } from 'rxjs'; @Injectable() @@ -12,7 +12,7 @@ export class QuestionService { // TODO: get from a remote source of question metadata getQuestions() { - let questions: QuestionBase[] = [ + const questions: QuestionBase[] = [ new DropdownQuestion({ key: 'brave', diff --git a/aio/content/examples/elements/e2e/src/app.e2e-spec.ts b/aio/content/examples/elements/e2e/src/app.e2e-spec.ts index d939501a86..99abc4309a 100644 --- a/aio/content/examples/elements/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/elements/e2e/src/app.e2e-spec.ts @@ -1,5 +1,3 @@ -'use strict'; // necessary for es6 output in node - import { browser, by, element, ElementFinder, ExpectedConditions as EC } from 'protractor'; /* tslint:disable:quotemark */ @@ -8,15 +6,15 @@ describe('Elements', () => { const popupButtons = element.all(by.css('button')); // Helpers - const click = (elem: ElementFinder) => { + const click = async (elem: ElementFinder) => { // Waiting for the element to be clickable, makes the tests less flaky. - browser.wait(EC.elementToBeClickable(elem), 5000); - elem.click(); + await browser.wait(EC.elementToBeClickable(elem), 5000); + await elem.click(); }; - const waitForText = (elem: ElementFinder) => { + const waitForText = async (elem: ElementFinder) => { // Waiting for the element to have some text, makes the tests less flaky. - browser.wait(async () => /\S/.test(await elem.getText()), 5000); - } + await browser.wait(async () => /\S/.test(await elem.getText()), 5000); + }; beforeEach(() => browser.get('')); @@ -25,29 +23,29 @@ describe('Elements', () => { const popupComponent = element(by.css('popup-component')); const closeButton = popupComponent.element(by.css('button')); - it('should be displayed on button click', () => { - expect(popupComponent.isPresent()).toBe(false); + it('should be displayed on button click', async () => { + expect(await popupComponent.isPresent()).toBe(false); - click(popupComponentButton); - expect(popupComponent.isPresent()).toBe(true); + await click(popupComponentButton); + expect(await popupComponent.isPresent()).toBe(true); }); - it('should display the specified message', () => { - messageInput.clear(); - messageInput.sendKeys('Angular rocks!'); + it('should display the specified message', async () => { + await messageInput.clear(); + await messageInput.sendKeys('Angular rocks!'); - click(popupComponentButton); - waitForText(popupComponent); + await click(popupComponentButton); + await waitForText(popupComponent); - expect(popupComponent.getText()).toContain('Popup: Angular rocks!'); + expect(await popupComponent.getText()).toContain('Popup: Angular rocks!'); }); - it('should be closed on "close" button click', () => { - popupComponentButton.click(); - expect(popupComponent.isPresent()).toBe(true); + it('should be closed on "close" button click', async () => { + await click(popupComponentButton); + expect(await popupComponent.isPresent()).toBe(true); - click(closeButton); - expect(popupComponent.isPresent()).toBe(false); + await click(closeButton); + expect(await popupComponent.isPresent()).toBe(false); }); }); @@ -56,29 +54,29 @@ describe('Elements', () => { const popupElement = element(by.css('popup-element')); const closeButton = popupElement.element(by.css('button')); - it('should be displayed on button click', () => { - expect(popupElement.isPresent()).toBe(false); + it('should be displayed on button click', async () => { + expect(await popupElement.isPresent()).toBe(false); - click(popupElementButton); - expect(popupElement.isPresent()).toBe(true); + await click(popupElementButton); + expect(await popupElement.isPresent()).toBe(true); }); - it('should display the specified message', () => { - messageInput.clear(); - messageInput.sendKeys('Angular rocks!'); + it('should display the specified message', async () => { + await messageInput.clear(); + await messageInput.sendKeys('Angular rocks!'); - click(popupElementButton); - waitForText(popupElement); + await click(popupElementButton); + await waitForText(popupElement); - expect(popupElement.getText()).toContain('Popup: Angular rocks!'); + expect(await popupElement.getText()).toContain('Popup: Angular rocks!'); }); - it('should be closed on "close" button click', () => { - popupElementButton.click(); - expect(popupElement.isPresent()).toBe(true); + it('should be closed on "close" button click', async () => { + await click(popupElementButton); + expect(await popupElement.isPresent()).toBe(true); - click(closeButton); - expect(popupElement.isPresent()).toBe(false); + await click(closeButton); + expect(await popupElement.isPresent()).toBe(false); }); }); }); diff --git a/aio/content/examples/elements/src/app/popup.component.ts b/aio/content/examples/elements/src/app/popup.component.ts index cc663a1b8f..45fe6ef647 100644 --- a/aio/content/examples/elements/src/app/popup.component.ts +++ b/aio/content/examples/elements/src/app/popup.component.ts @@ -1,4 +1,6 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +// tslint:disable: variable-name +// #docregion +import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations'; @Component({ @@ -7,9 +9,6 @@ import { animate, state, style, transition, trigger } from '@angular/animations' Popup: {{message}} `, - host: { - '[@state]': 'state', - }, animations: [ trigger('state', [ state('opened', style({transform: 'translateY(0%)'})), @@ -39,15 +38,16 @@ import { animate, state, style, transition, trigger } from '@angular/animations' `] }) export class PopupComponent { + @HostBinding('@state') state: 'opened' | 'closed' = 'closed'; @Input() + get message(): string { return this._message; } set message(message: string) { this._message = message; this.state = 'opened'; } - get message(): string { return this._message; } - _message: string; + private _message: string; @Output() closed = new EventEmitter(); diff --git a/aio/content/examples/event-binding/e2e/src/app.e2e-spec.ts b/aio/content/examples/event-binding/e2e/src/app.e2e-spec.ts index 881a49f700..a5a1dbf558 100644 --- a/aio/content/examples/event-binding/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/event-binding/e2e/src/app.e2e-spec.ts @@ -1,71 +1,66 @@ -'use strict'; // necessary for es6 output in node +import { browser, element, by } from 'protractor'; -import { browser, element, by, protractor } from 'protractor'; +describe('Event binding example', () => { -describe('Event binding example', function () { + beforeEach(() => browser.get('')); - beforeEach(function () { - browser.get(''); + const saveButton = element.all(by.css('button')).get(0); + const onSaveButton = element.all(by.css('button')).get(1); + const myClick = element.all(by.css('button')).get(2); + const deleteButton = element.all(by.css('button')).get(3); + const saveNoProp = element.all(by.css('button')).get(4); + const saveProp = element.all(by.css('button')).get(5); + + + it('should display Event Binding with Angular', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Event Binding'); }); - let saveButton = element.all(by.css('button')).get(0); - let onSaveButton = element.all(by.css('button')).get(1); - let myClick = element.all(by.css('button')).get(2); - let deleteButton = element.all(by.css('button')).get(3); - let saveNoProp = element.all(by.css('button')).get(4); - let saveProp = element.all(by.css('button')).get(5); - - - it('should display Event Binding with Angular', function () { - expect(element(by.css('h1')).getText()).toEqual('Event Binding'); + it('should display 6 buttons', async () => { + expect(await saveButton.getText()).toBe('Save'); + expect(await onSaveButton.getText()).toBe('on-click Save'); + expect(await myClick.getText()).toBe('click with myClick'); + expect(await deleteButton.getText()).toBe('Delete'); + expect(await saveNoProp.getText()).toBe('Save, no propagation'); + expect(await saveProp.getText()).toBe('Save with propagation'); }); - it('should display 6 buttons', function() { - expect(saveButton.getText()).toBe('Save'); - expect(onSaveButton.getText()).toBe('on-click Save'); - expect(myClick.getText()).toBe('click with myClick'); - expect(deleteButton.getText()).toBe('Delete'); - expect(saveNoProp.getText()).toBe('Save, no propagation'); - expect(saveProp.getText()).toBe('Save with propagation'); - }); - - it('should support user input', function () { - let input = element(by.css('input')); - let bindingResult = element.all(by.css('h4')).get(1); - expect(bindingResult.getText()).toEqual('Result: teapot'); - input.sendKeys('abc'); - expect(bindingResult.getText()).toEqual('Result: teapotabc'); + it('should support user input', async () => { + const input = element(by.css('input')); + const bindingResult = element.all(by.css('h4')).get(1); + expect(await bindingResult.getText()).toEqual('Result: teapot'); + await input.sendKeys('abc'); + expect(await bindingResult.getText()).toEqual('Result: teapotabc'); }); it('should hide the item img', async () => { - let deleteButton = element.all(by.css('button')).get(3); await deleteButton.click(); - browser.switchTo().alert().accept(); - expect(element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none'); + await browser.switchTo().alert().accept(); + expect(await element.all(by.css('img')).get(0).getCssValue('display')).toEqual('none'); }); it('should show two alerts', async () => { - let parentDiv = element.all(by.css('.parent-div')); - let childDiv = element.all(by.css('div > div')).get(1); + const parentDiv = element.all(by.css('.parent-div')); + const childDiv = element.all(by.css('div > div')).get(1); await parentDiv.click(); - browser.switchTo().alert().accept(); - expect(childDiv.getText()).toEqual('Click me too! (child)'); + await browser.switchTo().alert().accept(); + expect(await childDiv.getText()).toEqual('Click me too! (child)'); await childDiv.click(); - expect(browser.switchTo().alert().getText()).toEqual('Click me. Event target class is child-div'); - browser.switchTo().alert().accept(); + expect(await browser.switchTo().alert().getText()).toEqual('Click me. Event target class is child-div'); + await browser.switchTo().alert().accept(); }); it('should show 1 alert from Save, no prop, button', async () => { await saveNoProp.click(); - expect(browser.switchTo().alert().getText()).toEqual('Saved. Event target is Save, no propagation'); - browser.switchTo().alert().accept(); + expect(await browser.switchTo().alert().getText()).toEqual('Saved. Event target is Save, no propagation'); + await browser.switchTo().alert().accept(); }); it('should show 2 alerts from Save w/prop button', async () => { await saveProp.click(); - expect(browser.switchTo().alert().getText()).toEqual('Saved.'); - browser.switchTo().alert().accept(); - expect(browser.switchTo().alert().getText()).toEqual('Saved.'); - browser.switchTo().alert().accept(); + expect(await browser.switchTo().alert().getText()).toEqual('Saved.'); + await browser.switchTo().alert().accept(); + expect(await browser.switchTo().alert().getText()).toEqual('Saved.'); + await browser.switchTo().alert().accept(); }); }); diff --git a/aio/content/examples/event-binding/src/app/app.component.ts b/aio/content/examples/event-binding/src/app/app.component.ts index e8e7882d89..02a1328f17 100644 --- a/aio/content/examples/event-binding/src/app/app.component.ts +++ b/aio/content/examples/event-binding/src/app/app.component.ts @@ -12,7 +12,7 @@ export class AppComponent { clickMessage = ''; onSave(event?: KeyboardEvent) { - const evtMsg = event ? ' Event target is ' + (event.target).textContent : ''; + const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : ''; alert('Saved.' + evtMsg); if (event) { event.stopPropagation(); } } @@ -22,7 +22,7 @@ export class AppComponent { } onClickMe(event?: KeyboardEvent) { - const evtMsg = event ? ' Event target class is ' + (event.target).className : ''; + const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : ''; alert('Click me.' + evtMsg); } diff --git a/aio/content/examples/event-binding/src/app/click.directive.ts b/aio/content/examples/event-binding/src/app/click.directive.ts index 4e9e9085b1..a04863fac8 100644 --- a/aio/content/examples/event-binding/src/app/click.directive.ts +++ b/aio/content/examples/event-binding/src/app/click.directive.ts @@ -1,4 +1,4 @@ -/* tslint:disable use-output-property-decorator directive-class-suffix */ +// tslint:disable: directive-selector import { Directive, ElementRef, EventEmitter, Output } from '@angular/core'; @Directive({selector: '[myClick]'}) diff --git a/aio/content/examples/feature-modules/e2e/src/app.e2e-spec.ts b/aio/content/examples/feature-modules/e2e/src/app.e2e-spec.ts index fbc7dc198a..78e8b1ee46 100644 --- a/aio/content/examples/feature-modules/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/feature-modules/e2e/src/app.e2e-spec.ts @@ -7,11 +7,8 @@ describe('feature-modules App', () => { page = new AppPage(); }); - it('should display message saying app works', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('app works!'); + it('should display message saying app works', async () => { + await page.navigateTo(); + expect(await page.getTitleText()).toEqual('app works!'); }); }); - - - diff --git a/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts b/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts index e6f5e52951..f81c63b253 100644 --- a/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts @@ -1,13 +1,9 @@ -'use strict'; // necessary for node! - import { browser, element, by, protractor, ElementFinder, ElementArrayFinder } from 'protractor'; // THESE TESTS ARE INCOMPLETE -describe('Form Validation Tests', function () { +describe('Form Validation Tests', () => { - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); describe('Template-driven form', () => { beforeAll(() => { @@ -52,11 +48,11 @@ let page: { }; function getPage(sectionTag: string) { - let section = element(by.css(sectionTag)); - let buttons = section.all(by.css('button')); + const section = element(by.css(sectionTag)); + const buttons = section.all(by.css('button')); page = { - section: section, + section, form: section.element(by.css('form')), title: section.element(by.css('h1')), nameInput: section.element(by.css('#name')), @@ -73,178 +69,178 @@ function getPage(sectionTag: string) { function tests(title: string) { - it('should display correct title', function () { - expect(page.title.getText()).toContain(title); + it('should display correct title', async () => { + expect(await page.title.getText()).toContain(title); }); - it('should not display submitted message before submit', function () { - expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false); + it('should not display submitted message before submit', async () => { + expect(await page.heroSubmitted.isElementPresent(by.css('p'))).toBe(false); }); - it('should have form buttons', function () { - expect(page.heroFormButtons.count()).toEqual(2); + it('should have form buttons', async () => { + expect(await page.heroFormButtons.count()).toEqual(2); }); - it('should have error at start', function () { - expectFormIsInvalid(); + it('should have error at start', async () => { + await expectFormIsInvalid(); }); - // it('showForm', function () { + // it('showForm', () => { // page.form.getInnerHtml().then(html => console.log(html)); // }); - it('should have disabled submit button', function () { - expect(page.heroFormButtons.get(0).isEnabled()).toBe(false); + it('should have disabled submit button', async () => { + expect(await page.heroFormButtons.get(0).isEnabled()).toBe(false); }); - it('resetting name to valid name should clear errors', function () { + it('resetting name to valid name should clear errors', async () => { const ele = page.nameInput; - expect(ele.isPresent()).toBe(true, 'nameInput should exist'); - ele.clear(); - ele.sendKeys(testName); - expectFormIsValid(); + expect(await ele.isPresent()).toBe(true, 'nameInput should exist'); + await ele.clear(); + await ele.sendKeys(testName); + await expectFormIsValid(); }); - it('should produce "required" error after clearing name', function () { - page.nameInput.clear(); - // page.alterEgoInput.click(); // to blur ... didn't work - page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh! - expect(page.form.getAttribute('class')).toMatch('ng-invalid'); - expect(page.errorMessages.get(0).getText()).toContain('required'); + it('should produce "required" error after clearing name', async () => { + await page.nameInput.clear(); + // await page.alterEgoInput.click(); // to blur ... didn't work + await page.nameInput.sendKeys('x', protractor.Key.BACK_SPACE); // ugh! + expect(await page.form.getAttribute('class')).toMatch('ng-invalid'); + expect(await page.errorMessages.get(0).getText()).toContain('required'); }); - it('should produce "at least 4 characters" error when name="x"', function () { - page.nameInput.clear(); - page.nameInput.sendKeys('x'); // too short - expectFormIsInvalid(); - expect(page.errorMessages.get(0).getText()).toContain('at least 4 characters'); + it('should produce "at least 4 characters" error when name="x"', async () => { + await page.nameInput.clear(); + await page.nameInput.sendKeys('x'); // too short + await expectFormIsInvalid(); + expect(await page.errorMessages.get(0).getText()).toContain('at least 4 characters'); }); - it('resetting name to valid name again should clear errors', function () { - page.nameInput.sendKeys(testName); - expectFormIsValid(); + it('resetting name to valid name again should clear errors', async () => { + await page.nameInput.sendKeys(testName); + await expectFormIsValid(); }); - it('should have enabled submit button', function () { + it('should have enabled submit button', async () => { const submitBtn = page.heroFormButtons.get(0); - expect(submitBtn.isEnabled()).toBe(true); + expect(await submitBtn.isEnabled()).toBe(true); }); - it('should hide form after submit', function () { - page.heroFormButtons.get(0).click(); - expect(page.heroFormButtons.get(0).isDisplayed()).toBe(false); + it('should hide form after submit', async () => { + await page.heroFormButtons.get(0).click(); + expect(await page.heroFormButtons.get(0).isDisplayed()).toBe(false); }); - it('submitted form should be displayed', function () { - expect(page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true); + it('submitted form should be displayed', async () => { + expect(await page.heroSubmitted.isElementPresent(by.css('p'))).toBe(true); }); - it('submitted form should have new hero name', function () { - expect(page.heroSubmitted.getText()).toContain(testName); + it('submitted form should have new hero name', async () => { + expect(await page.heroSubmitted.getText()).toContain(testName); }); - it('clicking edit button should reveal form again', function () { + it('clicking edit button should reveal form again', async () => { const newFormBtn = page.heroSubmitted.element(by.css('button')); - newFormBtn.click(); - expect(page.heroSubmitted.isElementPresent(by.css('p'))) + await newFormBtn.click(); + expect(await page.heroSubmitted.isElementPresent(by.css('p'))) .toBe(false, 'submitted hidden again'); - expect(page.title.isDisplayed()).toBe(true, 'can see form title'); + expect(await page.title.isDisplayed()).toBe(true, 'can see form title'); }); } -function expectFormIsValid() { - expect(page.form.getAttribute('class')).toMatch('ng-valid'); +async function expectFormIsValid() { + expect(await page.form.getAttribute('class')).toMatch('ng-valid'); } -function expectFormIsInvalid() { - expect(page.form.getAttribute('class')).toMatch('ng-invalid'); +async function expectFormIsInvalid() { + expect(await page.form.getAttribute('class')).toMatch('ng-invalid'); } -function triggerAlterEgoValidation() { +async function triggerAlterEgoValidation() { // alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event - element(by.css('app-root')).click() + await element(by.css('app-root')).click(); } -function waitForAlterEgoValidation() { +async function waitForAlterEgoValidation() { // alterEgo async validation will be performed in 400ms - browser.sleep(400); + await browser.sleep(400); } function bobTests() { const emsg = 'Name cannot be Bob.'; - it('should produce "no bob" error after setting name to "Bobby"', function () { + it('should produce "no bob" error after setting name to "Bobby"', async () => { // Re-populate select element - page.powerSelect.click(); - page.powerOption.click(); + await page.powerSelect.click(); + await page.powerOption.click(); - page.nameInput.clear(); - page.nameInput.sendKeys('Bobby'); - expectFormIsInvalid(); - expect(page.errorMessages.get(0).getText()).toBe(emsg); + await page.nameInput.clear(); + await page.nameInput.sendKeys('Bobby'); + await expectFormIsInvalid(); + expect(await page.errorMessages.get(0).getText()).toBe(emsg); }); - it('should be ok again with valid name', function () { - page.nameInput.clear(); - page.nameInput.sendKeys(testName); - expectFormIsValid(); + it('should be ok again with valid name', async () => { + await page.nameInput.clear(); + await page.nameInput.sendKeys(testName); + await expectFormIsValid(); }); } function asyncValidationTests() { const emsg = 'Alter ego is already taken.'; - it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () { - page.alterEgoInput.clear(); - page.alterEgoInput.sendKeys('Eric'); + it(`should produce "${emsg}" error after setting alterEgo to Eric`, async () => { + await page.alterEgoInput.clear(); + await page.alterEgoInput.sendKeys('Eric'); - triggerAlterEgoValidation(); - waitForAlterEgoValidation(); + await triggerAlterEgoValidation(); + await waitForAlterEgoValidation(); - expectFormIsInvalid(); - expect(page.alterEgoErrors.getText()).toBe(emsg); + await expectFormIsInvalid(); + expect(await page.alterEgoErrors.getText()).toBe(emsg); }); - it('should be ok again with different values', function () { - page.alterEgoInput.clear(); - page.alterEgoInput.sendKeys('John'); + it('should be ok again with different values', async () => { + await page.alterEgoInput.clear(); + await page.alterEgoInput.sendKeys('John'); - triggerAlterEgoValidation(); - waitForAlterEgoValidation(); + await triggerAlterEgoValidation(); + await waitForAlterEgoValidation(); - expectFormIsValid(); - expect(page.alterEgoErrors.isPresent()).toBe(false); + await expectFormIsValid(); + expect(await page.alterEgoErrors.isPresent()).toBe(false); }); } function crossValidationTests() { const emsg = 'Name cannot match alter ego.'; - it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () { - page.nameInput.clear(); - page.nameInput.sendKeys('Batman'); + it(`should produce "${emsg}" error after setting name and alter ego to the same value`, async () => { + await page.nameInput.clear(); + await page.nameInput.sendKeys('Batman'); - page.alterEgoInput.clear(); - page.alterEgoInput.sendKeys('Batman'); + await page.alterEgoInput.clear(); + await page.alterEgoInput.sendKeys('Batman'); - triggerAlterEgoValidation(); - waitForAlterEgoValidation(); + await triggerAlterEgoValidation(); + await waitForAlterEgoValidation(); - expectFormIsInvalid(); - expect(page.crossValidationErrorMessage.getText()).toBe(emsg); + await expectFormIsInvalid(); + expect(await page.crossValidationErrorMessage.getText()).toBe(emsg); }); - it('should be ok again with different values', function () { - page.nameInput.clear(); - page.nameInput.sendKeys('Batman'); + it('should be ok again with different values', async () => { + await page.nameInput.clear(); + await page.nameInput.sendKeys('Batman'); - page.alterEgoInput.clear(); - page.alterEgoInput.sendKeys('Superman'); + await page.alterEgoInput.clear(); + await page.alterEgoInput.sendKeys('Superman'); - triggerAlterEgoValidation(); - waitForAlterEgoValidation(); + await triggerAlterEgoValidation(); + await waitForAlterEgoValidation(); - expectFormIsValid(); - expect(page.crossValidationErrorMessage.isPresent()).toBe(false); + await expectFormIsValid(); + expect(await page.crossValidationErrorMessage.isPresent()).toBe(false); }); } diff --git a/aio/content/examples/form-validation/src/app/app.module.ts b/aio/content/examples/form-validation/src/app/app.module.ts index bf08854159..340f181d4e 100644 --- a/aio/content/examples/form-validation/src/app/app.module.ts +++ b/aio/content/examples/form-validation/src/app/app.module.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroFormTemplateComponent } from './template/hero-form-template.component'; import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component'; import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive'; diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts index 72b5dbeaec..7a848cbe3d 100644 --- a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts @@ -1,7 +1,7 @@ /* tslint:disable: member-ordering forin */ // #docplaster // #docregion -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { forbiddenNameValidator } from '../shared/forbidden-name.directive'; @@ -22,13 +22,13 @@ export class HeroFormReactiveComponent implements OnInit { ngOnInit(): void { // #docregion custom-validator this.heroForm = new FormGroup({ - 'name': new FormControl(this.hero.name, [ + name: new FormControl(this.hero.name, [ Validators.required, Validators.minLength(4), forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator. ]), - 'alterEgo': new FormControl(this.hero.alterEgo), - 'power': new FormControl(this.hero.power, Validators.required) + alterEgo: new FormControl(this.hero.alterEgo), + power: new FormControl(this.hero.power, Validators.required) }); // #enddocregion custom-validator diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.2.ts b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.2.ts index 1bd4ca8ab5..3a9e0f1159 100644 --- a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.2.ts +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.2.ts @@ -22,16 +22,16 @@ export class HeroFormReactiveComponent implements OnInit { ngOnInit(): void { // #docregion async-validation this.heroForm = new FormGroup({ - 'name': new FormControl(this.hero.name, [ + name: new FormControl(this.hero.name, [ Validators.required, Validators.minLength(4), forbiddenNameValidator(/bob/i) ]), - 'alterEgo': new FormControl(this.hero.alterEgo, { + alterEgo: new FormControl(this.hero.alterEgo, { asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)], updateOn: 'blur' }), - 'power': new FormControl(this.hero.power, Validators.required) + power: new FormControl(this.hero.power, Validators.required) }); // #enddocregion async-validation } diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.ts b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.ts index 41541e7460..07d33afa73 100644 --- a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.ts +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.ts @@ -21,16 +21,16 @@ export class HeroFormReactiveComponent implements OnInit { ngOnInit(): void { this.heroForm = new FormGroup({ - 'name': new FormControl(this.hero.name, [ + name: new FormControl(this.hero.name, [ Validators.required, Validators.minLength(4), forbiddenNameValidator(/bob/i) ]), - 'alterEgo': new FormControl(this.hero.alterEgo, { + alterEgo: new FormControl(this.hero.alterEgo, { asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)], updateOn: 'blur' }), - 'power': new FormControl(this.hero.power, Validators.required) + power: new FormControl(this.hero.power, Validators.required) }, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level } diff --git a/aio/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts b/aio/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts index 9da3abef37..0c4f8b18c0 100644 --- a/aio/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts +++ b/aio/content/examples/form-validation/src/app/shared/forbidden-name.directive.ts @@ -7,7 +7,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } fr export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl): {[key: string]: any} | null => { const forbidden = nameRe.test(control.value); - return forbidden ? {'forbiddenName': {value: control.value}} : null; + return forbidden ? {forbiddenName: {value: control.value}} : null; }; } // #enddocregion custom-validator diff --git a/aio/content/examples/form-validation/src/app/shared/identity-revealed.directive.ts b/aio/content/examples/form-validation/src/app/shared/identity-revealed.directive.ts index b5dbd05c15..a13278295b 100644 --- a/aio/content/examples/form-validation/src/app/shared/identity-revealed.directive.ts +++ b/aio/content/examples/form-validation/src/app/shared/identity-revealed.directive.ts @@ -8,7 +8,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali const name = control.get('name'); const alterEgo = control.get('alterEgo'); - return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null; + return name && alterEgo && name.value === alterEgo.value ? { identityRevealed: true } : null; }; // #enddocregion cross-validation-validator @@ -19,7 +19,7 @@ export const identityRevealedValidator: ValidatorFn = (control: FormGroup): Vali }) export class IdentityRevealedValidatorDirective implements Validator { validate(control: AbstractControl): ValidationErrors { - return identityRevealedValidator(control) + return identityRevealedValidator(control); } } // #enddocregion cross-validation-directive diff --git a/aio/content/examples/forms-overview/e2e/src/app.e2e-spec.ts b/aio/content/examples/forms-overview/e2e/src/app.e2e-spec.ts index b15faa2a4c..bfcfba0846 100644 --- a/aio/content/examples/forms-overview/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/forms-overview/e2e/src/app.e2e-spec.ts @@ -1,10 +1,14 @@ -import { browser, element, by } from 'protractor'; +import { AppPage } from './app.po'; -describe('Forms Overview Tests', function () { +describe('forms-overvoew App', () => { + let page: AppPage; - beforeEach(function () { - browser.get(''); + beforeEach(() => { + page = new AppPage(); }); + it('should display a title', async () => { + await page.navigateTo(); + expect(await page.getTitleText()).toEqual('Forms Overview'); + }); }); - diff --git a/aio/content/examples/forms-overview/src/app/app.component.spec.ts b/aio/content/examples/forms-overview/src/app/app.component.spec.ts index 489e490485..81488e7bc6 100644 --- a/aio/content/examples/forms-overview/src/app/app.component.spec.ts +++ b/aio/content/examples/forms-overview/src/app/app.component.spec.ts @@ -1,31 +1,31 @@ -import { TestBed, async } from '@angular/core/testing'; +import { TestBed, waitForAsync } from '@angular/core/testing'; + import { AppComponent } from './app.component'; -import { TemplateModule } from './template/template.module'; import { ReactiveModule } from './reactive/reactive.module'; +import { TemplateModule } from './template/template.module'; describe('AppComponent', () => { - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ReactiveModule, TemplateModule], - declarations: [ - AppComponent - ], - }).compileComponents(); + beforeEach(waitForAsync(() => { + TestBed + .configureTestingModule({ + imports: [ReactiveModule, TemplateModule], + declarations: [AppComponent], + }) + .compileComponents(); })); - it('should create the app', async(() => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; + it('should create the app', waitForAsync(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; - expect(app).toBeTruthy(); - })); + expect(app).toBeTruthy(); + })); - it('should render title', async(() => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); + it('should render title', waitForAsync(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('h1').textContent).toContain('Forms Overview'); - })); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Forms Overview'); + })); }); diff --git a/aio/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts b/aio/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts index 5b676c08b8..47e7549501 100644 --- a/aio/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts +++ b/aio/content/examples/forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts @@ -1,19 +1,18 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; -import { FavoriteColorComponent } from './favorite-color.component'; import { createNewEvent } from '../../shared/utils'; +import { FavoriteColorComponent } from './favorite-color.component'; describe('Favorite Color Component', () => { let component: FavoriteColorComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ ReactiveFormsModule ], - declarations: [ FavoriteColorComponent ] - }) - .compileComponents(); + beforeEach(waitForAsync(() => { + TestBed + .configureTestingModule( + {imports: [ReactiveFormsModule], declarations: [FavoriteColorComponent]}) + .compileComponents(); })); beforeEach(() => { diff --git a/aio/content/examples/forms-overview/src/app/shared/utils.ts b/aio/content/examples/forms-overview/src/app/shared/utils.ts index a7cb3ce69c..37fdaf1659 100644 --- a/aio/content/examples/forms-overview/src/app/shared/utils.ts +++ b/aio/content/examples/forms-overview/src/app/shared/utils.ts @@ -1,5 +1,5 @@ export function createNewEvent(eventName: string, bubbles = false, cancelable = false) { - let evt = document.createEvent('CustomEvent'); + const evt = document.createEvent('CustomEvent'); evt.initCustomEvent(eventName, bubbles, cancelable, null); return evt; } diff --git a/aio/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts b/aio/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts index 60e1830b4d..0a00898167 100644 --- a/aio/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts +++ b/aio/content/examples/forms-overview/src/app/template/favorite-color/favorite-color.component.spec.ts @@ -1,19 +1,16 @@ -import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { FavoriteColorComponent } from './favorite-color.component'; import { createNewEvent } from '../../shared/utils'; +import { FavoriteColorComponent } from './favorite-color.component'; describe('FavoriteColorComponent', () => { let component: FavoriteColorComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ FormsModule ], - declarations: [ FavoriteColorComponent ] - }) - .compileComponents(); + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({imports: [FormsModule], declarations: [FavoriteColorComponent]}) + .compileComponents(); })); beforeEach(() => { @@ -28,29 +25,29 @@ describe('FavoriteColorComponent', () => { // #docregion model-to-view it('should update the favorite color on the input field', fakeAsync(() => { - component.favoriteColor = 'Blue'; + component.favoriteColor = 'Blue'; - fixture.detectChanges(); + fixture.detectChanges(); - tick(); + tick(); - const input = fixture.nativeElement.querySelector('input'); + const input = fixture.nativeElement.querySelector('input'); - expect(input.value).toBe('Blue'); - })); + expect(input.value).toBe('Blue'); + })); // #enddocregion model-to-view // #docregion view-to-model it('should update the favorite color in the component', fakeAsync(() => { - const input = fixture.nativeElement.querySelector('input'); - const event = createNewEvent('input'); + const input = fixture.nativeElement.querySelector('input'); + const event = createNewEvent('input'); - input.value = 'Red'; - input.dispatchEvent(event); + input.value = 'Red'; + input.dispatchEvent(event); - fixture.detectChanges(); + fixture.detectChanges(); - expect(component.favoriteColor).toEqual('Red'); - })); + expect(component.favoriteColor).toEqual('Red'); + })); // #enddocregion view-to-model }); diff --git a/aio/content/examples/forms/e2e/src/app.e2e-spec.ts b/aio/content/examples/forms/e2e/src/app.e2e-spec.ts index d3d3cff970..3b43373382 100644 --- a/aio/content/examples/forms/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/forms/e2e/src/app.e2e-spec.ts @@ -1,62 +1,57 @@ import { browser, element, by } from 'protractor'; -describe('Forms Tests', function () { +describe('Forms Tests', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display correct title', async () => { + expect(await element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form'); }); - it('should display correct title', function () { - expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form'); + it('should not display message before submit', async () => { + const ele = element(by.css('h2')); + expect(await ele.isDisplayed()).toBe(false); }); + it('should hide form after submit', async () => { + const ele = element.all(by.css('h1')).get(0); + expect(await ele.isDisplayed()).toBe(true); - it('should not display message before submit', function () { - let ele = element(by.css('h2')); - expect(ele.isDisplayed()).toBe(false); + const b = element.all(by.css('button[type=submit]')).get(0); + await b.click(); + expect(await ele.isDisplayed()).toBe(false); }); - it('should hide form after submit', function () { - let ele = element.all(by.css('h1')).get(0); - expect(ele.isDisplayed()).toBe(true); - let b = element.all(by.css('button[type=submit]')).get(0); - b.click().then(function() { - expect(ele.isDisplayed()).toBe(false); - }); + it('should display message after submit', async () => { + const b = element.all(by.css('button[type=submit]')).get(0); + await b.click(); + expect(await element(by.css('h2')).getText()).toContain('You submitted the following'); }); - it('should display message after submit', function () { - let b = element.all(by.css('button[type=submit]')).get(0); - b.click().then(function() { - expect(element(by.css('h2')).getText()).toContain('You submitted the following'); - }); + it('should hide form after submit', async () => { + const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); + expect(await alterEgoEle.isDisplayed()).toBe(true); + + const submitButtonEle = element.all(by.css('button[type=submit]')).get(0); + await submitButtonEle.click(); + expect(await alterEgoEle.isDisplayed()).toBe(false); }); - it('should hide form after submit', function () { - let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); - expect(alterEgoEle.isDisplayed()).toBe(true); - let submitButtonEle = element.all(by.css('button[type=submit]')).get(0); - submitButtonEle.click().then(function() { - expect(alterEgoEle.isDisplayed()).toBe(false); - }); - }); + it('should reflect submitted data after submit', async () => { + const alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); + const value = await alterEgoEle.getAttribute('value'); + const test = 'testing 1 2 3'; + const newValue = value + test; - it('should reflect submitted data after submit', function () { - let test = 'testing 1 2 3'; - let newValue: string; - let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0); - alterEgoEle.getAttribute('value').then(function(value: string) { - alterEgoEle.sendKeys(test); - newValue = value + test; - expect(alterEgoEle.getAttribute('value')).toEqual(newValue); - let b = element.all(by.css('button[type=submit]')).get(0); - return b.click(); - }).then(function() { - let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego')); - expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label'); - let divEle = element(by.cssContainingText('div', newValue)); - expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue); - }); + await alterEgoEle.sendKeys(test); + expect(await alterEgoEle.getAttribute('value')).toEqual(newValue); + + const b = element.all(by.css('button[type=submit]')).get(0); + await b.click(); + + const alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego')); + expect(await alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label'); + const divEle = element(by.cssContainingText('div', newValue)); + expect(await divEle.isPresent()).toBe(true, `cannot locate div with this text: ${newValue}`); }); }); - diff --git a/aio/content/examples/forms/src/app/app.module.ts b/aio/content/examples/forms/src/app/app.module.ts index 94d11f726e..f5482927a9 100644 --- a/aio/content/examples/forms/src/app/app.module.ts +++ b/aio/content/examples/forms/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 { HeroFormComponent } from './hero-form/hero-form.component'; @NgModule({ diff --git a/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts b/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts index 19f83e7d96..c155206786 100644 --- a/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts +++ b/aio/content/examples/forms/src/app/hero-form/hero-form.component.ts @@ -35,7 +35,7 @@ export class HeroFormComponent { skyDog(): Hero { // #docregion SkyDog - let myHero = new Hero(42, 'SkyDog', + const myHero = new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover'); console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog" @@ -48,9 +48,9 @@ export class HeroFormComponent { // Reveal in html: // Name via form.controls = {{showFormControls(heroForm)}} showFormControls(form: any) { - return form && form.controls['name'] && + return form && form.controls.name && // #docregion form-controls - form.controls['name'].value; // Dr. IQ + form.controls.name.value; // Dr. IQ // #enddocregion form-controls } diff --git a/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts b/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts index 09aa3abfed..2bf35c2de5 100644 --- a/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/getting-started-v0/e2e/src/app.e2e-spec.ts @@ -1,19 +1,15 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; describe('Getting Started V0', () => { - beforeEach(() => { - return browser.get('/'); - }); + beforeEach(() => browser.get('/')); - it('should display "My Store" in the top bar', async() => { + it('should display "My Store" in the top bar', async () => { const title = await element(by.css('app-root app-top-bar h1')).getText(); expect(title).toEqual('My Store'); }); - it('should display "Products" on the homepage', async() => { + it('should display "Products" on the homepage', async () => { const title = await element(by.css('app-root app-product-list h2')).getText(); expect(title).toEqual('Products'); diff --git a/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts b/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts index e2a2bd2db0..0dfcc856c9 100644 --- a/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/getting-started/e2e/src/app.e2e-spec.ts @@ -1,6 +1,4 @@ -'use strict'; // necessary for es6 output in node - -import { browser, element, by, ExpectedConditions as EC, logging, ElementFinder, ElementArrayFinder } from 'protractor'; +import { browser, element, by, ExpectedConditions as EC, logging } from 'protractor'; describe('Getting Started', () => { const pageElements = { @@ -15,17 +13,17 @@ describe('Getting Started', () => { }; describe('General', () => { - beforeAll(async() => { + beforeAll(async () => { await browser.get('/'); }); - it('should display "My Store"', async() => { + it('should display "My Store"', async () => { const title = await pageElements.topBarHeader.getText(); expect(title).toEqual('My Store'); }); - it('should display "Products" on the homepage', async() => { + it('should display "Products" on the homepage', async () => { const title = await pageElements.productListHeader.getText(); expect(title).toEqual('Products'); @@ -33,11 +31,11 @@ describe('Getting Started', () => { }); describe('Product List', () => { - beforeAll(async() => { + beforeAll(async () => { await browser.get('/'); }); - it('should display 3 items', async() => { + it('should display 3 items', async () => { const products = await pageElements.productListItems; expect(products.length).toEqual(3); @@ -45,11 +43,11 @@ describe('Getting Started', () => { }); describe('Product Details', () => { - beforeEach(async() => { + beforeEach(async () => { await browser.get('/'); }); - it('should display information for a product', async() => { + it('should display information for a product', async () => { await pageElements.productListLinks.get(0).click(); const product = pageElements.productDetailsPage; @@ -63,7 +61,7 @@ describe('Getting Started', () => { expect(productDescription).toBe('A large phone with one of the best screens'); }); - it('should add the product to the cart', async() => { + it('should add the product to the cart', async () => { await pageElements.productListLinks.get(0).click(); const product = pageElements.productDetailsPage; @@ -82,11 +80,11 @@ describe('Getting Started', () => { describe('Cart', () => { - beforeEach(async() => { + beforeEach(async () => { await browser.get('/'); }); - it('should go through the checkout process', async() => { + it('should go through the checkout process', async () => { await pageElements.productListLinks.get(0).click(); const checkoutLink = pageElements.topBarCheckoutLink; diff --git a/aio/content/examples/hierarchical-dependency-injection/e2e/src/app.e2e-spec.ts b/aio/content/examples/hierarchical-dependency-injection/e2e/src/app.e2e-spec.ts index 09a4f3d27b..5c418770ea 100644 --- a/aio/content/examples/hierarchical-dependency-injection/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/hierarchical-dependency-injection/e2e/src/app.e2e-spec.ts @@ -1,15 +1,11 @@ -'use strict'; // necessary for es6 output in node - import { browser, by, element } from 'protractor'; describe('Hierarchical dependency injection', () => { - beforeAll(() => { - browser.get(''); - }); + beforeAll(() => browser.get('')); describe('Heroes Scenario', () => { - let page = { + const page = { heroName: '', income: '', @@ -23,81 +19,68 @@ describe('Hierarchical dependency injection', () => { saveButtonEl: element(by.cssContainingText('app-heroes-list app-hero-tax-return button', 'Save')) }; - it('should list multiple heroes', () => { - expect(element.all(by.css('app-heroes-list li')).count()).toBeGreaterThan(1); + it('should list multiple heroes', async () => { + expect(await element.all(by.css('app-heroes-list li')).count()).toBeGreaterThan(1); }); - it('should show no hero tax-returns at the start', () => { - expect(element.all(by.css('app-heroes-list li app-hero-tax-return')).count()).toBe(0); + it('should show no hero tax-returns at the start', async () => { + expect(await element.all(by.css('app-heroes-list li app-hero-tax-return')).count()).toBe(0); }); - it('should open first hero in app-hero-tax-return view after click', () => { - page.heroEl.getText() - .then(val => { - page.heroName = val; - }) - .then(() => page.heroEl.click()) - .then(() => { - expect(page.heroCardEl.isDisplayed()).toBe(true); - }); + it('should open first hero in app-hero-tax-return view after click', async () => { + page.heroName = await page.heroEl.getText(); + await page.heroEl.click(); + expect(await page.heroCardEl.isDisplayed()).toBe(true); }); - it('hero tax-return should have first hero\'s name', () => { + it('hero tax-return should have first hero\'s name', async () => { // Not `page.tax-returnNameInputEl.getAttribute('value')` although later that is essential - expect(page.taxReturnNameEl.getText()).toEqual(page.heroName); + expect(await page.taxReturnNameEl.getText()).toEqual(page.heroName); }); - it('should be able to cancel change', () => { - page.incomeInputEl.clear() - .then(() => page.incomeInputEl.sendKeys('777')) - .then(() => { - expect(page.incomeInputEl.getAttribute('value')).toBe('777', 'income should be 777'); - return page.cancelButtonEl.click(); - }) - .then(() => { - expect(page.incomeInputEl.getAttribute('value')).not.toBe('777', 'income should not be 777'); - }); + it('should be able to cancel change', async () => { + await page.incomeInputEl.clear(); + await page.incomeInputEl.sendKeys('777'); + expect(await page.incomeInputEl.getAttribute('value')).toBe('777', 'income should be 777'); + + await page.cancelButtonEl.click(); + expect(await page.incomeInputEl.getAttribute('value')).not.toBe('777', 'income should not be 777'); }); - it('should be able to save change', () => { - page.incomeInputEl.clear() - .then(() => page.incomeInputEl.sendKeys('999')) - .then(() => { - expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should be 999'); - return page.saveButtonEl.click(); - }) - .then(() => { - expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should still be 999'); - }); + it('should be able to save change', async () => { + await page.incomeInputEl.clear(); + await page.incomeInputEl.sendKeys('999'); + expect(await page.incomeInputEl.getAttribute('value')).toBe('999', 'income should be 999'); + + await page.saveButtonEl.click(); + expect(await page.incomeInputEl.getAttribute('value')).toBe('999', 'income should still be 999'); }); - it('should be able to close tax-return', () => { - page.saveButtonEl.click() - .then(() => { - expect(element.all(by.css('app-heroes-list li app-hero-tax-return')).count()).toBe(0); - }); + it('should be able to close tax-return', async () => { + await page.saveButtonEl.click(); + expect(await element.all(by.css('app-heroes-list li app-hero-tax-return')).count()).toBe(0); }); }); describe('Villains Scenario', () => { - it('should list multiple villains', () => { - expect(element.all(by.css('app-villains-list li')).count()).toBeGreaterThan(1); + it('should list multiple villains', async () => { + expect(await element.all(by.css('app-villains-list li')).count()).toBeGreaterThan(1); }); }); describe('Cars Scenario', () => { - it('A-component should use expected services', () => { - expect(element(by.css('a-car')).getText()).toContain('C1-E1-T1'); + it('A-component should use expected services', async () => { + expect(await element(by.css('a-car')).getText()).toContain('C1-E1-T1'); }); - it('B-component should use expected services', () => { - expect(element(by.css('b-car')).getText()).toContain('C2-E2-T1'); + it('B-component should use expected services', async () => { + expect(await element(by.css('b-car')).getText()).toContain('C2-E2-T1'); }); - it('C-component should use expected services', () => { - expect(element(by.css('c-car')).getText()).toContain('C3-E2-T1'); + it('C-component should use expected services', async () => { + expect(await element(by.css('c-car')).getText()).toContain('C3-E2-T1'); }); }); }); diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts index 6445bb0f11..a43176f6e7 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/app.module.ts @@ -1,12 +1,12 @@ // #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 { HeroTaxReturnComponent } from './hero-tax-return.component'; -import { HeroesListComponent } from './heroes-list.component'; -import { VillainsListComponent } from './villains-list.component'; +import { HeroesListComponent } from './heroes-list.component'; +import { VillainsListComponent } from './villains-list.component'; import { carComponents } from './car.components'; diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts index e1a8daf656..9ee6325b2e 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.component.ts @@ -1,6 +1,7 @@ +// tslint:disable: no-output-native // #docregion import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { HeroTaxReturn } from './hero'; +import { HeroTaxReturn } from './hero'; import { HeroTaxReturnService } from './hero-tax-return.service'; @Component({ @@ -21,7 +22,7 @@ export class HeroTaxReturnComponent { } @Input() - set taxReturn (htr: HeroTaxReturn) { + set taxReturn(htr: HeroTaxReturn) { this.heroTaxReturnService.taxReturn = htr; } @@ -30,9 +31,9 @@ export class HeroTaxReturnComponent { onCanceled() { this.flashMessage('Canceled'); this.heroTaxReturnService.restoreTaxReturn(); - }; + } - onClose() { this.close.emit(); }; + onClose() { this.close.emit(); } onSaved() { this.flashMessage('Saved'); diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.service.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.service.ts index d6ff0f7fff..b82301d64b 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.service.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/hero-tax-return.service.ts @@ -1,5 +1,5 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { HeroTaxReturn } from './hero'; import { HeroesService } from './heroes.service'; @@ -10,12 +10,12 @@ export class HeroTaxReturnService { constructor(private heroService: HeroesService) { } - set taxReturn (htr: HeroTaxReturn) { + set taxReturn(htr: HeroTaxReturn) { this.originalTaxReturn = htr; this.currentTaxReturn = htr.clone(); } - get taxReturn (): HeroTaxReturn { + get taxReturn(): HeroTaxReturn { return this.currentTaxReturn; } diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/hero.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/hero.ts index 9f05cb6cff..954d652017 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/hero.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/hero.ts @@ -1,9 +1,9 @@ // #docregion export interface Hero { - id: number; + id: number; name: string; - tid: string; // tax id + tid: string; // tax id } //// HeroTaxReturn //// diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/heroes-list.component.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/heroes-list.component.ts index 7e598a8038..093e8a0dac 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/heroes-list.component.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/heroes-list.component.ts @@ -1,9 +1,9 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { Hero, HeroTaxReturn } from './hero'; -import { HeroesService } from './heroes.service'; +import { HeroesService } from './heroes.service'; @Component({ selector: 'app-heroes-list', diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/villains-list.component.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/villains-list.component.ts index aa70bdf3cb..b2bb01e255 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/villains-list.component.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/villains-list.component.ts @@ -1,5 +1,5 @@ // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { Villain, VillainsService } from './villains.service'; diff --git a/aio/content/examples/hierarchical-dependency-injection/src/app/villains.service.ts b/aio/content/examples/hierarchical-dependency-injection/src/app/villains.service.ts index a8605bd11d..3a8253bf76 100644 --- a/aio/content/examples/hierarchical-dependency-injection/src/app/villains.service.ts +++ b/aio/content/examples/hierarchical-dependency-injection/src/app/villains.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { of } from 'rxjs'; +import { of } from 'rxjs'; export interface Villain { id: number; name: string; } diff --git a/aio/content/examples/http/e2e/src/app.e2e-spec.ts b/aio/content/examples/http/e2e/src/app.e2e-spec.ts index 091e54acaa..41a4c2e3c7 100644 --- a/aio/content/examples/http/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/http/e2e/src/app.e2e-spec.ts @@ -1,4 +1,4 @@ -import { browser, element, by, ElementFinder } from 'protractor'; +import { browser, element, by } from 'protractor'; import { resolve } from 'path'; const page = { @@ -24,115 +24,113 @@ const page = { uploadMessage: element(by.css('app-uploader p')) }; -let checkLogForMessage = (message: string) => { - expect(page.logList.getText()).toContain(message); +const checkLogForMessage = async (message: string) => { + expect(await page.logList.getText()).toContain(message); }; -describe('Http Tests', function() { - beforeEach(() => { - browser.get(''); - }); +describe('Http Tests', () => { + beforeEach(() => browser.get('')); describe('Heroes', () => { - it('retrieves the list of heroes at startup', () => { - expect(page.heroesListItems.count()).toBe(4); - expect(page.heroesListItems.get(0).getText()).toContain('Dr Nice'); - checkLogForMessage('GET "api/heroes"'); + it('retrieves the list of heroes at startup', async () => { + expect(await page.heroesListItems.count()).toBe(4); + expect(await page.heroesListItems.get(0).getText()).toContain('Dr Nice'); + await checkLogForMessage('GET "api/heroes"'); }); - it('makes a POST to add a new hero', () => { - page.heroesListInput.sendKeys('Magneta'); - page.heroesListAddButton.click(); - expect(page.heroesListItems.count()).toBe(5); - checkLogForMessage('POST "api/heroes"'); + it('makes a POST to add a new hero', async () => { + await page.heroesListInput.sendKeys('Magneta'); + await page.heroesListAddButton.click(); + expect(await page.heroesListItems.count()).toBe(5); + await checkLogForMessage('POST "api/heroes"'); }); - it('makes a GET to search for a hero', () => { - page.heroesListInput.sendKeys('Celeritas'); - page.heroesListSearchButton.click(); - checkLogForMessage('GET "api/heroes?name=Celeritas"'); + it('makes a GET to search for a hero', async () => { + await page.heroesListInput.sendKeys('Celeritas'); + await page.heroesListSearchButton.click(); + await checkLogForMessage('GET "api/heroes?name=Celeritas"'); }); }); describe('Messages', () => { - it('can clear the logs', () => { - expect(page.logListItems.count()).toBe(1); - page.logClearButton.click(); - expect(page.logListItems.count()).toBe(0); + it('can clear the logs', async () => { + expect(await page.logListItems.count()).toBe(1); + await page.logClearButton.click(); + expect(await page.logListItems.count()).toBe(0); }); }); describe('Configuration', () => { - it('can fetch the configuration JSON file', () => { - page.configGetButton.click(); - checkLogForMessage('GET "assets/config.json"'); - expect(page.configSpan.getText()).toContain('Heroes API URL is "api/heroes"'); - expect(page.configSpan.getText()).toContain('Textfile URL is "assets/textfile.txt"'); + it('can fetch the configuration JSON file', async () => { + await page.configGetButton.click(); + await checkLogForMessage('GET "assets/config.json"'); + expect(await page.configSpan.getText()).toContain('Heroes API URL is "api/heroes"'); + expect(await page.configSpan.getText()).toContain('Textfile URL is "assets/textfile.txt"'); }); - it('can fetch the configuration JSON file with headers', () => { - page.configGetResponseButton.click(); - checkLogForMessage('GET "assets/config.json"'); - expect(page.configSpan.getText()).toContain('Response headers:'); - expect(page.configSpan.getText()).toContain('content-type: application/json; charset=UTF-8'); + it('can fetch the configuration JSON file with headers', async () => { + await page.configGetResponseButton.click(); + await checkLogForMessage('GET "assets/config.json"'); + expect(await page.configSpan.getText()).toContain('Response headers:'); + expect(await page.configSpan.getText()).toContain('content-type: application/json; charset=UTF-8'); }); - it('can clear the configuration log', () => { - page.configGetResponseButton.click(); - expect(page.configSpan.getText()).toContain('Response headers:'); - page.configClearButton.click(); - expect(page.configSpan.isPresent()).toBeFalsy(); + it('can clear the configuration log', async () => { + await page.configGetResponseButton.click(); + expect(await page.configSpan.getText()).toContain('Response headers:'); + await page.configClearButton.click(); + expect(await page.configSpan.isPresent()).toBeFalsy(); }); - it('throws an error for a non valid url', () => { - page.configErrorButton.click(); - checkLogForMessage('GET "not/a/real/url"'); - expect(page.configErrorMessage.getText()).toContain('"Something bad happened; please try again later."'); + it('throws an error for a non valid url', async () => { + await page.configErrorButton.click(); + await checkLogForMessage('GET "not/a/real/url"'); + expect(await page.configErrorMessage.getText()).toContain('"Something bad happened; please try again later."'); }); }); describe('Download', () => { - it('can download a txt file and show it', () => { - page.downloadButton.click(); - checkLogForMessage('DownloaderService downloaded "assets/textfile.txt"'); - checkLogForMessage('GET "assets/textfile.txt"'); - expect(page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "'); + it('can download a txt file and show it', async () => { + await page.downloadButton.click(); + await checkLogForMessage('DownloaderService downloaded "assets/textfile.txt"'); + await checkLogForMessage('GET "assets/textfile.txt"'); + expect(await page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "'); }); - it('can clear the log of the download', () => { - page.downloadButton.click(); - expect(page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "'); - page.downloadClearButton.click(); - expect(page.downloadMessage.isPresent()).toBeFalsy(); + it('can clear the log of the download', async () => { + await page.downloadButton.click(); + expect(await page.downloadMessage.getText()).toContain('Contents: "This is the downloaded text file "'); + await page.downloadClearButton.click(); + expect(await page.downloadMessage.isPresent()).toBeFalsy(); }); }); describe('Upload', () => { - it('can upload a file', () => { + it('can upload a file', async () => { const filename = 'app.po.ts'; const url = resolve(__dirname, filename); - page.uploadInput.sendKeys(url); - checkLogForMessage('POST "/upload/file" succeeded in'); - expect(page.uploadMessage.getText()).toContain( + await page.uploadInput.sendKeys(url); + await checkLogForMessage('POST "/upload/file" succeeded in'); + expect(await page.uploadMessage.getText()).toContain( `File "${filename}" was completely uploaded!`); }); }); describe('PackageSearch', () => { - it('can search for npm package and find in cache', () => { + it('can search for npm package and find in cache', async () => { const packageName = 'angular'; - page.searchInput.sendKeys(packageName); - checkLogForMessage( + await page.searchInput.sendKeys(packageName); + await checkLogForMessage( 'Caching response from "https://npmsearch.com/query?q=angular"'); - expect(page.searchListItems.count()).toBeGreaterThan(1, 'angular items'); + expect(await page.searchListItems.count()).toBeGreaterThan(1, 'angular items'); - page.searchInput.clear(); - page.searchInput.sendKeys(' '); - expect(page.searchListItems.count()).toBe(0, 'search empty'); + await page.searchInput.clear(); + await page.searchInput.sendKeys(' '); + expect(await page.searchListItems.count()).toBe(0, 'search empty'); - page.searchInput.clear(); - page.searchInput.sendKeys(packageName); - checkLogForMessage( + await page.searchInput.clear(); + await page.searchInput.sendKeys(packageName); + await checkLogForMessage( 'Found cached response for "https://npmsearch.com/query?q=angular"'); }); }); diff --git a/aio/content/examples/http/src/app/app.module.ts b/aio/content/examples/http/src/app/app.module.ts index 0929ec3a9b..d9a9a5934b 100644 --- a/aio/content/examples/http/src/app/app.module.ts +++ b/aio/content/examples/http/src/app/app.module.ts @@ -1,29 +1,29 @@ // #docplaster // #docregion sketch -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; // #enddocregion sketch -import { FormsModule } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; // #docregion sketch import { HttpClientModule } from '@angular/common/http'; // #enddocregion sketch import { HttpClientXsrfModule } from '@angular/common/http'; import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; -import { InMemoryDataService } from './in-memory-data.service'; +import { InMemoryDataService } from './in-memory-data.service'; import { RequestCache, RequestCacheWithMap } from './request-cache.service'; -import { AppComponent } from './app.component'; -import { AuthService } from './auth.service'; -import { ConfigComponent } from './config/config.component'; -import { DownloaderComponent } from './downloader/downloader.component'; -import { HeroesComponent } from './heroes/heroes.component'; -import { HttpErrorHandler } from './http-error-handler.service'; -import { MessageService } from './message.service'; -import { MessagesComponent } from './messages/messages.component'; +import { AppComponent } from './app.component'; +import { AuthService } from './auth.service'; +import { ConfigComponent } from './config/config.component'; +import { DownloaderComponent } from './downloader/downloader.component'; +import { HeroesComponent } from './heroes/heroes.component'; +import { HttpErrorHandler } from './http-error-handler.service'; +import { MessageService } from './message.service'; +import { MessagesComponent } from './messages/messages.component'; import { PackageSearchComponent } from './package-search/package-search.component'; -import { UploaderComponent } from './uploader/uploader.component'; +import { UploaderComponent } from './uploader/uploader.component'; import { httpInterceptorProviders } from './http-interceptors/index'; // #docregion sketch diff --git a/aio/content/examples/http/src/app/config/config.component.ts b/aio/content/examples/http/src/app/config/config.component.ts index c082e81030..39a847ffbb 100644 --- a/aio/content/examples/http/src/app/config/config.component.ts +++ b/aio/content/examples/http/src/app/config/config.component.ts @@ -40,8 +40,8 @@ export class ConfigComponent { this.configService.getConfig_1() // #docregion v1, v1_callback .subscribe((data: Config) => this.config = { - heroesUrl: data['heroesUrl'], - textfile: data['textfile'] + heroesUrl: data.heroesUrl, + textfile: data.textfile }); // #enddocregion v1_callback } diff --git a/aio/content/examples/http/src/app/config/config.service.ts b/aio/content/examples/http/src/app/config/config.service.ts index 4dd71c69ab..e41c24f39c 100644 --- a/aio/content/examples/http/src/app/config/config.service.ts +++ b/aio/content/examples/http/src/app/config/config.service.ts @@ -76,15 +76,15 @@ export class ConfigService { console.error('An error occurred:', error.error.message); } else { // The backend returned an unsuccessful response code. - // The response body may contain clues as to what went wrong, + // The response body may contain clues as to what went wrong. console.error( `Backend returned code ${error.status}, ` + `body was: ${error.error}`); } - // return an observable with a user-facing error message + // Return an observable with a user-facing error message. return throwError( 'Something bad happened; please try again later.'); - }; + } // #enddocregion handleError makeIntentionalError() { diff --git a/aio/content/examples/http/src/app/heroes/heroes.service.ts b/aio/content/examples/http/src/app/heroes/heroes.service.ts index a0c079027f..96b0d826f7 100644 --- a/aio/content/examples/http/src/app/heroes/heroes.service.ts +++ b/aio/content/examples/http/src/app/heroes/heroes.service.ts @@ -16,7 +16,7 @@ import { HttpErrorHandler, HandleError } from '../http-error-handler.service'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', - 'Authorization': 'my-auth-token' + Authorization: 'my-auth-token' }) }; // #enddocregion http-options @@ -33,7 +33,7 @@ export class HeroesService { } /** GET heroes from the server */ - getHeroes (): Observable { + getHeroes(): Observable { return this.http.get(this.heroesUrl) .pipe( catchError(this.handleError('getHeroes', [])) @@ -60,7 +60,7 @@ export class HeroesService { // #docregion addHero /** POST: add a new hero to the database */ - addHero (hero: Hero): Observable { + addHero(hero: Hero): Observable { return this.http.post(this.heroesUrl, hero, httpOptions) .pipe( catchError(this.handleError('addHero', hero)) @@ -70,7 +70,7 @@ export class HeroesService { // #docregion deleteHero /** DELETE: delete the hero from the server */ - deleteHero (id: number): Observable<{}> { + deleteHero(id: number): Observable<{}> { const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42 return this.http.delete(url, httpOptions) .pipe( @@ -81,7 +81,7 @@ export class HeroesService { // #docregion updateHero /** PUT: update the hero on the server. Returns the updated hero upon success. */ - updateHero (hero: Hero): Observable { + updateHero(hero: Hero): Observable { // #enddocregion updateHero // #docregion update-headers httpOptions.headers = diff --git a/aio/content/examples/http/src/app/http-error-handler.service.ts b/aio/content/examples/http/src/app/http-error-handler.service.ts index c7d0926fbd..12ef1ba35d 100644 --- a/aio/content/examples/http/src/app/http-error-handler.service.ts +++ b/aio/content/examples/http/src/app/http-error-handler.service.ts @@ -15,8 +15,10 @@ export class HttpErrorHandler { constructor(private messageService: MessageService) { } /** Create curried handleError function that already knows the service name */ - createHandleError = (serviceName = '') => - (operation = 'operation', result = {} as T) => this.handleError(serviceName, operation, result); + createHandleError = (serviceName = '') => { + return (operation = 'operation', result = {} as T) => + this.handleError(serviceName, operation, result); + } /** * Returns a function that handles Http operation failures. @@ -25,7 +27,7 @@ export class HttpErrorHandler { * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ - handleError (serviceName = '', operation = 'operation', result = {} as T) { + handleError(serviceName = '', operation = 'operation', result = {} as T) { return (error: HttpErrorResponse): Observable => { // TODO: send the error to remote logging infrastructure diff --git a/aio/content/examples/http/src/app/package-search/package-search.service.ts b/aio/content/examples/http/src/app/package-search/package-search.service.ts index a1ef8cc26b..a1454625ff 100644 --- a/aio/content/examples/http/src/app/package-search/package-search.service.ts +++ b/aio/content/examples/http/src/app/package-search/package-search.service.ts @@ -39,7 +39,7 @@ export class PackageSearchService { this.handleError = httpErrorHandler.createHandleError('HeroesService'); } - search (packageName: string, refresh = false): Observable { + search(packageName: string, refresh = false): Observable { // clear if no pkg name if (!packageName.trim()) { return of([]); } diff --git a/aio/content/examples/http/src/app/request-cache.service.ts b/aio/content/examples/http/src/app/request-cache.service.ts index a055a74d75..9e3fbc83b5 100644 --- a/aio/content/examples/http/src/app/request-cache.service.ts +++ b/aio/content/examples/http/src/app/request-cache.service.ts @@ -12,7 +12,7 @@ export interface RequestCacheEntry { // #docregion request-cache export abstract class RequestCache { abstract get(req: HttpRequest): HttpResponse | undefined; - abstract put(req: HttpRequest, response: HttpResponse): void + abstract put(req: HttpRequest, response: HttpResponse): void; } // #enddocregion request-cache @@ -44,8 +44,8 @@ export class RequestCacheWithMap implements RequestCache { const url = req.urlWithParams; this.messenger.add(`Caching response from "${url}".`); - const entry = { url, response, lastRead: Date.now() }; - this.cache.set(url, entry); + const newEntry = { url, response, lastRead: Date.now() }; + this.cache.set(url, newEntry); // remove expired cache entries const expired = Date.now() - maxAge; diff --git a/aio/content/examples/http/src/app/uploader/uploader.service.ts b/aio/content/examples/http/src/app/uploader/uploader.service.ts index 8bc659f26f..b461f6d876 100644 --- a/aio/content/examples/http/src/app/uploader/uploader.service.ts +++ b/aio/content/examples/http/src/app/uploader/uploader.service.ts @@ -24,7 +24,7 @@ export class UploaderService { // } upload(file: File) { - if (!file) { return; } + if (!file) { return of(); } // COULD HAVE WRITTEN: // return this.http.post('/upload/file', file, { diff --git a/aio/content/examples/http/src/main-specs.ts b/aio/content/examples/http/src/main-specs.ts index 53af1b08a8..e1ab261f4c 100644 --- a/aio/content/examples/http/src/main-specs.ts +++ b/aio/content/examples/http/src/main-specs.ts @@ -22,13 +22,13 @@ import './testing/http-client.spec.ts'; bootstrap(); // -function bootstrap () { - if (window['jasmineRef']) { +function bootstrap() { + if ((window as any).jasmineRef) { location.reload(); return; } else { window.onload(undefined); - window['jasmineRef'] = jasmine.getEnv(); + (window as any).jasmineRef = jasmine.getEnv(); } // First, initialize the Angular testing environment. diff --git a/aio/content/examples/http/src/testing/global-jasmine.ts b/aio/content/examples/http/src/testing/global-jasmine.ts index 560ff97d66..68d325d1d4 100644 --- a/aio/content/examples/http/src/testing/global-jasmine.ts +++ b/aio/content/examples/http/src/testing/global-jasmine.ts @@ -1,3 +1,3 @@ import jasmineRequire from 'jasmine-core/lib/jasmine-core/jasmine.js'; -window['jasmineRequire'] = jasmineRequire; +(window as any).jasmineRequire = jasmineRequire; diff --git a/aio/content/examples/http/src/testing/http-client.spec.ts b/aio/content/examples/http/src/testing/http-client.spec.ts index 853bacf5be..abbb1115d8 100644 --- a/aio/content/examples/http/src/testing/http-client.spec.ts +++ b/aio/content/examples/http/src/testing/http-client.spec.ts @@ -73,7 +73,7 @@ describe('HttpClient testing', () => { // Make an HTTP GET request with specific header httpClient.get(testUrl, { - headers: new HttpHeaders({'Authorization': 'my-auth-token'}) + headers: new HttpHeaders({Authorization: 'my-auth-token'}) }) .subscribe(data => expect(data).toEqual(testData) @@ -83,14 +83,14 @@ describe('HttpClient testing', () => { // #docregion predicate // Expect one request with an authorization header const req = httpTestingController.expectOne( - req => req.headers.has('Authorization') + request => request.headers.has('Authorization') ); // #enddocregion predicate req.flush(testData); }); it('can test multiple requests', () => { - let testData: Data[] = [ + const testData: Data[] = [ { name: 'bob' }, { name: 'carol' }, { name: 'ted' }, { name: 'alice' } ]; diff --git a/aio/content/examples/i18n/doc-files/messages.fr.xlf.html b/aio/content/examples/i18n/doc-files/messages.fr.xlf.html index 03635103b7..213dc74bb3 100644 --- a/aio/content/examples/i18n/doc-files/messages.fr.xlf.html +++ b/aio/content/examples/i18n/doc-files/messages.fr.xlf.html @@ -41,7 +41,6 @@ - The author is L'auteur est diff --git a/aio/content/examples/i18n/e2e/src/app.e2e-spec.ts b/aio/content/examples/i18n/e2e/src/app.e2e-spec.ts index ebafb55f96..42c50f03a7 100644 --- a/aio/content/examples/i18n/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/i18n/e2e/src/app.e2e-spec.ts @@ -2,44 +2,42 @@ import { browser, element, by } from 'protractor'; describe('i18n E2E Tests', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display i18n translated welcome: Bonjour !', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Bonjour i18n !'); }); - it('should display i18n translated welcome: Bonjour !', function () { - expect(element(by.css('h1')).getText()).toEqual('Bonjour i18n !'); + it('should display the node texts without elements', async () => { + expect(await element(by.css('app-root')).getText()).toContain(`Je n'affiche aucun élément`); }); - it('should display the node texts without elements', function () { - expect(element(by.css('app-root')).getText()).toContain(`Je n'affiche aucun élément`); - }); - - it('should display the translated title attribute', function () { - const title = element(by.css('img')).getAttribute('title'); + it('should display the translated title attribute', async () => { + const title = await element(by.css('img')).getAttribute('title'); expect(title).toBe(`Logo d'Angular`); }); - it('should display the ICU plural expression', function () { - expect(element.all(by.css('span')).get(0).getText()).toBe(`Mis à jour à l'instant`); + it('should display the ICU plural expression', async () => { + expect(await element.all(by.css('span')).get(0).getText()).toBe(`Mis à jour à l'instant`); }); - it('should display the ICU select expression', function () { + it('should display the ICU select expression', async () => { const selectIcuExp = element.all(by.css('span')).get(1); - expect(selectIcuExp.getText()).toBe(`L'auteur est une femme`); - element.all(by.css('button')).get(2).click(); - expect(selectIcuExp.getText()).toBe(`L'auteur est un homme`); + expect(await selectIcuExp.getText()).toBe(`L'auteur est une femme`); + await element.all(by.css('button')).get(2).click(); + expect(await selectIcuExp.getText()).toBe(`L'auteur est un homme`); }); - it('should display the nested expression', function() { + it('should display the nested expression', async () => { const nestedExp = element.all(by.css('span')).get(2); const incBtn = element.all(by.css('button')).get(0); - expect(nestedExp.getText()).toBe(`Mis à jour: à l'instant`); - incBtn.click(); - expect(nestedExp.getText()).toBe(`Mis à jour: il y a une minute`); - incBtn.click(); - incBtn.click(); - element.all(by.css('button')).get(4).click(); - expect(nestedExp.getText()).toBe(`Mis à jour: il y a 3 minutes par autre`); + expect(await nestedExp.getText()).toBe(`Mis à jour: à l'instant`); + await incBtn.click(); + expect(await nestedExp.getText()).toBe(`Mis à jour: il y a une minute`); + await incBtn.click(); + await incBtn.click(); + await element.all(by.css('button')).get(4).click(); + expect(await nestedExp.getText()).toBe(`Mis à jour: il y a 3 minutes par autre`); }); }); diff --git a/aio/content/examples/i18n/stackblitz.json b/aio/content/examples/i18n/stackblitz.json index 31759ca104..8326f3902b 100644 --- a/aio/content/examples/i18n/stackblitz.json +++ b/aio/content/examples/i18n/stackblitz.json @@ -3,7 +3,9 @@ "files":[ "!**/*.d.ts", "!**/*.js", - "!**/*.[0-9].*" + "!**/*.[0-9].*", + "!doc-files/**/*", + "**/*.xlf" ], "file": "src/app/app.component.ts", "tags": ["Angular", "i18n", "internationalization"] diff --git a/aio/content/examples/i18n/zipper.json b/aio/content/examples/i18n/zipper.json deleted file mode 100644 index ecd77116fa..0000000000 --- a/aio/content/examples/i18n/zipper.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "files": [ - "!dist/", - "!**/*.d.ts", - "!src/**/*.js", - "!doc-files/**/*", - "**/*.xlf" - ] -} diff --git a/aio/content/examples/inputs-outputs/e2e/src/app.e2e-spec.ts b/aio/content/examples/inputs-outputs/e2e/src/app.e2e-spec.ts index d62786fff5..50816b3260 100644 --- a/aio/content/examples/inputs-outputs/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/inputs-outputs/e2e/src/app.e2e-spec.ts @@ -1,70 +1,59 @@ -'use strict'; +import { browser, element, by, logging } from 'protractor'; -import { browser, element, by } from 'protractor'; -import { logging } from 'selenium-webdriver'; - -describe('Inputs and Outputs', function () { - - - beforeEach(() => { - browser.get(''); - }); +describe('Inputs and Outputs', () => { + beforeEach(() => browser.get('')); // helper function used to test what's logged to the console - async function logChecker(button, contents) { + async function logChecker(contents) { const logs = await browser .manage() .logs() .get(logging.Type.BROWSER); - const message = logs.filter(({ message }) => - message.indexOf(contents) !== -1 ? true : false - ); - console.log(message); - expect(message.length).toBeGreaterThan(0); + const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1); + expect(messages.length).toBeGreaterThan(0); } - it('should have title Inputs and Outputs', function () { - let title = element.all(by.css('h1')).get(0); - expect(title.getText()).toEqual('Inputs and Outputs'); + it('should have title Inputs and Outputs', async () => { + const title = element.all(by.css('h1')).get(0); + expect(await title.getText()).toEqual('Inputs and Outputs'); }); it('should add 123 to the parent list', async () => { - let addToParentButton = element.all(by.css('button')).get(0); - let addToListInput = element.all(by.css('input')).get(0); - let addedItem = element.all(by.css('li')).get(4); + const addToParentButton = element.all(by.css('button')).get(0); + const addToListInput = element.all(by.css('input')).get(0); + const addedItem = element.all(by.css('li')).get(4); await addToListInput.sendKeys('123'); await addToParentButton.click(); - expect(addedItem.getText()).toEqual('123'); + expect(await addedItem.getText()).toEqual('123'); }); it('should delete item', async () => { - let deleteButton = element.all(by.css('button')).get(1); + const deleteButton = element.all(by.css('button')).get(1); const contents = 'Child'; await deleteButton.click(); - await logChecker(deleteButton, contents); + await logChecker(contents); }); it('should log buy the item', async () => { - let buyButton = element.all(by.css('button')).get(2); + const buyButton = element.all(by.css('button')).get(2); const contents = 'Child'; await buyButton.click(); - await logChecker(buyButton, contents); + await logChecker(contents); }); it('should save item for later', async () => { - let saveButton = element.all(by.css('button')).get(3); + const saveButton = element.all(by.css('button')).get(3); const contents = 'Child'; await saveButton.click(); - await logChecker(saveButton, contents); + await logChecker(contents); }); it('should add item to wishlist', async () => { - let addToParentButton = element.all(by.css('button')).get(4); - let addedItem = element.all(by.css('li')).get(6); + const addToParentButton = element.all(by.css('button')).get(4); + const addedItem = element.all(by.css('li')).get(6); await addToParentButton.click(); - expect(addedItem.getText()).toEqual('Television'); + expect(await addedItem.getText()).toEqual('Television'); }); }); - diff --git a/aio/content/examples/inputs-outputs/src/app/aliasing/aliasing.component.ts b/aio/content/examples/inputs-outputs/src/app/aliasing/aliasing.component.ts index 8eed8bddbd..c150bcc214 100644 --- a/aio/content/examples/inputs-outputs/src/app/aliasing/aliasing.component.ts +++ b/aio/content/examples/inputs-outputs/src/app/aliasing/aliasing.component.ts @@ -1,9 +1,4 @@ -/* tslint:disable:use-input-property-decorator */ -/* tslint:disable:use-output-property-decorator */ - -/* tslint:disable:no-input-rename */ - - +// tslint:disable: no-input-rename no-output-rename use-input-property-decorator use-output-property-decorator import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ diff --git a/aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts b/aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts index 475e35c255..d12b2c47f3 100644 --- a/aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/interpolation/e2e/src/app.e2e-spec.ts @@ -2,46 +2,44 @@ import { browser, element, by } from 'protractor'; describe('Interpolation e2e tests', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display Interpolation and Template Expressions', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Interpolation and Template Expressions'); }); - it('should display Interpolation and Template Expressions', function () { - expect(element(by.css('h1')).getText()).toEqual('Interpolation and Template Expressions'); + it('should display Current customer: Maria', async () => { + expect(await element.all(by.css('h3')).get(0).getText()).toBe(`Current customer: Maria`); }); - it('should display Current customer: Maria', function () { - expect(element.all(by.css('h3')).get(0).getText()).toBe(`Current customer: Maria`); + it('should display The sum of 1 + 1 is not 4.', async () => { + expect(await element.all(by.css('p:last-child')).get(0).getText()).toBe(`The sum of 1 + 1 is not 4.`); }); - it('should display The sum of 1 + 1 is not 4.', function () { - expect(element.all(by.css('p:last-child')).get(0).getText()).toBe(`The sum of 1 + 1 is not 4.`); + it('should display Expression Context', async () => { + expect(await element.all(by.css('h2')).get(1).getText()).toBe(`Expression Context`); }); - it('should display Expression Context', function () { - expect(element.all(by.css('h2')).get(1).getText()).toBe(`Expression Context`); + it('should display a list of customers', async () => { + expect(await element.all(by.css('li')).get(0).getText()).toBe(`Maria`); }); - it('should display a list of customers', function () { - expect(element.all(by.css('li')).get(0).getText()).toBe(`Maria`); + it('should display two pictures', async () => { + const pottedPlant = element.all(by.css('img')).get(0); + const lamp = element.all(by.css('img')).get(1); + + expect(await pottedPlant.getAttribute('src')).toContain('potted-plant'); + expect(await pottedPlant.isDisplayed()).toBe(true); + + expect(await lamp.getAttribute('src')).toContain('lamp'); + expect(await lamp.isDisplayed()).toBe(true); }); - it('should display two pictures', function() { - let pottedPlant = element.all(by.css('img')).get(0); - let lamp = element.all(by.css('img')).get(1); - - expect(pottedPlant.getAttribute('src')).toContain('potted-plant'); - expect(pottedPlant.isDisplayed()).toBe(true); - - expect(lamp.getAttribute('src')).toContain('lamp'); - expect(lamp.isDisplayed()).toBe(true); - }); - - it('should support user input', function () { - let input = element(by.css('input')); - let label = element(by.css('label')); - expect(label.getText()).toEqual('Type something:'); - input.sendKeys('abc'); - expect(label.getText()).toEqual('Type something: abc'); + it('should support user input', async () => { + const input = element(by.css('input')); + const label = element(by.css('label')); + expect(await label.getText()).toEqual('Type something:'); + await input.sendKeys('abc'); + expect(await label.getText()).toEqual('Type something: abc'); }); }); diff --git a/aio/content/examples/lazy-loading-ngmodules/e2e/src/app.e2e-spec.ts b/aio/content/examples/lazy-loading-ngmodules/e2e/src/app.e2e-spec.ts index c6ff7f79cf..b7c0eef894 100644 --- a/aio/content/examples/lazy-loading-ngmodules/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/lazy-loading-ngmodules/e2e/src/app.e2e-spec.ts @@ -1,5 +1,5 @@ +import { element, by } from 'protractor'; import { AppPage } from './app.po'; -import { browser, element, by } from 'protractor'; describe('providers App', () => { @@ -7,41 +7,34 @@ describe('providers App', () => { const buttons = element.all(by.css('button')); const customersButton = buttons.get(0); const ordersButton = buttons.get(1); - const homeButton = buttons.get(2); - beforeEach(() => { + beforeEach(async () => { page = new AppPage(); + await page.navigateTo(); }); - it('should display message saying app works', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('Lazy loading feature modules'); + it('should display message saying app works', async () => { + expect(await page.getTitleText()).toEqual('Lazy loading feature modules'); }); - describe('Customers', function() { - beforeEach(function() { - customersButton.click(); - }); + describe('Customers', () => { + beforeEach(() => customersButton.click()); - it('should show customers when the button is clicked', function() { - let customersMessage = element(by.css('app-customers > p')); - expect(customersMessage.getText()).toBe('customers works!'); + it('should show customers when the button is clicked', async () => { + const customersMessage = element(by.css('app-customers > p')); + expect(await customersMessage.getText()).toBe('customers works!'); }); }); - describe('Orders', function() { - beforeEach(function() { - ordersButton.click(); - }); + describe('Orders', () => { + beforeEach(() => ordersButton.click()); - it('should show orders when the button is clicked', function() { - let ordersMessage = element(by.css('app-orders > p')); - expect(ordersMessage.getText()).toBe('orders works!'); + it('should show orders when the button is clicked', async () => { + const ordersMessage = element(by.css('app-orders > p')); + expect(await ordersMessage.getText()).toBe('orders works!'); }); }); }); - - diff --git a/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts b/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts index 7942db105d..45422afe34 100644 --- a/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/lifecycle-hooks/e2e/src/app.e2e-spec.ts @@ -1,70 +1,69 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, ElementFinder, by } from 'protractor'; describe('Lifecycle hooks', () => { - const sendKeys = (el: ElementFinder, input: string) => - input.split('').reduce((prev, c) => prev.then(() => el.sendKeys(c)), Promise.resolve()); + const sendKeys = async (el: ElementFinder, input: string) => { + for (const c of input.split('')) { + await el.sendKeys(c); + } + }; - beforeAll(() => { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('should open correctly', () => { - expect(element.all(by.css('h2')).get(0).getText()).toEqual('Peek-A-Boo'); + it('should open correctly', async () => { + expect(await element.all(by.css('h2')).get(0).getText()).toEqual('Peek-A-Boo'); }); it('should support peek-a-boo', async () => { - let pabComp = element(by.css('peek-a-boo-parent peek-a-boo')); - expect(pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component'); + const pabComp = element(by.css('peek-a-boo-parent peek-a-boo')); + expect(await pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component'); - let pabButton = element.all(by.css('peek-a-boo-parent button')).get(0); - let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1); - expect(pabButton.getText()).toContain('Create Peek'); + const pabButton = element.all(by.css('peek-a-boo-parent button')).get(0); + const updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1); + expect(await pabButton.getText()).toContain('Create Peek'); await pabButton.click(); - expect(pabButton.getText()).toContain('Destroy Peek'); - expect(pabComp.isDisplayed()).toBe(true, 'should be able to see the "peek-a-boo" component'); - expect(pabComp.getText()).toContain('Windstorm'); - expect(pabComp.getText()).not.toContain('Windstorm!'); - expect(updateHeroButton.isPresent()).toBe(true, 'should be able to see the update hero button'); + expect(await pabButton.getText()).toContain('Destroy Peek'); + expect(await pabComp.isDisplayed()).toBe(true, 'should be able to see the "peek-a-boo" component'); + expect(await pabComp.getText()).toContain('Windstorm'); + expect(await pabComp.getText()).not.toContain('Windstorm!'); + expect(await updateHeroButton.isPresent()).toBe(true, 'should be able to see the update hero button'); await updateHeroButton.click(); - expect(pabComp.getText()).toContain('Windstorm!'); + expect(await pabComp.getText()).toContain('Windstorm!'); await pabButton.click(); - expect(pabComp.isPresent()).toBe(false, 'should no longer be able to find the "peek-a-boo" component'); + expect(await pabComp.isPresent()).toBe(false, 'should no longer be able to find the "peek-a-boo" component'); }); - it('should support OnChanges hook', () => { - let onChangesViewEle = element.all(by.css('on-changes div')).get(0); - let inputEles = element.all(by.css('on-changes-parent input')); - let heroNameInputEle = inputEles.get(1); - let powerInputEle = inputEles.get(0); - let titleEle = onChangesViewEle.element(by.css('p')); - let changeLogEles = onChangesViewEle.all(by.css('div')); + it('should support OnChanges hook', async () => { + const onChangesViewEle = element.all(by.css('on-changes div')).get(0); + const inputEles = element.all(by.css('on-changes-parent input')); + const heroNameInputEle = inputEles.get(1); + const powerInputEle = inputEles.get(0); + const titleEle = onChangesViewEle.element(by.css('p')); + const changeLogEles = onChangesViewEle.all(by.css('div')); - expect(titleEle.getText()).toContain('Windstorm can sing'); - expect(changeLogEles.count()).toEqual(2, 'should start with 2 messages'); - heroNameInputEle.sendKeys('-foo-'); - expect(titleEle.getText()).toContain('Windstorm-foo- can sing'); - expect(changeLogEles.count()).toEqual(2, 'should still have 2 messages'); - powerInputEle.sendKeys('-bar-'); - expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); + expect(await titleEle.getText()).toContain('Windstorm can sing'); + expect(await changeLogEles.count()).toEqual(2, 'should start with 2 messages'); + await heroNameInputEle.sendKeys('-foo-'); + expect(await titleEle.getText()).toContain('Windstorm-foo- can sing'); + expect(await changeLogEles.count()).toEqual(2, 'should still have 2 messages'); + await powerInputEle.sendKeys('-bar-'); + expect(await titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); // 7 == 2 previously + length of '-bar-' - expect(changeLogEles.count()).toEqual(7, 'should have 7 messages now'); + expect(await changeLogEles.count()).toEqual(7, 'should have 7 messages now'); }); it('should support DoCheck hook', async () => { - let doCheckViewEle = element.all(by.css('do-check div')).get(0); - let inputEles = element.all(by.css('do-check-parent input')); - let heroNameInputEle = inputEles.get(1); - let powerInputEle = inputEles.get(0); - let titleEle = doCheckViewEle.element(by.css('p')); - let changeLogEles = doCheckViewEle.all(by.css('div')); + const doCheckViewEle = element.all(by.css('do-check div')).get(0); + const inputEles = element.all(by.css('do-check-parent input')); + const heroNameInputEle = inputEles.get(1); + const powerInputEle = inputEles.get(0); + const titleEle = doCheckViewEle.element(by.css('p')); + const changeLogEles = doCheckViewEle.all(by.css('div')); let logCount: number; - expect(titleEle.getText()).toContain('Windstorm can sing'); + expect(await titleEle.getText()).toContain('Windstorm can sing'); let count = await changeLogEles.count(); // 3 messages to start @@ -73,102 +72,102 @@ describe('Lifecycle hooks', () => { logCount = count; await sendKeys(heroNameInputEle, '-foo-'); count = await changeLogEles.count(); - expect(titleEle.getText()).toContain('Windstorm-foo- can sing'); + expect(await titleEle.getText()).toContain('Windstorm-foo- can sing'); expect(count).toBeGreaterThanOrEqual(logCount + 5, 'should add at least one more message for each keystroke'); logCount = count; await sendKeys(powerInputEle, '-bar-'); count = await changeLogEles.count(); - expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); + expect(await titleEle.getText()).toContain('Windstorm-foo- can sing-bar-'); expect(count).toBeGreaterThanOrEqual(logCount + 5, 'should add at least one more message for each keystroke'); }); it('should support AfterView hooks', async () => { - let parentEle = element(by.tagName('after-view-parent')); - let buttonEle = parentEle.element(by.tagName('button')); // Reset - let commentEle = parentEle.element(by.className('comment')); - let logEles = parentEle.all(by.css('h4 ~ div')); - let childViewInputEle = parentEle.element(by.css('app-child-view input')); + const parentEle = element(by.tagName('after-view-parent')); + const buttonEle = parentEle.element(by.tagName('button')); // Reset + const commentEle = parentEle.element(by.className('comment')); + const logEles = parentEle.all(by.css('h4 ~ div')); + const childViewInputEle = parentEle.element(by.css('app-child-view input')); let logCount: number; - expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); - expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); + expect(await childViewInputEle.getAttribute('value')).toContain('Magneta'); + expect(await commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); logCount = await logEles.count(); await childViewInputEle.sendKeys('-test-'); - expect(childViewInputEle.getAttribute('value')).toContain('-test-'); - expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); - expect(commentEle.getText()).toContain('long name'); + expect(await childViewInputEle.getAttribute('value')).toContain('-test-'); + expect(await commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); + expect(await commentEle.getText()).toContain('long name'); - let count = await logEles.count(); + const count = await logEles.count(); expect(logCount + 7).toBeGreaterThan(count - 3, '7 additional log messages should have been added'); expect(logCount + 7).toBeLessThan(count + 3, '7 additional log messages should have been added'); logCount = count; await buttonEle.click(); - expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); + expect(await logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); }); it('should support AfterContent hooks', async () => { - let parentEle = element(by.tagName('after-content-parent')); - let buttonEle = parentEle.element(by.tagName('button')); // Reset - let commentEle = parentEle.element(by.className('comment')); - let logEles = parentEle.all(by.css('h4 ~ div')); - let childViewInputEle = parentEle.element(by.css('app-child input')); + const parentEle = element(by.tagName('after-content-parent')); + const buttonEle = parentEle.element(by.tagName('button')); // Reset + const commentEle = parentEle.element(by.className('comment')); + const logEles = parentEle.all(by.css('h4 ~ div')); + const childViewInputEle = parentEle.element(by.css('app-child input')); let logCount = await logEles.count(); - expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); - expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); + expect(await childViewInputEle.getAttribute('value')).toContain('Magneta'); + expect(await commentEle.isPresent()).toBe(false, 'comment should not be in DOM'); await sendKeys(childViewInputEle, '-test-'); - let count = await logEles.count(); - expect(childViewInputEle.getAttribute('value')).toContain('-test-'); - expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); - expect(commentEle.getText()).toContain('long name'); + const count = await logEles.count(); + expect(await childViewInputEle.getAttribute('value')).toContain('-test-'); + expect(await commentEle.isPresent()).toBe(true, 'should have comment because >10 chars'); + expect(await commentEle.getText()).toContain('long name'); expect(count).toBeGreaterThanOrEqual(logCount + 5, 'additional log messages should have been added'); logCount = count; await buttonEle.click(); - expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); + expect(await logEles.count()).toBeLessThan(logCount, 'log should shrink after reset'); }); it('should support spy\'s OnInit & OnDestroy hooks', async () => { - let inputEle = element(by.css('spy-parent input')); - let addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero')); - let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes')); - let heroEles = element.all(by.css('spy-parent div[mySpy')); - let logEles = element.all(by.css('spy-parent h4 ~ div')); + const inputEle = element(by.css('spy-parent input')); + const addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero')); + const resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes')); + const heroEles = element.all(by.css('spy-parent div[appSpy')); + const logEles = element.all(by.css('spy-parent h4 ~ div')); - expect(heroEles.count()).toBe(2, 'should have two heroes displayed'); - expect(logEles.count()).toBe(2, 'should have two log entries'); + expect(await heroEles.count()).toBe(2, 'should have two heroes displayed'); + expect(await logEles.count()).toBe(2, 'should have two log entries'); await inputEle.sendKeys('-test-'); await addHeroButtonEle.click(); - expect(heroEles.count()).toBe(3, 'should have added one hero'); - expect(heroEles.get(2).getText()).toContain('-test-'); - expect(logEles.count()).toBe(3, 'should now have 3 log entries'); + expect(await heroEles.count()).toBe(3, 'should have added one hero'); + expect(await heroEles.get(2).getText()).toContain('-test-'); + expect(await logEles.count()).toBe(3, 'should now have 3 log entries'); await resetHeroesButtonEle.click(); - expect(heroEles.count()).toBe(0, 'should no longer have any heroes'); - expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 orig + 1 reset + 3 removeall'); + expect(await heroEles.count()).toBe(0, 'should no longer have any heroes'); + expect(await logEles.count()).toBe(7, 'should now have 7 log entries - 3 orig + 1 reset + 3 removeall'); }); it('should support "spy counter"', async () => { - let updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update')); - let resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset')); - let textEle = element(by.css('counter-parent app-counter > div')); - let logEles = element.all(by.css('counter-parent h4 ~ div')); + const updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update')); + const resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset')); + const textEle = element(by.css('counter-parent app-counter > div')); + const logEles = element.all(by.css('counter-parent h4 ~ div')); - expect(textEle.getText()).toContain('Counter = 0'); - expect(logEles.count()).toBe(2, 'should start with two log entries'); + expect(await textEle.getText()).toContain('Counter = 0'); + expect(await logEles.count()).toBe(2, 'should start with two log entries'); await updateCounterButtonEle.click(); - expect(textEle.getText()).toContain('Counter = 1'); - expect(logEles.count()).toBe(3, 'should now have 3 log entries'); + expect(await textEle.getText()).toContain('Counter = 1'); + expect(await logEles.count()).toBe(3, 'should now have 3 log entries'); await resetCounterButtonEle.click(); - expect(textEle.getText()).toContain('Counter = 0'); - expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init'); + expect(await textEle.getText()).toContain('Counter = 0'); + expect(await logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init'); }); }); diff --git a/aio/content/examples/lifecycle-hooks/src/app/after-content.component.ts b/aio/content/examples/lifecycle-hooks/src/app/after-content.component.ts index 36d2cff20f..1de8a84612 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/after-content.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/after-content.component.ts @@ -2,7 +2,7 @@ // #docregion import { AfterContentChecked, AfterContentInit, Component, ContentChild } from '@angular/core'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; ////////////////// @Component({ @@ -67,8 +67,8 @@ export class AfterContentComponent implements AfterContentChecked, AfterContentI } private logIt(method: string) { - let child = this.contentChild; - let message = `${method}: ${child ? child.hero : 'no'} child content`; + const child = this.contentChild; + const message = `${method}: ${child ? child.hero : 'no'} child content`; this.logger.log(message); } // #docregion hooks diff --git a/aio/content/examples/lifecycle-hooks/src/app/after-view.component.ts b/aio/content/examples/lifecycle-hooks/src/app/after-view.component.ts index fd09962406..534d0b4090 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/after-view.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/after-view.component.ts @@ -2,7 +2,7 @@ // #docregion import { AfterViewChecked, AfterViewInit, Component, ViewChild } from '@angular/core'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; ////////////////// // #docregion child-view @@ -32,17 +32,20 @@ export class ChildViewComponent { }) // #docregion hooks export class AfterViewComponent implements AfterViewChecked, AfterViewInit { + // #enddocregion hooks + comment = ''; + // #docregion hooks private prevHero = ''; // Query for a VIEW child of type `ChildViewComponent` @ViewChild(ChildViewComponent) viewChild: ChildViewComponent; -// #enddocregion hooks + // #enddocregion hooks constructor(private logger: LoggerService) { this.logIt('AfterView constructor'); } -// #docregion hooks + // #docregion hooks ngAfterViewInit() { // viewChild is set after the view has been initialized this.logIt('AfterViewInit'); @@ -59,27 +62,25 @@ export class AfterViewComponent implements AfterViewChecked, AfterViewInit { this.doSomething(); } } -// #enddocregion hooks + // #enddocregion hooks - comment = ''; - -// #docregion do-something + // #docregion do-something // This surrogate for real business logic sets the `comment` private doSomething() { - let c = this.viewChild.hero.length > 10 ? `That's a long name` : ''; + const c = this.viewChild.hero.length > 10 ? `That's a long name` : ''; if (c !== this.comment) { // Wait a tick because the component's view has already been checked this.logger.tick_then(() => this.comment = c); } } -// #enddocregion do-something + // #enddocregion do-something private logIt(method: string) { - let child = this.viewChild; - let message = `${method}: ${child ? child.hero : 'no'} child view`; + const child = this.viewChild; + const message = `${method}: ${child ? child.hero : 'no'} child view`; this.logger.log(message); } -// #docregion hooks + // #docregion hooks // ... } // #enddocregion hooks diff --git a/aio/content/examples/lifecycle-hooks/src/app/counter.component.ts b/aio/content/examples/lifecycle-hooks/src/app/counter.component.ts index 4d5198b696..a2d7b5fd9b 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/counter.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/counter.component.ts @@ -4,7 +4,7 @@ import { OnChanges, SimpleChanges, } from '@angular/core'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; @Component({ selector: 'app-counter', @@ -13,7 +13,7 @@ import { LoggerService } from './logger.service'; Counter = {{counter}}
-- Counter Change Log --
-
{{chg}}
+
{{chg}}
`, styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'] @@ -31,9 +31,9 @@ export class MyCounterComponent implements OnChanges { } // A change to `counter` is the only change we care about - let chng = changes['counter']; - let cur = chng.currentValue; - let prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer + const chng = changes.counter; + const cur = chng.currentValue; + const prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`); } } diff --git a/aio/content/examples/lifecycle-hooks/src/app/do-check.component.ts b/aio/content/examples/lifecycle-hooks/src/app/do-check.component.ts index 4b8a088b9a..82a830dd98 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/do-check.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/do-check.component.ts @@ -51,8 +51,8 @@ export class DoCheckComponent implements DoCheck { this.noChangeCount = 0; } else { // log that hook was called when there was no relevant change. - let count = this.noChangeCount += 1; - let noChangeMsg = `DoCheck called ${count}x when no change to hero or power`; + const count = this.noChangeCount += 1; + const noChangeMsg = `DoCheck called ${count}x when no change to hero or power`; if (count === 1) { // add new "no change" message this.changeLog.push(noChangeMsg); diff --git a/aio/content/examples/lifecycle-hooks/src/app/on-changes.component.ts b/aio/content/examples/lifecycle-hooks/src/app/on-changes.component.ts index 7b9cda05ef..b6b8ba2f9e 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/on-changes.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/on-changes.component.ts @@ -34,10 +34,10 @@ export class OnChangesComponent implements OnChanges { // #docregion ng-on-changes ngOnChanges(changes: SimpleChanges) { - for (let propName in changes) { - let chng = changes[propName]; - let cur = JSON.stringify(chng.currentValue); - let prev = JSON.stringify(chng.previousValue); + for (const propName in changes) { + const chng = changes[propName]; + const cur = JSON.stringify(chng.currentValue); + const prev = JSON.stringify(chng.previousValue); this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`); } } diff --git a/aio/content/examples/lifecycle-hooks/src/app/peek-a-boo.component.ts b/aio/content/examples/lifecycle-hooks/src/app/peek-a-boo.component.ts index 2539b7f763..c66e41a6c9 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/peek-a-boo.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/peek-a-boo.component.ts @@ -1,3 +1,5 @@ +// tslint:disable: no-conflicting-lifecycle +// #docregion import { AfterContentChecked, AfterContentInit, @@ -11,7 +13,7 @@ import { SimpleChanges } from '@angular/core'; import { Component, Input } from '@angular/core'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; let nextId = 1; @@ -48,16 +50,16 @@ export class PeekABooComponent extends PeekABooDirective implements constructor(logger: LoggerService) { super(logger); - let is = this.name ? 'is' : 'is not'; + const is = this.name ? 'is' : 'is not'; this.logIt(`name ${is} known at construction`); } // only called for/if there is an @input variable set by parent. ngOnChanges(changes: SimpleChanges) { - let changesMsgs: string[] = []; - for (let propName in changes) { + const changesMsgs: string[] = []; + for (const propName in changes) { if (propName === 'name') { - let name = changes['name'].currentValue; + const name = changes.name.currentValue; changesMsgs.push(`name ${this.verb} to "${name}"`); } else { changesMsgs.push(propName + ' ' + this.verb); diff --git a/aio/content/examples/lifecycle-hooks/src/app/spy.component.html b/aio/content/examples/lifecycle-hooks/src/app/spy.component.html index f7deae9448..96ca78194a 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/spy.component.html +++ b/aio/content/examples/lifecycle-hooks/src/app/spy.component.html @@ -7,7 +7,7 @@

-
+
{{hero}}
diff --git a/aio/content/examples/lifecycle-hooks/src/app/spy.component.ts b/aio/content/examples/lifecycle-hooks/src/app/spy.component.ts index 08fc0ee8a0..852788ffec 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/spy.component.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/spy.component.ts @@ -1,7 +1,7 @@ // #docregion import { Component } from '@angular/core'; -import { LoggerService } from './logger.service'; +import { LoggerService } from './logger.service'; @Component({ selector: 'spy-parent', diff --git a/aio/content/examples/lifecycle-hooks/src/app/spy.directive.ts b/aio/content/examples/lifecycle-hooks/src/app/spy.directive.ts index 01f3f95880..c14f4e6455 100644 --- a/aio/content/examples/lifecycle-hooks/src/app/spy.directive.ts +++ b/aio/content/examples/lifecycle-hooks/src/app/spy.directive.ts @@ -7,8 +7,8 @@ let nextId = 1; // #docregion spy-directive // Spy on any element to which it is applied. -// Usage:
...
-@Directive({selector: '[mySpy]'}) +// Usage:
...
+@Directive({selector: '[appSpy]'}) export class SpyDirective implements OnInit, OnDestroy { constructor(private logger: LoggerService) { } diff --git a/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts b/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts index c1d63cf14c..9e7613caf3 100644 --- a/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/ngmodules/e2e/src/app.e2e-spec.ts @@ -1,8 +1,6 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('NgModule-example', function () { +describe('NgModule-example', () => { // helpers const lightgray = 'rgba(239, 238, 237, 1)'; @@ -57,117 +55,110 @@ describe('NgModule-example', function () { // tests function appTitleTests(color: string, name?: string) { - return function() { - it('should have a gray header', function() { + return () => { + it('should have a gray header', async () => { const commons = getCommonsSectionStruct(); - expect(commons.title.getCssValue('backgroundColor')).toBe(color); + expect(await commons.title.getCssValue('backgroundColor')).toBe(color); }); - it('should welcome us', function () { + it('should welcome us', async () => { const commons = getCommonsSectionStruct(); - expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple')); + expect(await commons.subtitle.getText()).toBe(`Welcome, ${name || 'Miss Marple'}`); }); }; } function contactTests(color: string, name?: string) { - return function() { - it('shows the contact\'s owner', function() { + return () => { + it('shows the contact\'s owner', async () => { const contacts = getContactSectionStruct(); - expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts'); + expect(await contacts.header.getText()).toBe(`${name || 'Miss Marple'}'s Contacts`); }); - it('can cycle between contacts', function () { + it('can cycle between contacts', async () => { const contacts = getContactSectionStruct(); const nextButton = contacts.nextContactButton; - expect(contacts.contactNameHeader.getText()).toBe('Awesome Yasha'); - expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color); - nextButton.click().then(function () { - expect(contacts.contactNameHeader.getText()).toBe('Awesome Iulia'); - return nextButton.click(); - }).then(function () { - expect(contacts.contactNameHeader.getText()).toBe('Awesome Karina'); - }); + expect(await contacts.contactNameHeader.getText()).toBe('Awesome Yasha'); + expect(await contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color); + + await nextButton.click(); + expect(await contacts.contactNameHeader.getText()).toBe('Awesome Iulia'); + + await nextButton.click(); + expect(await contacts.contactNameHeader.getText()).toBe('Awesome Karina'); }); - it('can create a new contact', function () { + it('can create a new contact', async () => { const contacts = getContactSectionStruct(); const newContactButton = contacts.newContactButton; const nextButton = contacts.nextContactButton; const input = contacts.input; const saveButton = contacts.saveButton; - newContactButton.click().then(function () { - input.click(); - nextButton.click() - expect(contacts.validationError.getText()).toBe('Name is required.'); - input.click(); - contacts.input.sendKeys('Watson'); - saveButton.click() - expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson'); + await newContactButton.click(); + await input.click(); + await nextButton.click(); + expect(await contacts.validationError.getText()).toBe('Name is required.'); - }); + await input.click(); + await contacts.input.sendKeys('Watson'); + await saveButton.click(); + expect(await contacts.contactNameHeader.getText()).toBe('Awesome Watson'); }); }; } - describe('index.html', function () { - beforeEach(function () { - browser.get(''); - }); + describe('index.html', () => { + beforeEach(() => browser.get('')); describe('app-title', appTitleTests(white, 'Miss Marple')); describe('contact', contactTests(lightgray, 'Miss Marple')); - describe('item center', function () { - beforeEach(function () { - getCommonsSectionStruct().itemButton.click(); + describe('item center', () => { + beforeEach(() => getCommonsSectionStruct().itemButton.click()); + + it('shows a list of items', async () => { + const item = getItemSectionStruct(); + expect(await item.title.getText()).toBe('Items List'); + expect(await item.items.count()).toBe(4); + expect(await item.items.get(0).getText()).toBe('1 - Sticky notes'); }); - it('shows a list of items', function () { + it('can navigate to one item details', async () => { const item = getItemSectionStruct(); - expect(item.title.getText()).toBe('Items List'); - expect(item.items.count()).toBe(4); - expect(item.items.get(0).getText()).toBe('1 - Sticky notes'); - }); - it('can navigate to one item details', function () { - const item = getItemSectionStruct(); - item.items.get(0).click().then(function() { - expect(item.itemId.getText()).toBe('Item id: 1'); - return item.listLink.click(); - }).then(function () { - // We are back to the list - expect(item.items.count()).toBe(4); - }); + await item.items.get(0).click(); + expect(await item.itemId.getText()).toBe('Item id: 1'); + + await item.listLink.click(); + // We are back to the list + expect(await item.items.count()).toBe(4); }); }); - describe('customers', function () { - beforeEach(function () { - getCommonsSectionStruct().customersButton.click(); + describe('customers', () => { + beforeEach(() => getCommonsSectionStruct().customersButton.click()); + + it('shows a list of customers', async () => { + const customers = getCustomersSectionStruct(); + expect(await customers.header.getText()).toBe('Customers of Miss Marple times 2'); + expect(await customers.title.getText()).toBe('Customer List'); + expect(await customers.items.count()).toBe(6); + expect(await customers.items.get(0).getText()).toBe('11 - Julian'); }); - it('shows a list of customers', function() { + it('can navigate and edit one customer details', async () => { const customers = getCustomersSectionStruct(); - expect(customers.header.getText()).toBe('Customers of Miss Marple times 2'); - expect(customers.title.getText()).toBe('Customer List'); - expect(customers.items.count()).toBe(6); - expect(customers.items.get(0).getText()).toBe('11 - Julian'); - }); - it('can navigate and edit one customer details', function () { - const customers = getCustomersSectionStruct(); - customers.items.get(0).click().then(function () { - expect(customers.itemId.getText()).toBe('Id: 11'); - customers.itemInput.sendKeys(' try'); - return customers.listLink.click(); - }).then(function () { - // We are back to the list - expect(customers.items.count()).toBe(6); - expect(customers.items.get(0).getText()).toBe('11 - Julian try'); - }); + await customers.items.get(0).click(); + expect(await customers.itemId.getText()).toBe('Id: 11'); + + await customers.itemInput.sendKeys(' try'); + await customers.listLink.click(); + // We are back to the list + expect(await customers.items.count()).toBe(6); + expect(await customers.items.get(0).getText()).toBe('11 - Julian try'); }); }); }); diff --git a/aio/content/examples/ngmodules/src/app/app-routing.module.ts b/aio/content/examples/ngmodules/src/app/app-routing.module.ts index a6336eadce..f915627de8 100644 --- a/aio/content/examples/ngmodules/src/app/app-routing.module.ts +++ b/aio/content/examples/ngmodules/src/app/app-routing.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; export const routes: Routes = [ diff --git a/aio/content/examples/ngmodules/src/app/contact/contact-routing.module.ts b/aio/content/examples/ngmodules/src/app/contact/contact-routing.module.ts index 1bd2521873..a3c1119462 100644 --- a/aio/content/examples/ngmodules/src/app/contact/contact-routing.module.ts +++ b/aio/content/examples/ngmodules/src/app/contact/contact-routing.module.ts @@ -1,7 +1,7 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; -import { ContactComponent } from './contact.component'; +import { ContactComponent } from './contact.component'; const routes = [ { path: 'contact', component: ContactComponent} diff --git a/aio/content/examples/ngmodules/src/app/contact/contact.component.ts b/aio/content/examples/ngmodules/src/app/contact/contact.component.ts index e7dd2107b9..9cea3c8829 100644 --- a/aio/content/examples/ngmodules/src/app/contact/contact.component.ts +++ b/aio/content/examples/ngmodules/src/app/contact/contact.component.ts @@ -1,9 +1,9 @@ // Exact copy except import UserService from greeting -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; import { Contact, ContactService } from './contact.service'; -import { UserService } from '../greeting/user.service'; +import { UserService } from '../greeting/user.service'; @Component({ selector: 'app-contact', @@ -11,7 +11,7 @@ import { UserService } from '../greeting/user.service'; styleUrls: [ './contact.component.css' ] }) export class ContactComponent implements OnInit { - contact: Contact; + contact: Contact; contacts: Contact[]; msg = 'Loading contacts ...'; @@ -46,7 +46,7 @@ export class ContactComponent implements OnInit { } onSubmit() { - let newName = this.contactForm.get('name').value; + const newName = this.contactForm.get('name').value; this.displayMessage('Saved ' + newName); this.contact.name = newName; } diff --git a/aio/content/examples/ngmodules/src/app/contact/contact.module.ts b/aio/content/examples/ngmodules/src/app/contact/contact.module.ts index fe40245721..78244428b2 100644 --- a/aio/content/examples/ngmodules/src/app/contact/contact.module.ts +++ b/aio/content/examples/ngmodules/src/app/contact/contact.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from '../shared/shared.module'; +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; import { ReactiveFormsModule } from '@angular/forms'; -import { ContactComponent } from './contact.component'; -import { ContactService } from './contact.service'; +import { ContactComponent } from './contact.component'; +import { ContactService } from './contact.service'; import { ContactRoutingModule } from './contact-routing.module'; @NgModule({ diff --git a/aio/content/examples/ngmodules/src/app/contact/contact.service.ts b/aio/content/examples/ngmodules/src/app/contact/contact.service.ts index a140538ad9..f3326bcbdd 100644 --- a/aio/content/examples/ngmodules/src/app/contact/contact.service.ts +++ b/aio/content/examples/ngmodules/src/app/contact/contact.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; +import { delay } from 'rxjs/operators'; export class Contact { constructor(public id: number, public name: string) { } diff --git a/aio/content/examples/ngmodules/src/app/customers/customers-detail.component.ts b/aio/content/examples/ngmodules/src/app/customers/customers-detail.component.ts index 11683c9d68..7d0818f242 100644 --- a/aio/content/examples/ngmodules/src/app/customers/customers-detail.component.ts +++ b/aio/content/examples/ngmodules/src/app/customers/customers-detail.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { Customer, - CustomersService } from './customers.service'; + CustomersService } from './customers.service'; @Component({ template: ` @@ -25,7 +25,7 @@ export class CustomersDetailComponent implements OnInit { private customersService: CustomersService) { } ngOnInit() { - let id = parseInt(this.route.snapshot.paramMap.get('id'), 10); + const id = parseInt(this.route.snapshot.paramMap.get('id'), 10); this.customersService.getCustomer(id).subscribe(customer => this.customer = customer); } } diff --git a/aio/content/examples/ngmodules/src/app/customers/customers-routing.module.ts b/aio/content/examples/ngmodules/src/app/customers/customers-routing.module.ts index 3d268d46b6..035bac5f1a 100644 --- a/aio/content/examples/ngmodules/src/app/customers/customers-routing.module.ts +++ b/aio/content/examples/ngmodules/src/app/customers/customers-routing.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; -import { CustomersComponent } from './customers.component'; -import { CustomersListComponent } from './customers-list.component'; +import { CustomersComponent } from './customers.component'; +import { CustomersListComponent } from './customers-list.component'; import { CustomersDetailComponent } from './customers-detail.component'; const routes: Routes = [ diff --git a/aio/content/examples/ngmodules/src/app/customers/customers.component.ts b/aio/content/examples/ngmodules/src/app/customers/customers.component.ts index 18b6b03666..a0087247d2 100644 --- a/aio/content/examples/ngmodules/src/app/customers/customers.component.ts +++ b/aio/content/examples/ngmodules/src/app/customers/customers.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; import { CustomersService } from './customers.service'; import { UserService } from '../greeting/user.service'; diff --git a/aio/content/examples/ngmodules/src/app/customers/customers.module.ts b/aio/content/examples/ngmodules/src/app/customers/customers.module.ts index 3249b16ac7..baf7467881 100644 --- a/aio/content/examples/ngmodules/src/app/customers/customers.module.ts +++ b/aio/content/examples/ngmodules/src/app/customers/customers.module.ts @@ -1,12 +1,12 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '../shared/shared.module'; -import { CustomersComponent } from './customers.component'; +import { CustomersComponent } from './customers.component'; import { CustomersDetailComponent } from './customers-detail.component'; -import { CustomersListComponent } from './customers-list.component'; -import { CustomersRoutingModule } from './customers-routing.module'; -import { CustomersService } from './customers.service'; +import { CustomersListComponent } from './customers-list.component'; +import { CustomersRoutingModule } from './customers-routing.module'; +import { CustomersService } from './customers.service'; @NgModule({ diff --git a/aio/content/examples/ngmodules/src/app/customers/customers.service.ts b/aio/content/examples/ngmodules/src/app/customers/customers.service.ts index 0e4d50960b..174181ceb4 100644 --- a/aio/content/examples/ngmodules/src/app/customers/customers.service.ts +++ b/aio/content/examples/ngmodules/src/app/customers/customers.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; +import { delay } from 'rxjs/operators'; export class Customer { constructor(public id: number, public name: string) { } diff --git a/aio/content/examples/ngmodules/src/app/greeting/greeting.component.ts b/aio/content/examples/ngmodules/src/app/greeting/greeting.component.ts index d08e160665..6c03c4e55e 100644 --- a/aio/content/examples/ngmodules/src/app/greeting/greeting.component.ts +++ b/aio/content/examples/ngmodules/src/app/greeting/greeting.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { UserService } from '../greeting/user.service'; +import { UserService } from '../greeting/user.service'; @Component({ selector: 'app-greeting', diff --git a/aio/content/examples/ngmodules/src/app/greeting/greeting.module.ts b/aio/content/examples/ngmodules/src/app/greeting/greeting.module.ts index 92f39bfb60..dbd314684d 100644 --- a/aio/content/examples/ngmodules/src/app/greeting/greeting.module.ts +++ b/aio/content/examples/ngmodules/src/app/greeting/greeting.module.ts @@ -14,7 +14,7 @@ import { UserServiceConfig } from './user.service'; }) export class GreetingModule { // #docregion ctor - constructor (@Optional() @SkipSelf() parentModule?: GreetingModule) { + constructor(@Optional() @SkipSelf() parentModule?: GreetingModule) { if (parentModule) { throw new Error( 'GreetingModule is already loaded. Import it in the AppModule only'); diff --git a/aio/content/examples/ngmodules/src/app/greeting/user.service.ts b/aio/content/examples/ngmodules/src/app/greeting/user.service.ts index 3908e85a3c..d084d1b099 100644 --- a/aio/content/examples/ngmodules/src/app/greeting/user.service.ts +++ b/aio/content/examples/ngmodules/src/app/greeting/user.service.ts @@ -12,7 +12,6 @@ export class UserServiceConfig { }) export class UserService { id = nextId++; - private _userName = 'Sherlock Holmes'; // #docregion ctor constructor(@Optional() config?: UserServiceConfig) { @@ -25,4 +24,5 @@ export class UserService { const suffix = this.id > 1 ? ` times ${this.id}` : ''; return this._userName + suffix; } + private _userName = 'Sherlock Holmes'; // tslint:disable-line: variable-name } diff --git a/aio/content/examples/ngmodules/src/app/items/items-detail.component.ts b/aio/content/examples/ngmodules/src/app/items/items-detail.component.ts index a4580ad234..c88a997205 100644 --- a/aio/content/examples/ngmodules/src/app/items/items-detail.component.ts +++ b/aio/content/examples/ngmodules/src/app/items/items-detail.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { Item, - ItemService } from './items.service'; + ItemService } from './items.service'; @Component({ template: ` diff --git a/aio/content/examples/ngmodules/src/app/items/items-list.component.ts b/aio/content/examples/ngmodules/src/app/items/items-list.component.ts index c4859c93cb..a60564a76c 100644 --- a/aio/content/examples/ngmodules/src/app/items/items-list.component.ts +++ b/aio/content/examples/ngmodules/src/app/items/items-list.component.ts @@ -1,8 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { Observable }from 'rxjs'; +import { Observable } from 'rxjs'; -import { Item, - ItemService } from './items.service'; +import { Item, ItemService } from './items.service'; @Component({ template: ` diff --git a/aio/content/examples/ngmodules/src/app/items/items-routing.module.ts b/aio/content/examples/ngmodules/src/app/items/items-routing.module.ts index 55f0947a23..c2c551c755 100644 --- a/aio/content/examples/ngmodules/src/app/items/items-routing.module.ts +++ b/aio/content/examples/ngmodules/src/app/items/items-routing.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { Routes, - RouterModule } from '@angular/router'; + RouterModule } from '@angular/router'; -import { ItemsListComponent } from './items-list.component'; -import { ItemsDetailComponent } from './items-detail.component'; +import { ItemsListComponent } from './items-list.component'; +import { ItemsDetailComponent } from './items-detail.component'; const routes: Routes = [ { path: '', redirectTo: 'list', pathMatch: 'full'}, diff --git a/aio/content/examples/ngmodules/src/app/items/items.module.ts b/aio/content/examples/ngmodules/src/app/items/items.module.ts index bc749b582a..9b22b48917 100644 --- a/aio/content/examples/ngmodules/src/app/items/items.module.ts +++ b/aio/content/examples/ngmodules/src/app/items/items.module.ts @@ -1,11 +1,11 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; import { ItemsComponent } from './items.component'; -import { ItemsListComponent } from './items-list.component'; -import { ItemsDetailComponent } from './items-detail.component'; -import { ItemService } from './items.service'; -import { ItemsRoutingModule } from './items-routing.module'; +import { ItemsListComponent } from './items-list.component'; +import { ItemsDetailComponent } from './items-detail.component'; +import { ItemService } from './items.service'; +import { ItemsRoutingModule } from './items-routing.module'; @NgModule({ imports: [ CommonModule, ItemsRoutingModule ], diff --git a/aio/content/examples/ngmodules/src/app/items/items.service.ts b/aio/content/examples/ngmodules/src/app/items/items.service.ts index c764ad859e..e148a475b1 100644 --- a/aio/content/examples/ngmodules/src/app/items/items.service.ts +++ b/aio/content/examples/ngmodules/src/app/items/items.service.ts @@ -1,7 +1,7 @@ import { Injectable, OnDestroy } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { delay } from 'rxjs/operators'; +import { delay } from 'rxjs/operators'; export class Item { constructor(public id: number, public name: string) { } diff --git a/aio/content/examples/ngmodules/src/app/shared/shared.module.ts b/aio/content/examples/ngmodules/src/app/shared/shared.module.ts index 477f170c12..95615fba26 100644 --- a/aio/content/examples/ngmodules/src/app/shared/shared.module.ts +++ b/aio/content/examples/ngmodules/src/app/shared/shared.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; -import { AwesomePipe } from './awesome.pipe'; -import { HighlightDirective } from './highlight.directive'; +import { AwesomePipe } from './awesome.pipe'; +import { HighlightDirective } from './highlight.directive'; @NgModule({ imports: [ CommonModule ], diff --git a/aio/content/examples/observables-in-angular/src/main.ts b/aio/content/examples/observables-in-angular/src/main.ts index 16788a2f0e..3c846a598f 100644 --- a/aio/content/examples/observables-in-angular/src/main.ts +++ b/aio/content/examples/observables-in-angular/src/main.ts @@ -1,19 +1,22 @@ - +// TODO: Add unit tests for this file. +// tslint:disable: no-output-native +// #docregion import { Component, Output, OnInit, EventEmitter, NgModule } from '@angular/core'; import { Observable } from 'rxjs'; // #docregion eventemitter @Component({ - selector: 'zippy', + selector: 'app-zippy', template: ` -
-
Toggle
-
- +
+
Toggle
+
+ +
-
`}) - + `, +}) export class ZippyComponent { visible = true; @Output() open = new EventEmitter(); diff --git a/aio/content/examples/observables/example-config.json b/aio/content/examples/observables/example-config.json index c07fa9794c..3aa2059b4d 100644 --- a/aio/content/examples/observables/example-config.json +++ b/aio/content/examples/observables/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/observables/src/creating.spec.ts b/aio/content/examples/observables/src/creating.spec.ts new file mode 100644 index 0000000000..e1718a9585 --- /dev/null +++ b/aio/content/examples/observables/src/creating.spec.ts @@ -0,0 +1,55 @@ +import { docRegionFromEvent, docRegionSubscriber } from './creating'; + +describe('observables', () => { + it('should create an observable using the constructor', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionSubscriber(console); + expect(console.log).toHaveBeenCalledTimes(4); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [2], + [3], + ['Finished sequence'], + ]); + }); + + it('should listen to input changes', () => { + let triggerInputChange; + const input = { + value: 'Test', + addEventListener: jasmine + .createSpy('addEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'keydown') { + triggerInputChange = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEventListener'), + }; + + const document = { getElementById: () => input }; + docRegionFromEvent(document); + triggerInputChange({keyCode: 65}); + expect(input.value).toBe('Test'); + + triggerInputChange({keyCode: 27}); + expect(input.value).toBe(''); + }); + + it('should call removeEventListener when unsubscribing', (doneFn: DoneFn) => { + const input = { + addEventListener: jasmine.createSpy('addEvent'), + removeEventListener: jasmine + .createSpy('removeEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'keydown') { + doneFn(); + } + }) + }; + + const document = { getElementById: () => input }; + const subscription = docRegionFromEvent(document); + subscription.unsubscribe(); + }); +}); diff --git a/aio/content/examples/observables/src/creating.ts b/aio/content/examples/observables/src/creating.ts index 83003b99bf..7a673228cc 100644 --- a/aio/content/examples/observables/src/creating.ts +++ b/aio/content/examples/observables/src/creating.ts @@ -1,38 +1,39 @@ +// #docplaster import { Observable } from 'rxjs'; -// #docregion subscriber +export function docRegionSubscriber(console) { + // #docregion subscriber + // This function runs when subscribe() is called + function sequenceSubscriber(observer) { + // synchronously deliver 1, 2, and 3, then complete + observer.next(1); + observer.next(2); + observer.next(3); + observer.complete(); -// This function runs when subscribe() is called -function sequenceSubscriber(observer) { - // synchronously deliver 1, 2, and 3, then complete - observer.next(1); - observer.next(2); - observer.next(3); - observer.complete(); + // unsubscribe function doesn't need to do anything in this + // because values are delivered synchronously + return {unsubscribe() {}}; + } - // unsubscribe function doesn't need to do anything in this - // because values are delivered synchronously - return {unsubscribe() {}}; + // Create a new Observable that will deliver the above sequence + const sequence = new Observable(sequenceSubscriber); + + // execute the Observable and print the result of each notification + sequence.subscribe({ + next(num) { console.log(num); }, + complete() { console.log('Finished sequence'); } + }); + + // Logs: + // 1 + // 2 + // 3 + // Finished sequence + // #enddocregion subscriber } -// Create a new Observable that will deliver the above sequence -const sequence = new Observable(sequenceSubscriber); - -// execute the Observable and print the result of each notification -sequence.subscribe({ - next(num) { console.log(num); }, - complete() { console.log('Finished sequence'); } -}); - -// Logs: -// 1 -// 2 -// 3 -// Finished sequence - -// #enddocregion subscriber - // #docregion fromevent function fromEvent(target, eventName) { @@ -51,16 +52,18 @@ function fromEvent(target, eventName) { // #enddocregion fromevent -// #docregion fromevent_use +export function docRegionFromEvent(document) { + // #docregion fromevent_use -const ESC_KEY = 27; -const nameInput = document.getElementById('name') as HTMLInputElement; + const ESC_KEY = 27; + const nameInput = document.getElementById('name') as HTMLInputElement; -const subscription = fromEvent(nameInput, 'keydown') - .subscribe((e: KeyboardEvent) => { + const subscription = fromEvent(nameInput, 'keydown').subscribe((e: KeyboardEvent) => { if (e.keyCode === ESC_KEY) { nameInput.value = ''; } }); + // #enddocregion fromevent_use + return subscription; +} -// #enddocregion fromevent_use diff --git a/aio/content/examples/observables/src/geolocation.ts b/aio/content/examples/observables/src/geolocation.ts index 841fea556e..e1f0a2e40f 100644 --- a/aio/content/examples/observables/src/geolocation.ts +++ b/aio/content/examples/observables/src/geolocation.ts @@ -1,5 +1,5 @@ +// TODO: Add unit tests for this file. import { Observable } from 'rxjs'; - // #docregion // Create an Observable that will start listening to geolocation updates diff --git a/aio/content/examples/observables/src/multicasting.spec.ts b/aio/content/examples/observables/src/multicasting.spec.ts new file mode 100644 index 0000000000..90bab96151 --- /dev/null +++ b/aio/content/examples/observables/src/multicasting.spec.ts @@ -0,0 +1,48 @@ +import { docRegionDelaySequence, docRegionMulticastSequence } from './multicasting'; + +describe('multicasting', () => { + let console; + beforeEach(() => { + jasmine.clock().install(); + console = {log: jasmine.createSpy('log')}; + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('should create an observable and emit in sequence', () => { + docRegionDelaySequence(console); + jasmine.clock().tick(10000); + expect(console.log).toHaveBeenCalledTimes(12); + expect(console.log.calls.allArgs()).toEqual([ + [1], + ['1st subscribe: 1'], + ['2nd subscribe: 1'], + [2], + ['1st subscribe: 2'], + ['2nd subscribe: 2'], + [3], + ['Finished sequence'], + ['1st subscribe: 3'], + ['1st sequence finished.'], + ['2nd subscribe: 3'], + ['2nd sequence finished.'] + ]); + }); + + it('should create an observable and multicast the emissions', () => { + docRegionMulticastSequence(console); + jasmine.clock().tick(10000); + expect(console.log).toHaveBeenCalledTimes(7); + expect(console.log.calls.allArgs()).toEqual([ + ['1st subscribe: 1'], + ['1st subscribe: 2'], + ['2nd subscribe: 2'], + ['1st subscribe: 3'], + ['2nd subscribe: 3'], + ['1st sequence finished.'], + ['2nd sequence finished.'] + ]); + }); +}); diff --git a/aio/content/examples/observables/src/multicasting.ts b/aio/content/examples/observables/src/multicasting.ts index e8275842f0..fe821edd2a 100644 --- a/aio/content/examples/observables/src/multicasting.ts +++ b/aio/content/examples/observables/src/multicasting.ts @@ -1,155 +1,160 @@ +// #docplaster import { Observable } from 'rxjs'; -// #docregion delay_sequence +export function docRegionDelaySequence(console) { + // #docregion delay_sequence + function sequenceSubscriber(observer) { + const seq = [1, 2, 3]; + let timeoutId; -function sequenceSubscriber(observer) { - const seq = [1, 2, 3]; - let timeoutId; + // Will run through an array of numbers, emitting one value + // per second until it gets to the end of the array. + function doInSequence(arr, idx) { + timeoutId = setTimeout(() => { + observer.next(arr[idx]); + if (idx === arr.length - 1) { + observer.complete(); + } else { + doInSequence(arr, ++idx); + } + }, 1000); + } - // Will run through an array of numbers, emitting one value + doInSequence(seq, 0); + + // Unsubscribe should clear the timeout to stop execution + return { + unsubscribe() { + clearTimeout(timeoutId); + } + }; + } + + // Create a new Observable that will deliver the above sequence + const sequence = new Observable(sequenceSubscriber); + + sequence.subscribe({ + next(num) { console.log(num); }, + complete() { console.log('Finished sequence'); } + }); + + // Logs: + // (at 1 second): 1 + // (at 2 seconds): 2 + // (at 3 seconds): 3 + // (at 3 seconds): Finished sequence + + // #enddocregion delay_sequence + + // #docregion subscribe_twice + + // Subscribe starts the clock, and will emit after 1 second + sequence.subscribe({ + next(num) { console.log('1st subscribe: ' + num); }, + complete() { console.log('1st sequence finished.'); } + }); + + // After 1/2 second, subscribe again. + setTimeout(() => { + sequence.subscribe({ + next(num) { console.log('2nd subscribe: ' + num); }, + complete() { console.log('2nd sequence finished.'); } + }); + }, 500); + + // Logs: + // (at 1 second): 1st subscribe: 1 + // (at 1.5 seconds): 2nd subscribe: 1 + // (at 2 seconds): 1st subscribe: 2 + // (at 2.5 seconds): 2nd subscribe: 2 + // (at 3 seconds): 1st subscribe: 3 + // (at 3 seconds): 1st sequence finished + // (at 3.5 seconds): 2nd subscribe: 3 + // (at 3.5 seconds): 2nd sequence finished + + // #enddocregion subscribe_twice +} + +export function docRegionMulticastSequence(console) { + // #docregion multicast_sequence + function multicastSequenceSubscriber() { + const seq = [1, 2, 3]; + // Keep track of each observer (one for every active subscription) + const observers = []; + // Still a single timeoutId because there will only ever be one + // set of values being generated, multicasted to each subscriber + let timeoutId; + + // Return the subscriber function (runs when subscribe() + // function is invoked) + return observer => { + observers.push(observer); + // When this is the first subscription, start the sequence + if (observers.length === 1) { + timeoutId = doSequence({ + next(val) { + // Iterate through observers and notify all subscriptions + observers.forEach(obs => obs.next(val)); + }, + complete() { + // Notify all complete callbacks + observers.slice(0).forEach(obs => obs.complete()); + } + }, seq, 0); + } + + return { + unsubscribe() { + // Remove from the observers array so it's no longer notified + observers.splice(observers.indexOf(observer), 1); + // If there's no more listeners, do cleanup + if (observers.length === 0) { + clearTimeout(timeoutId); + } + } + }; + }; + } + + // Run through an array of numbers, emitting one value // per second until it gets to the end of the array. - function doSequence(arr, idx) { - timeoutId = setTimeout(() => { + function doSequence(observer, arr, idx) { + return setTimeout(() => { observer.next(arr[idx]); if (idx === arr.length - 1) { observer.complete(); } else { - doSequence(arr, ++idx); + doSequence(observer, arr, ++idx); } }, 1000); } - doSequence(seq, 0); + // Create a new Observable that will deliver the above sequence + const multicastSequence = new Observable(multicastSequenceSubscriber()); - // Unsubscribe should clear the timeout to stop execution - return {unsubscribe() { - clearTimeout(timeoutId); - }}; -} - -// Create a new Observable that will deliver the above sequence -const sequence = new Observable(sequenceSubscriber); - -sequence.subscribe({ - next(num) { console.log(num); }, - complete() { console.log('Finished sequence'); } -}); - -// Logs: -// (at 1 second): 1 -// (at 2 seconds): 2 -// (at 3 seconds): 3 -// (at 3 seconds): Finished sequence - -// #enddocregion delay_sequence - -// #docregion subscribe_twice - -// Subscribe starts the clock, and will emit after 1 second -sequence.subscribe({ - next(num) { console.log('1st subscribe: ' + num); }, - complete() { console.log('1st sequence finished.'); } -}); - -// After 1/2 second, subscribe again. -setTimeout(() => { - sequence.subscribe({ - next(num) { console.log('2nd subscribe: ' + num); }, - complete() { console.log('2nd sequence finished.'); } - }); -}, 500); - -// Logs: -// (at 1 second): 1st subscribe: 1 -// (at 1.5 seconds): 2nd subscribe: 1 -// (at 2 seconds): 1st subscribe: 2 -// (at 2.5 seconds): 2nd subscribe: 2 -// (at 3 seconds): 1st subscribe: 3 -// (at 3 seconds): 1st sequence finished -// (at 3.5 seconds): 2nd subscribe: 3 -// (at 3.5 seconds): 2nd sequence finished - -// #enddocregion subscribe_twice - -// #docregion multicast_sequence - -function multicastSequenceSubscriber() { - const seq = [1, 2, 3]; - // Keep track of each observer (one for every active subscription) - const observers = []; - // Still a single timeoutId because there will only ever be one - // set of values being generated, multicasted to each subscriber - let timeoutId; - - // Return the subscriber function (runs when subscribe() - // function is invoked) - return (observer) => { - observers.push(observer); - // When this is the first subscription, start the sequence - if (observers.length === 1) { - timeoutId = doSequence({ - next(val) { - // Iterate through observers and notify all subscriptions - observers.forEach(obs => obs.next(val)); - }, - complete() { - // Notify all complete callbacks - observers.slice(0).forEach(obs => obs.complete()); - } - }, seq, 0); - } - - return { - unsubscribe() { - // Remove from the observers array so it's no longer notified - observers.splice(observers.indexOf(observer), 1); - // If there's no more listeners, do cleanup - if (observers.length === 0) { - clearTimeout(timeoutId); - } - } - }; - }; -} - -// Run through an array of numbers, emitting one value -// per second until it gets to the end of the array. -function doSequence(observer, arr, idx) { - return setTimeout(() => { - observer.next(arr[idx]); - if (idx === arr.length - 1) { - observer.complete(); - } else { - doSequence(observer, arr, ++idx); - } - }, 1000); -} - -// Create a new Observable that will deliver the above sequence -const multicastSequence = new Observable(multicastSequenceSubscriber()); - -// Subscribe starts the clock, and begins to emit after 1 second -multicastSequence.subscribe({ - next(num) { console.log('1st subscribe: ' + num); }, - complete() { console.log('1st sequence finished.'); } -}); - -// After 1 1/2 seconds, subscribe again (should "miss" the first value). -setTimeout(() => { + // Subscribe starts the clock, and begins to emit after 1 second multicastSequence.subscribe({ - next(num) { console.log('2nd subscribe: ' + num); }, - complete() { console.log('2nd sequence finished.'); } + next(num) { console.log('1st subscribe: ' + num); }, + complete() { console.log('1st sequence finished.'); } }); -}, 1500); -// Logs: -// (at 1 second): 1st subscribe: 1 -// (at 2 seconds): 1st subscribe: 2 -// (at 2 seconds): 2nd subscribe: 2 -// (at 3 seconds): 1st subscribe: 3 -// (at 3 seconds): 1st sequence finished -// (at 3 seconds): 2nd subscribe: 3 -// (at 3 seconds): 2nd sequence finished + // After 1 1/2 seconds, subscribe again (should "miss" the first value). + setTimeout(() => { + multicastSequence.subscribe({ + next(num) { console.log('2nd subscribe: ' + num); }, + complete() { console.log('2nd sequence finished.'); } + }); + }, 1500); -// #enddocregion multicast_sequence + // Logs: + // (at 1 second): 1st subscribe: 1 + // (at 2 seconds): 1st subscribe: 2 + // (at 2 seconds): 2nd subscribe: 2 + // (at 3 seconds): 1st subscribe: 3 + // (at 3 seconds): 1st sequence finished + // (at 3 seconds): 2nd subscribe: 3 + // (at 3 seconds): 2nd sequence finished + + // #enddocregion multicast_sequence +} diff --git a/aio/content/examples/observables/src/subscribing.spec.ts b/aio/content/examples/observables/src/subscribing.spec.ts new file mode 100644 index 0000000000..43d0307e5d --- /dev/null +++ b/aio/content/examples/observables/src/subscribing.spec.ts @@ -0,0 +1,19 @@ +import { docRegionObserver } from './subscribing'; + +describe('subscribing', () => { + it('should subscribe and emit', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionObserver(console); + expect(console.log).toHaveBeenCalledTimes(8); + expect(console.log.calls.allArgs()).toEqual([ + ['Observer got a next value: 1'], + ['Observer got a next value: 2'], + ['Observer got a next value: 3'], + ['Observer got a complete notification'], + ['Observer got a next value: 1'], + ['Observer got a next value: 2'], + ['Observer got a next value: 3'], + ['Observer got a complete notification'], + ]); + }); +}); diff --git a/aio/content/examples/observables/src/subscribing.ts b/aio/content/examples/observables/src/subscribing.ts index 06a21575d1..47677b5d9e 100644 --- a/aio/content/examples/observables/src/subscribing.ts +++ b/aio/content/examples/observables/src/subscribing.ts @@ -1,32 +1,35 @@ +// #docplaster +import { of } from 'rxjs'; -import { Observable, of } from 'rxjs'; +export function docRegionObserver(console) { + // #docregion observer -// #docregion observer + // Create simple observable that emits three values + const myObservable = of(1, 2, 3); -// Create simple observable that emits three values -const myObservable = of(1, 2, 3); + // Create observer object + const myObserver = { + next: x => console.log('Observer got a next value: ' + x), + error: err => console.error('Observer got an error: ' + err), + complete: () => console.log('Observer got a complete notification'), + }; -// Create observer object -const myObserver = { - next: x => console.log('Observer got a next value: ' + x), - error: err => console.error('Observer got an error: ' + err), - complete: () => console.log('Observer got a complete notification'), -}; + // Execute with the observer object + myObservable.subscribe(myObserver); -// Execute with the observer object -myObservable.subscribe(myObserver); -// Logs: -// Observer got a next value: 1 -// Observer got a next value: 2 -// Observer got a next value: 3 -// Observer got a complete notification + // Logs: + // Observer got a next value: 1 + // Observer got a next value: 2 + // Observer got a next value: 3 + // Observer got a complete notification -// #enddocregion observer + // #enddocregion observer -// #docregion sub_fn -myObservable.subscribe( - x => console.log('Observer got a next value: ' + x), - err => console.error('Observer got an error: ' + err), - () => console.log('Observer got a complete notification') -); -// #enddocregion sub_fn + // #docregion sub_fn + myObservable.subscribe( + x => console.log('Observer got a next value: ' + x), + err => console.error('Observer got an error: ' + err), + () => console.log('Observer got a complete notification') + ); + // #enddocregion sub_fn +} diff --git a/aio/content/examples/pipes/e2e/src/app.e2e-spec.ts b/aio/content/examples/pipes/e2e/src/app.e2e-spec.ts index ec9d15cbdf..cabb8f0baa 100644 --- a/aio/content/examples/pipes/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/pipes/e2e/src/app.e2e-spec.ts @@ -1,116 +1,108 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -const { version: angularVersion } = require('@angular/core/package.json'); -describe('Pipes', function () { +describe('Pipes', () => { - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it('should open correctly', async () => { + expect(await element.all(by.tagName('h1')).get(0).getText()).toEqual('Pipes'); + expect(await element(by.css('app-hero-birthday p')).getText()).toEqual(`The hero's birthday is Apr 15, 1988`); }); - it('should open correctly', function () { - expect(element.all(by.tagName('h1')).get(0).getText()).toEqual('Pipes'); - expect(element(by.css('app-hero-birthday p')).getText()).toEqual(`The hero's birthday is Apr 15, 1988`); + it('should show 4 heroes', async () => { + expect(await element.all(by.css('app-hero-list div')).count()).toEqual(4); }); - it('should show 4 heroes', function () { - expect(element.all(by.css('app-hero-list div')).count()).toEqual(4); + it('should show a familiar hero in json', async () => { + expect(await element(by.cssContainingText('app-hero-list p', 'Heroes as JSON')).getText()).toContain('Bombasto'); }); - it('should show a familiar hero in json', function () { - expect(element(by.cssContainingText('app-hero-list p', 'Heroes as JSON')).getText()).toContain('Bombasto'); + it('should show alternate birthday formats', async () => { + expect(await element(by.cssContainingText('app-root > p', `The hero's birthday is Apr 15, 1988`)).isDisplayed()) + .toBe(true); + expect(await element(by.cssContainingText('app-root > p', `The hero's birthday is 04/15/88`)).isDisplayed()) + .toBe(true); }); - it('should show alternate birthday formats', function () { - expect(element(by.cssContainingText('app-root > p', `The hero's birthday is Apr 15, 1988`)).isDisplayed()).toBe(true); - expect(element(by.cssContainingText('app-root > p', `The hero's birthday is 04/15/88`)).isDisplayed()).toBe(true); + it('should be able to toggle birthday formats', async () => { + const birthDayEle = element(by.css('app-hero-birthday2 > p')); + expect(await birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`); + + const buttonEle = element(by.cssContainingText('app-hero-birthday2 > button', 'Toggle Format')); + expect(await buttonEle.isDisplayed()).toBe(true); + + await buttonEle.click(); + expect(await birthDayEle.getText()).toEqual(`The hero's birthday is Friday, April 15, 1988`); }); - it('should be able to toggle birthday formats', function () { - let birthDayEle = element(by.css('app-hero-birthday2 > p')); - if (angularVersion.indexOf('4.') === 0) { // Breaking change between v4 and v5 (https://github.com/angular/angular/commit/079d884) - expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/1988`); - } else { - expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`); - } - let buttonEle = element(by.cssContainingText('app-hero-birthday2 > button', 'Toggle Format')); - expect(buttonEle.isDisplayed()).toBe(true); - buttonEle.click().then(function() { - expect(birthDayEle.getText()).toEqual(`The hero's birthday is Friday, April 15, 1988`); - }); + it('should be able to chain and compose pipes', async () => { + const chainedPipeEles = element.all(by.cssContainingText('app-root p', `The chained hero's`)); + expect(await chainedPipeEles.count()).toBe(3, 'should have 3 chained pipe examples'); + expect(await chainedPipeEles.get(0).getText()).toContain('APR 15, 1988'); + expect(await chainedPipeEles.get(1).getText()).toContain('FRIDAY, APRIL 15, 1988'); + expect(await chainedPipeEles.get(2).getText()).toContain('FRIDAY, APRIL 15, 1988'); }); - it('should be able to chain and compose pipes', function () { - let chainedPipeEles = element.all(by.cssContainingText('app-root p', `The chained hero's`)); - expect(chainedPipeEles.count()).toBe(3, 'should have 3 chained pipe examples'); - expect(chainedPipeEles.get(0).getText()).toContain('APR 15, 1988'); - expect(chainedPipeEles.get(1).getText()).toContain('FRIDAY, APRIL 15, 1988'); - expect(chainedPipeEles.get(2).getText()).toContain('FRIDAY, APRIL 15, 1988'); + it('should be able to use ExponentialStrengthPipe pipe', async () => { + const ele = element(by.css('app-power-booster p')); + expect(await ele.getText()).toContain('Super power boost: 1024'); }); - it('should be able to use ExponentialStrengthPipe pipe', function () { - let ele = element(by.css('app-power-booster p')); - expect(ele.getText()).toContain('Super power boost: 1024'); - }); + it('should be able to use the exponential calculator', async () => { + const eles = element.all(by.css('app-power-boost-calculator input')); + const baseInputEle = eles.get(0); + const factorInputEle = eles.get(1); + const outputEle = element(by.css('app-power-boost-calculator p')); - it('should be able to use the exponential calculator', function () { - let eles = element.all(by.css('app-power-boost-calculator input')); - let baseInputEle = eles.get(0); - let factorInputEle = eles.get(1); - let outputEle = element(by.css('app-power-boost-calculator p')); - baseInputEle.clear().then(function() { - baseInputEle.sendKeys('7'); - return factorInputEle.clear(); - }).then(function() { - factorInputEle.sendKeys('3'); - expect(outputEle.getText()).toContain('343'); - }); + await baseInputEle.clear(); + await baseInputEle.sendKeys('7'); + await factorInputEle.clear(); + await factorInputEle.sendKeys('3'); + expect(await outputEle.getText()).toContain('343'); }); - it('should support flying heroes (pure) ', function () { - let nameEle = element(by.css('app-flying-heroes input[type="text"]')); - let canFlyCheckEle = element(by.css('app-flying-heroes #can-fly')); - let mutateCheckEle = element(by.css('app-flying-heroes #mutate')); - let resetEle = element(by.css('app-flying-heroes button')); - let flyingHeroesEle = element.all(by.css('app-flying-heroes #flyers div')); + it('should support flying heroes (pure) ', async () => { + const nameEle = element(by.css('app-flying-heroes input[type="text"]')); + const canFlyCheckEle = element(by.css('app-flying-heroes #can-fly')); + const mutateCheckEle = element(by.css('app-flying-heroes #mutate')); + const resetEle = element(by.css('app-flying-heroes button')); + const flyingHeroesEle = element.all(by.css('app-flying-heroes #flyers div')); - expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); - expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); - expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly'); + expect(await canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); + expect(await mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); + expect(await flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly'); - nameEle.sendKeys('test1\n'); - expect(flyingHeroesEle.count()).toEqual(2, 'no change while mutating array'); - mutateCheckEle.click().then(function() { - nameEle.sendKeys('test2\n'); - expect(flyingHeroesEle.count()).toEqual(4, 'not mutating; should see both adds'); - expect(flyingHeroesEle.get(2).getText()).toContain('test1'); - expect(flyingHeroesEle.get(3).getText()).toContain('test2'); - return resetEle.click(); - }) - .then(function() { - expect(flyingHeroesEle.count()).toEqual(2, 'reset should restore original flying heroes'); - }); + await nameEle.sendKeys('test1\n'); + expect(await flyingHeroesEle.count()).toEqual(2, 'no change while mutating array'); + + await mutateCheckEle.click(); + await nameEle.sendKeys('test2\n'); + expect(await flyingHeroesEle.count()).toEqual(4, 'not mutating; should see both adds'); + expect(await flyingHeroesEle.get(2).getText()).toContain('test1'); + expect(await flyingHeroesEle.get(3).getText()).toContain('test2'); + + await resetEle.click(); + expect(await flyingHeroesEle.count()).toEqual(2, 'reset should restore original flying heroes'); }); - it('should support flying heroes (impure) ', function () { - let nameEle = element(by.css('app-flying-heroes-impure input[type="text"]')); - let canFlyCheckEle = element(by.css('app-flying-heroes-impure #can-fly')); - let mutateCheckEle = element(by.css('app-flying-heroes-impure #mutate')); - let flyingHeroesEle = element.all(by.css('app-flying-heroes-impure #flyers div')); + it('should support flying heroes (impure) ', async () => { + const nameEle = element(by.css('app-flying-heroes-impure input[type="text"]')); + const canFlyCheckEle = element(by.css('app-flying-heroes-impure #can-fly')); + const mutateCheckEle = element(by.css('app-flying-heroes-impure #mutate')); + const flyingHeroesEle = element.all(by.css('app-flying-heroes-impure #flyers div')); - expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); - expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); - expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly'); + expect(await canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"'); + expect(await mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array'); + expect(await flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly'); - nameEle.sendKeys('test1\n'); - expect(flyingHeroesEle.count()).toEqual(3, 'new flying hero should show in mutating array'); + await nameEle.sendKeys('test1\n'); + expect(await flyingHeroesEle.count()).toEqual(3, 'new flying hero should show in mutating array'); }); - it('should show an async hero message', function () { - expect(element.all(by.tagName('app-hero-message')).get(0).getText()).toContain('hero'); + it('should show an async hero message', async () => { + expect(await element.all(by.tagName('app-hero-message')).get(0).getText()).toContain('hero'); }); }); diff --git a/aio/content/examples/pipes/src/app/app.component.html b/aio/content/examples/pipes/src/app/app.component.html index b03b8251bc..add2dd2455 100644 --- a/aio/content/examples/pipes/src/app/app.component.html +++ b/aio/content/examples/pipes/src/app/app.component.html @@ -10,6 +10,7 @@ Flying Heroes filter pipe (impure)
Async Hero Message and AsyncPipe
Hero List with caching FetchJsonPipe
+Pipes and Precedence

@@ -80,4 +81,7 @@ -
+
+ + +
diff --git a/aio/content/examples/pipes/src/app/app.module.ts b/aio/content/examples/pipes/src/app/app.module.ts index 8dd502c702..bd551b6fb9 100644 --- a/aio/content/examples/pipes/src/app/app.module.ts +++ b/aio/content/examples/pipes/src/app/app.module.ts @@ -15,6 +15,7 @@ import { HeroBirthday2Component } from './hero-birthday2.component'; import { HeroListComponent } from './hero-list.component'; import { PowerBoostCalculatorComponent } from './power-boost-calculator.component'; import { PowerBoosterComponent } from './power-booster.component'; +import { PrecedenceComponent } from './precedence.component'; @NgModule({ @@ -36,7 +37,8 @@ import { PowerBoosterComponent } from './power-booster.component'; FlyingHeroesPipe, FlyingHeroesImpurePipe, FetchJsonPipe, - ExponentialStrengthPipe + ExponentialStrengthPipe, + PrecedenceComponent ], bootstrap: [AppComponent] }) diff --git a/aio/content/examples/pipes/src/app/fetch-json.pipe.ts b/aio/content/examples/pipes/src/app/fetch-json.pipe.ts index a6696d3b1d..edd4eaa079 100644 --- a/aio/content/examples/pipes/src/app/fetch-json.pipe.ts +++ b/aio/content/examples/pipes/src/app/fetch-json.pipe.ts @@ -1,5 +1,5 @@ // #docregion -import { HttpClient } from '@angular/common/http'; +import { HttpClient } from '@angular/common/http'; import { Pipe, PipeTransform } from '@angular/core'; // #docregion pipe-metadata diff --git a/aio/content/examples/pipes/src/app/flying-heroes.component.ts b/aio/content/examples/pipes/src/app/flying-heroes.component.ts index 43474c5ba8..526f3b2842 100644 --- a/aio/content/examples/pipes/src/app/flying-heroes.component.ts +++ b/aio/content/examples/pipes/src/app/flying-heroes.component.ts @@ -1,8 +1,8 @@ // #docplaster // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; -import { HEROES } from './heroes'; +import { HEROES } from './heroes'; @Component({ selector: 'app-flying-heroes', @@ -23,7 +23,7 @@ export class FlyingHeroesComponent { addHero(name: string) { name = name.trim(); if (!name) { return; } - let hero = {name, canFly: this.canFly}; + const hero = {name, canFly: this.canFly}; // #enddocregion v1 if (this.mutate) { // Pure pipe won't update display because heroes array reference is unchanged diff --git a/aio/content/examples/pipes/src/app/precedence.component.html b/aio/content/examples/pipes/src/app/precedence.component.html new file mode 100644 index 0000000000..3b4a208e7e --- /dev/null +++ b/aio/content/examples/pipes/src/app/precedence.component.html @@ -0,0 +1,13 @@ + + +

{{title}}

+ +

Basic uppercase pipe (no precedence considerations necessary): {{ 'text' | uppercase }}

+

The following shows that a pipe has higher precedence than a ternary operator. If the pipe precedence were lower than the ternary operator precedence, the output would be 'TRUE'. Instead it is: {{ true ? 'true' : 'false' | uppercase }}

+

The following shows how parentheses help Angular evaluate the whole statement: + + +{{ (true ? 'true' : 'false') | uppercase }} + +

+ diff --git a/aio/content/examples/pipes/src/app/precedence.component.ts b/aio/content/examples/pipes/src/app/precedence.component.ts new file mode 100644 index 0000000000..c4132c881f --- /dev/null +++ b/aio/content/examples/pipes/src/app/precedence.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-precedence', + templateUrl: './precedence.component.html', + styles: [] +}) + +export class PrecedenceComponent { + title = 'Pipes and Precedence'; +} diff --git a/aio/content/examples/practical-observable-usage/example-config.json b/aio/content/examples/practical-observable-usage/example-config.json index c07fa9794c..3aa2059b4d 100644 --- a/aio/content/examples/practical-observable-usage/example-config.json +++ b/aio/content/examples/practical-observable-usage/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/practical-observable-usage/src/backoff.spec.ts b/aio/content/examples/practical-observable-usage/src/backoff.spec.ts new file mode 100644 index 0000000000..0390a476d1 --- /dev/null +++ b/aio/content/examples/practical-observable-usage/src/backoff.spec.ts @@ -0,0 +1,70 @@ +import { interval } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { backoff } from './backoff'; + +describe('backoff()', () => { + beforeEach(() => jasmine.clock().install()); + afterEach(() => jasmine.clock().uninstall()); + + it('should retry in case of error', () => { + const mockConsole = {log: jasmine.createSpy('log')}; + const source = interval(10).pipe( + tap(i => { + if (i > 0) { + throw new Error('Test error'); + } + }), + backoff(3, 100), + ); + source.subscribe({ + next: v => mockConsole.log(`Emitted: ${v}`), + error: e => mockConsole.log(`Errored: ${e.message || e}`), + complete: () => mockConsole.log('Completed'), + }); + + // Initial try: + // Errors on second emission and schedules retrying (with delay). + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // First re-attempt after 100ms: + // Errors again on second emission and schedules retrying (with larger delay). + jasmine.clock().tick(100); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // Second re-attempt after 400ms: + // Errors again on second emission and schedules retrying (with even larger delay). + jasmine.clock().tick(400); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + // Third re-attempt after 900ms: + // Errors again on second emission and gives up (no retrying). + jasmine.clock().tick(900); + expect(mockConsole.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Emitted: 0']]); + mockConsole.log.calls.reset(); + + jasmine.clock().tick(10); + expect(mockConsole.log.calls.allArgs()).toEqual([['Errored: Test error']]); + }); +}); diff --git a/aio/content/examples/practical-observable-usage/src/backoff.ts b/aio/content/examples/practical-observable-usage/src/backoff.ts index e9e88e7cb2..68d724d097 100644 --- a/aio/content/examples/practical-observable-usage/src/backoff.ts +++ b/aio/content/examples/practical-observable-usage/src/backoff.ts @@ -1,23 +1,32 @@ - -import { pipe, range, timer, zip } from 'rxjs'; +// #docplaster +// #docregion +import { of, pipe, range, throwError, timer, zip } from 'rxjs'; import { ajax } from 'rxjs/ajax'; -import { retryWhen, map, mergeMap } from 'rxjs/operators'; +import { map, mergeMap, retryWhen } from 'rxjs/operators'; -function backoff(maxTries, ms) { - return pipe( - retryWhen(attempts => zip(range(1, maxTries), attempts) - .pipe( - map(([i]) => i * i), - mergeMap(i => timer(i * ms)) - ) - ) - ); +export function backoff(maxTries, delay) { + return pipe( + retryWhen(attempts => + zip(range(1, maxTries + 1), attempts).pipe( + mergeMap(([i, err]) => (i > maxTries) ? throwError(err) : of(i)), + map(i => i * i), + mergeMap(v => timer(v * delay)), + ), + ), + ); } +// #enddocregion +/* + This function declaration is necessary to ensure that it does not get called + when running the unit tests. It will not get rendered into the docs. + The indentation needs to start in the leftmost level position as well because of how + the docplaster combines the different regions together. +*/ +function docRegionAjaxCall() { +// #docregion ajax('/api/endpoint') .pipe(backoff(3, 250)) - .subscribe(data => handleData(data)); - -function handleData(data) { - // ... + .subscribe(function handleData(data) { /* ... */ }); +// #enddocregion } diff --git a/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts b/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts new file mode 100644 index 0000000000..285dd7e12e --- /dev/null +++ b/aio/content/examples/practical-observable-usage/src/typeahead.spec.ts @@ -0,0 +1,72 @@ +import { of } from 'rxjs'; +import { docRegionTypeahead } from './typeahead'; + +describe('typeahead', () => { + let document; + let ajax; + let triggertInputChange; + + beforeEach(() => { + jasmine.clock().install(); + const input = { + addEventListener: jasmine + .createSpy('addEvent') + .and.callFake((eventName: string, cb: (e) => void) => { + if (eventName === 'input') { + triggertInputChange = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEvent'), + }; + + document = { getElementById: (id: string) => input }; + ajax = jasmine.createSpy('ajax').and.callFake((url: string) => of('foo bar')); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('should make an ajax call to the corrent endpoint', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=foo'); + }); + + it('should not make an ajax call, when the input length < 3', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: '' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + triggertInputChange({ target: { value: 'fo' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + }); + + it('should not make an ajax call for intermediate values when debouncing', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'bar' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'baz' } }); + jasmine.clock().tick(9); + triggertInputChange({ target: { value: 'qux' } }); + expect(ajax).not.toHaveBeenCalled(); + jasmine.clock().tick(10); + expect(ajax).toHaveBeenCalledTimes(1); + expect(ajax).toHaveBeenCalledWith('/api/endpoint?search=qux'); + }); + + it('should not make an ajax call, when the input value has not changed', () => { + docRegionTypeahead(document, ajax); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).toHaveBeenCalled(); + ajax.calls.reset(); + triggertInputChange({ target: { value: 'foo' } }); + jasmine.clock().tick(11); + expect(ajax).not.toHaveBeenCalled(); + }); +}); diff --git a/aio/content/examples/practical-observable-usage/src/typeahead.ts b/aio/content/examples/practical-observable-usage/src/typeahead.ts index 153ab7f23e..adbddd66c4 100644 --- a/aio/content/examples/practical-observable-usage/src/typeahead.ts +++ b/aio/content/examples/practical-observable-usage/src/typeahead.ts @@ -1,18 +1,32 @@ -import { fromEvent } from 'rxjs'; -import { ajax } from 'rxjs/ajax'; -import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +// #docplaster +// #docregion + import { fromEvent } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; +// #enddocregion +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ +export function docRegionTypeahead(document, ajax) { + // #docregion + const searchBox = document.getElementById('search-box'); -const searchBox = document.getElementById('search-box'); + const typeahead = fromEvent(searchBox, 'input').pipe( + map((e: KeyboardEvent) => (e.target as HTMLInputElement).value), + filter(text => text.length > 2), + debounceTime(10), + distinctUntilChanged(), + switchMap(searchTerm => ajax(`/api/endpoint?search=${searchTerm}`)) + ); -const typeahead = fromEvent(searchBox, 'input').pipe( - map((e: KeyboardEvent) => (e.target as HTMLInputElement).value), - filter(text => text.length > 2), - debounceTime(10), - distinctUntilChanged(), - switchMap(() => ajax('/api/endpoint')) -); + typeahead.subscribe(data => { + // Handle the data from the API + }); -typeahead.subscribe(data => { - // Handle the data from the API -}); + // #enddocregion + return typeahead; +} diff --git a/aio/content/examples/property-binding/e2e/src/app.e2e-spec.ts b/aio/content/examples/property-binding/e2e/src/app.e2e-spec.ts index 07b98491c1..b134930d07 100644 --- a/aio/content/examples/property-binding/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/property-binding/e2e/src/app.e2e-spec.ts @@ -3,52 +3,50 @@ import { browser, element, by } from 'protractor'; describe('Property binding e2e tests', () => { - beforeEach(function () { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display Property Binding with Angular', async () => { + expect(await element(by.css('h1')).getText()).toEqual('Property Binding with Angular'); }); - it('should display Property Binding with Angular', function () { - expect(element(by.css('h1')).getText()).toEqual('Property Binding with Angular'); + it('should display four phone pictures', async () => { + expect(await element.all(by.css('img')).isPresent()).toBe(true); + expect(await element.all(by.css('img')).count()).toBe(4); }); - it('should display four phone pictures', function() { - expect(element.all(by.css('img')).isPresent()).toBe(true); - expect(element.all(by.css('img')).count()).toBe(4); - + it('should display Disabled button', async () => { + expect(await element.all(by.css('button')).get(0).getText()).toBe(`Disabled Button`); }); - it('should display Disabled button', function () { - expect(element.all(by.css('button')).get(0).getText()).toBe(`Disabled Button`); + it('should display Binding to a property of a directive', async () => { + expect(await element.all(by.css('h2')).get(4).getText()).toBe(`Binding to a property of a directive`); }); - it('should display Binding to a property of a directive', function () { - expect(element.all(by.css('h2')).get(4).getText()).toBe(`Binding to a property of a directive`); + it('should display blue', async () => { + expect(await element.all(by.css('p')).get(0).getText()).toContain(`blue`); }); - it('should display Your item is: lamp', function () { - expect(element.all(by.css('p')).get(0).getText()).toContain(`blue`); - }); - it('should display Your item is: lamp', function () { - expect(element.all(by.css('p')).get(1).getText()).toContain(`Your item is: lamp`); + it('should display Your item is: lamp', async () => { + expect(await element.all(by.css('p')).get(1).getText()).toContain(`Your item is: lamp`); }); - it('should display Your item is: parentItem', function () { - expect(element.all(by.css('p')).get(2).getText()).toBe(`Your item is: parentItem`); + it('should display Your item is: parentItem', async () => { + expect(await element.all(by.css('p')).get(2).getText()).toBe(`Your item is: parentItem`); }); - it('should display a ul', function () { - expect(element.all(by.css('ul')).get(0).getText()).toContain(`tv`); + it('should display a ul', async () => { + expect(await element.all(by.css('ul')).get(0).getText()).toContain(`tv`); }); - it('should display a ul containing phone', function () { - expect(element.all(by.css('ul')).get(1).getText()).toBe(`21 phone`); + it('should display a ul containing phone', async () => { + expect(await element.all(by.css('ul')).get(1).getText()).toBe(`21 phone`); }); - it('should display one-time initialized string', function () { - expect(element.all(by.css('p')).get(3).getText()).toContain(`one-time initialized`); + it('should display one-time initialized string', async () => { + expect(await element.all(by.css('p')).get(3).getText()).toContain(`one-time initialized`); }); - it('should display Malicious content', function () { - expect(element.all(by.css('h2')).get(8).getText()).toBe(`Malicious content`); + it('should display Malicious content', async () => { + expect(await element.all(by.css('h2')).get(8).getText()).toBe(`Malicious content`); }); }); diff --git a/aio/content/examples/property-binding/src/app/app.component.html b/aio/content/examples/property-binding/src/app/app.component.html index 4e88175854..f9b22a6765 100644 --- a/aio/content/examples/property-binding/src/app/app.component.html +++ b/aio/content/examples/property-binding/src/app/app.component.html @@ -77,7 +77,7 @@

"" is the property bound evil title.

diff --git a/aio/content/examples/property-binding/src/app/app.component.ts b/aio/content/examples/property-binding/src/app/app.component.ts index cfe9abe4dd..5b58a8c6b3 100644 --- a/aio/content/examples/property-binding/src/app/app.component.ts +++ b/aio/content/examples/property-binding/src/app/app.component.ts @@ -7,9 +7,15 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.css'] }) export class AppComponent { + // #docregion item-image itemImageUrl = '../assets/phone.png'; + // #enddocregion item-image + // #docregion boolean isUnchanged = true; + // #enddocregion boolean + // #docregion directive-property classes = 'special'; + // #enddocregion directive-property // #docregion parent-data-type parentItem = 'lamp'; // #enddocregion parent-data-type diff --git a/aio/content/examples/providers-viewproviders/e2e/src/app.e2e-spec.ts b/aio/content/examples/providers-viewproviders/e2e/src/app.e2e-spec.ts index d9e83d93fe..a351968b70 100644 --- a/aio/content/examples/providers-viewproviders/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/providers-viewproviders/e2e/src/app.e2e-spec.ts @@ -1,24 +1,19 @@ import { browser, element, by } from 'protractor'; -import { logging } from 'selenium-webdriver'; -describe('Providers and ViewProviders', function () { +describe('Providers and ViewProviders', () => { + beforeEach(() => browser.get('')); - beforeEach(() => { - browser.get(''); + it('shows basic flower emoji', async () => { + expect(await element.all(by.css('p')).get(0).getText()).toContain('🌺'); }); - it('shows basic flower emoji', function() { - expect(element.all(by.css('p')).get(0).getText()).toContain('🌺'); + it('shows whale emoji', async () => { + expect(await element.all(by.css('p')).get(1).getText()).toContain('🐳'); }); - it('shows whale emoji', function() { - expect(element.all(by.css('p')).get(1).getText()).toContain('🐳'); - }); - - it('shows sunflower from FlowerService', function() { - expect(element.all(by.css('p')).get(8).getText()).toContain('🌻'); + it('shows sunflower from FlowerService', async () => { + expect(await element.all(by.css('p')).get(8).getText()).toContain('🌻'); }); }); - diff --git a/aio/content/examples/providers/e2e/src/app.e2e-spec.ts b/aio/content/examples/providers/e2e/src/app.e2e-spec.ts index 758c5fa0e5..1281bd9795 100644 --- a/aio/content/examples/providers/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/providers/e2e/src/app.e2e-spec.ts @@ -1,37 +1,23 @@ +import { element, by } from 'protractor'; import { AppPage } from './app.po'; -import { browser, element, by } from 'protractor'; describe('providers App', () => { let page: AppPage; - beforeEach(() => { + beforeEach(async () => { page = new AppPage(); + await page.navigateTo(); }); - function getUsersStruct() { - return { - user: element.all(by.css('ng-component li')).get(0), - userId: element.all(by.css('ng-component span')).get(0) - }; - } - - function getListSectionStruct() { - return { - items: element.all(by.css('app-root li')) - }; - } - - it('should display header that says Users list', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('Users list'); + it('should display header that says Users list', async () => { + expect(await page.getTitleText()).toEqual('Users list'); }); - - it('shows a list of customers', function() { - const list = getListSectionStruct(); - expect(list.items.count()).toBe(10); - expect(list.items.get(0).getText()).toBe('1 Maria'); - expect(list.items.get(9).getText()).toBe('10 Seth'); + it('shows a list of customers', async () => { + const items = element.all(by.css('app-root li')); + expect(await items.count()).toBe(10); + expect(await items.get(0).getText()).toBe('1 Maria'); + expect(await items.get(9).getText()).toBe('10 Seth'); }); }); diff --git a/aio/content/examples/providers/src/app/user.service.2.ts b/aio/content/examples/providers/src/app/user.service.2.ts new file mode 100644 index 0000000000..de97ce6a45 --- /dev/null +++ b/aio/content/examples/providers/src/app/user.service.2.ts @@ -0,0 +1,7 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'any', +}) +export class UserService { +} diff --git a/aio/content/examples/reactive-forms/e2e/src/app.e2e-spec.ts b/aio/content/examples/reactive-forms/e2e/src/app.e2e-spec.ts index c895a39bcc..53c3d756b7 100644 --- a/aio/content/examples/reactive-forms/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/reactive-forms/e2e/src/app.e2e-spec.ts @@ -1,18 +1,14 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Reactive forms', function () { +describe('Reactive forms', () => { const nameEditor = element(by.css('app-name-editor')); const profileEditor = element(by.css('app-profile-editor')); const nameEditorLink = element(by.cssContainingText('app-root > nav > a', 'Name Editor')); const profileEditorLink = element(by.cssContainingText('app-root > nav > a', 'Profile Editor')); - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - describe('Name Editor', function () { + describe('Name Editor', () => { const nameInput = nameEditor.element(by.css('input')); const updateButton = nameEditor.element(by.buttonText('Update Name')); const nameText = 'John Smith'; @@ -55,9 +51,8 @@ describe('Reactive forms', function () { }); }); - describe('Profile Editor', function () { + describe('Profile Editor', () => { const firstNameInput = getInput('firstName'); - const lastNameInput = getInput('lastName'); const streetInput = getInput('street'); const addAliasButton = element(by.buttonText('Add Alias')); const updateButton = profileEditor.element(by.buttonText('Update Profile')); @@ -119,7 +114,7 @@ describe('Reactive forms', function () { it('should update the displayed form value when form inputs are updated', async () => { const aliasText = 'Johnny'; - const inputs = await Promise.all( + await Promise.all( Object.keys(profile) .map(key => getInput(key).sendKeys(`${profile[key]}`) diff --git a/aio/content/examples/resolution-modifiers/e2e/src/app.e2e-spec.ts b/aio/content/examples/resolution-modifiers/e2e/src/app.e2e-spec.ts index 4d8b621f1d..1f1b85d289 100644 --- a/aio/content/examples/resolution-modifiers/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/resolution-modifiers/e2e/src/app.e2e-spec.ts @@ -1,21 +1,19 @@ import { browser, element, by } from 'protractor'; -describe('Resolution-modifiers-example', function () { +describe('Resolution-modifiers-example', () => { - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it('shows basic flower emoji', async () => { + expect(await element.all(by.css('p')).get(0).getText()).toContain('🌸'); }); - it('shows basic flower emoji', function() { - expect(element.all(by.css('p')).get(0).getText()).toContain('🌸'); + it('shows basic leaf emoji', async () => { + expect(await element.all(by.css('p')).get(1).getText()).toContain('🌿'); }); - it('shows basic leaf emoji', function() { - expect(element.all(by.css('p')).get(1).getText()).toContain('🌿'); - }); - - it('shows yellow flower in host child', function() { - expect(element.all(by.css('p')).get(9).getText()).toContain('🌼'); + it('shows yellow flower in host child', async () => { + expect(await element.all(by.css('p')).get(9).getText()).toContain('🌼'); }); }); diff --git a/aio/content/examples/resolution-modifiers/src/app/app.module.ts b/aio/content/examples/resolution-modifiers/src/app/app.module.ts index dea2cd72a4..999b0900dc 100755 --- a/aio/content/examples/resolution-modifiers/src/app/app.module.ts +++ b/aio/content/examples/resolution-modifiers/src/app/app.module.ts @@ -1,4 +1,3 @@ -; import { OptionalComponent } from './optional/optional.component'; import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; diff --git a/aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts b/aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts index ac01714126..c73d879e8e 100644 --- a/aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts @@ -2,41 +2,39 @@ import { browser, element, by } from 'protractor'; describe('Router basic tutorial e2e tests', () => { - beforeEach(() => { - browser.get(''); + beforeEach(() => browser.get('')); + + it('should display Angular Router Sample', async () => { + expect(await element(by.css('h1')).getText()).toBe('Angular Router Sample'); }); - it('should display Angular Router Sample', () => { - expect(element(by.css('h1')).getText()).toBe('Angular Router Sample'); + it('should display Crisis Center button', async () => { + expect(await element.all(by.css('a')).get(0).getText()).toBe('Crisis Center'); }); - it('should display Crisis Center button', () => { - expect(element.all(by.css('a')).get(0).getText()).toBe('Crisis Center'); + it('should display Heroes button', async () => { + expect(await element.all(by.css('a')).get(1).getText()).toBe('Heroes'); }); - it('should display Heroes button', () => { - expect(element.all(by.css('a')).get(1).getText()).toBe('Heroes'); - }); - - it('should display HEROES', () => { - expect(element(by.css('h3')).getText()).toBe('HEROES'); + it('should display HEROES', async () => { + expect(await element(by.css('h3')).getText()).toBe('HEROES'); }); it('should change to display crisis list component', async () => { const crisisButton = element.all(by.css('a')).get(0); await crisisButton.click(); - expect(element(by.css('h3')).getText()).toBe('CRISIS CENTER'); + expect(await element(by.css('h3')).getText()).toBe('CRISIS CENTER'); }); it('should change to display heroes component', async () => { const heroesButton = element.all(by.css('a')).get(1); await heroesButton.click(); - expect(element(by.css('h3')).getText()).toBe('HEROES'); + expect(await element(by.css('h3')).getText()).toBe('HEROES'); }); it('should use wildcard route', async () => { - browser.get('/fake-page'); - expect(browser.getCurrentUrl()).toContain('fake-page'); - expect(element(by.css('h2')).getText()).toBe('Page Not Found'); + await browser.get('/fake-page'); + expect(await browser.getCurrentUrl()).toContain('fake-page'); + expect(await element(by.css('h2')).getText()).toBe('Page Not Found'); }); }); diff --git a/aio/content/examples/router/e2e/src/app.e2e-spec.ts b/aio/content/examples/router/e2e/src/app.e2e-spec.ts index 9d0d9ed8bf..ae174e549e 100644 --- a/aio/content/examples/router/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/router/e2e/src/app.e2e-spec.ts @@ -1,11 +1,8 @@ -'use strict'; // necessary for es6 output in node - -import { browser, element, by, ExpectedConditions } from 'protractor'; +import { browser, element, by, ExpectedConditions as EC } from 'protractor'; const numDashboardTabs = 5; const numCrises = 4; const numHeroes = 10; -const EC = ExpectedConditions; describe('Router', () => { @@ -22,12 +19,12 @@ describe('Router', () => { crisisHref: hrefEles.get(0), crisisList: element.all(by.css('app-root > div > app-crisis-center > app-crisis-list li')), - crisisDetail: crisisDetail, + crisisDetail, crisisDetailTitle: crisisDetail.element(by.xpath('*[1]')), heroesHref: hrefEles.get(1), heroesList: element.all(by.css('app-root > div > app-hero-list li')), - heroDetail: heroDetail, + heroDetail, heroDetailTitle: heroDetail.element(by.xpath('*[2]')), adminHref: hrefEles.get(2), @@ -45,43 +42,43 @@ describe('Router', () => { }; } - it('has expected dashboard tabs', () => { + it('has expected dashboard tabs', async () => { const page = getPageStruct(); - expect(page.hrefs.count()).toEqual(numDashboardTabs, 'dashboard tab count'); - expect(page.crisisHref.getText()).toEqual('Crisis Center'); - expect(page.heroesHref.getText()).toEqual('Heroes'); - expect(page.adminHref.getText()).toEqual('Admin'); - expect(page.loginHref.getText()).toEqual('Login'); - expect(page.contactHref.getText()).toEqual('Contact'); + expect(await page.hrefs.count()).toEqual(numDashboardTabs, 'dashboard tab count'); + expect(await page.crisisHref.getText()).toEqual('Crisis Center'); + expect(await page.heroesHref.getText()).toEqual('Heroes'); + expect(await page.adminHref.getText()).toEqual('Admin'); + expect(await page.loginHref.getText()).toEqual('Login'); + expect(await page.contactHref.getText()).toEqual('Contact'); }); - it('has heroes selected as opening tab', () => { + it('has heroes selected as opening tab', async () => { const page = getPageStruct(); - expect(page.activeHref.getText()).toEqual('Heroes'); + expect(await page.activeHref.getText()).toEqual('Heroes'); }); it('has crises center items', async () => { const page = getPageStruct(); await page.crisisHref.click(); - expect(page.activeHref.getText()).toEqual('Crisis Center'); - expect(page.crisisList.count()).toBe(numCrises, 'crisis list count'); + expect(await page.activeHref.getText()).toEqual('Crisis Center'); + expect(await page.crisisList.count()).toBe(numCrises, 'crisis list count'); }); it('has hero items', async () => { const page = getPageStruct(); await page.heroesHref.click(); - expect(page.activeHref.getText()).toEqual('Heroes'); - expect(page.heroesList.count()).toBe(numHeroes, 'hero list count'); + expect(await page.activeHref.getText()).toEqual('Heroes'); + expect(await page.heroesList.count()).toBe(numHeroes, 'hero list count'); }); it('toggles views', async () => { const page = getPageStruct(); await page.crisisHref.click(); - expect(page.activeHref.getText()).toEqual('Crisis Center'); - expect(page.crisisList.count()).toBe(numCrises, 'crisis list count'); + expect(await page.activeHref.getText()).toEqual('Crisis Center'); + expect(await page.crisisList.count()).toBe(numCrises, 'crisis list count'); await page.heroesHref.click(); - expect(page.activeHref.getText()).toEqual('Heroes'); - expect(page.heroesList.count()).toBe(numHeroes, 'hero list count'); + expect(await page.activeHref.getText()).toEqual('Heroes'); + expect(await page.heroesList.count()).toBe(numHeroes, 'hero list count'); }); it('saves changed crisis details', async () => { @@ -102,24 +99,24 @@ describe('Router', () => { await page.heroesHref.click(); await browser.sleep(600); const heroEle = page.heroesList.get(4); - let text = await heroEle.getText(); + const text = await heroEle.getText(); expect(text.length).toBeGreaterThan(0, 'hero item text length'); // remove leading id from text const heroText = text.substr(text.indexOf(' ')).trim(); await heroEle.click(); await browser.sleep(600); - expect(page.heroesList.count()).toBe(0, 'hero list count'); - expect(page.heroDetail.isPresent()).toBe(true, 'hero detail'); - expect(page.heroDetailTitle.getText()).toContain(heroText); - let inputEle = page.heroDetail.element(by.css('input')); + expect(await page.heroesList.count()).toBe(0, 'hero list count'); + expect(await page.heroDetail.isPresent()).toBe(true, 'hero detail'); + expect(await page.heroDetailTitle.getText()).toContain(heroText); + const inputEle = page.heroDetail.element(by.css('input')); await inputEle.sendKeys('-foo'); - expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo'); + expect(await page.heroDetailTitle.getText()).toContain(heroText + '-foo'); - let buttonEle = page.heroDetail.element(by.css('button')); + const buttonEle = page.heroDetail.element(by.css('button')); await buttonEle.click(); await browser.sleep(600); - expect(heroEle.getText()).toContain(heroText + '-foo'); + expect(await heroEle.getText()).toContain(heroText + '-foo'); }); it('sees preloaded modules', async () => { @@ -127,7 +124,7 @@ describe('Router', () => { await page.loginHref.click(); await page.loginButton.click(); const list = page.adminPreloadList; - expect(list.count()).toBe(1, 'preloaded module'); + expect(await list.count()).toBe(1, 'preloaded module'); expect(await list.first().getText()).toBe('crisis-center', 'first preloaded module'); }); @@ -135,8 +132,8 @@ describe('Router', () => { const page = getPageStruct(); await page.heroesHref.click(); await page.contactHref.click(); - expect(page.primaryOutlet.count()).toBe(1, 'primary outlet'); - expect(page.secondaryOutlet.count()).toBe(1, 'secondary outlet'); + expect(await page.primaryOutlet.count()).toBe(1, 'primary outlet'); + expect(await page.secondaryOutlet.count()).toBe(1, 'secondary outlet'); }); it('should redirect with secondary route', async () => { @@ -161,33 +158,33 @@ describe('Router', () => { await page.loginButton.click(); expect(await page.adminPage.isDisplayed()).toBeTruthy(); - expect(page.secondaryOutlet.count()).toBeTruthy(); + expect(await page.secondaryOutlet.count()).toBeTruthy(); }); async function crisisCenterEdit(index: number, save: boolean) { const page = getPageStruct(); await page.crisisHref.click(); let crisisEle = page.crisisList.get(index); - let text = await crisisEle.getText(); + const text = await crisisEle.getText(); expect(text.length).toBeGreaterThan(0, 'crisis item text length'); // remove leading id from text const crisisText = text.substr(text.indexOf(' ')).trim(); await crisisEle.click(); - expect(page.crisisDetail.isPresent()).toBe(true, 'crisis detail present'); - expect(page.crisisDetailTitle.getText()).toContain(crisisText); - let inputEle = page.crisisDetail.element(by.css('input')); + expect(await page.crisisDetail.isPresent()).toBe(true, 'crisis detail present'); + expect(await page.crisisDetailTitle.getText()).toContain(crisisText); + const inputEle = page.crisisDetail.element(by.css('input')); await inputEle.sendKeys('-foo'); - let buttonEle = page.crisisDetail.element(by.buttonText(save ? 'Save' : 'Cancel')); + const buttonEle = page.crisisDetail.element(by.buttonText(save ? 'Save' : 'Cancel')); await buttonEle.click(); crisisEle = page.crisisList.get(index); if (save) { - expect(crisisEle.getText()).toContain(crisisText + '-foo'); + expect(await crisisEle.getText()).toContain(crisisText + '-foo'); } else { await browser.wait(EC.alertIsPresent(), 4000); await browser.switchTo().alert().accept(); - expect(crisisEle.getText()).toContain(crisisText); + expect(await crisisEle.getText()).toContain(crisisText); } } diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts index 8806d7cea8..6db32e4807 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.1.ts @@ -1,8 +1,8 @@ // #docregion -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; @Component({ selector: 'app-admin-dashboard', diff --git a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts index 2447f3ad9c..db892d9990 100644 --- a/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts +++ b/aio/content/examples/router/src/app/admin/admin-dashboard/admin-dashboard.component.ts @@ -1,8 +1,8 @@ // #docregion -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { SelectivePreloadingStrategyService } from '../../selective-preloading-strategy.service'; diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts index 6c3f6cbfc4..3f41e273ff 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.1.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin/admin.component'; -import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-routes const adminRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts index 9b80c24390..3a29df5db9 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.2.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin/admin.component'; -import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-route -import { AuthGuard } from '../auth/auth.guard'; +import { AuthGuard } from '../auth/auth.guard'; const adminRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts index b337cab0b5..5d9afe4728 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.3.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin/admin.component'; -import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; // #docregion admin-route -import { AuthGuard } from '../auth/auth.guard'; +import { AuthGuard } from '../auth/auth.guard'; // #docregion can-activate-child const adminRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/admin/admin-routing.module.ts b/aio/content/examples/router/src/app/admin/admin-routing.module.ts index 6faa557b16..985e2bb443 100644 --- a/aio/content/examples/router/src/app/admin/admin-routing.module.ts +++ b/aio/content/examples/router/src/app/admin/admin-routing.module.ts @@ -1,14 +1,14 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AdminComponent } from './admin/admin.component'; -import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; -import { AuthGuard } from '../auth/auth.guard'; +import { AuthGuard } from '../auth/auth.guard'; const adminRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/admin/admin.module.ts b/aio/content/examples/router/src/app/admin/admin.module.ts index d276b0efd5..c17b352d54 100644 --- a/aio/content/examples/router/src/app/admin/admin.module.ts +++ b/aio/content/examples/router/src/app/admin/admin.module.ts @@ -1,13 +1,13 @@ // #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; -import { AdminComponent } from './admin/admin.component'; -import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; -import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; -import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; +import { AdminComponent } from './admin/admin.component'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { ManageCrisesComponent } from './manage-crises/manage-crises.component'; +import { ManageHeroesComponent } from './manage-heroes/manage-heroes.component'; -import { AdminRoutingModule } from './admin-routing.module'; +import { AdminRoutingModule } from './admin-routing.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/router/src/app/app-routing.module.1.ts b/aio/content/examples/router/src/app/app-routing.module.1.ts index 181384575f..d568b27de9 100644 --- a/aio/content/examples/router/src/app/app-routing.module.1.ts +++ b/aio/content/examples/router/src/app/app-routing.module.1.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { HeroListComponent } from './hero-list/hero-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; // #docregion appRoutes diff --git a/aio/content/examples/router/src/app/app-routing.module.2.ts b/aio/content/examples/router/src/app/app-routing.module.2.ts index 01192901f6..c232c11091 100644 --- a/aio/content/examples/router/src/app/app-routing.module.2.ts +++ b/aio/content/examples/router/src/app/app-routing.module.2.ts @@ -1,11 +1,11 @@ // #docregion // #docregion milestone3 -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; // #enddocregion milestone3 -// import { HeroListComponent } from './hero-list/hero-list.component'; // <-- delete this line +// import { HeroListComponent } from './hero-list/hero-list.component'; // <-- delete this line // #docregion milestone3 import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; diff --git a/aio/content/examples/router/src/app/app-routing.module.3.ts b/aio/content/examples/router/src/app/app-routing.module.3.ts index 4351476a4e..5d7c74e921 100644 --- a/aio/content/examples/router/src/app/app-routing.module.3.ts +++ b/aio/content/examples/router/src/app/app-routing.module.3.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion , v3 -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; // #enddocregion v3 import { ComposeMessageComponent } from './compose-message/compose-message.component'; // #docregion v3 -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const appRoutes: Routes = [ // #enddocregion v3 diff --git a/aio/content/examples/router/src/app/app-routing.module.4.ts b/aio/content/examples/router/src/app/app-routing.module.4.ts index 5943ad7d05..892a70fec4 100644 --- a/aio/content/examples/router/src/app/app-routing.module.4.ts +++ b/aio/content/examples/router/src/app/app-routing.module.4.ts @@ -1,10 +1,10 @@ // #docregion -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { CanDeactivateGuard } from './can-deactivate.guard'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { CanDeactivateGuard } from './can-deactivate.guard'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const appRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/app-routing.module.5.ts b/aio/content/examples/router/src/app/app-routing.module.5.ts index 0bb4abe9b4..112493b86d 100644 --- a/aio/content/examples/router/src/app/app-routing.module.5.ts +++ b/aio/content/examples/router/src/app/app-routing.module.5.ts @@ -1,14 +1,14 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; // #docregion import-router import { RouterModule, Routes } from '@angular/router'; // #enddocregion import-router import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { AuthGuard } from './auth/auth.guard'; +import { AuthGuard } from './auth/auth.guard'; const appRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/app-routing.module.6.ts b/aio/content/examples/router/src/app/app-routing.module.6.ts index 8cdd93ca84..33edb0a2b5 100644 --- a/aio/content/examples/router/src/app/app-routing.module.6.ts +++ b/aio/content/examples/router/src/app/app-routing.module.6.ts @@ -1,6 +1,6 @@ // #docplaster // #docregion, preload-v1 -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes, // #enddocregion preload-v1 @@ -9,9 +9,9 @@ import { } from '@angular/router'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { AuthGuard } from './auth/auth.guard'; +import { AuthGuard } from './auth/auth.guard'; const appRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/app-routing.module.8.ts b/aio/content/examples/router/src/app/app-routing.module.8.ts index 7186ced298..f5334003f5 100644 --- a/aio/content/examples/router/src/app/app-routing.module.8.ts +++ b/aio/content/examples/router/src/app/app-routing.module.8.ts @@ -6,13 +6,11 @@ import { Routes, RouterModule } from '@angular/router'; // CLI imports router const routes: Routes = [ { path: 'first-component', component: FirstComponent }, { path: 'second-component', component: SecondComponent }, - // #enddocregion routes + // #enddocregion routes, routes-with-wildcard { path: '', redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component` - { path: '**', component: FirstComponent }, - // #enddocregion redirect + // #docregion routes-with-wildcard { path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page // #docregion routes - // #docregion redirect ]; // #enddocregion routes, routes-with-wildcard, redirect diff --git a/aio/content/examples/router/src/app/app-routing.module.9.ts b/aio/content/examples/router/src/app/app-routing.module.9.ts index cd4ddd1d70..a0b28981ca 100644 --- a/aio/content/examples/router/src/app/app-routing.module.9.ts +++ b/aio/content/examples/router/src/app/app-routing.module.9.ts @@ -4,18 +4,21 @@ import { Routes, RouterModule } from '@angular/router'; // CLI imports router // #docregion child-routes const routes: Routes = [ - { path: 'first-component', + { + path: 'first-component', component: FirstComponent, // this is the component with the in the template children: [ { path: 'child-a', // child route path - component: ChildAComponent // child route component that the router renders + component: ChildAComponent, // child route component that the router renders }, { path: 'child-b', - component: ChildBComponent // another child route component that the router renders - } - ] }, + component: ChildBComponent, // another child route component that the router renders + }, + ], + }, +]; // #enddocregion child-routes diff --git a/aio/content/examples/router/src/app/app-routing.module.ts b/aio/content/examples/router/src/app/app-routing.module.ts index 4993057d8d..deb5db507b 100644 --- a/aio/content/examples/router/src/app/app-routing.module.ts +++ b/aio/content/examples/router/src/app/app-routing.module.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion, preload-v1 -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { ComposeMessageComponent } from './compose-message/compose-message.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { AuthGuard } from './auth/auth.guard'; +import { AuthGuard } from './auth/auth.guard'; import { SelectivePreloadingStrategyService } from './selective-preloading-strategy.service'; const appRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/app.component.2.ts b/aio/content/examples/router/src/app/app.component.2.ts index 79b5689cc8..e003a71133 100644 --- a/aio/content/examples/router/src/app/app.component.2.ts +++ b/aio/content/examples/router/src/app/app.component.2.ts @@ -15,7 +15,7 @@ import { slideInAnimation } from './animations'; // #docregion function-binding export class AppComponent { getAnimationData(outlet: RouterOutlet) { - return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; + return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation; } } // #enddocregion function-binding diff --git a/aio/content/examples/router/src/app/app.component.3.ts b/aio/content/examples/router/src/app/app.component.3.ts index a8d8e0de0c..879600274d 100644 --- a/aio/content/examples/router/src/app/app.component.3.ts +++ b/aio/content/examples/router/src/app/app.component.3.ts @@ -1,7 +1,7 @@ /* tslint:disable:no-unused-variable */ // #docplaster import { Component } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router } from '@angular/router'; @Component({ selector: 'app-root', diff --git a/aio/content/examples/router/src/app/app.component.ts b/aio/content/examples/router/src/app/app.component.ts index 6747d50278..3fa874795c 100644 --- a/aio/content/examples/router/src/app/app.component.ts +++ b/aio/content/examples/router/src/app/app.component.ts @@ -12,6 +12,6 @@ import { slideInAnimation } from './animations'; }) export class AppComponent { getAnimationData(outlet: RouterOutlet) { - return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation']; + return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation; } } diff --git a/aio/content/examples/router/src/app/app.module.0.ts b/aio/content/examples/router/src/app/app.module.0.ts index ca3c93cb6b..df5b74c6d7 100644 --- a/aio/content/examples/router/src/app/app.module.0.ts +++ b/aio/content/examples/router/src/app/app.module.0.ts @@ -1,11 +1,11 @@ // NEVER USED. For docs only. Should compile though // #docplaster -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list/hero-list.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { PageNotFoundComponent as HeroDetailComponent } from './page-not-found/page-not-found.component'; // #docregion diff --git a/aio/content/examples/router/src/app/app.module.1.ts b/aio/content/examples/router/src/app/app.module.1.ts index 42a1b546fc..9587edc9b3 100644 --- a/aio/content/examples/router/src/app/app.module.1.ts +++ b/aio/content/examples/router/src/app/app.module.1.ts @@ -1,16 +1,16 @@ // #docplaster // #docregion // #docregion first-config -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; // #docregion import-router import { RouterModule, Routes } from '@angular/router'; // #enddocregion import-router -import { AppComponent } from './app.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { HeroListComponent } from './hero-list/hero-list.component'; +import { AppComponent } from './app.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; // #enddocregion first-config import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; // #docregion first-config diff --git a/aio/content/examples/router/src/app/app.module.2.ts b/aio/content/examples/router/src/app/app.module.2.ts index 5bb925b76c..5117f9aab4 100644 --- a/aio/content/examples/router/src/app/app.module.2.ts +++ b/aio/content/examples/router/src/app/app.module.2.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion // #docregion hero-import -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { HeroListComponent } from './hero-list/hero-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; @NgModule({ diff --git a/aio/content/examples/router/src/app/app.module.3.ts b/aio/content/examples/router/src/app/app.module.3.ts index 4393a1b935..0fd2e0557d 100644 --- a/aio/content/examples/router/src/app/app.module.3.ts +++ b/aio/content/examples/router/src/app/app.module.3.ts @@ -1,18 +1,18 @@ // #docplaster // #docregion // #docregion remove-heroes -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; // #enddocregion remove-heroes import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; // #docregion remove-heroes -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; -import { HeroesModule } from './heroes/heroes.module'; +import { HeroesModule } from './heroes/heroes.module'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; @NgModule({ diff --git a/aio/content/examples/router/src/app/app.module.4.ts b/aio/content/examples/router/src/app/app.module.4.ts index 104a352e5d..ae0fe92822 100644 --- a/aio/content/examples/router/src/app/app.module.4.ts +++ b/aio/content/examples/router/src/app/app.module.4.ts @@ -1,20 +1,20 @@ // #docplaster // #docregion // #docregion crisis-center-module, admin-module -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { AppRoutingModule } from './app-routing.module'; -import { HeroesModule } from './heroes/heroes.module'; -import { CrisisCenterModule } from './crisis-center/crisis-center.module'; +import { AppRoutingModule } from './app-routing.module'; +import { HeroesModule } from './heroes/heroes.module'; +import { CrisisCenterModule } from './crisis-center/crisis-center.module'; // #enddocregion crisis-center-module -import { AdminModule } from './admin/admin.module'; +import { AdminModule } from './admin/admin.module'; // #docregion crisis-center-module @NgModule({ diff --git a/aio/content/examples/router/src/app/app.module.5.ts b/aio/content/examples/router/src/app/app.module.5.ts index 593c513e04..46d3781abf 100644 --- a/aio/content/examples/router/src/app/app.module.5.ts +++ b/aio/content/examples/router/src/app/app.module.5.ts @@ -1,19 +1,19 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; -import { HeroesModule } from './heroes/heroes.module'; -import { CrisisCenterModule } from './crisis-center/crisis-center.module'; +import { HeroesModule } from './heroes/heroes.module'; +import { CrisisCenterModule } from './crisis-center/crisis-center.module'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; -import { AdminModule } from './admin/admin.module'; +import { AdminModule } from './admin/admin.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/router/src/app/app.module.6.ts b/aio/content/examples/router/src/app/app.module.6.ts index 6875f40d5e..f8dd92b238 100644 --- a/aio/content/examples/router/src/app/app.module.6.ts +++ b/aio/content/examples/router/src/app/app.module.6.ts @@ -1,10 +1,10 @@ // #docregion -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; import { Routes, RouterModule } from '@angular/router'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; const routes: Routes = [ diff --git a/aio/content/examples/router/src/app/app.module.7.ts b/aio/content/examples/router/src/app/app.module.7.ts index 2e0428583d..51a5e8446c 100644 --- a/aio/content/examples/router/src/app/app.module.7.ts +++ b/aio/content/examples/router/src/app/app.module.7.ts @@ -1,17 +1,17 @@ // #docregion -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; -import { Router } from '@angular/router'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; -import { AppComponent } from './app.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { AppRoutingModule } from './app-routing.module'; -import { HeroesModule } from './heroes/heroes.module'; -import { CrisisCenterModule } from './crisis-center/crisis-center.module'; -import { AuthModule } from './auth/auth.module'; +import { AppRoutingModule } from './app-routing.module'; +import { HeroesModule } from './heroes/heroes.module'; +import { CrisisCenterModule } from './crisis-center/crisis-center.module'; +import { AuthModule } from './auth/auth.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/router/src/app/app.module.ts b/aio/content/examples/router/src/app/app.module.ts index 38168eb011..4e85829763 100644 --- a/aio/content/examples/router/src/app/app.module.ts +++ b/aio/content/examples/router/src/app/app.module.ts @@ -1,8 +1,8 @@ // #docplaster // #docregion auth, preload -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; // #docregion animations-module import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -12,13 +12,13 @@ import { Router } from '@angular/router'; // #enddocregion inspect-config // #docregion auth -import { AppComponent } from './app.component'; -import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; +import { AppComponent } from './app.component'; +import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { ComposeMessageComponent } from './compose-message/compose-message.component'; -import { AppRoutingModule } from './app-routing.module'; -import { HeroesModule } from './heroes/heroes.module'; -import { AuthModule } from './auth/auth.module'; +import { AppRoutingModule } from './app-routing.module'; +import { HeroesModule } from './heroes/heroes.module'; +import { AuthModule } from './auth/auth.module'; // #docregion animations-module @NgModule({ diff --git a/aio/content/examples/router/src/app/auth/auth-routing.module.ts b/aio/content/examples/router/src/app/auth/auth-routing.module.ts index 78d28ddfc8..114752afa6 100644 --- a/aio/content/examples/router/src/app/auth/auth-routing.module.ts +++ b/aio/content/examples/router/src/app/auth/auth-routing.module.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AuthGuard } from './auth.guard'; -import { AuthService } from './auth.service'; -import { LoginComponent } from './login/login.component'; +import { AuthGuard } from './auth.guard'; +import { AuthService } from './auth.service'; +import { LoginComponent } from './login/login.component'; const authRoutes: Routes = [ { path: 'login', component: LoginComponent } diff --git a/aio/content/examples/router/src/app/auth/auth.guard.2.ts b/aio/content/examples/router/src/app/auth/auth.guard.2.ts index 1b813fc5c2..80df7d7032 100644 --- a/aio/content/examples/router/src/app/auth/auth.guard.2.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.2.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router'; -import { AuthService } from './auth.service'; +import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root', @@ -13,7 +13,7 @@ export class AuthGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree { - let url: string = state.url; + const url: string = state.url; return this.checkLogin(url); } diff --git a/aio/content/examples/router/src/app/auth/auth.guard.3.ts b/aio/content/examples/router/src/app/auth/auth.guard.3.ts index 5b958074f9..5125795eb8 100644 --- a/aio/content/examples/router/src/app/auth/auth.guard.3.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.3.ts @@ -1,13 +1,13 @@ // #docregion can-activate-child -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild, UrlTree -} from '@angular/router'; -import { AuthService } from './auth.service'; +} from '@angular/router'; +import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root', @@ -18,7 +18,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree { - let url: string = state.url; + const url: string = state.url; return this.checkLogin(url); } diff --git a/aio/content/examples/router/src/app/auth/auth.guard.4.ts b/aio/content/examples/router/src/app/auth/auth.guard.4.ts index 13c8f9dff1..bc4be84e46 100644 --- a/aio/content/examples/router/src/app/auth/auth.guard.4.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.4.ts @@ -1,6 +1,6 @@ // #docplaster // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, @@ -8,8 +8,8 @@ import { CanActivateChild, NavigationExtras, UrlTree -} from '@angular/router'; -import { AuthService } from './auth.service'; +} from '@angular/router'; +import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root', @@ -18,7 +18,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { constructor(private authService: AuthService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree { - let url: string = state.url; + const url: string = state.url; return this.checkLogin(url); } @@ -34,12 +34,12 @@ export class AuthGuard implements CanActivate, CanActivateChild { this.authService.redirectUrl = url; // Create a dummy session id - let sessionId = 123456789; + const sessionId = 123456789; // Set our navigation extras object // that contains our global query params and fragment - let navigationExtras: NavigationExtras = { - queryParams: { 'session_id': sessionId }, + const navigationExtras: NavigationExtras = { + queryParams: { session_id: sessionId }, fragment: 'anchor' }; diff --git a/aio/content/examples/router/src/app/auth/auth.guard.ts b/aio/content/examples/router/src/app/auth/auth.guard.ts index 8f7d863cc1..3aa5487708 100644 --- a/aio/content/examples/router/src/app/auth/auth.guard.ts +++ b/aio/content/examples/router/src/app/auth/auth.guard.ts @@ -1,5 +1,5 @@ // #docplaster -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, @@ -7,8 +7,8 @@ import { CanActivateChild, NavigationExtras, CanLoad, Route -} from '@angular/router'; -import { AuthService } from './auth.service'; +} from '@angular/router'; +import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root', @@ -17,7 +17,7 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { constructor(private authService: AuthService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { - let url: string = state.url; + const url: string = state.url; return this.checkLogin(url); } @@ -28,7 +28,7 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { // #docregion, canLoad canLoad(route: Route): boolean { - let url = `/${route.path}`; + const url = `/${route.path}`; return this.checkLogin(url); } @@ -41,12 +41,12 @@ export class AuthGuard implements CanActivate, CanActivateChild, CanLoad { this.authService.redirectUrl = url; // Create a dummy session id - let sessionId = 123456789; + const sessionId = 123456789; // Set our navigation extras object // that contains our global query params and fragment - let navigationExtras: NavigationExtras = { - queryParams: { 'session_id': sessionId }, + const navigationExtras: NavigationExtras = { + queryParams: { session_id: sessionId }, fragment: 'anchor' }; diff --git a/aio/content/examples/router/src/app/auth/auth.module.ts b/aio/content/examples/router/src/app/auth/auth.module.ts index f81d867862..0c6a43d684 100644 --- a/aio/content/examples/router/src/app/auth/auth.module.ts +++ b/aio/content/examples/router/src/app/auth/auth.module.ts @@ -1,10 +1,10 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; -import { LoginComponent } from './login/login.component'; +import { LoginComponent } from './login/login.component'; import { AuthRoutingModule } from './auth-routing.module'; // #docregion v1 diff --git a/aio/content/examples/router/src/app/auth/login/login.component.ts b/aio/content/examples/router/src/app/auth/login/login.component.ts index 652afc66f3..66461effca 100644 --- a/aio/content/examples/router/src/app/auth/login/login.component.ts +++ b/aio/content/examples/router/src/app/auth/login/login.component.ts @@ -32,7 +32,7 @@ export class LoginComponent { // #docregion preserve // Set our navigation extras object // that passes on our global query params and fragment - let navigationExtras: NavigationExtras = { + const navigationExtras: NavigationExtras = { queryParamsHandling: 'preserve', preserveFragment: true }; diff --git a/aio/content/examples/router/src/app/can-deactivate.guard.1.ts b/aio/content/examples/router/src/app/can-deactivate.guard.1.ts index b490093d42..70ac2190c4 100644 --- a/aio/content/examples/router/src/app/can-deactivate.guard.1.ts +++ b/aio/content/examples/router/src/app/can-deactivate.guard.1.ts @@ -1,9 +1,9 @@ // #docregion -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; import { CanDeactivate, ActivatedRouteSnapshot, - RouterStateSnapshot } from '@angular/router'; + RouterStateSnapshot } from '@angular/router'; import { CrisisDetailComponent } from './crisis-center/crisis-detail/crisis-detail.component'; diff --git a/aio/content/examples/router/src/app/can-deactivate.guard.ts b/aio/content/examples/router/src/app/can-deactivate.guard.ts index 41a93a2af4..5974ac266e 100644 --- a/aio/content/examples/router/src/app/can-deactivate.guard.ts +++ b/aio/content/examples/router/src/app/can-deactivate.guard.ts @@ -1,7 +1,7 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable } from 'rxjs'; export interface CanComponentDeactivate { canDeactivate: () => Observable | Promise | boolean; diff --git a/aio/content/examples/router/src/app/compose-message/compose-message.component.ts b/aio/content/examples/router/src/app/compose-message/compose-message.component.ts index 445eb8e396..534a341ca5 100644 --- a/aio/content/examples/router/src/app/compose-message/compose-message.component.ts +++ b/aio/content/examples/router/src/app/compose-message/compose-message.component.ts @@ -1,6 +1,6 @@ // #docregion import { Component, HostBinding } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router } from '@angular/router'; @Component({ selector: 'app-compose-message', diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts index c002b2ebb3..24dc4f7bc2 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.1.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #docregion routes const crisisCenterRoutes: Routes = [ diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts index 5bc0dfee02..d894507e1e 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.2.ts @@ -1,19 +1,19 @@ // #docplaster // #docregion routes -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #enddocregion routes // #docregion can-deactivate-guard -import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; // #enddocregion can-deactivate-guard // #docregion crisis-detail-resolver -import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; // #enddocregion crisis-detail-resolver // #docregion routes diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts index f134bd34e9..4082a7d433 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.3.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; // #docregion can-deactivate-guard -import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; const crisisCenterRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts index 03ffed2ec2..9774c2f0b3 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.4.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; -import { CanDeactivateGuard } from '../can-deactivate.guard'; -import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; const crisisCenterRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts index 393deb7d8d..74200a8427 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center-routing.module.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; -import { CanDeactivateGuard } from '../can-deactivate.guard'; -import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; +import { CanDeactivateGuard } from '../can-deactivate.guard'; +import { CrisisDetailResolverService } from './crisis-detail-resolver.service'; const crisisCenterRoutes: Routes = [ { diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts b/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts index fb6753011e..f14420fe8f 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-center.module.ts @@ -1,12 +1,12 @@ // #docregion -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; import { CrisisCenterHomeComponent } from './crisis-center-home/crisis-center-home.component'; -import { CrisisListComponent } from './crisis-list/crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; -import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; +import { CrisisListComponent } from './crisis-list/crisis-list.component'; +import { CrisisCenterComponent } from './crisis-center/crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail/crisis-detail.component'; import { CrisisCenterRoutingModule } from './crisis-center-routing.module'; diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts index eed3b2a190..cc521b439a 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail-resolver.service.ts @@ -1,15 +1,15 @@ // #docregion -import { Injectable } from '@angular/core'; +import { Injectable } from '@angular/core'; import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot -} from '@angular/router'; -import { Observable, of, EMPTY } from 'rxjs'; -import { mergeMap, take } from 'rxjs/operators'; +} from '@angular/router'; +import { Observable, of, EMPTY } from 'rxjs'; +import { mergeMap, take } from 'rxjs/operators'; -import { CrisisService } from './crisis.service'; +import { CrisisService } from './crisis.service'; import { Crisis } from './crisis'; @Injectable({ @@ -19,7 +19,7 @@ export class CrisisDetailResolverService implements Resolve { constructor(private cs: CrisisService, private router: Router) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Observable { - let id = route.paramMap.get('id'); + const id = route.paramMap.get('id'); return this.cs.getCrisis(id).pipe( take(1), diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts index 8a70cd9b86..6b1399eb9a 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.1.ts @@ -1,13 +1,13 @@ // #docplaster // #docregion -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router, ParamMap } from '@angular/router'; -import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; -import { CrisisService } from '../crisis.service'; -import { Crisis } from '../crisis'; -import { DialogService } from '../../dialog.service'; +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; +import { DialogService } from '../../dialog.service'; @Component({ selector: 'app-crisis-detail', @@ -62,7 +62,7 @@ export class CrisisDetailComponent implements OnInit { } gotoCrises() { - let crisisId = this.crisis ? this.crisis.id : null; + const crisisId = this.crisis ? this.crisis.id : null; // Pass along the crisis id if available // so that the CrisisListComponent can select that crisis. // Add a totally useless `foo` parameter for kicks. diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts index 1aeeff58aa..3ce296d04c 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts @@ -4,8 +4,8 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { Crisis } from '../crisis'; -import { DialogService } from '../../dialog.service'; +import { Crisis } from '../crisis'; +import { DialogService } from '../../dialog.service'; @Component({ selector: 'app-crisis-detail', @@ -56,7 +56,7 @@ export class CrisisDetailComponent implements OnInit { // #enddocregion canDeactivate gotoCrises() { - let crisisId = this.crisis ? this.crisis.id : null; + const crisisId = this.crisis ? this.crisis.id : null; // Pass along the crisis id if available // so that the CrisisListComponent can select that crisis. // Add a totally useless `foo` parameter for kicks. diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts index eac3ba228e..2c01ce0095 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.1.ts @@ -1,11 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, ParamMap } from '@angular/router'; -import { CrisisService } from '../crisis.service'; -import { Crisis } from '../crisis'; -import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; @Component({ selector: 'app-crisis-list', diff --git a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts index 21a6e63fb0..bb4bc56284 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis-list/crisis-list.component.ts @@ -1,11 +1,11 @@ // #docregion -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; -import { CrisisService } from '../crisis.service'; -import { Crisis } from '../crisis'; -import { Observable } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { CrisisService } from '../crisis.service'; +import { Crisis } from '../crisis'; +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; @Component({ selector: 'app-crisis-list', diff --git a/aio/content/examples/router/src/app/crisis-center/crisis.service.ts b/aio/content/examples/router/src/app/crisis-center/crisis.service.ts index a1ebc42256..957b65c9c9 100644 --- a/aio/content/examples/router/src/app/crisis-center/crisis.service.ts +++ b/aio/content/examples/router/src/app/crisis-center/crisis.service.ts @@ -29,7 +29,7 @@ export class CrisisService { addCrisis(name: string) { name = name.trim(); if (name) { - let crisis = { id: CrisisService.nextCrisisId++, name }; + const crisis = { id: CrisisService.nextCrisisId++, name }; CRISES.push(crisis); this.crises$.next(CRISES); } diff --git a/aio/content/examples/router/src/app/crisis-center/mock-crises.ts b/aio/content/examples/router/src/app/crisis-center/mock-crises.ts index 1870a86170..c21611815d 100644 --- a/aio/content/examples/router/src/app/crisis-center/mock-crises.ts +++ b/aio/content/examples/router/src/app/crisis-center/mock-crises.ts @@ -6,4 +6,4 @@ export const CRISES: Crisis[] = [ { id: 2, name: 'Sky Rains Great White Sharks' }, { id: 3, name: 'Giant Asteroid Heading For Earth' }, { id: 4, name: 'Procrastinators Meeting Delayed Again' }, -] +]; diff --git a/aio/content/examples/router/src/app/dialog.service.ts b/aio/content/examples/router/src/app/dialog.service.ts index e0ed6760cb..2c4de6b403 100644 --- a/aio/content/examples/router/src/app/dialog.service.ts +++ b/aio/content/examples/router/src/app/dialog.service.ts @@ -19,5 +19,5 @@ export class DialogService { const confirmation = window.confirm(message || 'Is it OK?'); return of(confirmation); - }; + } } diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts index 23d7e4fdc9..135863ebff 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.2.ts @@ -1,8 +1,8 @@ // Snapshot version // #docregion -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { HeroService } from '../hero.service'; import { Hero } from '../hero'; @@ -23,7 +23,7 @@ export class HeroDetailComponent implements OnInit { // #docregion snapshot ngOnInit() { - let id = this.route.snapshot.paramMap.get('id'); + const id = this.route.snapshot.paramMap.get('id'); this.hero$ = this.service.getHero(id); } diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts index 52d670637e..afcb4fa6c5 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.3.ts @@ -7,7 +7,7 @@ import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Observable } from 'rxjs'; -import { HeroService } from '../hero.service'; +import { HeroService } from '../hero.service'; import { Hero } from '../hero'; @Component({ @@ -37,7 +37,7 @@ export class HeroDetailComponent implements OnInit { // #docregion gotoHeroes gotoHeroes(hero: Hero) { - let heroId = hero ? hero.id : null; + const heroId = hero ? hero.id : null; // Pass along the hero id if available // so that the HeroList component can select that hero. // Include a junk 'foo' property for fun. diff --git a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts index 8f54532b65..b5114be22d 100644 --- a/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-detail/hero-detail.component.ts @@ -7,7 +7,7 @@ import { Router, ActivatedRoute, ParamMap } from '@angular/router'; // #enddocregion imports-route-info import { Observable } from 'rxjs'; -import { HeroService } from '../hero.service'; +import { HeroService } from '../hero.service'; import { Hero } from '../hero'; @Component({ @@ -38,7 +38,7 @@ export class HeroDetailComponent implements OnInit { // #docregion redirect gotoHeroes(hero: Hero) { - let heroId = hero ? hero.id : null; + const heroId = hero ? hero.id : null; // Pass along the hero id if available // so that the HeroList component can select that hero. // Include a junk 'foo' property for fun. diff --git a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts index 862e12e535..a83563ee58 100644 --- a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.1.ts @@ -1,11 +1,11 @@ // #docplaster // #docregion // TODO: Feature Componetized like HeroCenter -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs'; -import { HeroService } from '../hero.service'; +import { HeroService } from '../hero.service'; import { Hero } from '../hero'; @Component({ diff --git a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts index b367f7b76b..e6e01341aa 100644 --- a/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts +++ b/aio/content/examples/router/src/app/heroes/hero-list/hero-list.component.ts @@ -10,7 +10,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; // #enddocregion import-router -import { HeroService } from '../hero.service'; +import { HeroService } from '../hero.service'; import { Hero } from '../hero'; @Component({ diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts index 7a85522fb3..5cb604444a 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.1.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list/hero-list.component'; -import { HeroDetailComponent } from './hero-detail/hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', component: HeroListComponent }, diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts index d9997c873e..ae041fbaee 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.2.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list/hero-list.component'; -import { HeroDetailComponent } from './hero-detail/hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', component: HeroListComponent, data: { animation: 'heroes' } }, diff --git a/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts b/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts index b059e5d496..5903bd5407 100644 --- a/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts +++ b/aio/content/examples/router/src/app/heroes/heroes-routing.module.ts @@ -1,9 +1,9 @@ // #docregion -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { HeroListComponent } from './hero-list/hero-list.component'; -import { HeroDetailComponent } from './hero-detail/hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; const heroesRoutes: Routes = [ { path: 'heroes', redirectTo: '/superheroes' }, diff --git a/aio/content/examples/router/src/app/heroes/heroes.module.ts b/aio/content/examples/router/src/app/heroes/heroes.module.ts index 13091398ab..e5f8999bc1 100644 --- a/aio/content/examples/router/src/app/heroes/heroes.module.ts +++ b/aio/content/examples/router/src/app/heroes/heroes.module.ts @@ -1,12 +1,12 @@ // #docplaster // #docregion // #docregion v1 -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; -import { HeroListComponent } from './hero-list/hero-list.component'; -import { HeroDetailComponent } from './hero-detail/hero-detail.component'; +import { HeroListComponent } from './hero-list/hero-list.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; // #enddocregion v1 import { HeroesRoutingModule } from './heroes-routing.module'; diff --git a/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts b/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts index a22ac0227e..bcbd539d53 100644 --- a/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts +++ b/aio/content/examples/router/src/app/selective-preloading-strategy.service.ts @@ -10,7 +10,7 @@ export class SelectivePreloadingStrategyService implements PreloadingStrategy { preloadedModules: string[] = []; preload(route: Route, load: () => Observable): Observable { - if (route.data && route.data['preload']) { + if (route.data && route.data.preload) { // add the route path to the preloaded module array this.preloadedModules.push(route.path); diff --git a/aio/content/examples/rx-library/example-config.json b/aio/content/examples/rx-library/example-config.json index c07fa9794c..3aa2059b4d 100644 --- a/aio/content/examples/rx-library/example-config.json +++ b/aio/content/examples/rx-library/example-config.json @@ -2,7 +2,11 @@ "tests": [ { "cmd": "yarn", - "args": [ "tsc", "--project", "./tsconfig.app.json" ] + "args": ["tsc", "--project", "tsconfig.spec.json", "--module", "commonjs"] + }, + { + "cmd": "yarn", + "args": ["jasmine", "out-tsc/**/*.spec.js"] } ] } diff --git a/aio/content/examples/rx-library/src/error-handling.spec.ts b/aio/content/examples/rx-library/src/error-handling.spec.ts new file mode 100644 index 0000000000..9dc82b45e8 --- /dev/null +++ b/aio/content/examples/rx-library/src/error-handling.spec.ts @@ -0,0 +1,46 @@ +import { Subject, throwError } from 'rxjs'; +import { docRegionDefault } from './error-handling'; + +describe('error-handling', () => { + let mockConsole; + let ajaxSubject; + let ajax; + + beforeEach(() => { + mockConsole = {log: jasmine.createSpy('log')}; + ajaxSubject = new Subject(); + ajax = jasmine + .createSpy('ajax') + .and.callFake((url: string) => ajaxSubject); + }); + + afterEach(() => ajaxSubject.unsubscribe()); + + it('should return the response object', () => { + docRegionDefault(mockConsole, ajax); + + ajaxSubject.next({response: {foo: 'bar'}}); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', {foo: 'bar'}] + ]); + }); + + it('should return an empty array when using an object without a `response` property', () => { + docRegionDefault(mockConsole, ajax); + + ajaxSubject.next({foo: 'bar'}); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []] + ]); + }); + + it('should return an empty array when the ajax observable errors', () => { + ajax.and.returnValue(throwError('Test Error')); + + docRegionDefault(mockConsole, ajax); + + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []] + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/error-handling.ts b/aio/content/examples/rx-library/src/error-handling.ts index c7b6c2dc9f..4daca5de8f 100644 --- a/aio/content/examples/rx-library/src/error-handling.ts +++ b/aio/content/examples/rx-library/src/error-handling.ts @@ -1,25 +1,36 @@ - -import { of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion - -import { ajax } from 'rxjs/ajax'; -import { map, catchError } from 'rxjs/operators'; -// Return "response" from the API. If an error happens, -// return an empty array. -const apiData = ajax('/api/data').pipe( - map(res => { - if (!res.response) { - throw new Error('Value expected!'); - } - return res.response; - }), - catchError(err => of([])) -); - -apiData.subscribe({ - next(x) { console.log('data: ', x); }, - error(err) { console.log('errors already caught... will not run'); } -}); + import { of } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { map, catchError } from 'rxjs/operators'; // #enddocregion + +export function docRegionDefault(console, ajax) { + // #docregion + // Return "response" from the API. If an error happens, + // return an empty array. + const apiData = ajax('/api/data').pipe( + map((res: any) => { + if (!res.response) { + throw new Error('Value expected!'); + } + return res.response; + }), + catchError(err => of([])) + ); + + apiData.subscribe({ + next(x) { console.log('data: ', x); }, + error(err) { console.log('errors already caught... will not run'); } + }); + + // #enddocregion + return apiData; +} diff --git a/aio/content/examples/rx-library/src/operators.1.spec.ts b/aio/content/examples/rx-library/src/operators.1.spec.ts new file mode 100644 index 0000000000..04fb0b8de0 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.1.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators.1'; + +describe('squareOdd - operators.1.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [9], + [25], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.1.ts b/aio/content/examples/rx-library/src/operators.1.ts index be37217d9d..1ed0293098 100644 --- a/aio/content/examples/rx-library/src/operators.1.ts +++ b/aio/content/examples/rx-library/src/operators.1.ts @@ -1,23 +1,30 @@ -import { of, pipe } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion - -import { filter, map } from 'rxjs/operators'; - -const nums = of(1, 2, 3, 4, 5); - -// Create a function that accepts an Observable. -const squareOddVals = pipe( - filter((n: number) => n % 2 !== 0), - map(n => n * n) -); - -// Create an Observable that will run the filter and map functions -const squareOdd = squareOddVals(nums); - -// Subscribe to run the combined functions -squareOdd.subscribe(x => console.log(x)); + import { of, pipe } from 'rxjs'; + import { filter, map } from 'rxjs/operators'; // #enddocregion +export function docRegionDefault(console) { + // #docregion + const nums = of(1, 2, 3, 4, 5); + // Create a function that accepts an Observable. + const squareOddVals = pipe( + filter((n: number) => n % 2 !== 0), + map(n => n * n) + ); + + // Create an Observable that will run the filter and map functions + const squareOdd = squareOddVals(nums); + + // Subscribe to run the combined functions + squareOdd.subscribe(x => console.log(x)); + + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/operators.2.spec.ts b/aio/content/examples/rx-library/src/operators.2.spec.ts new file mode 100644 index 0000000000..f9d1d14103 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.2.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators.2'; + +describe('squareOdd - operators.2.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [9], + [25], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.2.ts b/aio/content/examples/rx-library/src/operators.2.ts index 9559ea8525..fd792bff99 100644 --- a/aio/content/examples/rx-library/src/operators.2.ts +++ b/aio/content/examples/rx-library/src/operators.2.ts @@ -1,16 +1,25 @@ -import { Observable, of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion - -import { filter, map } from 'rxjs/operators'; - -const squareOdd = of(1, 2, 3, 4, 5) - .pipe( - filter(n => n % 2 !== 0), - map(n => n * n) - ); - -// Subscribe to get values -squareOdd.subscribe(x => console.log(x)); + import { of } from 'rxjs'; + import { filter, map } from 'rxjs/operators'; // #enddocregion + +export function docRegionDefault(console) { + // #docregion + const squareOdd = of(1, 2, 3, 4, 5) + .pipe( + filter(n => n % 2 !== 0), + map(n => n * n) + ); + + // Subscribe to get values + squareOdd.subscribe(x => console.log(x)); + + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/operators.spec.ts b/aio/content/examples/rx-library/src/operators.spec.ts new file mode 100644 index 0000000000..2842d76806 --- /dev/null +++ b/aio/content/examples/rx-library/src/operators.spec.ts @@ -0,0 +1,14 @@ +import { docRegionDefault } from './operators'; + +describe('squaredNums - operators.ts', () => { + it('should return square odds', () => { + const console = {log: jasmine.createSpy('log')}; + docRegionDefault(console); + expect(console.log).toHaveBeenCalledTimes(3); + expect(console.log.calls.allArgs()).toEqual([ + [1], + [4], + [9], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/operators.ts b/aio/content/examples/rx-library/src/operators.ts index 461482a4bc..d650413c3c 100644 --- a/aio/content/examples/rx-library/src/operators.ts +++ b/aio/content/examples/rx-library/src/operators.ts @@ -1,20 +1,28 @@ - -import { Observable, of } from 'rxjs'; - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ // #docregion - -import { map } from 'rxjs/operators'; - -const nums = of(1, 2, 3); - -const squareValues = map((val: number) => val * val); -const squaredNums = squareValues(nums); - -squaredNums.subscribe(x => console.log(x)); - -// Logs -// 1 -// 4 -// 9 + import { of } from 'rxjs'; + import { map } from 'rxjs/operators'; // #enddocregion + +export function docRegionDefault(console) { + // #docregion + const nums = of(1, 2, 3); + + const squareValues = map((val: number) => val * val); + const squaredNums = squareValues(nums); + + squaredNums.subscribe(x => console.log(x)); + + // Logs + // 1 + // 4 + // 9 + + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/retry-on-error.spec.ts b/aio/content/examples/rx-library/src/retry-on-error.spec.ts new file mode 100644 index 0000000000..89b9f3145a --- /dev/null +++ b/aio/content/examples/rx-library/src/retry-on-error.spec.ts @@ -0,0 +1,69 @@ +import { of, throwError } from 'rxjs'; +import { mergeMap, tap } from 'rxjs/operators'; +import { docRegionDefault } from './retry-on-error'; + +describe('retry-on-error', () => { + let mockConsole; + beforeEach(() => mockConsole = { log: jasmine.createSpy('log') }); + + it('should return the response object', () => { + const ajax = () => of({ response: { foo: 'bar' } }); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', { foo: 'bar' }], + ]); + }); + + it('should return an empty array after 3 retries + 1 initial request', () => { + const ajax = () => { + return of({ noresponse: true }).pipe(tap(() => mockConsole.log('Subscribed to AJAX'))); + }; + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Subscribed to AJAX'], + ['Error occurred.'], + ['Subscribed to AJAX'], + ['Error occurred.'], + ['Subscribed to AJAX'], + ['Error occurred.'], + ['Subscribed to AJAX'], + ['Error occurred.'], + ['data: ', []], + ]); + }); + + it('should return the response if the request succeeds upon retrying', () => { + // Fail on the first two requests, but succeed from the 3rd onwards. + let failCount = 2; + const ajax = () => of(null).pipe( + tap(() => mockConsole.log('Subscribed to AJAX')), + // Fail on the first 2 requests, but succeed from the 3rd onwards. + mergeMap(() => { + if (failCount > 0) { + failCount--; + return throwError('Test error'); + } + return of({ response: { foo: 'bar' } }); + }), + ); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Subscribed to AJAX'], // Initial request | 1st attempt overall + ['Subscribed to AJAX'], // 1st retry attempt | 2nd attempt overall + ['Subscribed to AJAX'], // 2nd retry attempt | 3rd attempt overall + ['data: ', { foo: 'bar' }], + ]); + }); + + it('should return an empty array when the ajax observable throws an error', () => { + const ajax = () => throwError('Test Error'); + + docRegionDefault(mockConsole, ajax); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['data: ', []], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/retry-on-error.ts b/aio/content/examples/rx-library/src/retry-on-error.ts index b1a5389c1b..eaa8539379 100644 --- a/aio/content/examples/rx-library/src/retry-on-error.ts +++ b/aio/content/examples/rx-library/src/retry-on-error.ts @@ -1,26 +1,35 @@ - -import { Observable, of } from 'rxjs'; - - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion - -import { ajax } from 'rxjs/ajax'; -import { map, retry, catchError } from 'rxjs/operators'; - -const apiData = ajax('/api/data').pipe( - retry(3), // Retry up to 3 times before failing - map(res => { - if (!res.response) { - throw new Error('Value expected!'); - } - return res.response; - }), - catchError(err => of([])) -); - -apiData.subscribe({ - next(x) { console.log('data: ', x); }, - error(err) { console.log('errors already caught... will not run'); } -}); + import { of } from 'rxjs'; + import { ajax } from 'rxjs/ajax'; + import { map, retry, catchError } from 'rxjs/operators'; // #enddocregion + +export function docRegionDefault(console, ajax) { + // #docregion + const apiData = ajax('/api/data').pipe( + map((res: any) => { + if (!res.response) { + console.log('Error occurred.'); + throw new Error('Value expected!'); + } + return res.response; + }), + retry(3), // Retry up to 3 times before failing + catchError(err => of([])) + ); + + apiData.subscribe({ + next(x) { console.log('data: ', x); }, + error(err) { console.log('errors already caught... will not run'); } + }); + + // #enddocregion +} diff --git a/aio/content/examples/rx-library/src/simple-creation.1.spec.ts b/aio/content/examples/rx-library/src/simple-creation.1.spec.ts new file mode 100644 index 0000000000..8c2944dd44 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.1.spec.ts @@ -0,0 +1,14 @@ +import { of } from 'rxjs'; +import { docRegionPromise } from './simple-creation.1'; + +describe('simple-creation.1', () => { + it('should create a promise from an observable and return an empty object', () => { + const console = {log: jasmine.createSpy('log')}; + const fetch = () => of({foo: 42}); + docRegionPromise(console, fetch); + expect(console.log.calls.allArgs()).toEqual([ + [{foo: 42}], + ['Completed'], + ]); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.1.ts b/aio/content/examples/rx-library/src/simple-creation.1.ts new file mode 100644 index 0000000000..6388b18cc0 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.1.ts @@ -0,0 +1,24 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion promise + import { from } from 'rxjs'; + +// #enddocregion promise + +export function docRegionPromise(console, fetch) { + // #docregion promise + // Create an Observable out of a promise + const data = from(fetch('/api/endpoint')); + // Subscribe to begin listening for async result + data.subscribe({ + next(response) { console.log(response); }, + error(err) { console.error('Error: ' + err); }, + complete() { console.log('Completed'); } + }); + + // #enddocregion promise +} diff --git a/aio/content/examples/rx-library/src/simple-creation.2.spec.ts b/aio/content/examples/rx-library/src/simple-creation.2.spec.ts new file mode 100644 index 0000000000..4716482a48 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.2.spec.ts @@ -0,0 +1,21 @@ +import { docRegionInterval } from './simple-creation.2'; + +describe('simple-creation.2', () => { + beforeEach(() => jasmine.clock().install()); + afterEach(() => jasmine.clock().uninstall()); + + it('should create an Observable that will publish a value on an interval', () => { + const console = {log: jasmine.createSpy('log')}; + const subscription = docRegionInterval(console); + jasmine.clock().tick(1000); + expect(console.log).toHaveBeenCalledWith('It\'s been 1 seconds since subscribing!'); + console.log.calls.reset(); + + jasmine.clock().tick(999); + expect(console.log).not.toHaveBeenCalled(); + + jasmine.clock().tick(1); + expect(console.log).toHaveBeenCalledWith('It\'s been 2 seconds since subscribing!'); + subscription.unsubscribe(); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.2.ts b/aio/content/examples/rx-library/src/simple-creation.2.ts new file mode 100644 index 0000000000..fb186d693b --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.2.ts @@ -0,0 +1,22 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion interval + import { interval } from 'rxjs'; + +// #enddocregion interval + +export function docRegionInterval(console) { + // #docregion interval + // Create an Observable that will publish a value on an interval + const secondsCounter = interval(1000); + // Subscribe to begin publishing values + const subscription = secondsCounter.subscribe(n => + console.log(`It's been ${n + 1} seconds since subscribing!`)); + + // #enddocregion interval + return subscription; +} diff --git a/aio/content/examples/rx-library/src/simple-creation.3.spec.ts b/aio/content/examples/rx-library/src/simple-creation.3.spec.ts new file mode 100644 index 0000000000..7c1ed586a4 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.3.spec.ts @@ -0,0 +1,53 @@ +import { docRegionEvent } from './simple-creation.3'; + +describe('simple-creation.3', () => { + let triggerMousemove; + let mockConsole; + let input; + let mockDocument; + + beforeEach(() => { + mockConsole = {log: jasmine.createSpy('log')}; + input = { + addEventListener: jasmine + .createSpy('addEventListener') + .and.callFake((eventName, cb) => { + if (eventName === 'mousemove') { + triggerMousemove = cb; + } + }), + removeEventListener: jasmine.createSpy('removeEventListener'), + }; + mockDocument = { getElementById: () => input }; + }); + + it('should log coords when subscribing', () => { + docRegionEvent(mockConsole, mockDocument); + + expect(mockConsole.log).not.toHaveBeenCalled(); + + triggerMousemove({ clientX: 50, clientY: 50 }); + triggerMousemove({ clientX: 30, clientY: 50 }); + triggerMousemove({ clientX: 50, clientY: 30 }); + expect(mockConsole.log).toHaveBeenCalledTimes(3); + expect(mockConsole.log.calls.allArgs()).toEqual([ + ['Coords: 50 X 50'], + ['Coords: 30 X 50'], + ['Coords: 50 X 30'] + ]); + }); + + it('should call unsubscribe when clientX and clientY are below < 40 ', () => { + docRegionEvent(mockConsole, mockDocument); + + expect(mockConsole.log).not.toHaveBeenCalled(); + + // Ensure that we have unsubscribed. + triggerMousemove({ clientX: 30, clientY: 30 }); + expect(input.removeEventListener).toHaveBeenCalledWith('mousemove', triggerMousemove, undefined); + mockConsole.log.calls.reset(); + + triggerMousemove({ clientX: 50, clientY: 50 }); + expect(mockConsole.log).not.toHaveBeenCalled(); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.3.ts b/aio/content/examples/rx-library/src/simple-creation.3.ts new file mode 100644 index 0000000000..63a3651727 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.3.ts @@ -0,0 +1,32 @@ +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:align */ +// #docregion event + import { fromEvent } from 'rxjs'; + +// #enddocregion event + +export function docRegionEvent(console, document) { + // #docregion event + const el = document.getElementById('my-element'); + + // Create an Observable that will publish mouse movements + const mouseMoves = fromEvent(el, 'mousemove'); + + // Subscribe to start listening for mouse-move events + const subscription = mouseMoves.subscribe((evt: MouseEvent) => { + // Log coords of mouse movements + console.log(`Coords: ${evt.clientX} X ${evt.clientY}`); + + // When the mouse is over the upper-left of the screen, + // unsubscribe to stop listening for mouse movements + if (evt.clientX < 40 && evt.clientY < 40) { + subscription.unsubscribe(); + } + }); + + // #enddocregion event +} diff --git a/aio/content/examples/rx-library/src/simple-creation.spec.ts b/aio/content/examples/rx-library/src/simple-creation.spec.ts new file mode 100644 index 0000000000..8de9a846e2 --- /dev/null +++ b/aio/content/examples/rx-library/src/simple-creation.spec.ts @@ -0,0 +1,14 @@ +import { of } from 'rxjs'; +import { docRegionAjax } from './simple-creation'; + +describe('ajax', () => { + it('should make a request and console log the status and response', () => { + const console = {log: jasmine.createSpy('log')}; + const ajax = jasmine.createSpy('ajax').and.callFake((url: string) => { + return of({status: 200, response: 'foo bar'}); + }); + + docRegionAjax(console, ajax); + expect(console.log).toHaveBeenCalledWith(200, 'foo bar'); + }); +}); diff --git a/aio/content/examples/rx-library/src/simple-creation.ts b/aio/content/examples/rx-library/src/simple-creation.ts index ef83631dba..bc2ef49994 100644 --- a/aio/content/examples/rx-library/src/simple-creation.ts +++ b/aio/content/examples/rx-library/src/simple-creation.ts @@ -1,65 +1,19 @@ - -// #docregion promise - -import { from } from 'rxjs'; - -// Create an Observable out of a promise -const data = from(fetch('/api/endpoint')); -// Subscribe to begin listening for async result -data.subscribe({ - next(response) { console.log(response); }, - error(err) { console.error('Error: ' + err); }, - complete() { console.log('Completed'); } -}); - -// #enddocregion promise - -// #docregion interval - -import { interval } from 'rxjs'; - -// Create an Observable that will publish a value on an interval -const secondsCounter = interval(1000); -// Subscribe to begin publishing values -secondsCounter.subscribe(n => - console.log(`It's been ${n} seconds since subscribing!`)); - -// #enddocregion interval - - -// #docregion event - -import { fromEvent } from 'rxjs'; - -const el = document.getElementById('my-element'); - -// Create an Observable that will publish mouse movements -const mouseMoves = fromEvent(el, 'mousemove'); - -// Subscribe to start listening for mouse-move events -const subscription = mouseMoves.subscribe((evt: MouseEvent) => { - // Log coords of mouse movements - console.log(`Coords: ${evt.clientX} X ${evt.clientY}`); - - // When the mouse is over the upper-left of the screen, - // unsubscribe to stop listening for mouse movements - if (evt.clientX < 40 && evt.clientY < 40) { - subscription.unsubscribe(); - } -}); - -// #enddocregion event - - +// #docplaster +/* + Because of how the code is merged together using the doc regions, + we need to indent the imports with the function below. +*/ +/* tslint:disable:no-shadowed-variable */ +/* tslint:disable:align */ // #docregion ajax - -import { ajax } from 'rxjs/ajax'; + import { ajax } from 'rxjs/ajax'; // Create an Observable that will create an AJAX request -const apiData = ajax('/api/data'); -// Subscribe to create the request -apiData.subscribe(res => console.log(res.status, res.response)); - // #enddocregion ajax - - +export function docRegionAjax(console, ajax) { + // #docregion ajax + const apiData = ajax('/api/data'); + // Subscribe to create the request + apiData.subscribe(res => console.log(res.status, res.response)); + // #enddocregion ajax +} diff --git a/aio/content/examples/schematics-for-libraries/projects/my-lib/package.json b/aio/content/examples/schematics-for-libraries/projects/my-lib/package.json index 3607e2c0ce..a53579e4dd 100644 --- a/aio/content/examples/schematics-for-libraries/projects/my-lib/package.json +++ b/aio/content/examples/schematics-for-libraries/projects/my-lib/package.json @@ -16,5 +16,12 @@ "@angular/core": "^7.2.0" }, // #docregion collection - "schematics": "./schematics/collection.json" -} \ No newline at end of file + "schematics": "./schematics/collection.json", +// #enddocregion collection +// #docregion ng-add + "ng-add": { + "save": "devDependencies" + } +// #enddocregion ng-add +// #docregion collection +} diff --git a/aio/content/examples/schematics-for-libraries/projects/my-lib/schematics/ng-add/index.ts b/aio/content/examples/schematics-for-libraries/projects/my-lib/schematics/ng-add/index.ts index f749c7a61f..f56ff2ba1d 100644 --- a/aio/content/examples/schematics-for-libraries/projects/my-lib/schematics/ng-add/index.ts +++ b/aio/content/examples/schematics-for-libraries/projects/my-lib/schematics/ng-add/index.ts @@ -2,9 +2,9 @@ import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; // Just return the tree -export function ngAdd(_options: any): Rule { - return (tree: Tree, _context: SchematicContext) => { - _context.addTask(new NodePackageInstallTask()); +export function ngAdd(options: any): Rule { + return (tree: Tree, context: SchematicContext) => { + context.addTask(new NodePackageInstallTask()); return tree; }; } diff --git a/aio/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.spec.ts b/aio/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.spec.ts index f3c07b177c..ca72b617a9 100644 --- a/aio/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.spec.ts +++ b/aio/content/examples/schematics-for-libraries/projects/my-lib/src/lib/my-lib.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MyLibComponent } from './my-lib.component'; @@ -6,11 +6,8 @@ describe('MyLibComponent', () => { let component: MyLibComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ MyLibComponent ] - }) - .compileComponents(); + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({declarations: [MyLibComponent]}).compileComponents(); })); beforeEach(() => { diff --git a/aio/content/examples/security/e2e/src/app.e2e-spec.ts b/aio/content/examples/security/e2e/src/app.e2e-spec.ts index bc2686d35c..fb8c75ae58 100644 --- a/aio/content/examples/security/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/security/e2e/src/app.e2e-spec.ts @@ -1,37 +1,35 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, By } from 'protractor'; describe('Security E2E Tests', () => { beforeAll(() => browser.get('')); - it('sanitizes innerHTML', () => { - let interpolated = element(By.className('e2e-inner-html-interpolated')); - expect(interpolated.getText()) + it('sanitizes innerHTML', async () => { + const interpolated = element(By.className('e2e-inner-html-interpolated')); + expect(await interpolated.getText()) .toContain('Template Syntax'); - let bound = element(By.className('e2e-inner-html-bound')); - expect(bound.getText()).toContain('Template Syntax'); - let bold = element(By.css('.e2e-inner-html-bound b')); - expect(bold.getText()).toContain('Syntax'); + const bound = element(By.className('e2e-inner-html-bound')); + expect(await bound.getText()).toContain('Template Syntax'); + const bold = element(By.css('.e2e-inner-html-bound b')); + expect(await bold.getText()).toContain('Syntax'); }); - it('escapes untrusted URLs', () => { - let untrustedUrl = element(By.className('e2e-dangerous-url')); - expect(untrustedUrl.getAttribute('href')).toMatch(/^unsafe:javascript/); + it('escapes untrusted URLs', async () => { + const untrustedUrl = element(By.className('e2e-dangerous-url')); + expect(await untrustedUrl.getAttribute('href')).toMatch(/^unsafe:javascript/); }); - it('binds trusted URLs', () => { - let trustedUrl = element(By.className('e2e-trusted-url')); - expect(trustedUrl.getAttribute('href')).toMatch(/^javascript:alert/); + it('binds trusted URLs', async () => { + const trustedUrl = element(By.className('e2e-trusted-url')); + expect(await trustedUrl.getAttribute('href')).toMatch(/^javascript:alert/); }); - it('escapes untrusted resource URLs', () => { - let iframe = element(By.className('e2e-iframe-untrusted-src')); - expect(iframe.getAttribute('src')).toBe(''); + it('escapes untrusted resource URLs', async () => { + const iframe = element(By.className('e2e-iframe-untrusted-src')); + expect(await iframe.getAttribute('src')).toBe(''); }); - it('binds trusted resource URLs', () => { - let iframe = element(By.className('e2e-iframe-trusted-src')); - expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//); + it('binds trusted resource URLs', async () => { + const iframe = element(By.className('e2e-iframe-trusted-src')); + expect(await iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//); }); }); diff --git a/aio/content/examples/service-worker-getting-started/e2e/src/app.e2e-spec.ts b/aio/content/examples/service-worker-getting-started/e2e/src/app.e2e-spec.ts index 033a0af6aa..e0d18fc72c 100755 --- a/aio/content/examples/service-worker-getting-started/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/service-worker-getting-started/e2e/src/app.e2e-spec.ts @@ -1,45 +1,44 @@ import { AppPage } from './app.po'; -import { browser, element, by } from 'protractor'; +import { element, by } from 'protractor'; describe('sw-example App', () => { let page: AppPage; - beforeEach(() => { + beforeEach(async () => { page = new AppPage(); + await page.navigateTo(); }); - it('should display welcome message', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('Welcome to Service Workers!'); + it('should display welcome message', async () => { + expect(await page.getTitleText()).toEqual('Welcome to Service Workers!'); }); - it('should display the Angular logo', () => { - let logo = element(by.css('img')); - page.navigateTo(); - expect(logo.isPresent()).toBe(true); + it('should display the Angular logo', async () => { + const logo = element(by.css('img')); + expect(await logo.isPresent()).toBe(true); }); - it('should show a header for the list of links', () => { + it('should show a header for the list of links', async () => { const listHeader = element(by.css('app-root > h2')); - expect(listHeader.getText()).toEqual('Here are some links to help you start:'); + expect(await listHeader.getText()).toEqual('Here are some links to help you start:'); }); - it('should show a list of links', function () { - element.all(by.css('ul > li > h2 > a')).then((items) => { - expect(items.length).toBe(4); - expect(items[0].getText()).toBe('Angular Service Worker Intro'); - expect(items[1].getText()).toBe('Tour of Heroes'); - expect(items[2].getText()).toBe('CLI Documentation'); - expect(items[3].getText()).toBe('Angular blog'); - }); + it('should show a list of links', async () => { + const items = await element.all(by.css('ul > li > h2 > a')); + + expect(items.length).toBe(4); + expect(await items[0].getText()).toBe('Angular Service Worker Intro'); + expect(await items[1].getText()).toBe('Tour of Heroes'); + expect(await items[2].getText()).toBe('CLI Documentation'); + expect(await items[3].getText()).toBe('Angular blog'); }); // Check for a rejected promise as the service worker is not enabled - it('SwUpdate.checkForUpdate() should return a rejected promise', () => { + it('SwUpdate.checkForUpdate() should return a rejected promise', async () => { const button = element(by.css('button')); const rejectMessage = element(by.css('p')); - button.click(); - expect(rejectMessage.getText()).toContain('rejected: '); + await button.click(); + expect(await rejectMessage.getText()).toContain('rejected: '); }); }); diff --git a/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts new file mode 100644 index 0000000000..2754fa052a --- /dev/null +++ b/aio/content/examples/service-worker-getting-started/src/app/handle-unrecoverable-state.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { SwUpdate } from '@angular/service-worker'; + +function notifyUser(message: string): void { } + +// #docregion sw-unrecoverable-state +@Injectable() +export class HandleUnrecoverableStateService { + constructor(updates: SwUpdate) { + updates.unrecoverable.subscribe(event => { + notifyUser( + `An error occurred that we cannot recover from:\n${event.reason}\n\n` + + 'Please reload the page.'); + }); + } +} +// #enddocregion sw-unrecoverable-state diff --git a/aio/content/examples/set-document-title/e2e/src/app.e2e-spec.ts b/aio/content/examples/set-document-title/e2e/src/app.e2e-spec.ts index b86deb9aa6..c94d0dbd23 100644 --- a/aio/content/examples/set-document-title/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/set-document-title/e2e/src/app.e2e-spec.ts @@ -1,30 +1,21 @@ -'use strict'; // necessary for es6 output in node +import { browser, element, by } from 'protractor'; -import { browser, element, by, ElementFinder } from 'protractor'; +describe('Set Document Title', () => { -describe('Set Document Title', function () { + beforeEach(() => browser.get('')); - beforeAll(function () { - browser.get(''); - }); - - it('should set the document title', function () { - - let titles = [ - 'Good morning!', - 'Good afternoon!', - 'Good evening!' - ]; - - element.all( by.css( 'ul li a' ) ).each( - function iterator( element: ElementFinder, i: number ) { - - element.click(); - expect( browser.getTitle() ).toEqual( titles[ i ] ); - - } - ); + it('should set the document title', async () => { + const elems = await element.all(by.css('ul li a')); + const titles = [ + 'Good morning!', + 'Good afternoon!', + 'Good evening!', + ]; + for (let i = 0; i < elems.length; i++) { + await elems[i].click(); + expect(await browser.getTitle()).toEqual(titles[i]); + } }); }); diff --git a/aio/content/examples/set-document-title/src/app/app.component.ts b/aio/content/examples/set-document-title/src/app/app.component.ts index 14a5c86887..e4d6671a47 100644 --- a/aio/content/examples/set-document-title/src/app/app.component.ts +++ b/aio/content/examples/set-document-title/src/app/app.component.ts @@ -2,28 +2,28 @@ // #docregion // Import the native Angular services. import { Component } from '@angular/core'; -import { Title } from '@angular/platform-browser'; +import { Title } from '@angular/platform-browser'; @Component({ -selector: 'app-root', -template: - `

- Select a title to set on the current HTML document: -

+ selector: 'app-root', + template: ` +

+ Select a title to set on the current HTML document: +

- - ` + + `, }) // #docregion class export class AppComponent { - public constructor(private titleService: Title ) { } + public constructor(private titleService: Title) { } - public setTitle( newTitle: string) { - this.titleService.setTitle( newTitle ); + public setTitle(newTitle: string) { + this.titleService.setTitle(newTitle); } } // #enddocregion class diff --git a/aio/content/examples/set-document-title/src/app/app.module.ts b/aio/content/examples/set-document-title/src/app/app.module.ts index 81f13c244c..a6b1b547f1 100644 --- a/aio/content/examples/set-document-title/src/app/app.module.ts +++ b/aio/content/examples/set-document-title/src/app/app.module.ts @@ -1,6 +1,6 @@ // #docregion import { NgModule } from '@angular/core'; -import { BrowserModule, Title } from '@angular/platform-browser'; +import { BrowserModule, Title } from '@angular/platform-browser'; import { AppComponent } from './app.component'; diff --git a/aio/content/examples/setup/e2e/src/app.e2e-spec.ts b/aio/content/examples/setup/e2e/src/app.e2e-spec.ts index 73921707ee..db185ac5d9 100644 --- a/aio/content/examples/setup/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/setup/e2e/src/app.e2e-spec.ts @@ -1,17 +1,13 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('QuickStart E2E Tests', function () { +describe('QuickStart E2E Tests', () => { - let expectedMsg = 'Hello Angular'; + const expectedMsg = 'Hello Angular'; - beforeEach(function () { - browser.get(''); - }); + beforeEach(() => browser.get('')); - it(`should display: ${expectedMsg}`, function () { - expect(element(by.css('h1')).getText()).toEqual(expectedMsg); + it(`should display: ${expectedMsg}`, async () => { + expect(await element(by.css('h1')).getText()).toEqual(expectedMsg); }); }); diff --git a/aio/content/examples/setup/src/app/app.component.spec.ts b/aio/content/examples/setup/src/app/app.component.spec.ts index eebc33ed8a..36ded93437 100644 --- a/aio/content/examples/setup/src/app/app.component.spec.ts +++ b/aio/content/examples/setup/src/app/app.component.spec.ts @@ -1,19 +1,16 @@ -import { AppComponent } from './app.component'; - -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; describe('AppComponent', () => { let de: DebugElement; let comp: AppComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ AppComponent ] - }) - .compileComponents(); + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({declarations: [AppComponent]}).compileComponents(); })); beforeEach(() => { @@ -22,12 +19,11 @@ describe('AppComponent', () => { de = fixture.debugElement.query(By.css('h1')); }); - it('should create component', () => expect(comp).toBeDefined() ); + it('should create component', () => expect(comp).toBeDefined()); it('should have expected

text', () => { fixture.detectChanges(); const h1 = de.nativeElement; - expect(h1.textContent).toMatch(/angular/i, - '

should say something about "Angular"'); + expect(h1.textContent).toMatch(/angular/i, '

should say something about "Angular"'); }); }); diff --git a/aio/content/examples/setup/src/app/app.module.ts b/aio/content/examples/setup/src/app/app.module.ts index 50a0e6eb47..321a2e0b4a 100644 --- a/aio/content/examples/setup/src/app/app.module.ts +++ b/aio/content/examples/setup/src/app/app.module.ts @@ -1,7 +1,7 @@ // #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/setup/src/main.ts b/aio/content/examples/setup/src/main.ts index 02d58dceac..6b6532d428 100644 --- a/aio/content/examples/setup/src/main.ts +++ b/aio/content/examples/setup/src/main.ts @@ -1,5 +1,5 @@ // #docregion import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; +import { AppModule } from './app/app.module'; platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts b/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts index 9023c4ea89..12092d401e 100644 --- a/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/structural-directives/e2e/src/app.e2e-spec.ts @@ -1,58 +1,53 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Structural Directives', function () { +describe('Structural Directives', () => { - beforeAll(function () { - browser.get(''); - }); + beforeAll(() => browser.get('')); - it('first div should show hero name with *ngIf', function () { + it('first div should show hero name with *ngIf', async () => { const allDivs = element.all(by.tagName('div')); - expect(allDivs.get(0).getText()).toEqual('Dr Nice'); + expect(await allDivs.get(0).getText()).toEqual('Dr Nice'); }); - it('first li should show hero name with *ngFor', function () { + it('first li should show hero name with *ngFor', async () => { const allLis = element.all(by.tagName('li')); - expect(allLis.get(0).getText()).toEqual('Dr Nice'); + expect(await allLis.get(0).getText()).toEqual('Dr Nice'); }); - it('ngSwitch have two instances', function () { + it('ngSwitch have two instances', async () => { const happyHeroEls = element.all(by.tagName('app-happy-hero')); - expect(happyHeroEls.count()).toEqual(2); + expect(await happyHeroEls.count()).toEqual(2); }); - it('should toggle *ngIf="hero" with a button', function () { + it('should toggle *ngIf="hero" with a button', async () => { const toggleHeroButton = element.all(by.cssContainingText('button', 'Toggle hero')).get(0); const paragraph = element.all(by.cssContainingText('p', 'I turned the corner')); - expect(paragraph.get(0).getText()).toContain('I waved'); - toggleHeroButton.click().then(() => { - expect(paragraph.get(0).getText()).not.toContain('I waved'); - }); + expect(await paragraph.get(0).getText()).toContain('I waved'); + await toggleHeroButton.click(); + expect(await paragraph.get(0).getText()).not.toContain('I waved'); }); - it('should have only one "Hip!" (the other is erased)', function () { + it('should have only one "Hip!" (the other is erased)', async () => { const paragraph = element.all(by.cssContainingText('p', 'Hip!')); - expect(paragraph.count()).toEqual(1); + expect(await paragraph.count()).toEqual(1); }); - it('appUnless should show 3 paragraph (A)s and (B)s at the start', function () { + it('appUnless should show 3 paragraph (A)s and (B)s at the start', async () => { const paragraph = element.all(by.css('p.unless')); - expect(paragraph.count()).toEqual(3); + expect(await paragraph.count()).toEqual(3); for (let i = 0; i < 3; i++) { - expect(paragraph.get(i).getText()).toContain('(A)'); + expect(await paragraph.get(i).getText()).toContain('(A)'); } }); - it('appUnless should show 1 paragraph (B) after toggling condition', function () { + it('appUnless should show 1 paragraph (B) after toggling condition', async () => { const toggleConditionButton = element.all(by.cssContainingText('button', 'Toggle condition')).get(0); const paragraph = element.all(by.css('p.unless')); - toggleConditionButton.click().then(() => { - expect(paragraph.count()).toEqual(1); - expect(paragraph.get(0).getText()).toContain('(B)'); - }); + await toggleConditionButton.click(); + + expect(await paragraph.count()).toEqual(1); + expect(await paragraph.get(0).getText()).toContain('(B)'); }); }); diff --git a/aio/content/examples/structural-directives/src/app/app.module.ts b/aio/content/examples/structural-directives/src/app/app.module.ts index b6ffb456c9..04d28e5d40 100644 --- a/aio/content/examples/structural-directives/src/app/app.module.ts +++ b/aio/content/examples/structural-directives/src/app/app.module.ts @@ -1,11 +1,11 @@ // #docregion -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { heroSwitchComponents } from './hero-switch.components'; -import { UnlessDirective } from './unless.directive'; +import { UnlessDirective } from './unless.directive'; @NgModule({ imports: [ BrowserModule, FormsModule ], diff --git a/aio/content/examples/structural-directives/src/app/unless.directive.ts b/aio/content/examples/structural-directives/src/app/unless.directive.ts index 76098c50d5..a0bbda91a7 100644 --- a/aio/content/examples/structural-directives/src/app/unless.directive.ts +++ b/aio/content/examples/structural-directives/src/app/unless.directive.ts @@ -1,3 +1,4 @@ +// tslint:disable: jsdoc-format // #docplaster // #docregion // #docregion no-docs, skeleton diff --git a/aio/content/examples/styleguide/e2e/src/app.e2e-spec.ts b/aio/content/examples/styleguide/e2e/src/app.e2e-spec.ts index c1018c7dd3..e92c52dff5 100644 --- a/aio/content/examples/styleguide/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/styleguide/e2e/src/app.e2e-spec.ts @@ -1,193 +1,147 @@ -'use strict'; // necessary for es6 output in node - import { browser, element, by } from 'protractor'; -describe('Style Guide', function () { - it('01-01', function () { - browser.get('#/01-01'); +describe('Style Guide', () => { + it('01-01', async () => { + await browser.get('#/01-01'); - let pre = element(by.tagName('toh-heroes > pre')); - expect(pre.getText()).toContain('Bombasto'); - expect(pre.getText()).toContain('Tornado'); - expect(pre.getText()).toContain('Magneta'); + const pre = element(by.tagName('toh-heroes > pre')); + expect(await pre.getText()).toContain('Bombasto'); + expect(await pre.getText()).toContain('Tornado'); + expect(await pre.getText()).toContain('Magneta'); }); - it('02-07', function () { - browser.get('#/02-07'); + it('02-07', async () => { + await browser.get('#/02-07'); - let hero = element(by.tagName('toh-hero > div')); - let users = element(by.tagName('admin-users > div')); + const hero = element(by.tagName('toh-hero > div')); + const users = element(by.tagName('admin-users > div')); - expect(hero.getText()).toBe('hero component'); - expect(users.getText()).toBe('users component'); + expect(await hero.getText()).toBe('hero component'); + expect(await users.getText()).toBe('users component'); }); - it('02-08', function () { - browser.get('#/02-08'); + it('02-08', async () => { + await browser.get('#/02-08'); - let input = element(by.tagName('input[tohvalidate]')); - expect(input.isPresent()).toBe(true); + const input = element(by.tagName('input[tohvalidate]')); + expect(await input.isPresent()).toBe(true); }); - it('03-01', function () { - browser.get('#/03-01'); + it('04-10', async () => { + await browser.get('#/04-10'); - let div = element(by.tagName('sg-app > div')); - expect(div.getText()).toBe('The expected error is 42'); + const div = element(by.tagName('sg-app > toh-heroes > div')); + expect(await div.getText()).toBe('This is heroes component'); }); - it('03-02', function () { - browser.get('#/03-02'); + it('05-02', async () => { + await browser.get('#/05-02'); - let divs = element.all(by.tagName('sg-app > div')); - expect(divs.get(0).getText()).toBe('Heroes url: api/heroes'); - expect(divs.get(1).getText()).toBe('Villains url: api/villains'); + const button = element(by.tagName('sg-app > toh-hero-button > button')); + expect(await button.getText()).toBe('Hero button'); }); - it('03-03', function () { - browser.get('#/03-03'); + it('05-03', async () => { + await browser.get('#/05-03'); - let div = element(by.tagName('sg-app > div')); - expect(div.getText()).toBe('Our hero is RubberMan and He is so elastic'); + const button = element(by.tagName('sg-app > toh-hero-button > button')); + expect(await button.getText()).toBe('Hero button'); }); - it('03-04', function () { - browser.get('#/03-04'); + it('05-04', async () => { + await browser.get('#/05-04'); - let buttons = element.all(by.tagName('sg-app > button')); - expect(buttons.get(0).getText()).toBe('Show toast'); - expect(buttons.get(1).getText()).toBe('Hide toast'); + const h2 = element(by.tagName('sg-app > toh-heroes > div > h2')); + expect(await h2.getText()).toBe('My Heroes'); + }); + + it('05-12', async () => { + await browser.get('#/05-12'); + + const button = element(by.tagName('sg-app > toh-hero-button > button')); + expect(await button.getText()).toBe('OK'); + }); + + it('05-13', async () => { + await browser.get('#/05-13'); + + const button = element(by.tagName('sg-app > toh-hero-button > button')); + expect(await button.getText()).toBe('OK'); + }); + + it('05-14', async () => { + await browser.get('#/05-14'); + + const toast = element(by.tagName('sg-app > toh-toast')); + expect(await toast.getText()).toBe('...'); + }); + + it('05-15', async () => { + await browser.get('#/05-15'); + + const heroList = element(by.tagName('sg-app > toh-hero-list')); + expect(await heroList.getText()).toBe('...'); + }); + + it('05-16', async () => { + await browser.get('#/05-16'); + + const hero = element(by.tagName('sg-app > toh-hero')); + expect(await hero.getText()).toBe('...'); + }); + + it('05-17', async () => { + await browser.get('#/05-17'); + + const section = element(by.tagName('sg-app > toh-hero-list > section')); + expect(await section.getText()).toContain('Our list of heroes'); + expect(await section.getText()).toContain('Total powers'); + expect(await section.getText()).toContain('Average power'); + }); + + it('06-01', async () => { + await browser.get('#/06-01'); + + const div = element(by.tagName('sg-app > div[tohhighlight]')); + expect(await div.getText()).toBe('Bombasta'); + }); + + it('06-03', async () => { + await browser.get('#/06-03'); + + const input = element(by.tagName('input[tohvalidator]')); + expect(await input.isPresent()).toBe(true); }); // temporarily disabled because of a weird issue when used with rxjs v6 with rxjs-compat - xit('03-06', function () { - browser.get('#/03-06'); + xit('07-01', async () => { + await browser.get('#/07-01'); - let div = element(by.tagName('sg-app > div')); - expect(div.getText()).toBe('Actual favorite: Windstorm'); - - let lis = element.all(by.tagName('sg-app > ul > li')); - expect(lis.get(0).getText()).toBe('Windstorm'); - expect(lis.get(1).getText()).toBe('Bombasto'); - expect(lis.get(2).getText()).toBe('Magneta'); - expect(lis.get(3).getText()).toBe('Tornado'); + const lis = element.all(by.tagName('sg-app > ul > li')); + expect(await lis.get(0).getText()).toBe('Windstorm'); + expect(await lis.get(1).getText()).toBe('Bombasto'); + expect(await lis.get(2).getText()).toBe('Magneta'); + expect(await lis.get(3).getText()).toBe('Tornado'); }); - it('04-10', function () { - browser.get('#/04-10'); + it('07-03', async () => { + await browser.get('#/07-03'); - let div = element(by.tagName('sg-app > toh-heroes > div')); - expect(div.getText()).toBe('This is heroes component'); + const pre = element(by.tagName('toh-heroes > pre')); + expect(await pre.getText()).toContain('[]'); }); - it('05-02', function () { - browser.get('#/05-02'); + it('07-04', async () => { + await browser.get('#/07-04'); - let button = element(by.tagName('sg-app > toh-hero-button > button')); - expect(button.getText()).toBe('Hero button'); + const pre = element(by.tagName('toh-app > pre')); + expect(await pre.getText()).toContain('[]'); }); - it('05-03', function () { - browser.get('#/05-03'); + it('09-01', async () => { + await browser.get('#/09-01'); - let button = element(by.tagName('sg-app > toh-hero-button > button')); - expect(button.getText()).toBe('Hero button'); - }); - - it('05-04', function () { - browser.get('#/05-04'); - - let h2 = element(by.tagName('sg-app > toh-heroes > div > h2')); - expect(h2.getText()).toBe('My Heroes'); - }); - - it('05-12', function () { - browser.get('#/05-12'); - - let button = element(by.tagName('sg-app > toh-hero-button > button')); - expect(button.getText()).toBe('OK'); - }); - - it('05-13', function () { - browser.get('#/05-13'); - - let button = element(by.tagName('sg-app > toh-hero-button > button')); - expect(button.getText()).toBe('OK'); - }); - - it('05-14', function () { - browser.get('#/05-14'); - - let toast = element(by.tagName('sg-app > toh-toast')); - expect(toast.getText()).toBe('...'); - }); - - it('05-15', function () { - browser.get('#/05-15'); - - let heroList = element(by.tagName('sg-app > toh-hero-list')); - expect(heroList.getText()).toBe('...'); - }); - - it('05-16', function () { - browser.get('#/05-16'); - - let hero = element(by.tagName('sg-app > toh-hero')); - expect(hero.getText()).toBe('...'); - }); - - it('05-17', function () { - browser.get('#/05-17'); - - let section = element(by.tagName('sg-app > toh-hero-list > section')); - expect(section.getText()).toContain('Our list of heroes'); - expect(section.getText()).toContain('Total powers'); - expect(section.getText()).toContain('Average power'); - }); - - it('06-01', function () { - browser.get('#/06-01'); - - let div = element(by.tagName('sg-app > div[tohhighlight]')); - expect(div.getText()).toBe('Bombasta'); - }); - - it('06-03', function () { - browser.get('#/06-03'); - - let input = element(by.tagName('input[tohvalidator]')); - expect(input.isPresent()).toBe(true); - }); - - // temporarily disabled because of a weird issue when used with rxjs v6 with rxjs-compat - xit('07-01', function () { - browser.get('#/07-01'); - - let lis = element.all(by.tagName('sg-app > ul > li')); - expect(lis.get(0).getText()).toBe('Windstorm'); - expect(lis.get(1).getText()).toBe('Bombasto'); - expect(lis.get(2).getText()).toBe('Magneta'); - expect(lis.get(3).getText()).toBe('Tornado'); - }); - - it('07-03', function () { - browser.get('#/07-03'); - - let pre = element(by.tagName('toh-heroes > pre')); - expect(pre.getText()).toContain('[]'); - }); - - it('07-04', function () { - browser.get('#/07-04'); - - let pre = element(by.tagName('toh-app > pre')); - expect(pre.getText()).toContain('[]'); - }); - - it('09-01', function () { - browser.get('#/09-01'); - - let button = element(by.tagName('sg-app > toh-hero-button > button')); - expect(button.getText()).toBe('OK'); + const button = element(by.tagName('sg-app > toh-hero-button > button')); + expect(await button.getText()).toBe('OK'); }); }); diff --git a/aio/content/examples/styleguide/src/02-05/main.ts b/aio/content/examples/styleguide/src/02-05/main.ts index 6c32161f84..9e1b271ce4 100644 --- a/aio/content/examples/styleguide/src/02-05/main.ts +++ b/aio/content/examples/styleguide/src/02-05/main.ts @@ -1,7 +1,7 @@ // #docregion import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; +import { AppModule } from './app/app.module'; platformBrowserDynamic().bootstrapModule(AppModule) .then(success => console.log(`Bootstrap success`)) diff --git a/aio/content/examples/styleguide/src/02-08/app/shared/input-highlight.directive.ts b/aio/content/examples/styleguide/src/02-08/app/shared/input-highlight.directive.ts index fd74771981..13f8bc1e98 100644 --- a/aio/content/examples/styleguide/src/02-08/app/shared/input-highlight.directive.ts +++ b/aio/content/examples/styleguide/src/02-08/app/shared/input-highlight.directive.ts @@ -1,3 +1,4 @@ +// tslint:disable: directive-selector // #docregion import { Directive, ElementRef } from '@angular/core'; diff --git a/aio/content/examples/styleguide/src/03-01/app/app.component.ts b/aio/content/examples/styleguide/src/03-01/app/app.component.ts deleted file mode 100644 index cb9479d2d9..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/app.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { ExceptionService } from './core'; - -@Component({ - selector: 'sg-app', - template: '
The expected error is {{errorCode}}
', - providers: [ExceptionService] -}) -export class AppComponent implements OnInit { - errorCode: number; - - constructor(private exceptionService: ExceptionService) { } - - ngOnInit() { - this.errorCode = this.exceptionService.getException(); - } -} diff --git a/aio/content/examples/styleguide/src/03-01/app/app.module.ts b/aio/content/examples/styleguide/src/03-01/app/app.module.ts deleted file mode 100644 index 48079f21c7..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/app.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppComponent } from './app.component'; - -@NgModule({ - imports: [ - RouterModule.forChild([{ path: '03-01', component: AppComponent }]) - ], - declarations: [ - AppComponent - ], - exports: [ AppComponent ] -}) -export class AppModule {} diff --git a/aio/content/examples/styleguide/src/03-01/app/core/exception.service.avoid.ts b/aio/content/examples/styleguide/src/03-01/app/core/exception.service.avoid.ts deleted file mode 100644 index 0a22811fe3..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/core/exception.service.avoid.ts +++ /dev/null @@ -1,11 +0,0 @@ -// #docregion -import { Injectable } from '@angular/core'; - -@Injectable() -// #docregion example -/* avoid */ - -export class exceptionService { - constructor() { } -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-01/app/core/exception.service.ts b/aio/content/examples/styleguide/src/03-01/app/core/exception.service.ts deleted file mode 100644 index dd77b4f7dc..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/core/exception.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -// #docplaster -// #docregion -import { Injectable } from '@angular/core'; - -@Injectable() -// #docregion example -export class ExceptionService { - constructor() { } - // #enddocregion example - // testing harness - getException() { return 42; } - // #docregion example -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-01/app/core/index.ts b/aio/content/examples/styleguide/src/03-01/app/core/index.ts deleted file mode 100644 index 8acaa4bcf9..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/core/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './exception.service'; diff --git a/aio/content/examples/styleguide/src/03-01/app/index.ts b/aio/content/examples/styleguide/src/03-01/app/index.ts deleted file mode 100644 index e120e2dbfd..0000000000 --- a/aio/content/examples/styleguide/src/03-01/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './app.component'; diff --git a/aio/content/examples/styleguide/src/03-02/app/app.component.ts b/aio/content/examples/styleguide/src/03-02/app/app.component.ts deleted file mode 100644 index 132ea54c85..0000000000 --- a/aio/content/examples/styleguide/src/03-02/app/app.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component } from '@angular/core'; - -import { heroesUrl, mockHeroes, VILLAINS_URL } from './core'; - -@Component({ - selector: 'sg-app', - template: ` -
Heroes url: {{heroesUrl}}
-
Villains url: {{villainsUrl}}
- -

Mock Heroes

-
{{hero}}
- ` -}) -export class AppComponent { - heroes = mockHeroes; // prefer - heroesUrl = heroesUrl; // prefer - villainsUrl = VILLAINS_URL; // tolerate -} diff --git a/aio/content/examples/styleguide/src/03-02/app/core/data.service.ts b/aio/content/examples/styleguide/src/03-02/app/core/data.service.ts deleted file mode 100644 index 5c26478c7b..0000000000 --- a/aio/content/examples/styleguide/src/03-02/app/core/data.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -// #docregion -export const mockHeroes = ['Sam', 'Jill']; // prefer -export const heroesUrl = 'api/heroes'; // prefer -export const VILLAINS_URL = 'api/villains'; // tolerate diff --git a/aio/content/examples/styleguide/src/03-02/app/core/index.ts b/aio/content/examples/styleguide/src/03-02/app/core/index.ts deleted file mode 100644 index 2ba773ede8..0000000000 --- a/aio/content/examples/styleguide/src/03-02/app/core/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './data.service'; diff --git a/aio/content/examples/styleguide/src/03-02/app/index.ts b/aio/content/examples/styleguide/src/03-02/app/index.ts deleted file mode 100644 index e120e2dbfd..0000000000 --- a/aio/content/examples/styleguide/src/03-02/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './app.component'; diff --git a/aio/content/examples/styleguide/src/03-03/app/app.component.ts b/aio/content/examples/styleguide/src/03-03/app/app.component.ts deleted file mode 100644 index 3ca522bc45..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/app.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { Hero, HeroCollectorService } from './core'; - -@Component({ - selector: 'sg-app', - template: '
Our hero is {{hero.name}} and {{hero.power}}
', - providers: [HeroCollectorService] -}) -export class AppComponent implements OnInit { - hero: Hero; - - constructor(private heroCollectorService: HeroCollectorService) { } - - ngOnInit() { - this.hero = this.heroCollectorService.getHero(); - } -} diff --git a/aio/content/examples/styleguide/src/03-03/app/app.module.ts b/aio/content/examples/styleguide/src/03-03/app/app.module.ts deleted file mode 100644 index 29b3d2e765..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/app.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { AppComponent } from './app.component'; - -@NgModule({ - imports: [ - RouterModule.forChild([{ path: '03-03', component: AppComponent }]) - ], - declarations: [ - AppComponent - ], - exports: [ AppComponent ] -}) -export class AppModule {} diff --git a/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.avoid.ts b/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.avoid.ts deleted file mode 100644 index f481af18b6..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.avoid.ts +++ /dev/null @@ -1,15 +0,0 @@ -// #docregion -// #docregion example -/* avoid */ - -import { Injectable } from '@angular/core'; - -import { IHero } from './hero.model.avoid'; - -@Injectable() -export class HeroCollectorService { - hero: IHero; - - constructor() { } -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.ts b/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.ts deleted file mode 100644 index 1df5c0deb0..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/core/hero-collector.service.ts +++ /dev/null @@ -1,25 +0,0 @@ -// #docplaster -// #docregion -// #docregion example -import { Injectable } from '@angular/core'; - -import { Hero } from './hero.model'; - -@Injectable() -export class HeroCollectorService { - hero: Hero; - - constructor() { } - // #enddocregion example - // testing harness - getHero() { - this.hero = { - name: 'RubberMan', - power: 'He is so elastic' - }; - - return this.hero; - } - // #docregion example -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-03/app/core/hero.model.avoid.ts b/aio/content/examples/styleguide/src/03-03/app/core/hero.model.avoid.ts deleted file mode 100644 index ce93b2c59a..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/core/hero.model.avoid.ts +++ /dev/null @@ -1,14 +0,0 @@ -// #docregion -// #docregion example -/* avoid */ - -export interface IHero { - name: string; - power: string; -} - -export class Hero implements IHero { - name: string; - power: string; -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-03/app/core/hero.model.ts b/aio/content/examples/styleguide/src/03-03/app/core/hero.model.ts deleted file mode 100644 index fab0d4cb97..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/core/hero.model.ts +++ /dev/null @@ -1,7 +0,0 @@ -// #docregion -// #docregion example -export interface Hero { - name: string; - power: string; -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-03/app/core/index.ts b/aio/content/examples/styleguide/src/03-03/app/core/index.ts deleted file mode 100644 index 17ad67b0b1..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/core/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './hero-collector.service'; -export * from './hero.model'; diff --git a/aio/content/examples/styleguide/src/03-03/app/index.ts b/aio/content/examples/styleguide/src/03-03/app/index.ts deleted file mode 100644 index e120e2dbfd..0000000000 --- a/aio/content/examples/styleguide/src/03-03/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './app.component'; diff --git a/aio/content/examples/styleguide/src/03-04/app/app.component.ts b/aio/content/examples/styleguide/src/03-04/app/app.component.ts deleted file mode 100644 index 5aaf3402e1..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/app.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { ToastService } from './core'; - -@Component({ - selector: 'sg-app', - template: ` - - - `, - providers: [ToastService] -}) -export class AppComponent implements OnInit { - constructor(private toastService: ToastService) { } - - hide() { - this.toastService.hide(); - } - - show() { - this.toastService.show(); - } - - ngOnInit() { - this.toastService.activate('Hello Style Guide!'); - } -} diff --git a/aio/content/examples/styleguide/src/03-04/app/app.module.ts b/aio/content/examples/styleguide/src/03-04/app/app.module.ts deleted file mode 100644 index a5a8d5bb4e..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/app.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { RouterModule } from '@angular/router'; - -import { AppComponent } from './app.component'; - -@NgModule({ - imports: [ - BrowserModule, - RouterModule.forChild([{ path: '03-04', component: AppComponent }]) - ], - declarations: [ - AppComponent - ], - exports: [ AppComponent ] -}) -export class AppModule {} diff --git a/aio/content/examples/styleguide/src/03-04/app/core/index.ts b/aio/content/examples/styleguide/src/03-04/app/core/index.ts deleted file mode 100644 index e78b628f9c..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/core/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './toast.service'; diff --git a/aio/content/examples/styleguide/src/03-04/app/core/toast.service.avoid.ts b/aio/content/examples/styleguide/src/03-04/app/core/toast.service.avoid.ts deleted file mode 100644 index 0f3a7c25ea..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/core/toast.service.avoid.ts +++ /dev/null @@ -1,27 +0,0 @@ -// #docregion -// #docregion example -/* avoid */ - -import { Injectable } from '@angular/core'; - -@Injectable() -export class ToastService { - message: string; - - private _toastCount: number; - - hide() { - this._toastCount--; - this._log(); - } - - show() { - this._toastCount++; - this._log(); - } - - private _log() { - console.log(this.message); - } -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-04/app/core/toast.service.ts b/aio/content/examples/styleguide/src/03-04/app/core/toast.service.ts deleted file mode 100644 index ab148a1732..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/core/toast.service.ts +++ /dev/null @@ -1,32 +0,0 @@ -// #docplaster -// #docregion -// #docregion example -import { Injectable } from '@angular/core'; - -@Injectable() -export class ToastService { - message: string; - - private toastCount: number; - - hide() { - this.toastCount--; - this.log(); - } - - show() { - this.toastCount++; - this.log(); - } - - private log() { - console.log(this.message); - } - // #enddocregion example - // testing harness - activate(message: string) { - this.message = message; - } - // #docregion example -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-04/app/index.ts b/aio/content/examples/styleguide/src/03-04/app/index.ts deleted file mode 100644 index e120e2dbfd..0000000000 --- a/aio/content/examples/styleguide/src/03-04/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './core'; -export * from './app.component'; diff --git a/aio/content/examples/styleguide/src/03-06/app/app.component.html b/aio/content/examples/styleguide/src/03-06/app/app.component.html deleted file mode 100644 index 67fb0d5964..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/app.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
Actual favorite: {{favorite?.name}}
-
    -
  • - {{hero.name}} -
  • -
diff --git a/aio/content/examples/styleguide/src/03-06/app/app.component.ts b/aio/content/examples/styleguide/src/03-06/app/app.component.ts deleted file mode 100644 index 8ec308bc6a..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/app.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -import { Hero, HeroService } from './heroes'; -import { ExceptionService, SpinnerService, ToastService } from './core'; - -@Component({ - selector: 'sg-app', - templateUrl: './app.component.html', - providers: [HeroService, ExceptionService, SpinnerService, ToastService] -}) -export class AppComponent implements OnInit { - favorite: Hero; - heroes: Hero[]; - - constructor(private heroService: HeroService) { } - - ngOnInit() { - this.heroService.getHero(1).subscribe(hero => this.favorite = hero); - this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes); - } -} diff --git a/aio/content/examples/styleguide/src/03-06/app/core/exception.service.ts b/aio/content/examples/styleguide/src/03-06/app/core/exception.service.ts deleted file mode 100644 index 7180c88e6b..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/exception.service.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable() -export class ExceptionService { } diff --git a/aio/content/examples/styleguide/src/03-06/app/core/index.ts b/aio/content/examples/styleguide/src/03-06/app/core/index.ts deleted file mode 100644 index e4e6723f91..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// #docregion -// #docregion example -export * from './exception.service'; -export * from './spinner'; -export * from './toast'; -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-06/app/core/spinner/index.ts b/aio/content/examples/styleguide/src/03-06/app/core/spinner/index.ts deleted file mode 100644 index 19618714a5..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/spinner/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// #docregion -export * from './spinner.service'; diff --git a/aio/content/examples/styleguide/src/03-06/app/core/spinner/spinner.service.ts b/aio/content/examples/styleguide/src/03-06/app/core/spinner/spinner.service.ts deleted file mode 100644 index ad5d2ed6e0..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/spinner/spinner.service.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable } from '@angular/core'; - -export interface ISpinnerState { } - -@Injectable() -export class SpinnerService { - spinnerState: any; - - show() { } - - hide() { } -} diff --git a/aio/content/examples/styleguide/src/03-06/app/core/toast/index.ts b/aio/content/examples/styleguide/src/03-06/app/core/toast/index.ts deleted file mode 100644 index 0a88fb952d..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/toast/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// #docregion -export * from './toast.service'; diff --git a/aio/content/examples/styleguide/src/03-06/app/core/toast/toast.service.ts b/aio/content/examples/styleguide/src/03-06/app/core/toast/toast.service.ts deleted file mode 100644 index e92e75ee45..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/core/toast/toast.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable() -export class ToastService { - activate: (message?: string, title?: string) => void; -} diff --git a/aio/content/examples/styleguide/src/03-06/app/heroes/index.ts b/aio/content/examples/styleguide/src/03-06/app/heroes/index.ts deleted file mode 100644 index c3da79f741..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/heroes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './shared'; diff --git a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.model.ts b/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.model.ts deleted file mode 100644 index fab0d4cb97..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.model.ts +++ /dev/null @@ -1,7 +0,0 @@ -// #docregion -// #docregion example -export interface Hero { - name: string; - power: string; -} -// #enddocregion example diff --git a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.avoid.ts b/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.avoid.ts deleted file mode 100644 index fea2e49d99..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.avoid.ts +++ /dev/null @@ -1,29 +0,0 @@ -// #docregion -// #docregion example -/* avoid */ - -import { ExceptionService, SpinnerService, ToastService } from '../../core'; -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Hero } from './hero.model'; -// #enddocregion example - -@Injectable() -export class HeroService { - - constructor( - private exceptionService: ExceptionService, - private spinnerService: SpinnerService, - private toastService: ToastService, - private http: HttpClient - ) { } - - getHero(id: number) { - return this.http.get(`api/heroes/${id}`); - } - - getHeroes() { - return this.http.get(`api/heroes`); - } - -} diff --git a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.ts b/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.ts deleted file mode 100644 index 0dd5931a7a..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/hero.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -// #docregion -// #docregion example -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; - -import { ExceptionService, SpinnerService, ToastService } from '../../core'; -import { Hero } from './hero.model'; - -// #enddocregion example - -@Injectable() -export class HeroService { - cachedHeroes: Hero[]; - - constructor( - private exceptionService: ExceptionService, - private spinnerService: SpinnerService, - private toastService: ToastService, - private http: HttpClient - ) { } - - getHero(id: number) { - return this.http.get(`api/heroes/${id}`); - } - - getHeroes() { - return this.http.get(`api/heroes`); - } - -} diff --git a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/index.ts b/aio/content/examples/styleguide/src/03-06/app/heroes/shared/index.ts deleted file mode 100644 index dbb150d3f8..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/heroes/shared/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './hero.model'; -export * from './hero.service'; diff --git a/aio/content/examples/styleguide/src/03-06/app/index.ts b/aio/content/examples/styleguide/src/03-06/app/index.ts deleted file mode 100644 index cf861e261a..0000000000 --- a/aio/content/examples/styleguide/src/03-06/app/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './heroes'; -export * from './core'; -export * from './app.component'; diff --git a/aio/content/examples/styleguide/src/04-08/app/app.module.ts b/aio/content/examples/styleguide/src/04-08/app/app.module.ts index 25568b7fb4..d30bce75ca 100644 --- a/aio/content/examples/styleguide/src/04-08/app/app.module.ts +++ b/aio/content/examples/styleguide/src/04-08/app/app.module.ts @@ -1,13 +1,13 @@ // #docplaster // #docregion // #docregion example -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; // #enddocregion example -import { RouterModule } from '@angular/router'; +import { RouterModule } from '@angular/router'; // #docregion example -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; @NgModule({ diff --git a/aio/content/examples/styleguide/src/04-10/app/app.module.ts b/aio/content/examples/styleguide/src/04-10/app/app.module.ts index 78106657ad..1e597e19b8 100644 --- a/aio/content/examples/styleguide/src/04-10/app/app.module.ts +++ b/aio/content/examples/styleguide/src/04-10/app/app.module.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion // #docregion example -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; // #enddocregion example -import { RouterModule } from '@angular/router'; +import { RouterModule } from '@angular/router'; // #docregion example -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; -import { SharedModule } from './shared/shared.module'; +import { SharedModule } from './shared/shared.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/styleguide/src/04-10/app/shared/filter-text/filter-text.service.ts b/aio/content/examples/styleguide/src/04-10/app/shared/filter-text/filter-text.service.ts index 87978e10e5..131c79ad64 100644 --- a/aio/content/examples/styleguide/src/04-10/app/shared/filter-text/filter-text.service.ts +++ b/aio/content/examples/styleguide/src/04-10/app/shared/filter-text/filter-text.service.ts @@ -11,14 +11,14 @@ export class FilterTextService { let filteredList: any[]; if (data && props && originalList) { data = data.toLowerCase(); - let filtered = originalList.filter(item => { + const filtered = originalList.filter(item => { let match = false; - for (let prop of props) { + for (const prop of props) { if (item[prop].toString().toLowerCase().indexOf(data) > -1) { match = true; break; } - }; + } return match; }); filteredList = filtered; diff --git a/aio/content/examples/styleguide/src/04-10/app/shared/shared.module.ts b/aio/content/examples/styleguide/src/04-10/app/shared/shared.module.ts index 6160abcc84..1b698a052d 100644 --- a/aio/content/examples/styleguide/src/04-10/app/shared/shared.module.ts +++ b/aio/content/examples/styleguide/src/04-10/app/shared/shared.module.ts @@ -1,11 +1,11 @@ // #docregion -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; import { FilterTextComponent } from './filter-text/filter-text.component'; -import { FilterTextService } from './filter-text/filter-text.service'; -import { InitCapsPipe } from './init-caps.pipe'; +import { FilterTextService } from './filter-text/filter-text.service'; +import { InitCapsPipe } from './init-caps.pipe'; @NgModule({ imports: [CommonModule, FormsModule], diff --git a/aio/content/examples/styleguide/src/04-11/app/app.module.ts b/aio/content/examples/styleguide/src/04-11/app/app.module.ts index ef3c156bcf..a4abbd7263 100644 --- a/aio/content/examples/styleguide/src/04-11/app/app.module.ts +++ b/aio/content/examples/styleguide/src/04-11/app/app.module.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion // #docregion example -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; // #enddocregion example import { RouterModule } from '@angular/router'; // #docregion example -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; -import { CoreModule } from './core/core.module'; +import { CoreModule } from './core/core.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/styleguide/src/04-11/app/core/spinner/spinner.service.ts b/aio/content/examples/styleguide/src/04-11/app/core/spinner/spinner.service.ts index e092d4bfda..67881864a7 100644 --- a/aio/content/examples/styleguide/src/04-11/app/core/spinner/spinner.service.ts +++ b/aio/content/examples/styleguide/src/04-11/app/core/spinner/spinner.service.ts @@ -15,10 +15,10 @@ export class SpinnerService { constructor() { } show() { - this.spinnerSubject.next({ show: true }); + this.spinnerSubject.next({ show: true }); } hide() { - this.spinnerSubject.next({ show: false }); + this.spinnerSubject.next({ show: false }); } } diff --git a/aio/content/examples/styleguide/src/04-11/app/heroes/heroes.component.ts b/aio/content/examples/styleguide/src/04-11/app/heroes/heroes.component.ts index 856baadd74..b0064be1b2 100644 --- a/aio/content/examples/styleguide/src/04-11/app/heroes/heroes.component.ts +++ b/aio/content/examples/styleguide/src/04-11/app/heroes/heroes.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; -import { LoggerService } from '../core/logger.service'; +import { LoggerService } from '../core/logger.service'; import { SpinnerService } from '../core/spinner/spinner.service'; @Component({ diff --git a/aio/content/examples/styleguide/src/04-12/app/app.module.ts b/aio/content/examples/styleguide/src/04-12/app/app.module.ts index 7c9fb44182..c51d7d10eb 100644 --- a/aio/content/examples/styleguide/src/04-12/app/app.module.ts +++ b/aio/content/examples/styleguide/src/04-12/app/app.module.ts @@ -1,15 +1,15 @@ // #docplaster // #docregion // #docregion example -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; // #enddocregion example import { RouterModule } from '@angular/router'; // #docregion example -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; -import { CoreModule } from './core/core.module'; +import { CoreModule } from './core/core.module'; @NgModule({ imports: [ diff --git a/aio/content/examples/styleguide/src/05-04/app/app.module.ts b/aio/content/examples/styleguide/src/05-04/app/app.module.ts index 07f97cc6e4..c925f6c7e0 100644 --- a/aio/content/examples/styleguide/src/05-04/app/app.module.ts +++ b/aio/content/examples/styleguide/src/05-04/app/app.module.ts @@ -1,10 +1,10 @@ -import { NgModule } from '@angular/core'; +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { RouterModule } from '@angular/router'; +import { RouterModule } from '@angular/router'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes'; -import { HeroService } from './heroes/shared'; +import { HeroService } from './heroes/shared'; @NgModule({ imports: [ diff --git a/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.avoid.ts b/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.avoid.ts index 89e5d1b010..eaa59a752d 100644 --- a/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.avoid.ts +++ b/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.avoid.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { Hero, HeroService } from './shared'; diff --git a/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.ts b/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.ts index b85c5b70ab..4d6ae1aee6 100644 --- a/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.ts +++ b/aio/content/examples/styleguide/src/05-04/app/heroes/heroes.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { Hero, HeroService } from './shared'; diff --git a/aio/content/examples/styleguide/src/05-13/app/heroes/shared/hero-highlight.directive.ts b/aio/content/examples/styleguide/src/05-13/app/heroes/shared/hero-highlight.directive.ts index 737af31f4f..3af48d241e 100644 --- a/aio/content/examples/styleguide/src/05-13/app/heroes/shared/hero-highlight.directive.ts +++ b/aio/content/examples/styleguide/src/05-13/app/heroes/shared/hero-highlight.directive.ts @@ -1,3 +1,4 @@ +// tslint:disable: directive-selector // #docregion import { Directive, ElementRef, Input, OnChanges } from '@angular/core'; diff --git a/aio/content/examples/styleguide/src/05-15/app/heroes/shared/hero.service.ts b/aio/content/examples/styleguide/src/05-15/app/heroes/shared/hero.service.ts index 3f8e476766..94bdf2615c 100644 --- a/aio/content/examples/styleguide/src/05-15/app/heroes/shared/hero.service.ts +++ b/aio/content/examples/styleguide/src/05-15/app/heroes/shared/hero.service.ts @@ -8,7 +8,7 @@ import { Hero } from './hero.model'; @Injectable() export class HeroService { getHeroes() { - let heroes: Hero[] = []; + const heroes: Hero[] = []; return of(heroes); } } diff --git a/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts b/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts index f007512949..0b704d2b87 100644 --- a/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts +++ b/aio/content/examples/styleguide/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts @@ -10,8 +10,8 @@ import { Hero } from '../shared/hero.model'; template: `
Our list of heroes: - - + + Total powers: {{totalPowers}}
Average power: {{totalPowers / heroes.length}}
diff --git a/aio/content/examples/styleguide/src/06-03/app/shared/validator2.directive.ts b/aio/content/examples/styleguide/src/06-03/app/shared/validator2.directive.ts index 05a0f9f07f..dc61f4e504 100644 --- a/aio/content/examples/styleguide/src/06-03/app/shared/validator2.directive.ts +++ b/aio/content/examples/styleguide/src/06-03/app/shared/validator2.directive.ts @@ -1,3 +1,4 @@ +// tslint:disable: no-host-metadata-property // #docregion import { Directive } from '@angular/core'; diff --git a/aio/content/examples/styleguide/src/07-03/app/heroes/shared/hero.service.ts b/aio/content/examples/styleguide/src/07-03/app/heroes/shared/hero.service.ts index d3d8857caa..02bc7f6f0b 100644 --- a/aio/content/examples/styleguide/src/07-03/app/heroes/shared/hero.service.ts +++ b/aio/content/examples/styleguide/src/07-03/app/heroes/shared/hero.service.ts @@ -10,7 +10,7 @@ import { Hero } from './hero.model'; }) export class HeroService { getHeroes() { - let heroes: Hero[] = []; + const heroes: Hero[] = []; return of(heroes); } } diff --git a/aio/content/examples/styleguide/src/07-04/app/heroes/shared/hero.service.ts b/aio/content/examples/styleguide/src/07-04/app/heroes/shared/hero.service.ts index 3f8e476766..94bdf2615c 100644 --- a/aio/content/examples/styleguide/src/07-04/app/heroes/shared/hero.service.ts +++ b/aio/content/examples/styleguide/src/07-04/app/heroes/shared/hero.service.ts @@ -8,7 +8,7 @@ import { Hero } from './hero.model'; @Injectable() export class HeroService { getHeroes() { - let heroes: Hero[] = []; + const heroes: Hero[] = []; return of(heroes); } } diff --git a/aio/content/examples/styleguide/src/app/app.module.ts b/aio/content/examples/styleguide/src/app/app.module.ts index 2420170d4f..29542aa0dc 100644 --- a/aio/content/examples/styleguide/src/app/app.module.ts +++ b/aio/content/examples/styleguide/src/app/app.module.ts @@ -9,18 +9,13 @@ import { RouterModule } from '@angular/router'; import { HashLocationStrategy, LocationStrategy } from '@angular/common'; -import { HeroData } from '../app/hero-data'; +import { HeroData } from '../app/hero-data'; import { AppComponent } from '../app/app.component'; import * as s0101 from '../01-01/app/app.module'; import * as s0205 from '../02-05/app/app.module'; import * as s0207 from '../02-07/app/app.module'; import * as s0208 from '../02-08/app/app.module'; -import * as s0301 from '../03-01/app/app.module'; -import * as s0302 from '../03-02/app/app.module'; -import * as s0303 from '../03-03/app/app.module'; -import * as s0304 from '../03-04/app/app.module'; -import * as s0306 from '../03-06/app/app.module'; import * as s0408 from '../04-08/app/app.module'; import * as s0410 from '../04-10/app/app.module'; import * as s0411 from '../04-11/app/app.module'; @@ -51,11 +46,6 @@ import * as s0901 from '../09-01/app/app.module'; s0205.AppModule, s0207.AppModule, s0208.AppModule, - s0301.AppModule, - s0302.AppModule, - s0303.AppModule, - s0304.AppModule, - s0306.AppModule, s0408.AppModule, s0410.AppModule, s0411.AppModule, diff --git a/aio/content/examples/styleguide/src/app/hero-data.ts b/aio/content/examples/styleguide/src/app/hero-data.ts index f3e6feb91c..893b5f8b8d 100644 --- a/aio/content/examples/styleguide/src/app/hero-data.ts +++ b/aio/content/examples/styleguide/src/app/hero-data.ts @@ -1,6 +1,6 @@ export class HeroData { createDb() { - let heroes = [ + const heroes = [ { id: 1, name: 'Windstorm' }, { id: 2, name: 'Bombasto' }, { id: 3, name: 'Magneta' }, diff --git a/aio/content/examples/styleguide/src/main.ts b/aio/content/examples/styleguide/src/main.ts index 049f9792a4..537b861085 100644 --- a/aio/content/examples/styleguide/src/main.ts +++ b/aio/content/examples/styleguide/src/main.ts @@ -1,4 +1,4 @@ -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; diff --git a/aio/content/examples/template-expression-operators/e2e/src/app.e2e-spec.ts b/aio/content/examples/template-expression-operators/e2e/src/app.e2e-spec.ts index bb5e8f4501..05fcf51d9b 100644 --- a/aio/content/examples/template-expression-operators/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/template-expression-operators/e2e/src/app.e2e-spec.ts @@ -1,31 +1,27 @@ import { browser, element, by } from 'protractor'; -import { logging } from 'selenium-webdriver'; -describe('Template Expression Operators', function () { +describe('Template Expression Operators', () => { - beforeAll(function () { - browser.get(''); + beforeAll(() => browser.get('')); + + it('should have title Inputs and Outputs', async () => { + const title = element.all(by.css('h1')).get(0); + expect(await title.getText()).toEqual('Template Expression Operators'); }); - it('should have title Inputs and Outputs', function () { - let title = element.all(by.css('h1')).get(0); - expect(title.getText()).toEqual('Template Expression Operators'); + it('should display json data', async () => { + const jsonDate = element.all(by.css('p')).get(4); + expect(await jsonDate.getText()).toContain('1980'); }); - it('should display json data', function () { - let jsonDate = element.all(by.css('p')).get(4); - expect(jsonDate.getText()).toContain('1980'); + it('should display $98', async () => { + const jsonDate = element.all(by.css('p')).get(5); + expect(await jsonDate.getText()).toContain('$98.00'); }); - it('should display $98', function () { - let jsonDate = element.all(by.css('p')).get(5); - expect(jsonDate.getText()).toContain('$98.00'); + it('should display Telephone', async () => { + const jsonDate = element.all(by.css('p')).get(6); + expect(await jsonDate.getText()).toContain('Telephone'); }); - it('should display Telephone', function () { - let jsonDate = element.all(by.css('p')).get(6); - expect(jsonDate.getText()).toContain('Telephone'); - }); - - }); diff --git a/aio/content/examples/template-reference-variables/e2e/src/app.e2e-spec.ts b/aio/content/examples/template-reference-variables/e2e/src/app.e2e-spec.ts index 3d742d5a97..9adeed1dae 100644 --- a/aio/content/examples/template-reference-variables/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/template-reference-variables/e2e/src/app.e2e-spec.ts @@ -1,62 +1,53 @@ -'use strict'; // necessary for es6 output in node +import { browser, element, by, logging } from 'protractor'; -import { browser, element, by } from 'protractor'; -import { logging } from 'selenium-webdriver'; - -describe('Template-reference-variables-example', function() { - beforeEach(function() { - browser.get(''); - - }); +describe('Template-reference-variables-example', () => { + beforeEach(() => browser.get('')); // helper function used to test what's logged to the console - async function logChecker(button, contents) { + async function logChecker(contents) { const logs = await browser .manage() .logs() .get(logging.Type.BROWSER); - const message = logs.filter(({ message }) => - message.indexOf(contents) !== -1 ? true : false - ); - expect(message.length).toBeGreaterThan(0); + const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1); + expect(messages.length).toBeGreaterThan(0); } - it('should display Template reference variables', function() { - expect(element(by.css('h1')).getText()).toEqual( + it('should display Template reference variables', async () => { + expect(await element(by.css('h1')).getText()).toEqual( 'Template reference variables' ); }); it('should log a Calling 123 ... message', async () => { - let callButton = element.all(by.css('button')).get(0); - let phoneInput = element.all(by.css('input')).get(0); + const callButton = element.all(by.css('button')).get(0); + const phoneInput = element.all(by.css('input')).get(0); await phoneInput.sendKeys('123'); await callButton.click(); const contents = 'Calling 123 ...'; - await logChecker(callButton, contents); + await logChecker(contents); }); it('should log a Faxing 123 ... message', async () => { - let faxButton = element.all(by.css('button')).get(1); - let faxInput = element.all(by.css('input')).get(1); + const faxButton = element.all(by.css('button')).get(1); + const faxInput = element.all(by.css('input')).get(1); await faxInput.sendKeys('123'); await faxButton.click(); const contents = 'Faxing 123 ...'; - await logChecker(faxButton, contents); + await logChecker(contents); }); - it('should display a disabled button', function() { - let disabledButton = element.all(by.css('button')).get(2); - expect(disabledButton.isEnabled()).toBe(false); + it('should display a disabled button', async () => { + const disabledButton = element.all(by.css('button')).get(2); + expect(await disabledButton.isEnabled()).toBe(false); }); it('should submit form', async () => { - let submitButton = element.all(by.css('button')).get(3); - let nameInput = element.all(by.css('input')).get(2); + const submitButton = element.all(by.css('button')).get(3); + const nameInput = element.all(by.css('input')).get(2); await nameInput.sendKeys('123'); await submitButton.click(); - expect(element.all(by.css('div > p')).get(2).getText()).toEqual('Submitted. Form value is {"name":"123"}'); + expect(await element.all(by.css('div > p')).get(2).getText()).toEqual('Submitted. Form value is {"name":"123"}'); }); - }); diff --git a/aio/content/examples/template-reference-variables/src/app/app.component.css b/aio/content/examples/template-reference-variables/src/app/app.component.css index e69de29bb2..9a9e6dc50f 100644 --- a/aio/content/examples/template-reference-variables/src/app/app.component.css +++ b/aio/content/examples/template-reference-variables/src/app/app.component.css @@ -0,0 +1,14 @@ +h3 { + font-weight: 700; +} + +pre, .wrapper { + background-color: rgb(240, 250, 250); + padding: 1rem; + border: 1px solid #444; +} + +input { + margin: .5rem; + padding: .5rem; +} diff --git a/aio/content/examples/template-reference-variables/src/app/app.component.html b/aio/content/examples/template-reference-variables/src/app/app.component.html index d335a33956..5458d96ad3 100644 --- a/aio/content/examples/template-reference-variables/src/app/app.component.html +++ b/aio/content/examples/template-reference-variables/src/app/app.component.html @@ -39,8 +39,7 @@

Reference variables, forms, and NgForm

-

One-Two
@@ -1299,7 +1287,7 @@ The Angular code is shown using TypeScript. For more information on modules, see the [Modules](guide/architecture#modules) section of the [Architecture Overview](guide/architecture). - 要了解关于模块的更多信息,参见[架构概览](guide/architecture)中的[模块](guide/architecture#modules)部分。 + 要了解关于模块的更多信息,参阅[架构概览](guide/architecture)中的[模块](guide/architecture#modules)部分。 @@ -1344,7 +1332,7 @@ The Angular code is shown using TypeScript. For more information on modules, see [NgModules](guide/ngmodules). - 要了解关于模块的更多知识,参见[NgModules](guide/ngmodules)。 + 要了解关于模块的更多知识,参阅[NgModules](guide/ngmodules)。 @@ -1400,7 +1388,7 @@ The Angular code is shown using TypeScript. For more information, see the [Components](guide/architecture#components) section of the [Architecture Overview](guide/architecture) page. - 要了解关于组件的更多信息,参见[架构概览](guide/architecture)中的[组件](guide/architecture#components)部分。 + 要了解关于组件的更多信息,参阅[架构概览](guide/architecture)中的[组件](guide/architecture#components)部分。 @@ -1440,7 +1428,7 @@ The Angular code is shown using TypeScript. For more information, see the [Components](guide/architecture#components) section of the [Architecture Overview](guide/architecture) page. - 要了解关于组件的更多信息,参见[架构概览](guide/architecture)中的[组件](guide/architecture#components)部分。 + 要了解关于组件的更多信息,参阅[架构概览](guide/architecture)中的[组件](guide/architecture#components)部分。 @@ -1492,7 +1480,7 @@ The Angular code is shown using TypeScript. For more information, see the [Dependency injection](guide/architecture#dependency-injection) section of the [Architecture Overview](guide/architecture). - 要了解关于依赖注入的更多信息,参见[架构概览](guide/architecture)中的[依赖注入](guide/architecture#dependency-injection)部分。 + 要了解关于依赖注入的更多信息,参阅[架构概览](guide/architecture)中的[依赖注入](guide/architecture#dependency-injection)部分。 diff --git a/aio/content/guide/angular-compiler-options.md b/aio/content/guide/angular-compiler-options.md index ed3f585c15..9d1ba868b6 100644 --- a/aio/content/guide/angular-compiler-options.md +++ b/aio/content/guide/angular-compiler-options.md @@ -46,7 +46,7 @@ For example: ```json { - "extends": "../tsconfig.base.json", + "extends": "../tsconfig.json", "compilerOptions": { "experimentalDecorators": true, ... @@ -61,7 +61,7 @@ For example: For more information, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html). -欲知详情,请参见 [TypeScript 手册](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)。 +欲知详情,请参阅 [TypeScript 手册](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)。 ## Template options @@ -93,7 +93,7 @@ Modifies how Angular-specific annotations are emitted to improve tree-shaking. N ### `annotateForClosureCompiler` -When `true`, use [Tsickle](https://github.com/angular/tsickle) to annotate the emitted JavaScript with [JSDoc](http://usejsdoc.org/) comments needed by the +When `true`, use [Tsickle](https://github.com/angular/tsickle) to annotate the emitted JavaScript with [JSDoc](https://jsdoc.app/) comments needed by the [Closure Compiler](https://github.com/google/closure-compiler). Default is `false`. 如果为 `true`,则使用 [Tsickle](https://github.com/angular/tsickle) 来用 [JSDoc](http://usejsdoc.org/) 对生成的 JavaScript 代码进行注解,这些注释是供 [Closure 编译器](https://github.com/google/closure-compiler) 使用的。默认值为 `false`。 @@ -102,7 +102,7 @@ When `true`, use [Tsickle](https://github.com/angular/tsickle) to annotate the e When `true` (the default), transforms code that is or could be used in an annotation, to allow it to be imported from template factory modules. See [metadata rewriting](guide/aot-compiler#metadata-rewriting) for more information. -如果为 `true`(默认值),则转换在注解中使用或允许使用的代码,以允许从模板的工厂模块导入代码。欲知详情,请参见[元数据重写](guide/aot-compiler#metadata-rewriting)。 +如果为 `true`(默认值),则转换在注解中使用或允许使用的代码,以允许从模板的工厂模块导入代码。欲知详情,请参阅[元数据重写](guide/aot-compiler#metadata-rewriting)。 When `false`, disables this rewriting, requiring the rewriting to be done manually. @@ -114,6 +114,27 @@ When `true`, the compiler does not check the TypeScript version and does not rep 如果为 `true`,则在使用不受支持的 TypeScript 版本时,编译器不会检查 TypeScript 版本,并且不会报错。不建议使用,因为不受支持的 TypeScript 版本可能具有未定义的行为。默认值为 `false`。 +### `enableI18nLegacyMessageIdFormat` + +Instructs the Angular template compiler to generate legacy ids for messages that are tagged in templates by the `i18n` attribute. +See [Localizing your app](guide/i18n#mark-text-for-translations) for more information about marking messages for localization. + +指示 Angular 模板编译器为模板中用 `i18n` 属性标出的消息生成旧版 ID。关于为本地化而对消息进行标记的更多信息,请参阅[本地化你的应用程序。](guide/i18n#mark-text-for-translations) + +Set this option to `false` unless your project relies upon translations that were previously generated using legacy ids. Default is `true`. + +除非你的项目依赖先前已用旧版 ID 生成的翻译,否则请将此选项设置为 `false`。默认值为 `true` 。 + +The pre-Ivy message extraction tooling generated a variety of legacy formats for extracted message ids. +These message formats have a number of issues, such as whitespace handling and reliance upon information inside the original HTML of a template. + +Ivy 之前版本的消息提取工具为所提取的消息 id 生成了多种旧格式。这些消息格式存在许多问题,例如对空白字符的处理和对模板原始 HTML 内部信息的依赖。 + +The new message format is more resilient to whitespace changes, is the same across all translation file formats, and can be generated directly from calls to `$localize`. +This allows `$localize` messages in application code to use the same id as identical `i18n` messages in component templates. + +新的消息格式对空白字符的改动更宽容,在所有翻译文件格式中都相同,并且可以直接通过调用 `$localize` 生成。这允许应用程序代码中的 `$localize` 消息使用与组件模板中 `i18n` 消息完全相同的 id。 + ### `enableIvy` Enables the [Ivy](guide/ivy) compilation and rendering pipeline. Default is `true`, as of version 9. In version 9, you can [opt out of Ivy](guide/ivy#opting-out-of-angular-ivy) to continue using the previous compiler, View Engine. @@ -132,7 +153,7 @@ When `true`, replaces the `templateUrl` and `styleUrls` property in all `@Compon When enabled, the `.js` output of `ngc` does not include any lazy-loaded template or style URLs. -启用后,`ngc` 的 `.js` 输出不会包含任何延迟加载的模板或样式 URL。 +启用后,`ngc` 的 `.js` 输出不会包含任何惰性加载的模板或样式 URL。 For library projects generated with the CLI, the dev configuration default is `true`. @@ -188,7 +209,7 @@ would be `"index.d.ts"`. When `true` (recommended), enables the [binding expression validation](guide/aot-compiler#binding-expression-validation) phase of the template compiler, which uses TypeScript to validate binding expressions. For more information, see [Template type checking](guide/template-typecheck). -为 `true`(推荐)时,会启用模板编译器的[绑定表达式验证](guide/aot-compiler#binding-expression-validation)阶段,该阶段使用 TypeScript 来验证绑定表达式。欲知详情,请参见[模板类型检查](guide/template-typecheck)。 +为 `true`(推荐)时,会启用模板编译器的[绑定表达式验证](guide/aot-compiler#binding-expression-validation)阶段,该阶段使用 TypeScript 来验证绑定表达式。欲知详情,请参阅[模板类型检查](guide/template-typecheck)。 Default is `false`, but when you use the CLI command `ng new`, it is set to `true` by default in the generated project's configuration. @@ -256,7 +277,7 @@ Default is `false`. Use only when `"skipMetadataEmit"` is `false` and `"skipTemp This option is intended to validate the `.metadata.json` files emitted for bundling with an `npm` package. The validation is strict and can emit errors for metadata that would never produce an error when used by the template compiler. You can choose to suppress the error emitted by this option for an exported symbol by including `@dynamic` in the comment documenting the symbol. -该选项是为了验证为生成 `npm` 包而产生的 `.metadata.json` 文件。这种验证是严格的,并且会报告元数据中的错误,以免当模板编译器使用它时再出错。你可以通过在某个导出符号的注释文档中使用 `@dynamic` 注释来暂时防止(suppress)该选项报告错误。 +该选项是为了验证为生成 `npm` 包而产生的 `.metadata.json` 文件。这种验证是严格的,并且会报告元数据中的错误,以免当模板编译器使用它时再出错。你可以通过在某个导出符号的注释文档中使用 `@dynamic` 注解来暂时防止(suppress)该选项报告错误。 It is valid for `.metadata.json` files to contain errors. The template compiler reports these errors if the metadata is used to determine the contents of an annotation. @@ -281,13 +302,13 @@ When `true` (recommended), reports an error for a supplied parameter whose injec 如果为 `true`(推荐),则报告所提供的参数的错误,无法确定该参数的注入类型。如果为 `false`(当前为默认值),则标记为 `@Injectable` 但其类型无法解析的类的构造函数参数会产生警告。 -When you use the CLI command `ng new`, it is set to `true` by default in the generated project's configuration. +When you use the CLI command `ng new --strict`, it is set to `true` in the generated project's configuration. 当你使用 CLI 命令 `ng new` 时,默认生成的项目配置中将其设置为 `true`。 ### `strictTemplates` -When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode) in Angular version 9. Strict mode is only available when using [Ivy](guide/ivy). +When `true`, enables [strict template type checking](guide/template-typecheck#strict-mode). Strict mode is only available when using [Ivy](guide/ivy) (Angular version 9 and later). 如果为 `true`,则在 Angular 9 中启用[严格的模板类型检查](guide/template-typecheck#strict-mode)。仅当使用 [Ivy](guide/ivy) 时,才能使用严格模式。 @@ -295,6 +316,10 @@ Additional strictness flags allow you to enable and disable specific types of st 其它严格性标志允许你启用和禁用特定类型的严格模板类型检查。请参阅[排除模板错误](guide/template-typecheck#troubleshooting-template-errors)。 +When you use the CLI command `ng new --strict`, it is set to `true` in the generated project's configuration. + +当你使用 CLI 命令 `ng new --strict` 时,默认生成的项目配置中将其设置为 `true`。 + ### `trace` When `true`, prints extra information while compiling templates. Default is `false`. diff --git a/aio/content/guide/animations.md b/aio/content/guide/animations.md index 5f27d182be..dee59410c8 100644 --- a/aio/content/guide/animations.md +++ b/aio/content/guide/animations.md @@ -100,7 +100,7 @@ If you plan to use specific animation functions in component files, import those **Note:** See a [summary of available animation functions](guide/animations#animation-api-summary) at the end of this guide. -**注意:**参见本章末尾的[可用动画函数汇总表](guide/animations#animation-api-summary)。 +**注意:**参阅本章末尾的[可用动画函数汇总表](guide/animations#animation-api-summary)。 @@ -216,7 +216,7 @@ The second argument, `delay`, has the same syntax as `duration`. For example: 等待 100 毫秒,然后运行 200 毫秒表示为:`'0.2s 100ms'` -The third argument, `easing`, controls how the animation [accelerates and decelerates](http://easings.net/) during its runtime. For example, `ease-in` causes the animation to begin slowly, and to pick up speed as it progresses. +The third argument, `easing`, controls how the animation [accelerates and decelerates](https://easings.net/) during its runtime. For example, `ease-in` causes the animation to begin slowly, and to pick up speed as it progresses. 第三个参数 `easing` 控制动画在运行期间如何进行[加速和减速](http://easings.net/)。比如 `ease-in` 表示动画开始时很慢,然后逐渐加速。 @@ -542,7 +542,7 @@ Staggers the starting time for animations for multiple elements. Produces a reusable animation that can be invoked from elsewhere. Used together with useAnimation(). -生成可在其它地方调用的可重用动画。与 useAnimation() 一起使用。 +生成可在其它地方调用的可复用动画。与 useAnimation() 一起使用。 @@ -597,8 +597,7 @@ You may also be interested in the following:
-Check out this full animation [demo](http://animationsftw.in/#/) with accompanying [presentation](https://www.youtube.com/watch?v=JhNo3Wvj6UQ&feature=youtu.be&t=2h47m53s), shown at the AngularConnect conference in November 2017. +Check out this [presentation](https://www.youtube.com/watch?v=rnTK9meY5us), shown at the AngularConnect conference in November 2017, and the accompanying [source code](https://github.com/matsko/animationsftw.in). 到这个 [Demo](http://animationsftw.in/#/) 中查看 2017 年 11 月的 AngularConnect 大会上完整的动画及其[演示](https://www.youtube.com/watch?v=JhNo3Wvj6UQ&feature=youtu.be&t=2h47m53s)。 -
diff --git a/aio/content/guide/aot-compiler.md b/aio/content/guide/aot-compiler.md index cc67f2bfae..9e2b851a68 100644 --- a/aio/content/guide/aot-compiler.md +++ b/aio/content/guide/aot-compiler.md @@ -89,7 +89,7 @@ When you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) See the [CLI command reference](cli) and [Building and serving Angular apps](guide/build) for more information. -要了解更多,请参见[CLI 文档](cli),和 [构建与运行 Angular 应用](guide/build)。 +要了解更多,请参阅[CLI 文档](cli),和 [构建与运行 Angular 应用](guide/build)。 ## How AOT works @@ -110,7 +110,7 @@ In the following example, the `@Component()` metadata object and the class const @Component({ selector: 'app-typical', template: '
A typical component for {{data.name}}
' -)} +}) export class TypicalComponent { @Input() data: TypicalData; constructor(private someService: SomeService) { ... } @@ -175,7 +175,7 @@ You write metadata in a _subset_ of TypeScript that must conform to the followin For additional guidelines and instructions on preparing an application for AOT compilation, see [Angular: Writing AOT-friendly applications](https://medium.com/sparkles-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f). -有关准备 AOT 编译应用程序的其它准则和说明,请参阅 [Angular:编写 AOT 友好的应用程序](https://medium.com/sparkles-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f)。 +关于准备 AOT 编译应用程序的其它准则和说明,请参阅 [Angular:编写 AOT 友好的应用程序](https://medium.com/sparkles-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f)。
@@ -192,7 +192,7 @@ AOT 编译中的错误通常是由于元数据不符合编译器的要求而发 You can provide options in the [TypeScript configuration file](guide/typescript-configuration) that controls the compilation process. See [Angular compiler options](guide/angular-compiler-options) for a complete list of available options. -你可以在 `tsconfig.json` [TypeScript 配置文件](guide/typescript-configuration)中提供控制编译过程的选项。有关可用选项的完整列表,请参见 [Angular 编译器](guide/angular-compiler-options)选项。 +你可以在 `tsconfig.json` [TypeScript 配置文件](guide/typescript-configuration)中提供控制编译过程的选项。关于可用选项的完整列表,请参阅 [Angular 编译器](guide/angular-compiler-options)选项。 ## Phase 1: Code analysis @@ -1345,7 +1345,7 @@ We do expect to make strict type checking the default in the future. For more information about type-checking options, and about improvements to template type checking in version 9 and above, see [Template type checking](guide/template-typecheck). -关于这些类型检查选项的更多信息以及 Angular 9 及后续版本对模板类型检查做出的改进,请参见 [模板类型检查](guide/template-typecheck)。 +关于这些类型检查选项的更多信息以及 Angular 9 及后续版本对模板类型检查做出的改进,请参阅 [模板类型检查](guide/template-typecheck)。
@@ -1438,15 +1438,15 @@ Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in For more information about input type narrowing, see [Input setter coercion](guide/template-typecheck#input-setter-coercion) and [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks). -关于输入类型窄化的更多信息,请参见 [Input setter 的强制类型转换](guide/template-typecheck#input-setter-coercion)和[为自定义指令强化模板类型检查](guide/structural-directives#directive-type-checks) +关于输入类型窄化的更多信息,请参阅 [Input setter 的强制类型转换](guide/template-typecheck#input-setter-coercion)和[为自定义指令强化模板类型检查](guide/structural-directives#directive-type-checks) ### Non-null type assertion operator ### 非空类型断言操作符 -Use the [non-null type assertion operator](guide/template-syntax#non-null-assertion-operator) to suppress the `Object is possibly 'undefined'` error when it is inconvenient to use `*ngIf` or when some constraint in the component ensures that the expression is always non-null when the binding expression is interpolated. +Use the [non-null type assertion operator](guide/template-expression-operators#non-null-assertion-operator) to suppress the `Object is possibly 'undefined'` error when it is inconvenient to use `*ngIf` or when some constraint in the component ensures that the expression is always non-null when the binding expression is interpolated. -使用 [非空类型断言操作符](guide/template-syntax#non-null-assertion-operator)可以在不方便使用 `*ngIf` 或 +使用 [非空类型断言操作符](guide/template-expression-operators#non-null-assertion-operator)可以在不方便使用 `*ngIf` 或 当组件中的某些约束可以确保这个绑定表达式在求值时永远不会为空时,防止出现 `Object is possibly 'undefined'` 错误。 In the following example, the `person` and `address` properties are always set together, implying that `address` is always non-null if `person` is non-null. diff --git a/aio/content/guide/aot-metadata-errors.md b/aio/content/guide/aot-metadata-errors.md index 0b08afd9f6..7a07b5d62c 100644 --- a/aio/content/guide/aot-metadata-errors.md +++ b/aio/content/guide/aot-metadata-errors.md @@ -38,7 +38,7 @@ The following are metadata errors you may encounter, with explanations and sugge Language features outside of the compiler's [restricted expression syntax](guide/aot-compiler#expression-syntax) can produce this error, as seen in the following example: -如以下示例所示,使用了编译器的[受限表达式语法](guide/aot-compiler#expression-syntax)之外的语言特性可能会产生此错误: +如以下范例所示,使用了编译器的[受限表达式语法](guide/aot-compiler#expression-syntax)之外的语言特性可能会产生此错误: ```ts @@ -610,7 +610,7 @@ Angular 也用 `DOCUMENT` 令牌做了类似的事情,所以你也可以注入 ```ts import { Inject } from '@angular/core'; -import { DOCUMENT } from '@angular/platform-browser'; +import { DOCUMENT } from '@angular/common'; @Component({ ... }) export class MyComponent { @@ -637,7 +637,7 @@ export class MyComponent { This can happen if you use a number as a property name as in the following example. -如果将数字用作属性名称,则可能发生这种情况,如以下示例所示。 +如果将数字用作属性名称,则可能发生这种情况,如以下范例所示。 ```ts diff --git a/aio/content/guide/app-shell.md b/aio/content/guide/app-shell.md index 94519bf7b8..00aec74cf9 100644 --- a/aio/content/guide/app-shell.md +++ b/aio/content/guide/app-shell.md @@ -13,7 +13,7 @@ This gives users a meaningful first paint of your application that appears quick Learn more in [The App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell). -欲知详情,参见[应用外壳模型](https://developers.google.com/web/fundamentals/architecture/app-shell)。 +欲知详情,参阅[应用外壳模型](https://developers.google.com/web/fundamentals/architecture/app-shell)。 ## Step 1: Prepare the application diff --git a/aio/content/guide/architecture-components.md b/aio/content/guide/architecture-components.md index 1c05b03697..827b35caa5 100644 --- a/aio/content/guide/architecture-components.md +++ b/aio/content/guide/architecture-components.md @@ -27,7 +27,7 @@ The class interacts with the view through an API of properties and methods. For example, `HeroListComponent` has a `heroes` property that holds an array of heroes. Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list. -The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. +The component acquires the heroes from a service, which is a TypeScript [parameter property](https://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system. 比如,`HeroListComponent` 中有一个 名为 `heroes` 的属性,它储存着一个数组的英雄数据。 @@ -137,7 +137,7 @@ This template uses typical HTML elements like `

` and `

`, and also includ * `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below. - `{{hero.name}}`、`(click)` 和 `[hero]` 把程序数据绑定到及绑定回 DOM,以响应用户的输入。更多内容参见稍后的[数据绑定](#data-binding)部分。 + `{{hero.name}}`、`(click)` 和 `[hero]` 把程序数据绑定到及绑定回 DOM,以响应用户的输入。更多内容参阅稍后的[数据绑定](#data-binding)部分。 * The `` tag in the example is an element that represents a new component, `HeroDetailComponent`. `HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`. @@ -175,15 +175,15 @@ This example from the `HeroListComponent` template uses three of these forms. -* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation) +* The `{{hero.name}}` [*interpolation*](guide/interpolation) displays the component's `hero.name` property value within the `

  • ` element. - `{{hero.name}}` 这个[*插值*](guide/displaying-data#interpolation)在 `
  • ` 标签中显示组件的 `hero.name` 属性的值。 + `{{hero.name}}` 这个[*插值*](guide/interpolation)在 `
  • ` 标签中显示组件的 `hero.name` 属性的值。 -* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of +* The `[hero]` [*property binding*](guide/property-binding) passes the value of `selectedHero` from the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`. - `[hero]`[*属性绑定*](guide/template-syntax#property-binding)把父组件 `HeroListComponent` 的 `selectedHero` 的值传到子组件 `HeroDetailComponent` 的 `hero` 属性中。 + `[hero]`[*属性绑定*](guide/property-binding)把父组件 `HeroListComponent` 的 `selectedHero` 的值传到子组件 `HeroDetailComponent` 的 `hero` 属性中。 * The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name. @@ -229,13 +229,13 @@ Angular pipes let you declare display-value transformations in your template HTM Angular 的管道可以让你在模板中声明显示值的转换逻辑。 带有 `@Pipe` 装饰器的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。 -Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes. +Angular defines various pipes, such as the [date](api/common/DatePipe) pipe and [currency](api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](api?type=pipe). You can also define new pipes. -Angular 自带了很多管道,比如 [date](api/common/DatePipe) 管道和 [currency](api/common/CurrencyPipe) 管道,完整的列表参见 [Pipes API 列表](api?type=pipe)。你也可以自己定义一些新管道。 +Angular 自带了很多管道,比如 [date](api/common/DatePipe) 管道和 [currency](api/common/CurrencyPipe) 管道,完整的列表参阅 [Pipes API 列表](api?type=pipe)。你也可以自己定义一些新管道。 -To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe). +To specify a value transformation in an HTML template, use the [pipe operator (|)](guide/pipes). -要在 HTML 模板中指定值的转换方式,请使用 [管道操作符 (|)](guide/template-syntax#pipe)。 +要在 HTML 模板中指定值的转换方式,请使用 [管道操作符 (|)](guide/pipes)。 `{{interpolated_value | pipe_name}}` @@ -300,13 +300,13 @@ The example template uses two built-in structural directives to add application - * [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `
  • ` per hero in the `heroes` list. + * [`*ngFor`](guide/structural-directives#inside-ngfor) is an iterative; it tells Angular to stamp out one `
  • ` per hero in the `heroes` list. - [`*ngFor`](guide/displaying-data#ngFor) 是一个迭代器,它要求 Angular 为 `heroes` 列表中的每个英雄渲染出一个 `
  • `。 + [`*ngFor`](guide/structural-directives#inside-ngfor) 是一个迭代器,它要求 Angular 为 `heroes` 列表中的每个英雄渲染出一个 `
  • `。 - * [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists. + * [`*ngIf`](guide/structural-directives#ngif-case-study) is a conditional; it includes the `HeroDetail` component only if a selected hero exists. - [`*ngIf`](guide/displaying-data#ngIf) 是个条件语句,只有当选中的英雄存在时,它才会包含 `HeroDetail` 组件。 + [`*ngIf`](guide/structural-directives#ngif-case-study) 是个条件语句,只有当选中的英雄存在时,它才会包含 `HeroDetail` 组件。 #### Attribute directives @@ -326,16 +326,16 @@ The `ngModel` directive, which implements two-way data binding, is an example of Angular has more pre-defined directives that either alter the layout structure -(for example, [ngSwitch](guide/template-syntax#ngSwitch)) +(for example, [ngSwitch](guide/built-in-directives#ngSwitch)) or modify aspects of DOM elements and components -(for example, [ngStyle](guide/template-syntax#ngStyle) and [ngClass](guide/template-syntax#ngClass)). +(for example, [ngStyle](guide/built-in-directives#ngStyle) and [ngClass](guide/built-in-directives#ngClass)). -Angular 还有很多预定义指令,有些修改布局结构(比如 [ngSwitch](guide/template-syntax#ngSwitch)),有些修改 DOM 元素和组件的样子(比如 [ngStyle](guide/template-syntax#ngStyle) 和 [ngClass](guide/template-syntax#ngClass))。 +Angular 还有很多预定义指令,有些修改布局结构(比如 [ngSwitch](guide/built-in-directives#ngSwitch)),有些修改 DOM 元素和组件的样子(比如 [ngStyle](guide/built-in-directives#ngStyle) 和 [ngClass](guide/built-in-directives#ngClass))。
    Learn more in the [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives) guides. -欲知详情,参见[属性型指令](guide/attribute-directives)和[结构型指令](guide/structural-directives)这两章。 +欲知详情,参阅[属性型指令](guide/attribute-directives)和[结构型指令](guide/structural-directives)这两章。
    diff --git a/aio/content/guide/architecture-modules.md b/aio/content/guide/architecture-modules.md index e14ed6a65d..e03ac3be5e 100644 --- a/aio/content/guide/architecture-modules.md +++ b/aio/content/guide/architecture-modules.md @@ -38,7 +38,7 @@ NgModule 是一个带有 `@NgModule()` 装饰器的类。`@NgModule()` 装饰器 `imports`(导入表) —— 那些导出了*本*模块中的组件模板所需的类的其它模块。 -* `providers`: Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level, which is often preferred.) +* `providers`: Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level.) `providers` —— 本模块向全局服务中贡献的那些[服务](guide/architecture-services)的创建器。 这些服务能被本应用中的任何部分使用。(你也可以在组件级别指定服务提供者,这通常是首选方式。) @@ -127,10 +127,9 @@ JavaScript 中,每个*文件*是一个模块,文件中定义的所有对象
    ## Angular libraries @@ -176,6 +175,6 @@ In this way you're using the Angular and JavaScript module systems *together*. A Learn more from the [NgModules](guide/ngmodules) guide. - 更多信息,参见 [NgModules](guide/ngmodules)。 + 更多信息,参阅 [NgModules](guide/ngmodules)。 diff --git a/aio/content/guide/architecture-next-steps.md b/aio/content/guide/architecture-next-steps.md index 0d61a8f7f8..ad05fd1e6d 100644 --- a/aio/content/guide/architecture-next-steps.md +++ b/aio/content/guide/architecture-next-steps.md @@ -9,7 +9,7 @@ about the features and tools that can help you develop and deliver Angular appli * Work through the [Tour of Heroes](tutorial) tutorial to get a feel for how to fit the basic building blocks together to create a well-designed application. - 参考“[英雄指南”](tutorial/index)教程,了解如何将这些基本构建块放在一起,来创建设计精良的应用。 + 参考“[英雄之旅”](tutorial/index)教程,了解如何将这些基本构建块放在一起,来创建设计精良的应用。 * Check out the [Glossary](guide/glossary) to understand Angular-specific terms and usage. @@ -23,9 +23,9 @@ about the features and tools that can help you develop and deliver Angular appli ## 应用架构 -* The [Components and templates](guide/displaying-data) guide explains how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application. +* The **Main Concepts** section located in the table of contents contains several topics that explain how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application. - [组件与模板](guide/displaying-data)一章中介绍了如何把组件中的应用数据与页面显示[模板](guide/glossary#template)联系起来,以创建一个完整的交互式应用。 + 目录中的**主要概念**部分包含一系列主题,用于解释如何把[组件](guide/glossary#component)中的应用数据与页面显示[模板](guide/glossary#template)联系起来,以创建一个完整的交互式应用。 * The [NgModules](guide/ngmodules) guide provides in-depth information on the modular structure of an Angular application. @@ -43,7 +43,7 @@ about the features and tools that can help you develop and deliver Angular appli ## 响应式编程 -The **Components and Templates** guide provides guidance and details of the [template syntax](guide/template-syntax) that you use to display your component data when and where you want it within a view, and to collect input from users that you can respond to. +The [template syntax](guide/template-syntax) and related topics contain details about how to display your component data when and where you want it within a view, and how to collect input from users that you can respond to. **“组件和模板”**一章提供了[模板语法](guide/template-syntax)的指南和详细信息,用于在视图中随时随地显示组件数据,并从用户那里收集输入,以便做出响应。 @@ -102,10 +102,6 @@ Angular 为单页面应用提供了一个框架,其中的大多数逻辑和数 ## 为开发周期提供支持 -The **Development Workflow** section describes the tools and processes you use to compile, test, and deploy Angular applications. - -**“开发工作流”**部分描述了用于编译、测试和部署 Angular 应用的工具和过程。 - * [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment. [CLI 命令参考手册](cli):Angular CLI 是一个命令行工具,可用于创建项目、生成应用和库代码,以及执行各种持续开发任务,如测试、打包和部署。 diff --git a/aio/content/guide/architecture-services.md b/aio/content/guide/architecture-services.md index aa8ab553d4..e432a84eab 100644 --- a/aio/content/guide/architecture-services.md +++ b/aio/content/guide/architecture-services.md @@ -148,11 +148,11 @@ or in the `@NgModule()` or `@Component()` metadata When you provide the service at the root level, Angular creates a single, shared instance of `HeroService` and injects it into any class that asks for it. Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app - by removing the service from the compiled app if it isn't used. + by removing the service from the compiled app if it isn't used, a process known as *tree-shaking*. 当你在根一级提供服务时,Angular 会为 HeroService 创建一个单一的共享实例,并且把它注入到任何想要它的类中。这种在 `@Injectable` 元数据中注册提供者的方式还让 Angular 能够通过移除那些从未被用过的服务来优化大小。 -* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator, +* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator. 当你使用[特定的 NgModule](guide/architecture-modules) 注册提供者时,该服务的同一个实例将会对该 NgModule 中的所有组件可用。要想在这一层注册,请用 `@NgModule()` 装饰器中的 `providers` 属性: @@ -177,4 +177,4 @@ At the component level, register a service provider in the `providers` property For more detailed information, see the [Dependency Injection](guide/dependency-injection) section. -要了解更多细节,请参见[依赖注入](guide/dependency-injection)一节。 +要了解更多细节,请参阅[依赖注入](guide/dependency-injection)一节。 diff --git a/aio/content/guide/architecture.md b/aio/content/guide/architecture.md index 3a32a79017..bd8fb74435 100644 --- a/aio/content/guide/architecture.md +++ b/aio/content/guide/architecture.md @@ -10,7 +10,7 @@ Angular 是一个用 HTML 和 TypeScript 构建客户端应用的平台与框架 Angular 本身就是用 TypeScript 写成的。它将核心功能和可选功能作为一组 TypeScript 库进行实现,你可以把它们导入你的应用中。 The architecture of an Angular application relies on certain fundamental concepts. -The basic building blocks are *NgModules*, which provide a compilation context for *components*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*. +The basic building blocks of the Angular framework are Angular components that are organized into *NgModules*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*. Angular 的基本构造块是 *NgModule*,它为*组件*提供了编译的上下文环境。 NgModule 会把相关的代码收集到一些功能集中。Angular 应用就是由一组 NgModule 定义出的。 @@ -49,7 +49,7 @@ An app's components typically define many views, arranged hierarchically. Angula See the [Angular Glossary](guide/glossary) for basic definitions of important Angular terms and usage. - 参见 [Angular 词汇表](guide/glossary) 以了解对 Angular 重要名词和用法的基本定义。 + 参阅 [Angular 词汇表](guide/glossary) 以了解对 Angular 重要名词和用法的基本定义。 @@ -57,7 +57,7 @@ An app's components typically define many views, arranged hierarchically. Angula For the sample app that this page describes, see the . - 要想查看本页所讲的范例程序,参见。 + 要想查看本页所讲的范例程序,参阅。 @@ -90,7 +90,7 @@ Organizing your code into distinct functional modules helps in managing developm For a more detailed discussion, see [Introduction to modules](guide/architecture-modules). - 更深入的讨论,参见[模块简介](guide/architecture-modules)。 + 更深入的讨论,参阅[模块简介](guide/architecture-modules)。 @@ -155,7 +155,7 @@ Angular 为一些通用的转换提供了预定义管道,你还可以定义自 For a more detailed discussion of these concepts, see [Introduction to components](guide/architecture-components). - 要了解对这些概念的深入讨论,参见[组件介绍](guide/architecture-components)。 + 要了解对这些概念的深入讨论,参阅[组件介绍](guide/architecture-components)。 @@ -178,7 +178,7 @@ For data or logic that isn't associated with a specific view, and that you want For a more detailed discussion, see [Introduction to services and DI](guide/architecture-services). - 更深入的讨论,参见[服务和 DI 简介](guide/architecture-services)。 + 更深入的讨论,参阅[服务和 DI 简介](guide/architecture-services)。 @@ -228,7 +228,7 @@ To define navigation rules, you associate *navigation paths* with your component For a more detailed discussion, see [Routing and navigation](guide/router). - 更深入的讨论,参见[路由与导航](guide/router)。 + 更深入的讨论,参阅[路由与导航](guide/router)。 @@ -302,5 +302,5 @@ Each of these subjects is introduced in more detail in the following pages. When you're familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps: tools and techniques](guide/architecture-next-steps). 当你熟悉了这些基础构造块之后,就可以在本文档中进一步查看它们的详情了。 -要学习能帮你构建和发布 Angular 应用的更多工具和技巧,参见[后续步骤:工具与技巧](guide/architecture-next-steps)。 +要学习能帮你构建和发布 Angular 应用的更多工具和技巧,参阅[后续步骤:工具与技巧](guide/architecture-next-steps)。 diff --git a/aio/content/guide/attribute-binding.md b/aio/content/guide/attribute-binding.md new file mode 100644 index 0000000000..8ef1d80362 --- /dev/null +++ b/aio/content/guide/attribute-binding.md @@ -0,0 +1,388 @@ +# Attribute, class, and style bindings + +# Attribute 绑定、类绑定和样式绑定 + +Attribute binding in Angular helps you set values for attributes directly. +With attribute binding, you can improve accessibility, style your application dynamically, and manage multiple CSS classes or styles simultaneously. + +Angular 中的 Attribute 绑定可帮助你直接设置 Attribute 值。使用 Attribute 绑定,你可以提升无障碍性、动态设置应用程序样式以及同时管理多个 CSS 类或样式。 + +
    + +See the for a working example containing the code snippets in this guide. + +包含本指南中的代码片段的可工作示例,请参阅。 + +
    + +## Binding to an attribute + +## 绑定到 Attribute + +It is recommended that you set an element property with a [property binding](guide/property-binding) whenever possible. +However, sometimes you don't have an element property to bind. +In those situations, you can use attribute binding. + +建议你尽可能设置带有 [Property 绑定](guide/property-binding)的元素的 Property。但是,有时你没有可绑定的元素 Property。在这种情况下,可以使用 Attribute 绑定。 + +For example, [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and +[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG) are purely attributes. +Neither ARIA nor SVG correspond to element properties and don't set element properties. +In these cases, you must use attribute binding because there are no corresponding property targets. + +例如,[ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA)和[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG) 只有 Attribute。 ARIA 和 SVG 都不对应于元素的 Property,也不设置元素的 Property。在这些情况下,必须使用 Attribute 绑定,因为没有相应的目标 Property。 + +## Syntax + +## 语法 + +Attribute binding syntax resembles [property binding](guide/property-binding), but instead of an element property between brackets, you precede the name of the attribute with the prefix `attr`, followed by a dot. +Then, you set the attribute value with an expression that resolves to a string. + +Attribute 绑定语法类似于 [Property 绑定](guide/property-binding),但不是直接在方括号之间放置元素的 Property,而是在 Attribute 名称前面加上前缀 `attr`,后跟一个点 `.`。然后,使用解析为字符串的表达式设置 Attribute 值。 + + + + <p [attr.attribute-you-are-targeting]="expression"></p> + + + +
    + +When the expression resolves to `null`, Angular removes the attribute altogether. + +当表达式解析为 `null` 时,Angular 会完全删除该 Attribute。 + +
    + +## Binding ARIA attributes + +## 绑定 ARIA Attribute + +One of the primary use cases for attribute binding +is to set ARIA attributes, as in this example: + +Attribute 绑定的主要用例之一是设置 ARIA Attribute,如下所示: + + + +{@a colspan} + +## Binding to `colspan` + +## 绑定到 `colspan` + +Another common use case for attribute binding is with the `colspan` attribute in tables. +Binding to the `colspan` attribute helps you keep your tables programmatically dynamic. +Depending on the amount of data that your application populates a table with, the number of columns that a row spans could change. + +Attribute 绑定的另一个常见用例是绑定到表格中的 `colspan` Attribute。`colspan` Attribute 可帮助你以编程方式让表格保持动态。根据应用中用来填充表的数据量,某一行要跨越的列数可能会发生变化。 + +To use attribute binding with the `
  • ` to span two columns. + +此绑定会导致 `` 跨越两列。 + +
    + +Sometimes there are differences between the name of property and an attribute. + +有时,Property 名和 Attribute 名之间存在差异。 + +`colspan` is an attribute of `
    `, while `colSpan` with a capital "S" is a property. +When using attribute binding, use `colspan` with a lowercase "s". +For more information on how to bind to the `colSpan` property, see the [`colspan` and `colSpan`](guide/property-binding#colspan) section of [Property Binding](guide/property-binding). + +`colspan` 是 `` 的 Attribute,而 `colSpan`(注意 “S” 是大写)是 Property。使用 Attribute 绑定时,请使用带小写 “s” 的 `colspan`。有关如何绑定到 `colSpan` Property 的更多信息,请参见 [Property 绑定](guide/property-binding) 中的 [`colspan` 和 `colSpan`](guide/property-binding#colspan) 部分。 + + + +
    + +{@a class-binding} + +## Binding to the `class` attribute + +## 绑定到 `class` Attribute + +You can use class binding to add and remove CSS class names from an element's `class` attribute. + +你可以使用类绑定从元素的 `class` Attribute 中添加和删除 CSS 类名称。 + +### Binding to a single CSS `class` + +### 绑定到单个 CSS `class` + +To create a single class binding, use the prefix `class` followed by a dot and the name of the CSS class—for example, `[class.sale]="onSale"`. +Angular adds the class when the bound expression, `onSale` is truthy, and it removes the class when the expression is falsy—with the exception of `undefined`. +See [styling delegation](guide/style-precedence#styling-delegation) for more information. + +要创建单个类绑定,请使用前缀 `class` 后跟一个点和 CSS 类的名称,例如 `[class.sale]="onSale"`。`onSale` 为真值时添加类,在表达式为假值时(`undefined` 除外)删除类。欲知详情,请参见[样式委托](guide/style-precedence#styling-delegation)部分。 + +### Binding to multiple CSS classes + +### 绑定到多个 CSS 类 + +To bind to multiple classes, use `[class]` set to an expression—for example, `[class]="classExpression"`. +The expression can be a space-delimited string of class names, or an object with class names as the keys and truthy or falsy expressions as the values. +With an object format, Angular adds a class only if its associated value is truthy. + +要绑定到多个类,请使用 `[class]` 来设置表达式 - 例如,`[class]="classExpression"`。表达式可以是用空格分隔的类名字符串,也可以是将类名作为键并将真或假表达式作为值的对象。对于对象格式,Angular 会在其关联的值为真时才添加类。 + +
    + +With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list. +Updating the property without changing object identity has no effect. + +对于任何类似对象的表达式(例如 `object`、`Array`、`Map` 或 `Set`,必须更改对象的引用,Angular 才能更新类列表。在不更改对象引用的情况下只更新其 Attribute 是不会生效的。 + +
    + +If there are multiple bindings to the same class name, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use. + +如果同一类名有多个绑定,Angular 会根据[样式优先级](guide/style-precedence)来确定要使用的绑定。 + +The following table summarizes class binding syntax. + +下表是各种类绑定语法的小结。 + + + +
    ` attribute `colspan`: + +要将 Attribute 绑定到 `` 的 `colspan` Attribute: + +1. Specify the `colspan` attribute by using the following syntax: `[attr.colspan]`. + + 使用以下语法指定 `colspan`:`[attr.colspan]` 。 + +1. Set `[attr.colspan]` equal to an expression. + + 将 `[attr.colspan]` 设置为等于某个表达式。 + +In the following example, we bind the `colspan` attribute to the expression `1 + 1`. + +在下面的示例中,我们将 `colspan` Attribute 绑定到表达式 `1 + 1`。 + + + +This binding causes the `
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Binding Type + + Syntax + + Input Type + + Example Input Values +
    + 绑定类型 + + 语法 + + 输入类型 + + 范例输入值 +
    Single class binding[class.sale]="onSale"boolean | undefined | nulltrue, false
    单一类绑定[class.sale]="onSale"boolean | undefined | nulltrue, false
    Multi-class binding[class]="classExpression"string"my-class-1 my-class-2 my-class-3"
    多重类绑定[class]="classExpression"string"my-class-1 my-class-2 my-class-3"
    {[key: string]: boolean | undefined | null}{foo: true, bar: false}
    Array<string>['foo', 'bar']
    + +
    + +{@a style-binding} + +## Binding to the style attribute + +## 绑定到 style Attribute + +You can use style binding to set styles dynamically. + +你可以使用样式绑定来动态设置样式。 + +### Binding to a single style + +### 绑定到单一样式 + +To create a single style binding, use the prefix `style` followed by a dot and the name of the CSS style property—for example, `[style.width]="width"`. +Angular sets the property to the value of the bound expression, which is usually a string. +Optionally, you can add a unit extension like `em` or `%`, which requires a number type. + +要创建对单个样式的绑定,请使用前缀 `style` 后跟一个点和 CSS style Attribute 的名称,例如 `[style.width]="width"`。 Angular 会将该 Attribute 设置为绑定表达式的值,这个值通常是一个字符串。(可选)你还可以添加单位扩展,例如 `em` 或 `%` ,它的值需要数字类型。 + +
    + +You can write a style property name in either [dash-case](guide/glossary#dash-case), or +[camelCase](guide/glossary#camelcase). + +你可以用[中线格式](guide/glossary#dash-case)或 [camelCase 格式](guide/glossary#camelcase)编写样式 Attribute 名。 + +
    + +### Binding to multiple styles + +### 绑定到多个样式 + +To toggle multiple styles, bind to the `[style]` attribute—for example, `[style]="styleExpression"`. +The expression is often a string list of styles such as `"width: 100px; height: 100px;"`. + +要切换多个样式,请绑定到 `[style]` Attribute,例如 `[style]="styleExpression"` 。该表达式通常是样式的字符串列表,例如 `"width: 100px; height: 100px;"` 。 + +You can also format the expression as an object with style names as the keys and style values as the values, such as `{width: '100px', height: '100px'}`. + +你还可以将表达式格式化为对象,此对象以样式名作为键、以样式值作为值,例如 `{width: '100px', height: '100px'}`。 + +
    + +With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list. +Updating the property without changing object identity has no effect. + +对于任何类似对象的表达式(例如 `object`、`Array`、`Map` 或 `Set`,必须更改对象的引用,Angular 才能更新类列表。在不更改对象引用的情况下更新其 Attribute 值是不会生效的。 + +
    + +If there are multiple bindings to the same style attribute, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use. + +如果同一个样式 Attribute 有多个绑定,Angular 将使用[样式优先级](guide/style-precedence)来确定要使用的绑定。 + +The following table summarizes style binding syntax. + +下表是各种样式绑定语法的小结。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Binding Type + + Syntax + + Input Type + + Example Input Values +
    + 绑定类型 + + 语法 + + 输入属性 + + 范例输入值 +
    Single style binding[style.width]="width"string | undefined | null"100px"
    单一样式绑定[style.width]="width"string | undefined | null"100px"
    Single style binding with units[style.width.px]="width"number | undefined | null100
    带单位的单一样式绑定[style.width.px]="width"number | undefined | null100
    Multi-style binding[style]="styleExpression"string"width: 100px; height: 100px"
    多重样式绑定[style]="styleExpression"string"width: 100px; height: 100px"
    {[key: string]: string | undefined | null}{width: '100px', height: '100px'}
    Array<string>['width', '100px']
    diff --git a/aio/content/guide/attribute-directives.md b/aio/content/guide/attribute-directives.md index b8e8d27348..31df1257cd 100644 --- a/aio/content/guide/attribute-directives.md +++ b/aio/content/guide/attribute-directives.md @@ -39,18 +39,18 @@ You saw a component for the first time in the [Getting Started](start "Getting S 你在[快速上手](start "Getting Started with Angular")例子中第一次见到组件。 *Structural Directives* change the structure of the view. -Two examples are [NgFor](guide/template-syntax#ngFor) and [NgIf](guide/template-syntax#ngIf). +Two examples are [NgFor](guide/built-in-directives#ngFor) and [NgIf](guide/built-in-directives#ngIf). Learn about them in the [Structural Directives](guide/structural-directives) guide. -*结构型*指令修改视图的结构。例如,[NgFor](guide/template-syntax#ngFor) 和 [NgIf](guide/template-syntax#ngIf)。 -要了解更多,参见[结构型指令](guide/structural-directives) 指南。 +*结构型*指令修改视图的结构。例如,[NgFor](guide/built-in-directives#ngFor) 和 [NgIf](guide/built-in-directives#ngIf)。 +要了解更多,参阅[结构型指令](guide/structural-directives) 指南。 *Attribute directives* are used as attributes of elements. -The built-in [NgStyle](guide/template-syntax#ngStyle) directive in the -[Template Syntax](guide/template-syntax) guide, for example, +The built-in [NgStyle](guide/built-in-directives#ngStyle) directive in the +[Built-in directives](guide/built-in-directives) guide, for example, can change several element styles at the same time. -*属性型*指令改变一个元素的外观或行为。例如,内置的 [NgStyle](guide/template-syntax#ngStyle) 指令可以同时修改元素的多个样式。 +*属性型*指令改变一个元素的外观或行为。例如,内置的 [NgStyle](guide/built-in-directives#ngStyle) 指令可以同时修改元素的多个样式。 ## Build a simple attribute directive @@ -115,7 +115,7 @@ The imported `Directive` symbol provides Angular the `@Directive` decorator. 这里导入的 `Directive` 符号提供了 Angular 的 `@Directive` 装饰器。 The `@Directive` decorator's lone configuration property specifies the directive's -[CSS attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors), `[appHighlight]`. +[CSS attribute selector](https://developer.mozilla.org/docs/Web/CSS/Attribute_selectors), `[appHighlight]`. `@Directive` 装饰器的配置属性中指定了该指令的 [CSS 属性型选择器](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) `[appHighlight]` @@ -320,19 +320,19 @@ Add a `highlightColor` property to the directive class like this: {@a input} -### Binding to an _@Input_ property +### Binding to an `@Input()` property ### 绑定到 *@Input* 属性 -Notice the `@Input` decorator. It adds metadata to the class that makes the directive's `highlightColor` property available for binding. +Notice the `@Input()` decorator. It adds metadata to the class that makes the directive's `highlightColor` property available for binding. 注意看 `@Input` 装饰器。它往类上添加了一些元数据,从而让该指令的 `highlightColor` 能用于绑定。 It's called an *input* property because data flows from the binding expression _into_ the directive. -Without that input metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more about that. +Without that `@Input()` metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more information. 它之所以称为*输入*属性,是因为数据流是从绑定表达式流向指令内部的。 -如果没有这个元数据,Angular 就会拒绝绑定,参见[稍后](guide/attribute-directives#why-input "为什么要添加@Input?")了解更多。 +如果没有这个元数据,Angular 就会拒绝绑定,参阅[稍后](guide/attribute-directives#why-input "为什么要添加@Input?")了解更多。 Try it by adding the following directive binding variations to the `AppComponent` template: @@ -387,7 +387,7 @@ Fortunately you can name the directive property whatever you want _and_ **_alias 幸运的是,你可以随意命名该指令的属性,并且**给它指定一个用于绑定的别名**。 -Restore the original property name and specify the selector as the alias in the argument to `@Input`. +Restore the original property name and specify the selector as the alias in the argument to `@Input()`. 恢复原始属性名,并在 `@Input` 的参数中把该选择器指定为别名。 @@ -492,7 +492,7 @@ and fall back to "violet" as the default color. Angular knows that the `defaultColor` binding belongs to the `HighlightDirective` -because you made it _public_ with the `@Input` decorator. +because you made it _public_ with the `@Input()` decorator. Angular 之所以知道 `defaultColor` 绑定属于 `HighlightDirective`,是因为你已经通过 `@Input` 装饰器把它设置成了*公共*属性。 @@ -504,6 +504,35 @@ Here's how the harness should work when you're done coding. Final Highlight
    +
    + +{@a ngNonBindable} + +## `ngNonBindable` + +With the built-in template primitive `ngNonBindable`, Angular won't +evaluate expressions in elements. For example: + +使用由模板引擎原生支持的 `ngNonBindable` 伪指令,可以让 Angular 不对模板中的表达式进行求值。例如: + + + +The expression `{{ 1 + 1 }}` will render just as it does in your code editor, +and will not display `2`. This is helpful when you want to render code in the browser. + +表达式 `{{ 1 + 1 }}` 将会原样渲染,就像你的代码编辑器中一样,而不会显示为 `2`。当你要在浏览器中渲染代码时,这很有用。 + +When you apply `ngNonBindable` to an element, it stops any binding starting at that element, including child elements. However, `ngNonBindable` still allows +directives to work to the element where you apply `ngNonBindable`. In the following example, the `appHighlight` directive will still be active but Angular will not evaluate the expression `{{ 1 + 1 }}`. + +当你把 `ngNonBindable` 应用在元素上时,它会阻止元素及其所有子元素的绑定。不过,`ngNonBindable` 仍然允许指令作用于受 `ngNonBindable` 影响的元素上。下面的例子中,`appHighlight` 指令仍然会生效,但是 Angular 不会对表达式 `{{ 1 + 1 }}` 进行求值。 + + + +Additionally, if you apply `ngNonBindable` to a parent element, interpolation and binding of any sort, such as property binding, or event binding, is disabled for its children. + +另外,如果你把 `ngNonBindable` 应用于某个父元素,就会在它的所有子元素上禁用插值和任何类型的绑定比如属性绑定或事件绑定。 + ## Summary ## 小结 @@ -528,6 +557,10 @@ This page covered how to: [把值**绑定**到指令中](guide/attribute-directives#bindings)。 +* [Prevent expression evaluation](guide/attribute-directives#ngNonBindable). + + [防止对表达式求值](guide/attribute-directives#ngNonBindable). + The final source code follows: 最终的源码如下: @@ -547,11 +580,11 @@ You can also experience and download the -Either way, the `@Input` decorator tells Angular that this property is +Either way, the `@Input()` decorator tells Angular that this property is _public_ and available for binding by a parent component. -Without `@Input`, Angular refuses to bind to the property. +Without `@Input()`, Angular refuses to bind to the property. 无论哪种方式,`@Input` 装饰器都告诉 Angular,该属性是*公共的*,并且能被父组件绑定。 如果没有 `@Input`,Angular 就会拒绝绑定到该属性。 -You've bound template HTML to component properties before and never used `@Input`. +You've bound template HTML to component properties before and never used `@Input()`. What's different? 但你以前也曾经把模板 HTML 绑定到组件的属性,而且从来没有用过 `@Input`。 @@ -581,7 +614,7 @@ The difference is a matter of trust. Angular treats a component's template as _belonging_ to the component. The component and its template trust each other implicitly. Therefore, the component's own template may bind to _any_ property of that component, -with or without the `@Input` decorator. +with or without the `@Input()` decorator. 差异在于信任度不同。 Angular 把组件的模板看做*从属于*该组件的。 @@ -591,7 +624,7 @@ Angular 把组件的模板看做*从属于*该组件的。 But a component or directive shouldn't blindly trust _other_ components and directives. The properties of a component or directive are hidden from binding by default. They are _private_ from an Angular binding perspective. -When adorned with the `@Input` decorator, the property becomes _public_ from an Angular binding perspective. +When adorned with the `@Input()` decorator, the property becomes _public_ from an Angular binding perspective. Only then can it be bound by some other component or directive. 但组件或指令不应该盲目的信任其它组件或指令。 @@ -599,18 +632,18 @@ Only then can it be bound by some other component or directive. 从 Angular 绑定机制的角度来看,它们是*私有*的,而当添加了 `@Input` 时,Angular 绑定机制才会把它们当成*公共*的。 只有这样,它们才能被其它组件或属性绑定。 -You can tell if `@Input` is needed by the position of the property name in a binding. +You can tell if `@Input()` is needed by the position of the property name in a binding. 你可以根据属性名在绑定中出现的位置来判定是否要加 `@Input`。 * When it appears in the template expression to the ***right*** of the equals (=), - it belongs to the template's component and does not require the `@Input` decorator. + it belongs to the template's component and does not require the `@Input()` decorator. 当它出现在等号***右侧***的模板表达式中时,它属于模板所在的组件,不需要 `@Input` 装饰器。 * When it appears in **square brackets** ([ ]) to the **left** of the equals (=), the property belongs to some _other_ component or directive; - that property must be adorned with the `@Input` decorator. + that property must be adorned with the `@Input()` decorator. 当它出现在等号**左边**的**方括号([ ])**中时,该属性属于*其它*组件或指令,它必须带有 `@Input` 装饰器。 @@ -622,14 +655,14 @@ Now apply that reasoning to the following example: * The `color` property in the expression on the right belongs to the template's component. The template and its component trust each other. - The `color` property doesn't require the `@Input` decorator. + The `color` property doesn't require the `@Input()` decorator. `color` 属性位于右侧的绑定表达式中,它属于模板所在的组件。 该模板和组件相互信任。因此 `color` 不需要 `@Input` 装饰器。 * The `appHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`, - not a property of the template's component. There are trust issues. - Therefore, the directive property must carry the `@Input` decorator. + not a property of the template's component. + For security, the directive property must carry the `@Input()` decorator. `appHighlight` 属性位于左侧,它引用了 `HighlightDirective` 中一个*带别名的*属性,它不是模板所属组件的一部分,因此存在信任问题。 所以,该属性必须带 `@Input` 装饰器。 diff --git a/aio/content/guide/binding-syntax.md b/aio/content/guide/binding-syntax.md new file mode 100644 index 0000000000..07cc6aebb2 --- /dev/null +++ b/aio/content/guide/binding-syntax.md @@ -0,0 +1,563 @@ +# Binding syntax: an overview + +# 绑定语法:概述 + +Data-binding is a mechanism for coordinating what users see, specifically +with application data values. +While you could push values to and pull values from HTML, +the application is easier to write, read, and maintain if you turn these tasks over to a binding framework. +You simply declare bindings between binding sources, target HTML elements, and let the framework do the rest. + +数据绑定是一种机制,用来协调用户可见的内容,特别是应用数据的值。 +虽然也可以手动从 HTML 中推送或拉取这些值,但是如果将这些任务转交给绑定框架,应用就会更易于编写、阅读和维护。 +你只需声明数据源和目标 HTML 元素之间的绑定关系就可以了,框架会完成其余的工作。 + +
    + +See the for a working example containing the code snippets in this guide. + +包含本指南中的代码段的工作示例,请参阅。 + +
    + +Angular provides many kinds of data-binding. Binding types can be grouped into three categories distinguished by the direction of data flow: + +Angular 提供了多种数据绑定方式。绑定类型可以分为三类,按数据流的方向分为: + +* From the _source-to-view_ + + 从*数据源到视图* + +* From _view-to-source_ + + 从*视图到数据源* + +* Two-way sequence: _view-to-source-to-view_ + + 双向:*视图到数据源到视图* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + Type + + 绑定类型 + + + + Syntax + + 语法 + + + + Category + + 分类 + +
    + + Interpolation
    + Property
    + Attribute
    + Class
    + Style + + 插值
    + 属性
    + Attribute
    + CSS 类
    + 样式 + +
    + + + {{expression}} + [target]="expression" + bind-target="expression" + + + + + One-way
    from data source
    to view target + + 单向
    从数据源
    到视图 + +
    + + Event + + 事件 + + + + + (target)="statement" + on-target="statement" + + + + + One-way
    from view target
    to data source + + 从视图到数据源的单向绑定 + +
    + + Two-way + + 双向 + + + + + [(target)]="expression" + bindon-target="expression" + + + + + Two-way + + 双向 + +
    + +Binding types other than interpolation have a **target name** to the left of the equal sign, either surrounded by punctuation, `[]` or `()`, +or preceded by a prefix: `bind-`, `on-`, `bindon-`. + +除插值以外的其它绑定类型在等号的左侧都有一个“目标名称”,由绑定符 `[]` 或 `()` 包起来, +或者带有前缀:`bind-`,`on-`,`bindon-`。 + +The *target* of a binding is the property or event inside the binding punctuation: `[]`, `()` or `[()]`. + +绑定的“目标”是绑定符内部的属性或事件:`[]`、`()` 或 `[()]`。 + +Every public member of a **source** directive is automatically available for binding. +You don't have to do anything special to access a directive member in a template expression or statement. + +在绑定时可以使用**来源**指令的每个公共成员。 +你无需进行任何特殊操作即可在模板表达式或语句内访问指令的成员。 + +### Data-binding and HTML + +### 数据绑定与 HTML + +In the normal course of HTML development, you create a visual structure with HTML elements, and +you modify those elements by setting element attributes with string constants. + +在正常的 HTML 开发过程中,你使用 HTML 元素来创建视觉结构, +通过把字符串常量设置到元素的 attribute 来修改那些元素。 + +```html + +
    Plain old HTML
    + + + + +``` + +With data-binding, you can control things like the state of a button: + +使用数据绑定,你可以控制按钮状态等各个方面: + + + +Notice that the binding is to the `disabled` property of the button's DOM element, +**not** the attribute. This applies to data-binding in general. Data-binding works with *properties* of DOM elements, components, and directives, not HTML *attributes*. + +请注意,这里绑定到的是按钮的 DOM 元素的 `disabled` 这个 *Property*,而不是 *Attribute*。 +这是数据绑定的通用规则。数据绑定使用 DOM 元素、组件和指令的 *Property*,而不是 HTML 的*Attribute*。 + +{@a html-attribute-vs-dom-property} + +### HTML attribute vs. DOM property + +### HTML attribute 与 DOM property 的对比 + +The distinction between an HTML attribute and a DOM property is key to understanding +how Angular binding works. **Attributes are defined by HTML. Properties are accessed from DOM (Document Object Model) nodes.** + +理解 HTML 属性和 DOM 属性之间的区别,是了解 Angular 绑定如何工作的关键。**Attribute 是由 HTML 定义的。Property 是从 DOM(文档对象模型)节点访问的。** + +* A few HTML attributes have 1:1 mapping to properties; for example, `id`. + + 一些 HTML Attribute 可以 1:1 映射到 Property;例如,“ id”。 + +* Some HTML attributes don't have corresponding properties; for example, `aria-*`. + + 某些 HTML Attribute 没有相应的 Property。例如,`aria-*`。 + +* Some DOM properties don't have corresponding attributes; for example, `textContent`. + + 某些 DOM Property 没有相应的 Attribute。例如,`textContent`。 + +It is important to remember that *HTML attribute* and the *DOM property* are different things, even when they have the same name. +In Angular, the only role of HTML attributes is to initialize element and directive state. + +重要的是要记住,*HTML Attribute* 和 *DOM Property* 是不同的,就算它们具有相同的名称也是如此。 +在 Angular 中,HTML Attribute 的唯一作用是初始化元素和指令的状态。 + +**Template binding works with *properties* and *events*, not *attributes*.** + +**模板绑定使用的是 *Property* 和*事件*,而不是 *Attribute*。** + +When you write a data-binding, you're dealing exclusively with the *DOM properties* and *events* of the target object. + +编写数据绑定时,你只是在和目标对象的 *DOM Property* 和*事件*打交道。 + +
    + +This general rule can help you build a mental model of attributes and DOM properties: +**Attributes initialize DOM properties and then they are done. +Property values can change; attribute values can't.** + +该通用规则可以帮助你建立 HTML Attribute 和 DOM Property 的思维模型: +**属性负责初始化 DOM 属性,然后完工。Property 值可以改变;Attribute 值则不能。** + +There is one exception to this rule. +Attributes can be changed by `setAttribute()`, which re-initializes corresponding DOM properties. + +此规则有一个例外。 +可以通过 `setAttribute()` 来更改 Attribute,接着它会重新初始化相应的 DOM 属性。 + +
    + +For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties. +Comparing the [`` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) to the [`` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) provides a helpful example for differentiation. +In particular, you can navigate from the attributes page to the properties via "DOM interface" link, and navigate the inheritance hierarchy up to `HTMLTableCellElement`. + +欲知详情,参见 [MDN 接口文档](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces),其中包含所有标准 DOM 元素及其 Property 的 API 文档。 +[`` Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) 与 [`` Property](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) 之间的比较是一个很有用的例子。 +特别是,你可以通过 “DOM 接口” 链接从 Attribute 页面导航到 Property 页面,并在继承层次中导航到 `HTMLTableCellElement`。 + +#### Example 1: an `` + +#### 范例 1:`` + +When the browser renders ``, it creates a +corresponding DOM node with a `value` property initialized to "Sarah". + +当浏览器渲染 `` 时,它会创建一个对应的 DOM 节点,其 `value` Property 已初始化为 “Sarah”。 + +```html + + + +``` + +When the user enters "Sally" into the ``, the DOM element `value` *property* becomes "Sally". +However, if you look at the HTML attribute `value` using `input.getAttribute('value')`, you can see that the *attribute* remains unchanged—it returns "Sarah". + +当用户在 `` 中输入 `Sally` 时,DOM 元素的 `value` *Property* 将变为 `Sally`。 +但是,如果使用 `input.getAttribute('value')` 查看 HTML 的 Attribute `value`,则可以看到该 *attribute* 保持不变 —— 它返回了 `Sarah`。 + +The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value. + +HTML 的 `value` 这个 attribute 指定了*初始*值;DOM 的 `value` 这个 property 是*当前*值。 + +To see attributes versus DOM properties in a functioning app, see the especially for binding syntax. + +要通过可运行的应用查看 Attribute 和 DOM Property 的差别,请参阅 ,特别注意其绑定语法。 + +#### Example 2: a disabled button + +#### 范例 2:禁用按钮 + +The `disabled` attribute is another example. A button's `disabled` +*property* is `false` by default so the button is enabled. + +`disabled` Attribute 是另一个例子。按钮的 `disabled` *Property* 默认为 `false`,因此按钮是启用的。 + +When you add the `disabled` *attribute*, its presence alone +initializes the button's `disabled` *property* to `true` +so the button is disabled. + +当你添加 `disabled` *Attribute* 时,仅仅它的出现就将按钮的 `disabled` *Property* 初始化成了 `true`,因此该按钮就被禁用了。 + +```html + + + +``` + +Adding and removing the `disabled` *attribute* disables and enables the button. +However, the value of the *attribute* is irrelevant, +which is why you cannot enable a button by writing ``. + +添加和删​​除 `disabled` *Attribute* 会禁用和启用该按钮。 +但是,*Attribute* 的值无关紧要,这就是为什么你不能通过编写 `` 来启用此按钮的原因。 + +To control the state of the button, set the `disabled` *property*, + +要控制按钮的状态,请设置 `disabled` *Property*, + +
    + +Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to be a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following: + +虽然技术上说你可以设置 `[attr.disabled]` 属性绑定,但是它们的值是不同的,Property 绑定要求一个布尔值,而其相应的 Attribute 绑定则取决于该值是否为 `null`。例子如下: + +```html + + + + +``` + +Generally, use property binding over attribute binding as it is more intuitive (being a boolean value), has a shorter syntax, and is more performant. + +通常,要使用 Property 绑定而不是 Attribute 绑定,因为它更直观(是一个布尔值),语法更短,并且性能更高。 + +
    + + +To see the `disabled` button example in a functioning app, see the especially for binding syntax. This example shows you how to toggle the disabled property from the component. + +要通过可运行的应用查看 `disabled` 按钮示例,请参见,特别注意其绑定语法。本示例展示了如何从组件中切换禁用属性。 + +## Binding types and targets + +## 绑定类型和目标 + +The **target of a data-binding** is something in the DOM. +Depending on the binding type, the target can be a property (element, component, or directive), +an event (element, component, or directive), or sometimes an attribute name. +The following table summarizes the targets for the different binding types. + +**数据绑定**的目标是 DOM 中的某些东西。根据绑定类型,目标可以是属性(元素,组件或指令),事件(元素,组件或指令)或有时是属性名称。下表总结了不同绑定类型的目标。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Type + + Target + + Examples +
    + 类型 + + 目标 + + 范例 +
    + Property + + Element property
    + Component property
    + Directive property +
    + src, hero, and ngClass in the following: + + +
    + 属性 + + 元素属性
    + 组件属性
    + 指令属性 +
    + srcherongClass,代码如下: + + +
    + Event + + Element event
    + Component event
    + Directive event +
    + click, deleteRequest, and myClick in the following: + + + +
    + 事件 + + 元素事件
    + 组件事件
    + 指令事件 +
    + clickdeleteRequestmyClick,代码如下: + + + +
    + Two-way + + Event and property + + +
    + 双向 + + 事件与属性 + + +
    + Attribute + + Attribute + (the exception) + + +
    + Attribute + + Attribute + (少数特例情况) + + +
    + Class + + class property + + +
    + 类 + + class 属性 + + +
    + Style + + style property + + +
    + 样式 + + style 属性 + + +
    + diff --git a/aio/content/guide/bootstrapping.md b/aio/content/guide/bootstrapping.md index 1f064fd818..afc4294d38 100644 --- a/aio/content/guide/bootstrapping.md +++ b/aio/content/guide/bootstrapping.md @@ -25,7 +25,7 @@ By convention and by default, this NgModule is named `AppModule`. 每个应用有至少一个 Angular 模块,*根*模块就是你用来启动此应用的模块。 按照惯例,它通常命名为 `AppModule`。 -When you use the [Angular CLI](cli) command `ng new` to generate an app, the default `AppModule` is as follows. +When you use the [Angular CLI](cli) command `ng new` to generate an app, the default `AppModule` looks like the following: 当你使用 [Angular CLI](cli) 命令 `ng new` 生成一个应用时,其默认的 `AppModule` 是这样的: @@ -34,8 +34,6 @@ When you use the [Angular CLI](cli) command `ng new` to generate an app, the def /* JavaScript imports */ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; @@ -45,9 +43,7 @@ import { AppComponent } from './app.component'; AppComponent ], imports: [ - BrowserModule, - FormsModule, - HttpClientModule + BrowserModule ], providers: [], bootstrap: [AppComponent] @@ -144,10 +140,6 @@ import the module that has the declarable you need in it. 每个可声明对象都只能属于一个模块,所以只能把它声明在一个 `@NgModule` 中。当你需要在其它模块中使用它时,就要在那里导入包含这个可声明对象的模块。 -**Only `@NgModule` references** go in the `imports` array. - -**只有 `@NgModule`** 可以出现在 `imports` 数组中。 - ### Using directives with `@NgModule` ### 通过 `@NgModule` 使用指令 @@ -193,7 +185,7 @@ And in the same file, add it to the `@NgModule` `declarations` array: Now you could use your `ItemDirective` in a component. This example uses `AppModule`, but you'd do it the same way for a feature module. For more about directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives). You'd also use the same technique for [pipes](guide/pipes) and components. 现在,你就可以在组件中使用 `ItemDirective` 了。这个例子中使用的是 `AppModule`,但是在特性模块中你也可以这么做。 -要进一步了解指令,参见[属性型指令](guide/attribute-directives)和[结构型指令](guide/structural-directives)。 +要进一步了解指令,参阅[属性型指令](guide/attribute-directives)和[结构型指令](guide/structural-directives)。 这些也同样适用于[管道](guide/pipes)和组件。 Remember, components, directives, and pipes belong to one module only. You only need to declare them once in your app because you share them by importing the necessary modules. This saves you time and helps keep your app lean. @@ -212,8 +204,14 @@ It tells Angular about other NgModules that this particular module needs to func 模块的 `imports` 数组只会出现在 `@NgModule` 元数据对象中。 它告诉 Angular 该模块想要正常工作,还需要哪些模块。 + + + This list of modules are those that export components, directives, or pipes -that the component templates in this module reference. In this case, the component is +that component templates in this module reference. In this case, the component is `AppComponent`, which references components, directives, or pipes in `BrowserModule`, `FormsModule`, or `HttpClientModule`. A component template can reference another component, directive, @@ -235,7 +233,7 @@ them when using feature modules and lazy loading. For more information, see [Providers](guide/providers). `providers` 数组中列出了该应用所需的服务。当直接把服务列在这里时,它们是全应用范围的。 -当你使用特性模块和惰性加载时,它们是范围化的。要了解更多,参见[服务提供者](guide/providers)。 +当你使用特性模块和惰性加载时,它们是范围化的。要了解更多,参阅[服务提供者](guide/providers)。 ## The `bootstrap` array @@ -273,4 +271,4 @@ root module's `bootstrap` array. For more on NgModules you're likely to see frequently in apps, see [Frequently Used Modules](guide/frequent-ngmodules). -要进一步了解常见的 NgModules 知识,参见 [关于模块的常见问题](guide/frequent-ngmodules)。 +要进一步了解常见的 NgModules 知识,参阅 [关于模块的常见问题](guide/frequent-ngmodules)。 diff --git a/aio/content/guide/browser-support.md b/aio/content/guide/browser-support.md index 167feaff8e..c2ddddc2a4 100644 --- a/aio/content/guide/browser-support.md +++ b/aio/content/guide/browser-support.md @@ -89,32 +89,7 @@ Angular 支持大多数常用浏览器,包括下列版本: - - 11, 10*, 9* ("compatibility view" mode not supported) - - 11, 10*, 9* (不支持“兼容性视图”) - - *deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}. - - *在 v10 中弃用,参见弃用指南。 - - - - - - - - IE Mobile* - - - - - 11 - -
    *deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.
    - -
    *已在 v10 中弃用,参见 {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.
    - +
    11
    @@ -162,7 +137,7 @@ Angular 支持大多数常用浏览器,包括下列版本: - X (10.0), Pie (9.0), Oreo (8.0), Nougat (7.0) + Q (10.0), Pie (9.0), Oreo (8.0), Nougat (7.0) @@ -172,8 +147,8 @@ Angular 支持大多数常用浏览器,包括下列版本:
    Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request, -using SauceLabs and -Browserstack. +using [Sauce Labs](https://saucelabs.com/) and +[BrowserStack](https://www.browserstack.com/). Angular 在持续集成过程中,对每一个提交都会使用 SauceLabsBrowserstack 在上述所有浏览器上执行单元测试。 @@ -221,7 +196,7 @@ In Angular CLI version 8 and higher, applications are built using *differential This strategy allows you to continue to build your web application to support multiple browsers, but only load the necessary code that the browser needs. For more information about how this works, see [Differential Loading](guide/deployment#differential-loading) in the [Deployment guide](guide/deployment). -通过此策略,你可以继续构建 Web 应用程序以支持多个浏览器,但仅加载当前浏览器所需的必要代码。有关此工作原理的更多信息,请参见《[部署指南》](guide/deployment)中的“[差异化加载](guide/deployment#differential-loading) ”。 +通过此策略,你可以继续构建 Web 应用程序以支持多个浏览器,但仅加载当前浏览器所需的必要代码。关于此工作原理的更多信息,请参阅《[部署指南》](guide/deployment)中的“[差异化加载](guide/deployment#differential-loading) ”。 ## Enabling polyfills with CLI projects @@ -245,7 +220,7 @@ This file incorporates the mandatory and many of the optional polyfills as JavaS 如果需要*可选的*腻子脚本,则必须安装其 npm 捆绑包,然后取消注释或在 `src/polyfills.ts` 配置文件中创建相应的 import 语句。 -For example, if you need the optional [web animations polyfill](http://caniuse.com/#feat=web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent): +For example, if you need the optional [web animations polyfill](https://caniuse.com/web-animation), you could install it with `npm`, using the following command (or the `yarn` equivalent): 比如,如果需要可选的 [Web 动画腻子脚本](http://caniuse.com/#feat=web-animation),则可以使用以下命令来通过 `npm`(或等效的 `yarn` )安装它: @@ -265,7 +240,7 @@ For many polyfills, you can simply un-comment the corresponding `import` stateme * Required to support Web Animations `@angular/platform-browser/animations`. - * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation + * Needed for: All but Chrome, Firefox and Opera. https://caniuse.com/web-animation **/ import 'web-animations-js'; // Run `npm install --save web-animations-js`. @@ -310,7 +285,7 @@ These are the polyfills required to run an Angular application on each supported Chrome, Firefox, Edge,
    - Safari, Android, IE 10+ + Safari, Android, IE 11 @@ -321,20 +296,6 @@ These are the polyfills required to run an Angular application on each supported - - - - IE9 - - - - - ES2015
    [classList](guide/browser-support#classlist) - - - - - ### Optional browser features to polyfill @@ -423,34 +384,7 @@ Some features of Angular may require additional polyfills. - IE10, IE11 - - - - - - - - - [Http](guide/http) when sending and receiving binary data - - 用 [Http](guide/http) 发送和接收二进制数据时 - - - - - [Typed Array](guide/browser-support#typedarray)
    - - [Blob](guide/browser-support#blob)
    - - [FormData](guide/browser-support#formdata) - - - - - - IE 9 - + IE 11 @@ -625,76 +559,10 @@ The following polyfills are used to test the framework itself. They are a good s - - - - - Typed Array - - - - - - MIT - - - - - - 4KB - - - - - - - - - - Blob - - - - - - MIT - - - - - - 1.3KB - - - - - - - - - - FormData - - - - - - MIT - - - - - - 0.4KB - - - - - \* Figures are for minified and gzipped code, -computed with the closure compiler. +computed with the [closure compiler](https://closure-compiler.appspot.com/home). \* 这里的数据都按最小化并且 gzip 压缩后的版本算,是由closure compiler计算出的。 diff --git a/aio/content/guide/build.md b/aio/content/guide/build.md index 869ae2eac6..d195962154 100644 --- a/aio/content/guide/build.md +++ b/aio/content/guide/build.md @@ -425,7 +425,7 @@ For more information, see [How CommonJS is making your bundles larger](https://w 建议你在 Angular 应用中避免依赖 CommonJS 模块。对 CommonJS 模块的依赖会阻止打包器和压缩器优化你的应用,这会导致更大的打包尺寸。 建议你在整个应用中都使用 [ECMAScript 模块](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)。 -欲知详情,参见[为什么 CommonJS 会导致更大的打包尺寸](https://web.dev/commonjs-larger-bundles/)。 +欲知详情,参阅[为什么 CommonJS 会导致更大的打包尺寸](https://web.dev/commonjs-larger-bundles/)。
    @@ -491,7 +491,7 @@ Browserlist 会在 `package.json` 的 `browserlist` 属性中或一个名叫 `.b See the [browserslist repo](https://github.com/browserslist/browserslist) for more examples of how to target specific browsers and versions. -参见 [browserslist 的代码库](https://github.com/browserslist/browserslist)以得到如何指定浏览器及其版本的更多例子。 +参阅 [browserslist 的代码库](https://github.com/browserslist/browserslist)以得到如何指定浏览器及其版本的更多例子。 ### Backward compatibility with Lighthouse @@ -537,7 +537,7 @@ or For more information, see [Autoprefixer documentation](https://autoprefixer.github.io/). -欲知详情,参见 [Autoprefixer 文档](https://autoprefixer.github.io/)。 +欲知详情,参阅 [Autoprefixer 文档](https://autoprefixer.github.io/)。 {@a proxy} @@ -592,7 +592,7 @@ You can edit the proxy configuration file to add configuration options; some exa For a description of all options, see [webpack DevServer documentation](https://webpack.js.org/configuration/dev-server/#devserverproxy). 你可以编辑这个代理配置文件,以添加配置项,例子如下。 -要查看所有选项的详细说明,参见 [webpack DevServer 文档](https://webpack.js.org/configuration/dev-server/#devserver-proxy)。 +要查看所有选项的详细说明,参阅 [webpack DevServer 文档](https://webpack.js.org/configuration/dev-server/#devserver-proxy)。 Note that if you edit the proxy configuration file, you must relaunch the `ng serve` process to make your changes effective. diff --git a/aio/content/guide/built-in-directives.md b/aio/content/guide/built-in-directives.md new file mode 100644 index 0000000000..6e207b2752 --- /dev/null +++ b/aio/content/guide/built-in-directives.md @@ -0,0 +1,639 @@ +# Built-in directives + +# 内置指令 + +Angular offers two kinds of built-in directives: [_attribute_ directives](guide/attribute-directives) and [_structural_ directives](guide/structural-directives). + +Angular 提供了两种内置指令[*属性型*指令](guide/attribute-directives)和[*结构型*指令](guide/structural-directives)。 + +
    + +See the for a working example containing the code snippets in this guide. + +要查看包含本指南中代码的可工作范例,请参阅。 + +
    + +For more detail, including how to build your own custom directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives). + +欲知详情,包括如何构建你自己的自定义指令,请参阅[属性型指令](guide/attribute-directives)和[结构型指令](guide/structural-directives)。 + +
    + +{@a attribute-directives} + +## Built-in attribute directives + +## 内置属性型指令 + +Attribute directives listen to and modify the behavior of +other HTML elements, attributes, properties, and components. +You usually apply them to elements as if they were HTML attributes, hence the name. + +属性型指令会监听并修改其它 HTML 元素和组件的行为、Attribute 和 Property。 +它们通常被应用在元素上,就好像它们是 HTML 属性一样,因此得名属性型指令。 + +Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation") +and the [`FormsModule`](guide/forms "Forms") define their own attribute directives. +The most common attribute directives are as follows: + +许多 NgModule(例如 [`RouterModule`](guide/router "路由和导航") 和 [`FormsModule`](guide/forms "表单") 都定义了自己的属性型指令。最常见的属性型指令如下: + +* [`NgClass`](guide/built-in-directives#ngClass)—adds and removes a set of CSS classes. + + [`NgClass`](guide/built-in-directives#ngClass) —— 添加和删除一组 CSS 类。 + +* [`NgStyle`](guide/built-in-directives#ngStyle)—adds and removes a set of HTML styles. + + [`NgStyle`](guide/built-in-directives#ngStyle) —— 添加和删除一组 HTML 样式。 + +* [`NgModel`](guide/built-in-directives#ngModel)—adds two-way data binding to an HTML form element. + + [`NgModel`](guide/built-in-directives#ngModel) —— 将数据双向绑定添加到 HTML 表单元素。 + +
    + +{@a ngClass} + +## `NgClass` + +Add or remove several CSS classes simultaneously with `ngClass`. + +用 `ngClass` 同时添加或删除几个 CSS 类。 + + + +
    + +To add or remove a *single* class, use [class binding](guide/attribute-binding#class-binding) rather than `NgClass`. + +要添加或删除*单个*类,请使用[类绑定](guide/attribute-binding#class-binding)而不是 `NgClass`。 + +
    + +Consider a `setCurrentClasses()` component method that sets a component property, +`currentClasses`, with an object that adds or removes three classes based on the +`true`/`false` state of three other component properties. Each key of the object is a CSS class name; its value is `true` if the class should be added, +`false` if it should be removed. + +考虑一个 `setCurrentClasses()` 组件方法,该方法设置一个组件属性 `currentClasses`,该对象具有一个根据其它三个组件属性的 `true` / `false` 状态来添加或删除三个 CSS 类的对象。该对象的每个键(key)都是一个 CSS 类名。如果要添加上该类,则其值为 `true`,反之则为 `false`。 + + + +Adding an `ngClass` property binding to `currentClasses` sets the element's classes accordingly: + +把 `NgClass` 属性绑定到 `currentClasses`,根据它来设置此元素的 CSS 类: + + + +
    + +Remember that in this situation you'd call `setCurrentClasses()`, +both initially and when the dependent properties change. + +请记住,在这种情况下,你要在初始化时和它依赖的属性发生变化时调用 `setCurrentClasses()`。 + +
    + +
    + +{@a ngStyle} + +## `NgStyle` + +Use `NgStyle` to set many inline styles simultaneously and dynamically, based on the state of the component. + +使用 `NgStyle` 根据组件的状态同时动态设置多个内联样式。 + +### Without `NgStyle` + +### 不用 `NgStyle` + +For context, consider setting a *single* style value with [style binding](guide/attribute-binding#style-binding), without `NgStyle`. + +有些情况下,要考虑使用[样式绑定](guide/attribute-binding#style-binding)来设置*单个*样式值,而不使用 `NgStyle`。 + + + +However, to set *many* inline styles at the same time, use the `NgStyle` directive. + +但是,如果要同时设置*多个*内联样式,请使用 `NgStyle` 指令。 + +The following is a `setCurrentStyles()` method that sets a component +property, `currentStyles`, with an object that defines three styles, +based on the state of three other component properties: + +下面的例子是一个 `setCurrentStyles()` 方法,它基于该组件另外三个属性的状态,用一个定义了三个样式的对象设置了 `currentStyles` 属性。 + + + +Adding an `ngStyle` property binding to `currentStyles` sets the element's styles accordingly: + +把 `ngStyle` 属性绑定到 `currentStyles`,来根据它设置此元素的样式: + + + +
    + +Remember to call `setCurrentStyles()`, both initially and when the dependent properties change. + +请记住,无论是在初始时还是其依赖的属性发生变化时,都要调用 `setCurrentStyles()`。 + +
    + +
    + +{@a ngModel} + +## `[(ngModel)]`: Two-way binding + +## `[(ngModel)]` :双向绑定 + +The `NgModel` directive allows you to display a data property and +update that property when the user makes changes. Here's an example: + +`NgModel` 指令允许你显示数据属性并在用户进行更改时更新该属性。这是一个例子: + + + +### Import `FormsModule` to use `ngModel` + +### 导入 `FormsModule` 以使用 `ngModel` + +Before using the `ngModel` directive in a two-way data binding, +you must import the `FormsModule` and add it to the NgModule's `imports` list. +Learn more about the `FormsModule` and `ngModel` in [Forms](guide/forms#ngModel). + +要想在双向数据绑定中使用 `ngModel` 指令,必须先导入 `FormsModule` 并将其添加到 NgModule 的 `imports` 列表中。要了解关于 `FormsModule` 和 `ngModel` 的更多信息,参阅[表单](guide/forms#ngModel)一章。 + +Remember to import the `FormsModule` to make `[(ngModel)]` available as follows: + +记住,要导入 `FormsModule` 才能让 `[(ngModel)]` 可用,如下所示: + + + +You could achieve the same result with separate bindings to +the `` element's `value` property and `input` event: + +通过分别绑定到 `` 元素的 `value` 属性和 `input` 事件,可以达到同样的效果: + + + +To streamline the syntax, the `ngModel` directive hides the details behind its own `ngModel` input and `ngModelChange` output properties: + +为了简化语法,`ngModel` 指令把技术细节隐藏在其输入属性 `ngModel` 和输出属性 `ngModelChange` 的后面: + + + +The `ngModel` data property sets the element's value property and the `ngModelChange` event property +listens for changes to the element's value. + +`ngModel` 输入属性会设置该元素的值,并通过 `ngModelChange` 的输出属性来监听元素值的变化。 + +### `NgModel` and value accessors + +### `NgModel` 和值访问器 + +The details are specific to each kind of element and therefore the `NgModel` directive only works for an element +supported by a [ControlValueAccessor](api/forms/ControlValueAccessor) +that adapts an element to this protocol. +Angular provides *value accessors* for all of the basic HTML form elements and the +[Forms](guide/forms) guide shows how to bind to them. + +这些技术细节是针对每种具体元素的,因此 `NgModel` 指令仅适用于通过 [ControlValueAccessor](api/forms/ControlValueAccessor) 适配过这种协议的元素。Angular 已经为所有基本的 HTML 表单元素提供了*值访问器*,[表单](guide/forms)一章示范了如何绑定到它们。 + +You can't apply `[(ngModel)]` to a non-form native element or a +third-party custom component until you write a suitable value accessor. For more information, see +the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor). + +在编写适当的值访问器之前,不能将 `[(ngModel)]` 应用于非表单的原生元素或第三方自定义组件。欲知详情,参阅[DefaultValueAccessor](api/forms/DefaultValueAccessor)上的 API 文档。 + +You don't need a value accessor for an Angular component that +you write because you can name the value and event properties +to suit Angular's basic [two-way binding syntax](guide/two-way-binding) +and skip `NgModel` altogether. +The `sizer` in the +[Two-way Binding](guide/two-way-binding) section is an example of this technique. + +你不一定非用为所编写的 Angular 组件提供值访问器,因为你还可以把值属性和事件属性命名为符合 Angular 的基本[双向绑定语法](guide/two-way-binding)的形式,并完全跳过 `NgModel`。[双向绑定](guide/two-way-binding)部分的 `sizer` 是此技术的一个范例。 + +Separate `ngModel` bindings are an improvement over binding to the +element's native properties, but you can streamline the binding with a +single declaration using the `[(ngModel)]` syntax: + +单独的 `ngModel` 绑定是对绑定到元素的原生属性方式的一种改进,但你可以使用 `[(ngModel)]` 语法来通过单个声明简化绑定: + + + +This `[(ngModel)]` syntax can only _set_ a data-bound property. +If you need to do something more, you can write the expanded form; +for example, the following changes the `` value to uppercase: + +此 `[(ngModel)]` 语法只能*设置*数据绑定属性。如果你要做得更多,可以编写扩展表单。例如,下面的代码将 `` 值更改为大写: + + + +Here are all variations in action, including the uppercase version: + +这里是所有这些变体的动画,包括这个大写转换的版本: + + + +
    + +{@a structural-directives} + +## Built-in _structural_ directives + +## 内置*结构型*指令 + +Structural directives are responsible for HTML layout. +They shape or reshape the DOM's structure, typically by adding, removing, and manipulating +the host elements to which they are attached. + +结构型指令的职责是 HTML 布局。 +它们塑造或重塑 DOM 的*结构*,这通常是通过添加、移除和操纵它们所附加到的宿主元素来实现的。 + +This section is an introduction to the common built-in structural directives: + +本节会介绍常见的内置结构型指令: + +* [`NgIf`](guide/built-in-directives#ngIf)—conditionally creates or destroys subviews from the template. + + [`NgIf`](guide/built-in-directives#ngIf) —— 从模板中创建或销毁子视图。 + +* [`NgFor`](guide/built-in-directives#ngFor)—repeat a node for each item in a list. + + [`NgFor`](guide/built-in-directives#ngFor) —— 为列表中的每个条目重复渲染一个节点。 + +* [`NgSwitch`](guide/built-in-directives#ngSwitch)—a set of directives that switch among alternative views. + + [`NgSwitch`](guide/built-in-directives#ngSwitch) —— 一组在备用视图之间切换的指令。 + +
    + +The deep details of structural directives are covered in the +[Structural Directives](guide/structural-directives) guide, +which explains the following: + +[结构型指令](guide/structural-directives)一章涵盖了结构型指令的详细内容,它解释了以下内容: + +* Why you +[prefix the directive name with an asterisk (\*)](guide/structural-directives#the-asterisk--prefix). + + 为什么[在要指令名称前加上星号(\*)](guide/structural-directives#the-asterisk--prefix)。 + +* Using [``](guide/structural-directives#ngcontainer "") +to group elements when there is no suitable host element for the directive. + + 当指令没有合适的宿主元素时,使用 [``](guide/structural-directives#ngcontainer "<ng-container>") 对元素进行分组。 + +* How to write your own structural directive. + + 如何写自己的结构型指令。 + +* Why you [can only apply one structural directive](guide/structural-directives#one-per-element "one per host element") to an element. + + 为什么你[只能往一个元素上应用一个结构型指令](guide/structural-directives#one-per-element "one per host element")。 + +
    + +
    + +{@a ngIf} + +## NgIf + +You can add or remove an element from the DOM by applying an `NgIf` directive to +a host element. +Bind the directive to a condition expression like `isActive` in this example. + +你可以通过将 `NgIf` 指令应用在宿主元素上来从 DOM 中添加或删除元素。在此范例中,将指令绑定到了条件表达式,例如 `isActive`。 + + + +
    + +Don't forget the asterisk (`*`) in front of `ngIf`. For more information +on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of +[Structural Directives](guide/structural-directives). + +不要忘了 `ngIf` 前面的星号(`*`)。关于星号的更多信息,请参阅 [结构型指令](guide/structural-directives)中的[星号(\*)前缀](guide/structural-directives#the-asterisk--prefix)部分。 + +
    + +When the `isActive` expression returns a truthy value, `NgIf` adds the +`ItemDetailComponent` to the DOM. +When the expression is falsy, `NgIf` removes the `ItemDetailComponent` +from the DOM, destroying that component and all of its sub-components. + +当 `isActive` 表达式返回真值时,`NgIf` 会把 `ItemDetailComponent` 添加到 DOM 中。当表达式为假值时,`NgIf` 将从 DOM 中删除 `ItemDetailComponent`,从而销毁该组件及其所有子组件。 + +### Show/hide vs. `NgIf` + +### 显示/隐藏与 `NgIf` + +Hiding an element is different from removing it with `NgIf`. +For comparison, the following example shows how to control +the visibility of an element with a +[class](guide/attribute-binding#class-binding) or [style](guide/attribute-binding#style-binding) binding. + +隐藏元素与使用 `NgIf` 删除元素不同。为了进行比较,下面的范例演示如何使用[类](guide/attribute-binding#class-binding)或[样式](guide/attribute-binding#style-binding)绑定来控制元素的可见性。 + + + +When you hide an element, that element and all of its descendants remain in the DOM. +All components for those elements stay in memory and +Angular may continue to check for changes. +You could be holding onto considerable computing resources and degrading performance +unnecessarily. + +隐藏元素时,该元素及其所有后代仍保留在 DOM 中。这些元素的所有组件都保留在内存中,Angular 会继续做变更检查。它可能会占用大量计算资源,并且会不必要地降低性能。 + +`NgIf` works differently. When `NgIf` is `false`, Angular removes the element and its descendants from the DOM. +It destroys their components, freeing up resources, which +results in a better user experience. + +`NgIf` 工作方式有所不同。如果 `NgIf` 为 `false`,则 Angular 将从 DOM 中删除该元素及其后代。这销毁了它们的组件,释放了资源,从而带来更好的用户体验。 + +If you are hiding large component trees, consider `NgIf` as a more +efficient alternative to showing/hiding. + +如果要隐藏大型组件树,请考虑使用 `NgIf` 作为显示/隐藏的更有效替代方法。 + +
    + +For more information on `NgIf` and `ngIfElse`, see the [API documentation about NgIf](api/common/NgIf). + +关于 `NgIf` 和 `ngIfElse` 的更多信息,请参阅 [关于 NgIf 的 API 文档](api/common/NgIf)。 + +
    + +### Guard against null + +### 防范空指针错误 + +Another advantage of `ngIf` is that you can use it to guard against null. Show/hide +is best suited for very simple use cases, so when you need a guard, opt instead for `ngIf`. Angular will throw an error if a nested expression tries to access a property of `null`. + +`ngIf` 另一个优点是你可以使用它来防范空指针错误。显示/隐藏就是最合适的极简用例,当你需要防范时,请改用 `ngIf` 代替。如果其中嵌套的表达式尝试访问 `null` 的属性,Angular 将引发错误。 + +The following shows `NgIf` guarding two `
    `s. +The `currentCustomer` name appears only when there is a `currentCustomer`. +The `nullCustomer` will not be displayed as long as it is `null`. + +下面的例子中 `NgIf` 保护着两个 `
    `。仅当存在 `currentCustomer` 时,才会显示 `currentCustomer` 名称。除非它为 `null` 否则不会显示 `nullCustomer`。 + + + + + +
    + +{@a ngFor} + +## `NgFor` + +`NgFor` is a repeater directive—a way to present a list of items. +You define a block of HTML that defines how a single item should be displayed +and then you tell Angular to use that block as a template for rendering each item in the list. +The text assigned to `*ngFor` is the instruction that guides the repeater process. + +`NgFor` 是一个重复器指令 —— 一种用来显示条目列表的方法。你定义了一个 HTML 块,该 HTML 块定义了应如何显示单个条目,然后告诉 Angular 以该块为模板来渲染列表中的每个条目。赋值给 `*ngFor` 的文本是用来指导重复器工作过程的指令。 + +The following example shows `NgFor` applied to a simple `
    `. + +以下范例显示了如何将 `NgFor` 应用于简单的 `
    `。 + + + +
    + +Don't forget the asterisk (`*`) in front of `ngFor`. For more information +on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of +[Structural Directives](guide/structural-directives). + +不要忘了 `ngFor` 前面的星号(`*`)。关于星号的更多信息,请参阅[结构型指令](guide/structural-directives)中的[星号(\*)前缀](guide/structural-directives#the-asterisk--prefix)部分。 + +
    + +You can also apply an `NgFor` to a component element, as in the following example. + +你还可以将 `NgFor` 应用于组件元素,如以下范例所示。 + + + +{@a microsyntax} + +
    + +
    *ngFor microsyntax
    + +
    `*ngFor` 微语法
    + +The string assigned to `*ngFor` is not a [template expression](guide/interpolation). Rather, +it's a *microsyntax*—a little language of its own that Angular interprets. +The string `"let item of items"` means: + +赋值给 `*ngFor` 的字符串不是[模板表达式](guide/interpolation)。而是一个*微语法* —— 由 Angular 解释的一种小型语言。字符串 `"let item of items"` 的意思是: + +> *Take each item in the `items` array, store it in the local `item` looping variable, and +make it available to the templated HTML for each iteration.* + +> *将 `items` 数组中的每个条目存储在局部循环变量 `item` 中,并使其可用于每次迭代的模板 HTML 中。* + +Angular translates this instruction into an `` around the host element, +then uses this template repeatedly to create a new set of elements and bindings for each `item` +in the list. +For more information about microsyntax, see the [Structural Directives](guide/structural-directives#microsyntax) guide. + +Angular 将该指令转换为包裹着宿主元素的 ``,然后反复使用此模板为列表中的每个 `item` 创建一组新的元素和绑定。关于微语法的更多信息,请参阅[结构型指令](guide/structural-directives#microsyntax)一章。 + +
    + +{@a template-input-variable} + +{@a template-input-variables} + +### Template input variables + +### 模板输入变量 + +The `let` keyword before `item` creates a template input variable called `item`. +The `ngFor` directive iterates over the `items` array returned by the parent component's `items` property +and sets `item` to the current item from the array during each iteration. + +`item` 前面的 `let` 关键字创建了一个名为 `item` 的模板输入变量。`ngFor` 指令迭代父组件的 `items` 属性所返回的 `items` 数组,并在每次迭代期间将 `item` 设置为该数组中的当前条目。 + +Reference `item` within the `ngFor` host element +as well as within its descendants to access the item's properties. +The following example references `item` first in an interpolation +and then passes in a binding to the `item` property of the `` component. + +`ngFor` 的宿主元素及其后代中可引用 `item`,来访问该条目的属性。以下范例首先在插值中引用 `item`,然后把一个绑定表达式传入 `` 组件的 `item` 属性。 + + + +For more information about template input variables, see +[Structural Directives](guide/structural-directives#template-input-variable). + +关于模板输入变量的更多信息,请参阅[结构型指令](guide/structural-directives#template-input-variable)。 + +### `*ngFor` with `index` + +### `*ngFor` 与 `index` + +The `index` property of the `NgFor` directive context +returns the zero-based index of the item in each iteration. +You can capture the `index` in a template input variable and use it in the template. + +`NgFor` 指令上下文中的 `index` 属性在每次迭代中返回该条目的从零开始的索引。 +你可以在模板输入变量中捕获 `index`,并在模板中使用它。 + +The next example captures the `index` in a variable named `i` and displays it with the item name. + +下面的例子在名为 `i` 的变量中捕获 `index`,并将其与条目名称一起显示。 + + + +
    + +`NgFor` is implemented by the `NgForOf` directive. Read more about the other `NgForOf` context values such as `last`, `even`, +and `odd` in the [NgForOf API reference](api/common/NgForOf). + +要学习更多的*类似 index* 的值,例如 `last`、`even` 和 `odd`,请参阅 [NgFor API 参考](api/common/NgForOf)。 + +
    + +{@a trackBy} + +### *ngFor with `trackBy` + +### 带 `trackBy` 的 `*ngFor` + +If you use `NgFor` with large lists, a small change to one item, such as removing or adding an item, can trigger a cascade of DOM manipulations. For example, re-querying the server could reset a list with all new item objects, even when those items were previously displayed. In this case, Angular sees only a fresh list of new object references and has no choice but to replace the old DOM elements with all new DOM elements. + +如果将 `NgFor` 与大型列表一起使用,则对某个条目的较小更改(例如删除或添加一项)就会触发一系列 DOM 操作。 +例如,重新查询服务器可能会重置包含所有新条目对象的列表,即使先前已显示这些条目也是如此。在这种情况下,Angular 只能看到由新的对象引用组成的新列表,它别无选择,只能用所有新的 DOM 元素替换旧的 DOM 元素。 + +You can make this more efficient with `trackBy`. +Add a method to the component that returns the value `NgFor` should track. +In this case, that value is the hero's `id`. If the `id` has already been rendered, +Angular keeps track of it and doesn't re-query the server for the same `id`. + +你可以使用 `trackBy` 来让它更加高效。向该组件添加一个方法,该方法返回 `NgFor` 应该跟踪的值。这个例子中,该值是英雄的 `id`。如果 `id` 已经被渲染,Angular 就会跟踪它,而不会重新向服务器查询相同的 `id`。 + + + +In the microsyntax expression, set `trackBy` to the `trackByItems()` method. + +在微语法表达式中,将 `trackBy` 设置为 `trackByItems()` 方法。 + + + +Here is an illustration of the `trackBy` effect. +"Reset items" creates new items with the same `item.id`s. +"Change ids" creates new items with new `item.id`s. + +这就是 `trackBy` 效果的说明。“Reset items” 将创建具有相同 `item.id` 的新条目。“Change ids” 将使用新的 `item.id` 创建新条目。 + +* With no `trackBy`, both buttons trigger complete DOM element replacement. + + 如果没有 `trackBy`,这些按钮都会触发完全的 DOM 元素替换。 + +* With `trackBy`, only changing the `id` triggers element replacement. + + 有了 `trackBy`,则只有修改了 `id` 的按钮才会触发元素替换。 + + + +
    + +Built-in directives use only public APIs; that is, +they do not have special access to any private APIs that other directives can't access. + +内置指令仅仅使用了公共 API。也就是说,它们没有用到任何其它指令无权访问的私有 API。 + +
    + +
    + +{@a ngSwitch} + +## The `NgSwitch` directives + +## `NgSwitch` 指令 + +NgSwitch is like the JavaScript `switch` statement. +It displays one element from among several possible elements, based on a switch condition. +Angular puts only the selected element into the DOM. + +NgSwitch 类似于 JavaScript `switch` 语句。它根据切换条件显示几个可能的元素中的一个。Angular 只会将选定的元素放入 DOM。 + + + +`NgSwitch` is actually a set of three, cooperating directives: +`NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault` as in the following example. + +`NgSwitch` 实际上是三个协作指令的集合: `NgSwitch`,`NgSwitchCase` 和 `NgSwitchDefault`,如以下范例所示。 + + + + + +`NgSwitch` is the controller directive. Bind it to an expression that returns +the *switch value*, such as `feature`. Though the `feature` value in this +example is a string, the switch value can be of any type. + +`NgSwitch` 是控制器指令。把它绑定到一个返回*开关值*的表达式,例如 `feature`。尽管此范例中的 `feature` 值是字符串,但开关值可以是任何类型。 + +**Bind to `[ngSwitch]`**. You'll get an error if you try to set `*ngSwitch` because +`NgSwitch` is an *attribute* directive, not a *structural* directive. +Rather than touching the DOM directly, it changes the behavior of its companion directives. + +**绑定到 `[ngSwitch]`**。如果试图写成 `*ngSwitch`,就会出现错误,因为 `NgSwitch` 是*属性型*指令,而不是*结构型*指令。它不会直接接触 DOM,而是会更改与之相伴的指令的行为。 + +**Bind to `*ngSwitchCase` and `*ngSwitchDefault`**. +The `NgSwitchCase` and `NgSwitchDefault` directives are _structural_ directives +because they add or remove elements from the DOM. + +**绑定到 `*ngSwitchCase` 和 `*ngSwitchDefault`** +`NgSwitchCase` 和 `NgSwitchDefault` 指令都是*结构型指令*,因为它们会从 DOM 中添加或移除元素。 + +* `NgSwitchCase` adds its element to the DOM when its bound value equals the switch value and removes +its bound value when it doesn't equal the switch value. + + 当 `NgSwitchCase` 的绑定值等于开关值时,就将其元素添加到 DOM 中;否则从 DOM 中删除。 + +* `NgSwitchDefault` adds its element to the DOM when there is no selected `NgSwitchCase`. + + `NgSwitchDefault` 会在没有任何一个 `NgSwitchCase` 被选中时把它所在的元素加入 DOM 中。 + +The switch directives are particularly useful for adding and removing *component elements*. +This example switches among four `item` components defined in the `item-switch.components.ts` file. +Each component has an `item` [input property](guide/inputs-outputs#input "Input property") +which is bound to the `currentItem` of the parent component. + +开关指令对于添加和删除*组件元素*特别有用。本范例在 `item-switch.components.ts` 文件中定义的四个 `item` 组件之间切换。每个组件都有一个名叫 `item` 的[输入属性](guide/inputs-outputs "输入属性"),它会绑定到父组件的 `currentItem`。 + +Switch directives work as well with native elements and web components too. +For example, you could replace the `` switch case with the following. + +开关指令也同样适用于原生元素和 Web Component。 +比如,你可以把 `` 分支替换为如下代码。 + + + +
    diff --git a/aio/content/guide/cli-builder.md b/aio/content/guide/cli-builder.md index 3a844ba788..ab7f30954e 100644 --- a/aio/content/guide/cli-builder.md +++ b/aio/content/guide/cli-builder.md @@ -74,14 +74,14 @@ For example, your `myBuilder` folder could contain the following files. | `src/my-builder.spec.ts` | 测试的源码。 | | `src/schema.json` | Definition of builder input options. | | `src/schema.json` | 构建器输入选项的定义。 | -| `builders.json` | Testing configuration. | +| `builders.json` | Builders definition. | | `builders.json` | 测试配置。 | | `package.json` | Dependencies. See . | -| `package.json` | 依赖包。参见。 | +| `package.json` | 依赖包。参阅。 | | `tsconfig.json` | [TypeScript configuration](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html). | | `tsconfig.json` | [TypeScript 配置文件](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) | -You can publish the builder to `npm` (see [Publishing your Library](https://angular.io/guide/creating-libraries#publishing-your-library)). If you publish it as `@example/my-builder`, you can install it using the following command. +You can publish the builder to `npm` (see [Publishing your Library](guide/creating-libraries#publishing-your-library)). If you publish it as `@example/my-builder`, you can install it using the following command. 你可以把构建器发布到 `npm`(请参阅[发布你的库](guide/creating-libraries#publishing-your-library))。如果把它发布成了 `@example/my-builder`,就可以使用下面的命令来安装它。 @@ -190,7 +190,7 @@ The Architect tool collects the resolved input values into an `options` object, For our example builder, we expect the `options` value to be a `JsonObject` with two keys: a `command` that is a string, and an `args` array of string values. -对于这个示例构建器,我们希望 `options` 值是带有两个键的 `JsonObject`:一个是字符串型的 `command`,一个是字符串数组型的 `args`。 +对于这个范例构建器,我们希望 `options` 值是带有两个键的 `JsonObject`:一个是字符串型的 `command`,一个是字符串数组型的 `args`。 We can provide the following schema for type validation of these values. @@ -479,7 +479,7 @@ We need to update the `angular.json` file to add a target for this builder to th * The target named "touch" uses our builder, which we published to `@example/command-runner`. (See [Publishing your Library](guide/creating-libraries#publishing-your-library)) - 名为 `touch` 的目标使用了我们的构建器,它发布到了 `@example/command-runner`。(参见[发布你的库](guide/creating-libraries#publishing-your-library) ) + 名为 `touch` 的目标使用了我们的构建器,它发布到了 `@example/command-runner`。(参阅[发布你的库](guide/creating-libraries#publishing-your-library) ) * The options object provides default values for the two inputs that we defined; `command`, which is the Unix command to execute, and `args`, an array that contains the file to operate on. @@ -614,7 +614,7 @@ Architect can support watch mode, but there are some things to look out for. * To be used with watch mode, a builder handler function should return an Observable. Architect subscribes to the Observable until it completes and might reuse it if the builder is scheduled again with the same arguments. - 要在监视模式下使用,构建器处理函数应返回一个 Observable。建筑师会订阅这个 Observable,直到这个 Observable 完成(complete)为止。此外,如果使用相同的参数再次调度这个构建器,建筑师还能重用这个 Observable。 + 要在监视模式下使用,构建器处理函数应返回一个 Observable。建筑师会订阅这个 Observable,直到这个 Observable 完成(complete)为止。此外,如果使用相同的参数再次调度这个构建器,建筑师还能复用这个 Observable。 * The builder should always emit a `BuilderOutput` object after each execution. Once it’s been executed, it can enter a watch mode, to be triggered by an external event. If an event triggers it to restart, the builder should execute the `BuilderContext.reportRunning()` function to tell Architect that it is running again. This prevents Architect from stopping the builder if another run is scheduled. diff --git a/aio/content/guide/comparing-observables.md b/aio/content/guide/comparing-observables.md index 4dc3bb696b..53ea1ac38c 100644 --- a/aio/content/guide/comparing-observables.md +++ b/aio/content/guide/comparing-observables.md @@ -43,9 +43,9 @@ Observables are often compared to promises. Here are some key differences: 在有消费者订阅之前,可观察对象不会执行。`subscribe()` 会执行一次定义好的行为,并且可以再次调用它。每次订阅都是单独计算的。重新订阅会导致重新计算这些值。 - @@ -53,8 +53,8 @@ Observables are often compared to promises. Here are some key differences: 承诺会立即执行,并且只执行一次。当承诺创建时,会立即计算出结果。没有办法重新做一次。所有的 `then` 语句(订阅)都会共享同一次计算。 - @@ -85,9 +85,9 @@ Observables are often compared to promises. Here are some key differences: 可观察对象的订阅是可取消的。取消订阅会移除监听器,使其不再接受将来的值,并通知订阅者函数取消正在进行的工作。 - @@ -103,8 +103,8 @@ Observables are often compared to promises. Here are some key differences: 可观察对象的错误处理工作交给了订阅者的错误处理器,并且该订阅者会自动取消对这个可观察对象的订阅。 - @@ -113,8 +113,8 @@ Observables are often compared to promises. Here are some key differences: 承诺会把错误推给其子承诺。 - @@ -318,9 +318,9 @@ Here are some code samples that illustrate how the same kind of operation is def
    // 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 **. -**参见**。 +**参阅**。 {@a parent-to-child} @@ -50,9 +50,9 @@ in which two or more components share information. ## 通过输入型绑定把数据从父组件传到子组件。 `HeroChildComponent` has two ***input properties***, -typically adorned with [@Input decorations](guide/template-syntax#inputs-outputs). +typically adorned with [@Input() decorator](guide/inputs-outputs#input). -`HeroChildComponent` 有两个***输入型属性***,它们通常带[@Input 装饰器](guide/template-syntax#inputs-outputs)。 +`HeroChildComponent` 有两个***输入型属性***,它们通常带[@Input 装饰器](guide/inputs-outputs#input)。 @@ -60,7 +60,7 @@ typically adorned with [@Input decorations](guide/template-syntax#inputs-outputs The second `@Input` aliases the child component property name `masterName` as `'master'`. -第二个 `@Input` 为子组件的属性名 `masterName` 指定一个别名 `master`(译者注:不推荐为起别名,请参见风格指南). +第二个 `@Input` 为子组件的属性名 `masterName` 指定一个别名 `master`(译者注:不推荐为起别名,请参阅风格指南). The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater, binding its `master` string property to the child's `master` alias, @@ -161,7 +161,7 @@ You may prefer this approach to the property setter when watching multiple, inte Learn about `ngOnChanges()` in the [Lifecycle Hooks](guide/lifecycle-hooks) chapter. -学习关于 `ngOnChanges()` 的更多知识,参见[生命周期钩子](guide/lifecycle-hooks)一章。 +学习关于 `ngOnChanges()` 的更多知识,参阅[生命周期钩子](guide/lifecycle-hooks)一章。
    @@ -218,10 +218,10 @@ The parent binds to that event property and reacts to those events. 子组件暴露一个 `EventEmitter` 属性,当事件发生时,子组件利用该属性 `emits`(向上弹射)事件。父组件绑定到这个事件属性,并在事件发生时作出回应。 The child's `EventEmitter` property is an ***output property***, - typically adorned with an [@Output decoration](guide/template-syntax#inputs-outputs) + typically adorned with an [@Output() decorator](guide/inputs-outputs#output) as seen in this `VoterComponent`: -子组件的 `EventEmitter` 属性是一个**输出属性**,通常带有[@Output 装饰器](guide/template-syntax#inputs-outputs),就像在 `VoterComponent` 中看到的。 +子组件的 `EventEmitter` 属性是一个**输出属性**,通常带有[@Output 装饰器](guide/inputs-outputs#output),就像在 `VoterComponent` 中看到的。 diff --git a/aio/content/guide/component-overview.md b/aio/content/guide/component-overview.md new file mode 100644 index 0000000000..8331e97191 --- /dev/null +++ b/aio/content/guide/component-overview.md @@ -0,0 +1,310 @@ +# Angular Components Overview + +# Angular Components 概述 + +Components are the main building block for Angular applications. Each component consists of: + +组件是 Angular 应用的主要构造块。每个组件包括如下部分: + +* An HTML template that declares what renders on the page + + 一个 HTML 模板,用于声明页面要渲染的内容 + +* A Typescript class that defines behavior + + 一个用于定义行为的 Typescript 类 + +* A CSS selector that defines how the component is used in a template + + 一个 CSS 选择器,用于定义组件在模板中的使用方式 + +* Optionally, CSS styles applied to the template + + (可选)要应用在模板上的 CSS 样式 + +This topic describes how to create and configure an Angular component. + +本主题描述如何创建和配置 Angular 组件。 + +
    + +To view or download the example code used in this topic, see the . + +要查看或下载本主题中使用的范例代码,请参阅 。 + +
    + +## Prerequisites + +## 先决条件 + +To create a component, verify that you have met the following prerequisites: + +要创建一个组件,请先验证你是否满足以下先决条件: + +1. Install the Angular CLI. + + 安装 Angular CLI。 + +1. Create an Angular project. + If you don't have a project, you can create one using `ng new `, where `` is the name of your Angular application. + + 建立一个 Angular 项目。如果你没有项目,你可以用 `ng new ` 创建一个项目,其中 `` 是你的 Angular 应用的名字。 + +## Creating a component + +## 创建一个组件 + +The easiest way to create a component is with the Angular CLI. You can also create a component manually. + +Angular CLI 是用来创建组件的最简途径。你也可以手动创建一个组件。 + +### Creating a component using the Angular CLI + +### 使用 Angular CLI 创建组件 + +To create a component using the Angular CLI: + +使用 Angular CLI 创建一个组件: + +1. From a terminal window, navigate to the directory containing your application. + + 在终端窗口中,导航到要放置你应用的目录。 + +1. Run the `ng generate component ` command, where `` is the name of your new component. + + 运行 `ng generate component ` 命令,其中 `` 是新组件的名字。 + +By default, this command creates the following: + +默认情况下,该命令会创建以下内容: + +* A folder named after the component + + 一个以该组件命名的文件夹 + +* A component file, `.component.ts` + + 一个组件文件 `.component.ts` + +* A template file, `.component.html` + + 一个模板文件 `.component.html` + +* A CSS file, `.component.css` + + 一个 CSS 文件, `.component.css` + +* A testing specification file, `.component.spec.ts` + + 测试文件 `.component.spec.ts` + +Where `` is the name of your component. + +其中 `` 是组件的名称。 + +
    + +You can change how `ng generate component` creates new components. +For more information, see [ng generate component](cli/generate#component-command) in the Angular CLI documentation. + +你可以更改 `ng generate component` 创建新组件的方式。欲知详情,请参阅 Angular CLI 文档中的 [ng generate component](cli/generate#component-command)。 + +
    + +### Creating a component manually + +### 手动创建组件 + +Although the Angular CLI is the easiest way to create an Angular component, you can also create a component manually. +This section describes how to create the core component file within an existing Angular project. + +虽然 Angular CLI 是创建 Angular 组件的最简途径,但你也可以手动创建一个组件。本节将介绍如何在现有的 Angular 项目中创建核心组件文件。 + +To create a new component manually: + +要手动创建一个新组件: + +1. Navigate to your Angular project directory. + + 导航到你的 Angular 项目目录。 + +1. Create a new file, `.component.ts`. + + 创建一个新文件 `.component.ts` 。 + +1. At the top of the file, add the following import statement. + + 在文件的顶部,添加下面的 import 语句。 + + + + +1. After the `import` statement, add a `@Component` decorator. + + 在 `import` 语句之后,添加一个 `@Component` 装饰器。 + + + + +1. Choose a CSS selector for the component. + + 为组件选择一个 CSS 选择器。 + + + + + For more information on choosing a selector, see [Specifying a component's selector](#specifying-a-components-css-selector). + + 关于选择选择器的更多信息,参阅[指定组件的选择器](#specifying-a-components-css-selector)。 + +1. Define the HTML template that the component uses to display information. + In most cases, this template is a separate HTML file. + + 定义组件用以显示信息的 HTML 模板。在大多数情况下,这个模板是一个单独的 HTML 文件。 + + + + + For more information on defining a component's template, see [Defining a component's template](#defining-a-components-template). + + 关于定义组件模板的更多信息,请参阅[定义组件的模板](#defining-a-components-template)。 + +1. Select the styles for the component's template. + In most cases, you define the styles for your component's template in a separate file. + + 为组件的模板选择样式。在大多数情况下,你可以在单独的文件中定义组件模板的样式。 + + + + +1. Add a `class` statement that includes the code for the component. + + 添加一个包含该组件代码 `class` 语句。 + + + + +## Specifying a component's CSS selector + +## 指定组件的 CSS 选择器 + +Every component requires a CSS *selector*. A selector instructs Angular to instantiate this component wherever it finds the corresponding tag in template HTML. For example, consider a component `hello-world.component.ts` that defines its selector as `app-hello-world`. This selector instructs Angular to instantiate this component any time the tag `` appears in a template. + +每个组件都需要一个 CSS *选择器*。选择器会告诉 Angular:当在模板 HTML 中找到相应的标签时,就把该组件实例化在那里。例如,考虑一个组件 `hello-world.component.ts` ,它的选择器定义为 `app-hello-world` 。 当 `` 出现在模板中时,这个选择器就会让 Angular 实例化该组件。 + +Specify a component's selector by adding a `selector` statement to the `@Component` decorator. + +在 `@Component` 装饰器中添加一个 `selector` 语句来指定组件的选择器。 + + + + +## Defining a component's template + +## 定义一个组件的模板 + +A template is a block of HTML that tells Angular how to render the component in your application. +You can define a template for your component in one of two ways: by referencing an external file, or directly within the component. + +模板是一段 HTML,它告诉 Angular 如何在应用中渲染组件。你可以通过以下两种方式之一为组件定义模板:引用外部文件,或直接写在组件内部。 + +To define a template as an external file, add a `templateUrl` property to the `@Component` decorator. + +要把模板定义为外部文件,就要把 `templateUrl` 添加到 `@Component` 装饰器中。 + + + + +To define a template within the component, add a `template` property to the `@Component` decorator that contains the HTML you want to use. + +要在组件中定义模板,就要把一个 `template` 属性添加到 `@Component` 中,该属性的内容是要使用的 HTML。 + + + + +If you want your template to span multiple lines, you can use backticks ( ` ). +For example: + +如果你想让你的模板跨越多行,你可以使用反引号( `` ` `` )。例如: + + + + +
    + +An Angular component requires a template defined using `template` or `templateUrl`. You cannot have both statements in a component. + +Angular 组件需要一个用 `template` 或 `templateUrl` 定义的模板。但你不能在组件中同时拥有这两个语句。 + +
    + +## Declaring a component's styles + +## 声明组件的样式 + +You can declare component styles uses for its template in one of two ways: by referencing an external file, or directly within the component. + +你有以下两种方式来为组件的模板声明样式:引用一个外部文件,或直接写在组件内部。 + +To declare the styles for a component in a separate file, add a `stylesUrls` property to the `@Component` decorator. + +要在单独的文件中声明组件的样式,就要把 `stylesUrls` 属性添加到 `@Component` 装饰器中。 + + + + +To select the styles within the component, add a `styles` property to the `@Component` decorator that contains the styles you want to use. + +要想在组件内部声明样式,就要把 `styles` 属性添加到 `@Component`,该属性的内容是你要用的样式。 + + + + +The `styles` property takes an array of strings that contain the CSS rule declarations. + +`styles` 属性接受一个包含 CSS 规则的字符串数组。 + +## Next steps + +## 下一步 + +* For an architectural overview of components, see [Introduction to components and templates](guide/architecture-components). + + 关于组件的体系结构概述,请参阅[组件和模板简介](guide/architecture-components)。 + +* For additional options you can use when creating a component, see [Component](api/core/Component) in the API Reference. + + 关于创建组件时可以使用的其他选项,请参阅“API 参考手册”中的[“组件”](api/core/Component)。 + +* For more information on styling components, see [Component styles](guide/component-styles). + + 要了解关于为组件指定样式的更多信息,请参阅[组件样式](guide/component-styles)。 + +* For more information on templates, see [Template syntax](guide/template-syntax). + + 关于模板的详细信息,请参阅[模板语法](guide/template-syntax)。 diff --git a/aio/content/guide/component-styles.md b/aio/content/guide/component-styles.md index 24a57dc59e..070e5151ed 100644 --- a/aio/content/guide/component-styles.md +++ b/aio/content/guide/component-styles.md @@ -185,17 +185,17 @@ The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`. Use `/deep/`, `>>>` and `::ng-deep` only with *emulated* view encapsulation. Emulated is the default and most commonly used view encapsulation. For more information, see the -[Controlling view encapsulation](guide/component-styles#view-encapsulation) section. +[View Encapsulation](guide/view-encapsulation) section. `/deep/` 和 `>>>` 选择器只能被用在**仿真 (emulated) **模式下。 这种方式是默认值,也是用得最多的方式。 -更多信息,见[控制视图封装模式](guide/component-styles#view-encapsulation)一节。 +更多信息,见[控制视图封装模式](guide/view-encapsulation)一节。
    -The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/features/6750456638341120) and tools. +The shadow-piercing descendant combinator is deprecated and [support is being removed from major browsers](https://www.chromestatus.com/feature/6750456638341120) and tools. As such we plan to drop support in Angular (for all 3 of `/deep/`, `>>>` and `::ng-deep`). Until then `::ng-deep` should be preferred for a broader compatibility with the tools. @@ -332,7 +332,7 @@ You can also write `` tags into the component's HTML template. When building with the CLI, be sure to include the linked style file among the assets to be copied to the server as described in the [CLI wiki](https://github.com/angular/angular-cli/wiki/stories-asset-configuration). -当使用 CLI 进行构建时,要确保这个链接到的样式表文件被复制到了服务器上。参见 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-asset-configuration)。 +当使用 CLI 进行构建时,要确保这个链接到的样式表文件被复制到了服务器上。参阅 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-asset-configuration)。 Once included, the CLI will include the stylesheet, whether the link tag's href URL is relative to the application root or the component file. @@ -373,14 +373,14 @@ Register **global** style files in the `styles` section which, by default, is pr See the [CLI wiki](https://github.com/angular/angular-cli/wiki/stories-global-styles) to learn more. -要了解更多,参见 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-global-styles)。 +要了解更多,参阅 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-global-styles)。 ### Non-CSS style files ### 非 CSS 样式文件 If you're building with the CLI, -you can write style files in [sass](http://sass-lang.com/), [less](http://lesscss.org/), or [stylus](http://stylus-lang.com/) and specify those files in the `@Component.styleUrls` metadata with the appropriate extensions (`.scss`, `.less`, `.styl`) as in the following example: +you can write style files in [sass](https://sass-lang.com/), [less](http://lesscss.org/), or [stylus](https://stylus-lang.com/) and specify those files in the `@Component.styleUrls` metadata with the appropriate extensions (`.scss`, `.less`, `.styl`) as in the following example: 如果使用 CLI 进行构建,那么你可以用 [sass](http://sass-lang.com/)、[less](http://lesscss.org/) 或 [stylus](http://stylus-lang.com/) 来编写样式,并使用相应的扩展名(`.scss`、`.less`、`.styl`)把它们指定到 `@Component.styleUrls` 元数据中。例子如下: @@ -404,7 +404,7 @@ as explained in the [CLI wiki](https://github.com/angular/angular-cli/wiki/stori 当使用 `ng generate component` 命令生成组件文件时,CLI 会默认生成一个空白的 CSS 样式文件(`.css`)。 -你可以配置 CLI,让它默认使用你喜欢的 CSS 预处理器,参见 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-css-preprocessors +你可以配置 CLI,让它默认使用你喜欢的 CSS 预处理器,参阅 [CLI 官方文档](https://github.com/angular/angular-cli/wiki/stories-css-preprocessors "CSS Preprocessor integration") 中的解释。
    @@ -414,132 +414,3 @@ Style strings added to the `@Component.styles` array _must be written in CSS_ be 添加到 `@Component.styles` 数组中的字符串*必须写成 CSS*,因为 CLI 没法对这些内联的样式使用任何 CSS 预处理器。
    - -{@a view-encapsulation} - -## View encapsulation - -## 视图封装模式 - -As discussed earlier, component CSS styles are encapsulated into the component's view and don't -affect the rest of the application. - -像上面讨论过的一样,组件的 CSS 样式被封装进了自己的视图中,而不会影响到应用程序的其它部分。 - -To control how this encapsulation happens on a *per -component* basis, you can set the *view encapsulation mode* in the component metadata. -Choose from the following modes: - -通过在组件的元数据上设置*视图封装模式*,你可以分别控制*每个组件*的封装模式。 -可选的封装模式一共有如下几种: - -* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see - [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) - on the [MDN](https://developer.mozilla.org) site) - to attach a shadow DOM to the component's host element, and then puts the component - view inside that shadow DOM. The component's styles are included within the shadow DOM. - - `ShadowDom` 模式使用浏览器原生的 Shadow DOM 实现(参见 [MDN](https://developer.mozilla.org) 上的 [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM))来为组件的宿主元素附加一个 Shadow DOM。组件的视图被附加到这个 Shadow DOM 中,组件的样式也被包含在这个 Shadow DOM 中。(译注:不进不出,没有样式能进来,组件样式出不去。) - -* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/). - - `Native` 视图包装模式使用浏览器原生 Shadow DOM 的一个废弃实现 —— [参见变化详情](https://hayato.io/2016/shadowdomv1/)。 - -* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing - (and renaming) the CSS code to effectively scope the CSS to the component's view. - For details, see [Inspecting generated CSS](guide/component-styles#inspect-generated-css) below. - - `Emulated` 模式(**默认值**)通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,以达到把 CSS 样式局限在组件视图中的目的。 - 更多信息,见[附录 1](guide/component-styles#inspect-generated-css)。(译注:只进不出,全局样式能进来,组件样式出不去) - -* `None` means that Angular does no view encapsulation. - Angular adds the CSS to the global styles. - The scoping rules, isolations, and protections discussed earlier don't apply. - This is essentially the same as pasting the component's styles into the HTML. - - `None` 意味着 Angular 不使用视图封装。 - Angular 会把 CSS 添加到全局样式中。而不会应用上前面讨论过的那些作用域规则、隔离和保护等。 - 从本质上来说,这跟把组件的样式直接放进 HTML 是一样的。(译注:能进能出。) - -To set the components encapsulation mode, use the `encapsulation` property in the component metadata: - -通过组件元数据中的 `encapsulation` 属性来设置组件封装模式: - - - -`ShadowDom` view encapsulation only works on browsers that have native support -for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the -[Can I use](http://caniuse.com) site). The support is still limited, -which is why `Emulated` view encapsulation is the default mode and recommended -in most cases. - -`ShadowDom` 模式只适用于提供了原生 Shadow DOM 支持的浏览器(参见 [Can I use](http://caniuse.com/) 上的 [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) 部分)。 -它仍然受到很多限制,这就是为什么仿真 (`Emulated`) 模式是默认选项,并建议将其用于大多数情况。 - -{@a inspect-generated-css} - -## Inspecting generated CSS - -## 查看生成的 CSS - -When using emulated view encapsulation, Angular preprocesses -all component styles so that they approximate the standard shadow CSS scoping rules. - -当使用默认的仿真模式时,Angular 会对组件的所有样式进行预处理,让它们模仿出标准的 Shadow CSS 作用域规则。 - -In the DOM of a running Angular application with emulated view -encapsulation enabled, each DOM element has some extra attributes -attached to it: - -在启用了仿真模式的 Angular 应用的 DOM 树中,每个 DOM 元素都被加上了一些额外的属性。 - - - <hero-details _nghost-pmm-5> - <h2 _ngcontent-pmm-5>Mister Fantastic</h2> - <hero-team _ngcontent-pmm-5 _nghost-pmm-6> - <h3 _ngcontent-pmm-6>Team</h3> - </hero-team> - </hero-detail> - - - -There are two kinds of generated attributes: - -生成出的属性分为两种: - -* An element that would be a shadow DOM host in native encapsulation has a - generated `_nghost` attribute. This is typically the case for component host elements. - - 一个元素在原生封装方式下可能是 Shadow DOM 的宿主,在这里被自动添加上一个 `_nghost` 属性。 - 这是组件宿主元素的典型情况。 - -* An element within a component's view has a `_ngcontent` attribute -that identifies to which host's emulated shadow DOM this element belongs. - - 组件视图中的每一个元素,都有一个 `_ngcontent` 属性,它会标记出该元素属于哪个宿主的模拟 Shadow DOM。 - -The exact values of these attributes aren't important. They are automatically -generated and you never refer to them in application code. But they are targeted -by the generated component styles, which are in the `` section of the DOM: - -这些属性的具体值并不重要。它们是自动生成的,并且你永远不会在程序代码中直接引用到它们。 -但它们会作为生成的组件样式的目标,就像 DOM 的 `` 中一样: - - - [_nghost-pmm-5] { - display: block; - border: 1px solid black; - } - - h3[_ngcontent-pmm-6] { - background-color: white; - border: 1px solid #777; - } - - -These styles are post-processed so that each selector is augmented -with `_nghost` or `_ngcontent` attribute selectors. -These extra selectors enable the scoping rules described in this page. - -这些就是那些样式被处理后的结果,每个选择器都被增加了 `_nghost` 或 `_ngcontent` 属性选择器。 -这些额外的选择器实现了本文所描述的这些作用域规则。 diff --git a/aio/content/guide/creating-libraries.md b/aio/content/guide/creating-libraries.md index 5adaf80b71..2e864a658d 100644 --- a/aio/content/guide/creating-libraries.md +++ b/aio/content/guide/creating-libraries.md @@ -28,20 +28,22 @@ Use the Angular CLI to generate a new library skeleton in a new workspace with t The `ng generate` command creates the `projects/my-lib` folder in your workspace, which contains a component and a service inside an NgModule. +`ng generate` 命令会在你的工作空间中创建 `projects/my-lib` 文件夹,其中包含带有一个组件和一个服务的 NgModule。 +
    For more details on how a library project is structured, refer to the [Library project files](guide/file-structure#library-project-files) section of the [Project File Structure guide](guide/file-structure). - 要了解关于库项目的目录结构的详细信息,参见[项目文件结构](guide/file-structure)中的[库项目文件](guide/file-structure#library-project-files)部分。 + 要了解关于库项目的目录结构的详细信息,参阅[项目文件结构](guide/file-structure)中的[库项目文件](guide/file-structure#library-project-files)部分。 You can use the monorepo model to use the same workspace for multiple projects. See [Setting up for a multi-project workspace](guide/file-structure#multiple-projects). - 你可以使用单一仓库(monorepo)模式将同一个工作空间用于多个项目。参见[建立多项目型工作区](guide/file-structure#multiple-projects)。 + 你可以使用单一仓库(monorepo)模式将同一个工作空间用于多个项目。参阅[建立多项目型工作区](guide/file-structure#multiple-projects)。
    -When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type 'library'. +When you generate a new library, the workspace configuration file, `angular.json`, is updated with a project of type `library`. 当你生成一个新库时,该工作区的配置文件 `angular.json` 中也增加了一个 'library' 类型的项目。 @@ -129,13 +131,13 @@ Here are some things to consider in migrating application functionality to a lib 考虑如何为客户端应用提供服务。 - * Services should declare their own providers (rather than declaring providers in the NgModule or a component), so that they are *tree-shakable*. This allows the compiler to leave the service out of the bundle if it never gets injected into the application that imports the library. For more about this, see [Tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers). + * Services should declare their own providers, rather than declaring providers in the NgModule or a component. Declaring a provider makes that service *tree-shakable*. This practice allows the compiler to leave the service out of the bundle if it never gets injected into the application that imports the library. For more about this, see [Tree-shakable providers](guide/architecture-services#providing-services). - 服务应该自己声明提供者(而不是在 NgModule 或组件中声明提供者),以便它们是*可摇树优化的* 。这样,如果服务器从未被注入到导入该库的应用中,编译器就会把该服务从该 bundle 中删除。有关这方面的更多信息,请参阅[Tree-shakable 提供者](guide/dependency-injection-providers#tree-shakable-providers) 。 + 服务应该自己声明提供者(而不是在 NgModule 或组件中声明提供者),以便它们是*可摇树优化的* 。这样,如果服务器从未被注入到导入该库的应用中,编译器就会把该服务从该 bundle 中删除。关于这方面的更多信息,请参阅[Tree-shakable 提供者](guide/architecture-services#providing-services) 。 * If you register global service providers or share providers across multiple NgModules, use the [`forRoot()` and `forChild()` design patterns](guide/singleton-services) provided by the [RouterModule](api/router/RouterModule). - 如果您在多个 NgModules 注册全局服务供应商或供应商共享,使用[`forRoot()` 和 `forChild()` 设计模式](guide/singleton-services)由提供[RouterModule](api/router/RouterModule) 。 + 如果你在多个 NgModules 注册全局服务提供者或提供者共享,使用[`forRoot()` 和 `forChild()` 设计模式](guide/singleton-services)由提供[RouterModule](api/router/RouterModule) 。 * If your library provides optional services that might not be used by all client applications, support proper tree-shaking for that case by using the [lightweight token design pattern](guide/lightweight-injection-tokens). @@ -186,14 +188,14 @@ If you want a dropdown that would contain different passed-in values each time, Suppose you want to read a configuration file and then generate a form based on that configuration. If that form will need additional customization by the developer who is using your library, it might work best as a schematic. -However, if the forms will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form. +However, if the form will always be the same and not need much customization by developers, then you could create a dynamic component that takes the configuration and generates the form. In general, the more complex the customization, the more useful the schematic approach. 假设你要读取配置文件,然后根据该配置生成表单。如果该表单需要库的用户进行额外的自定义,它可能最适合用作 schematic。但是,如果这些表单总是一样的,开发人员不需要做太多自定义工作,那么你就可以创建一个动态的组件来获取配置并生成表单。通常,自定义越复杂,schematic 方式就越有用。 To learn more, see [Schematics Overview](guide/schematics) and [Schematics for Libraries](guide/schematics-for-libraries). -要了解更多信息,参见 [原理图概览](guide/schematics) 和 [供库使用的原理图](guide/schematics-for-libraries)。 +要了解更多信息,参阅 [原理图概览](guide/schematics) 和 [供库使用的原理图](guide/schematics-for-libraries)。 ## Publishing your library @@ -201,6 +203,8 @@ To learn more, see [Schematics Overview](guide/schematics) and [Schematics for Use the Angular CLI and the npm package manager to build and publish your library as an npm package. +使用 Angular CLI 和 npm 包管理器来构建你的库并发布为 npm 包。 + Before publishing a library to NPM, build it using the `--prod` flag which will use the older compiler and runtime known as View Engine instead of Ivy. 使用 Angular CLI 和 npm 包管理器来把你的库构建并发布成 npm 包。不建议把 Ivy 格式的库发布到 NPM 仓库。在把某个库发布到 NPM 之前,使用 `--prod` 标志构建它,此标志会使用老的编译器和运行时,也就是视图引擎(View Engine),以代替 Ivy. @@ -219,7 +223,11 @@ If you've never published a package in npm before, you must create a user accoun For now, it is not recommended to publish Ivy libraries to NPM because Ivy generated code is not backward compatible with View Engine, so apps using View Engine will not be able to consume them. Furthermore, the internal Ivy instructions are not yet stable, which can potentially break consumers using a different Angular version from the one used to build the library. -When a published library is used in an Ivy app, the Angular CLI will automatically convert it to Ivy using a tool known as the Angular compatibility compiler (`ngcc`). Thus, by publishing your libraries using the View Engine compiler ensures that they can be transparently consumed by both View Engine and Ivy apps. +目前,不建议把 Ivy 库发布到 NPM 上,因为 Ivy 生成的代码不会向后兼容 View Engine 的应用,所以,使用 View Engine 的应用将不能消费它们。此外,Ivy 的内部用法尚未完全稳定,这有潜在的可能会导致使用其它 Angular 版本的消费代码被此库的构建者破坏。 + +When a published library is used in an Ivy app, the Angular CLI will automatically convert it to Ivy using a tool known as the Angular compatibility compiler (`ngcc`). Thus, publishing your libraries using the View Engine compiler ensures that they can be transparently consumed by both View Engine and Ivy apps. + +当已发布的库在 Ivy 应用中使用时,Angular CLI 会自动把它用一个叫作 Angular 兼容性编译器(`ngcc`)的工具转换成 Ivy 版本的。这样一来,把你的库用 View Engine 编译器发布,就可以确保它们能同时被 View Engine 和 Ivy 的应用透明的消费。
    @@ -257,9 +265,9 @@ For example, `main` should point at a JavaScript file, not a TypeScript file. 必须在每次修改时都重新构建这个库。在链接库时,确保构建步骤在监视模式下运行,并且该库的 `package.json` 配置指向了正确的入口点。例如,`main` 应该指向一个 JavaScript 文件,而不是一个 TypeScript 文件。 -## Use TypeScript path mapping for peer dependencies +### Use TypeScript path mapping for peer dependencies -## 对同级依赖使用 TypeScript 路径映射 +### 对同级依赖使用 TypeScript 路径映射 Angular libraries should list all `@angular/*` dependencies as peer dependencies. This ensures that when modules ask for Angular, they all get the exact same module. diff --git a/aio/content/guide/dependency-injection-in-action.md b/aio/content/guide/dependency-injection-in-action.md index 9459d1b5cb..2d55fae97a 100644 --- a/aio/content/guide/dependency-injection-in-action.md +++ b/aio/content/guide/dependency-injection-in-action.md @@ -2,128 +2,15 @@ # 依赖注入实战 -This section explores many of the features of dependency injection (DI) in Angular. +This guide explores many of the features of dependency injection (DI) in Angular. -本节将会涉及 Angular 依赖注入(DI)的很多特性。 - -{@a toc} - -See the -of the code in this cookbook. - -要获取本文的代码,**参见**。 - -{@a nested-dependencies} - -## Nested service dependencies - -## 嵌套的服务依赖 - -The _consumer_ of an injected service doesn't need to know how to create that service. -It's the job of the DI framework to create and cache dependencies. The consumer just -needs to let the DI framework know which dependencies it needs. - -这些被注入服务的消费者不需要知道如何创建这个服务。新建和缓存这个服务是依赖注入器的工作。消费者只要让依赖注入框架知道它需要哪些依赖项就可以了。 - -Sometimes a service depends on other services, which may depend on yet other services. -The dependency injection framework resolves these nested dependencies in the correct order. -At each step, the consumer of dependencies declares what it requires in its -constructor, and lets the framework provide them. - -有时候一个服务依赖其它服务...而其它服务可能依赖另外的更多服务。 -依赖注入框架会负责正确的顺序解析这些嵌套的依赖项。 -在每一步,依赖的使用者只要在它的构造函数里简单声明它需要什么,框架就会完成所有剩下的事情。 - -The following example shows that `AppComponent` declares its dependence on `LoggerService` and `UserContext`. - -下面的例子往 `AppComponent` 里声明它依赖 `LoggerService` 和 `UserContext`。 - - - -`UserContext` in turn depends on both `LoggerService` and -`UserService`, another service that gathers information about a particular user. - -`UserContext` 转而依赖 `LoggerService` 和 `UserService`(这个服务用来收集特定用户信息)。 - - - -When Angular creates `AppComponent`, the DI framework creates an instance of `LoggerService` and starts to create `UserContextService`. -`UserContextService` also needs `LoggerService`, which the framework already has, so the framework can provide the same instance. `UserContextService` also needs `UserService`, which the framework has yet to create. `UserService` has no further dependencies, so the framework can simply use `new` to instantiate the class and provide the instance to the `UserContextService` constructor. - -当 Angular 新建 `AppComponent` 时,依赖注入框架会先创建一个 `LoggerService` 的实例,然后创建 `UserContextService` 实例。 -`UserContextService` 也需要框架刚刚创建的这个 `LoggerService` 实例,这样框架才能为它提供同一个实例。`UserContextService` 还需要框架创建过的 `UserService`。 -`UserService` 没有其它依赖,所以依赖注入框架可以直接 `new` 出该类的一个实例,并把它提供给 `UserContextService` 的构造函数。 - -The parent `AppComponent` doesn't need to know about the dependencies of dependencies. -Declare what's needed in the constructor (in this case `LoggerService` and `UserContextService`) -and the framework resolves the nested dependencies. - -父组件 `AppComponent` 不需要了解这些依赖的依赖。 -只要在构造函数中声明自己需要的依赖即可(这里是 `LoggerService` 和 `UserContextService`),框架会帮你解析这些嵌套的依赖。 - -When all dependencies are in place, `AppComponent` displays the user information. - -当所有的依赖都就位之后,`AppComponent` 就会显示该用户的信息。 - - - -{@a service-scope} - -## Limit service scope to a component subtree - -## 把服务的范围限制到某个组件的子树下 - -An Angular application has multiple injectors, arranged in a tree hierarchy that parallels the component tree. -Each injector creates a singleton instance of a dependency. -That same instance is injected wherever that injector provides that service. -A particular service can be provided and created at any level of the injector hierarchy, -which means that there can be multiple instances of a service if it is provided by multiple injectors. - -Angular 应用程序有多个依赖注入器,组织成一个与组件树平行的树状结构。 -每个注入器都会创建依赖的一个单例。在所有该注入器负责提供服务的地方,所提供的都是同一个实例。 -可以在注入器树的任何层级提供和建立特定的服务。这意味着,如果在多个注入器中提供该服务,那么该服务也就会有多个实例。 - -Dependencies provided by the root injector can be injected into *any* component *anywhere* in the application. -In some cases, you might want to restrict service availability to a particular region of the application. -For instance, you might want to let users explicitly opt in to use a service, -rather than letting the root injector provide it automatically. - -由根注入器提供的依赖可以注入到应用中任何地方的任何组件中。 -但有时候你可能希望把服务的有效性限制到应用程序的一个特定区域。 -比如,你可能希望用户明确选择一个服务,而不是让根注入器自动提供它。 - -You can limit the scope of an injected service to a *branch* of the application hierarchy -by providing that service *at the sub-root component for that branch*. -This example shows how to make a different instance of `HeroService` available to `HeroesBaseComponent` -by adding it to the `providers` array of the `@Component()` decorator of the sub-component. - -通过*在组件树的子级根组件*中提供服务,可以把一个被注入服务的作用域局限在应用程序结构中的某个*分支*中。 -这个例子中展示了如何通过把服务添加到子组件 `@Component()` 装饰器的 `providers` 数组中,来为 `HeroesBaseComponent` 提供另一个 `HeroService` 实例: - - - - - -When Angular creates `HeroesBaseComponent`, it also creates a new instance of `HeroService` -that is visible only to that component and its children, if any. - -当 Angular 新建 `HeroBaseComponent` 的时候,它会同时新建一个 `HeroService` 实例,该实例只在该组件及其子组件(如果有)中可见。 - -You could also provide `HeroService` to a different component elsewhere in the application. -That would result in a different instance of the service, living in a different injector. - -也可以在应用程序别处的另一个组件里提供 `HeroService`。这样就会导致在另一个注入器中存在该服务的另一个实例。 +本章涉及到 Angular 依赖注入(DI)的很多特性。
    -Examples of such scoped `HeroService` singletons appear throughout the accompanying sample code, -including `HeroBiosComponent`, `HeroOfTheMonthComponent`, and `HeroesBaseComponent`. -Each of these components has its own `HeroService` instance managing its own independent collection of heroes. +See the for a working example containing the code snippets in this guide. -这个例子中,局部化的 `HeroService` 单例,遍布整份范例代码,包括 `HeroBiosComponent`、`HeroOfTheMonthComponent` 和 `HeroBaseComponent`。 -这些组件每个都有自己的 `HeroService` 实例,用来管理独立的英雄库。 +要查看包含本章代码片段的可工作范例,参阅
    @@ -395,7 +282,7 @@ As a result, you might need to access a component's DOM element. 即便开发者极力避免,仍然会有很多视觉效果和第三方工具 (比如 jQuery) 需要访问 DOM。这会让你不得不访问组件所在的 DOM 元素。 -To illustrate, here's a simplified version of `HighlightDirective` from +To illustrate, here's a minimal version of `HighlightDirective` from the [Attribute Directives](guide/attribute-directives) page. 为了说明这一点,请看[属性型指令](guide/attribute-directives)中那个 `HighlightDirective` 的简化版。 @@ -429,64 +316,6 @@ The following image shows the effect of mousing over the ` Highlighted bios
    - -{@a providers} - -## Define dependencies with providers - -## 使用提供者来定义依赖 - -This section demonstrates how to write providers that deliver dependent services. - -本节会示范如何编写提供者来交付被依赖的服务。 - -In order to get a service from a dependency injector, you have to give it a [token](guide/glossary#token). -Angular usually handles this transaction by specifying a constructor parameter and its type. -The parameter type serves as the injector lookup token. -Angular passes this token to the injector and assigns the result to the parameter. - -为了从依赖注入器中获取服务,你必须传给它一个[令牌](guide/glossary#token)。 -Angular 通常会通过指定构造函数参数以及参数的类型来处理它。 -参数的类型可以用作注入器的查阅令牌。 -Angular 会把该令牌传给注入器,并把它的结果赋给相应的参数。 - -The following is a typical example. - -下面是一个典型的例子。 - - - -Angular asks the injector for the service associated with `LoggerService` -and assigns the returned value to the `logger` parameter. - -Angular 会要求注入器提供与 `LoggerService` 相关的服务,并把返回的值赋给 `logger` 参数。 - -If the injector has already cached an instance of the service associated with the token, -it provides that instance. -If it doesn't, it needs to make one using the provider associated with the token. - -如果注入器已经缓存了与该令牌相关的服务实例,那么它就会直接提供此实例。 -如果它没有,它就要使用与该令牌相关的提供者来创建一个。 - -
    - -If the injector doesn't have a provider for a requested token, it delegates the request -to its parent injector, where the process repeats until there are no more injectors. -If the search fails, the injector throws an error—unless the request was [optional](guide/dependency-injection-in-action#optional). - -如果注入器无法根据令牌在自己内部找到对应的提供者,它便将请求移交给它的父级注入器,这个过程不断重复,直到没有更多注入器为止。 -如果没找到,注入器就抛出一个错误...除非这个请求是[可选的](guide/dependency-injection-in-action#optional)。 - -
    - -A new injector has no providers. -Angular initializes the injectors it creates with a set of preferred providers. -You have to configure providers for your own app-specific dependencies. - -新的注入器没有提供者。 -Angular 会使用一组首选提供者来初始化它本身的注入器。 -你必须为自己应用程序特有的依赖项来配置提供者。 - {@a defining-providers} ### Defining providers @@ -762,7 +591,7 @@ Look at the for the full source code. 该函数从 `HeroService` 中接受候选的英雄,从中取 `2` 个参加竞赛,并把他们的名字串接起来返回。 -参见 查看完整源码。 +参阅 查看完整源码。
    @@ -823,9 +652,9 @@ When you use a class this way, it's called a *class interface*. 当你通过这种方式使用类时,它称作*类接口*。 -As mentioned in [DI Providers](guide/dependency-injection-providers#interface-not-valid-token), an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time. Use this abstract class interface to get the strong typing of an interface, and also use it as a provider token in the way you would a normal class. +As mentioned in [DI Providers](guide/dependency-injection-providers#di-and-interfaces), an interface is not a valid DI token because it is a TypeScript artifact that doesn't exist at run time. Use this abstract class interface to get the strong typing of an interface, and also use it as a provider token in the way you would a normal class. -就像 [DI 提供者](guide/dependency-injection-providers#interface-not-valid-token)中提到的那样,接口不是有效的 DI 令牌,因为它是 TypeScript 自己用的,在运行期间不存在。使用这种抽象类接口不但可以获得像接口一样的强类型,而且可以像普通类一样把它用作提供者令牌。 +就像 [DI 提供者](guide/dependency-injection-providers#di-and-interfaces)中提到的那样,接口不是有效的 DI 令牌,因为它是 TypeScript 自己用的,在运行期间不存在。使用这种抽象类接口不但可以获得像接口一样的强类型,而且可以像普通类一样把它用作提供者令牌。 A class interface should define *only* the members that its consumers are allowed to call. Such a narrowing interface helps decouple the concrete class from its consumers. @@ -1016,52 +845,3 @@ Break the circularity with `forwardRef`. 使用 `forwardRef` 来打破这种循环: - - diff --git a/aio/content/guide/dependency-injection-navtree.md b/aio/content/guide/dependency-injection-navtree.md index 0ba97de47a..c33e4ae563 100644 --- a/aio/content/guide/dependency-injection-navtree.md +++ b/aio/content/guide/dependency-injection-navtree.md @@ -2,6 +2,18 @@ # 使用 DI 浏览组件树 +
    +
    Marked for archiving
    + +To ensure that you have the best experience possible, this topic is marked for archiving until we determine +that it clearly conveys the most accurate information possible. + +In the meantime, this topic might be helpful: [Hierarchical injectors](guide/hierarchical-dependency-injection). + +If you think this content should not be archived, please file a [GitHub issue](https://github.com/angular/angular/issues/new?template=3-docs-bug.md). + +
    + Application components often need to share information. You can often use loosely coupled techniques for sharing information, such as data binding and service sharing, @@ -222,10 +234,10 @@ Here's *Barry*. *Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers). -If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](#provideparent). +If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a helper function. *Barry* 的 `providers` 数组看起来和 [*Alex*](#alex-providers) 的一样。 -如果你准备继续像这样编写[*别名提供者*](guide/dependency-injection-in-action#useexisting),就应该创建一个[辅助函数](#provideparent)。 +如果你准备继续像这样编写[*别名提供者*](guide/dependency-injection-in-action#useexisting),就应该创建一个辅助函数。 For now, focus on *Barry*'s constructor. @@ -260,7 +272,7 @@ which *is* what parent means. 如果你省略了 `@SkipSelf` 装饰器,Angular 就会抛出循环依赖错误。 - `Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)` + `NG0200: Circular dependency in DI detected for BethComponent. Dependency path: BethComponent -> Parent -> BethComponent` Here's *Alice*, *Barry*, and family in action. @@ -318,46 +330,3 @@ It doesn't in this example *only* to demonstrate that the code will compile and 这个例子中之所以没这样做,*只是*为了证明即使没有声明接口,代码也可以编译和运行。
    - -{@a provideparent} - -### `provideParent()` helper function - -### `provideParent()` 辅助函数 - -Writing variations of the same parent *alias provider* gets old quickly, -especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref). - -你很快就会厌倦为同一个父组件编写*别名提供者*的变体形式,特别是带有 [*forwardRef*](guide/dependency-injection-in-action#forwardref) 的那种。 - - - -You can extract that logic into a helper function like the following. - -你可以像把这些逻辑抽取到辅助函数中,就像这样。 - - - -Now you can add a simpler, more meaningful parent provider to your components. - -现在,你可以为组件添加一个更简单、更有意义的父组件提供者。 - - - -You can do better. The current version of the helper function can only alias the `Parent` class interface. -The application might have a variety of parent types, each with its own class interface token. - -你还可以做得更好。当前版本的辅助函数只能为类接口 `Parent` 定义别名。 -应用可能具有多种父组件类型,每个父组件都有自己的类接口令牌。 - -Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface. - -这是一个修订后的版本,它默认为 `parent`,但是也能接受另一个父类接口作为可选的第二参数。 - - - -And here's how you could use it with a different parent type. - -下面是针对不同父组件类型的用法。 - - diff --git a/aio/content/guide/dependency-injection-providers.md b/aio/content/guide/dependency-injection-providers.md index 548409fc4c..fe4ba69414 100644 --- a/aio/content/guide/dependency-injection-providers.md +++ b/aio/content/guide/dependency-injection-providers.md @@ -2,58 +2,45 @@ # 依赖提供者 -A dependency [provider](guide/glossary#provider) configures an injector -with a [DI token](guide/glossary#di-token), -which that injector uses to provide the concrete, runtime version of a dependency value. -The injector relies on the provider configuration to create instances of the dependencies -that it injects into components, directives, pipes, and other services. +By configuring providers, you can make services available to the parts of your application that need them. + +通过配置提供者,你可以把服务提供给那些需要它们的应用部件。 + +A dependency [provider](guide/glossary#provider) configures an injector with a [DI token](guide/glossary#di-token), which that injector uses to provide the runtime version of a dependency value. 依赖[提供者](guide/glossary#provider)会使用 [DI 令牌](guide/glossary#di-token)来配置注入器,注入器会用它来提供这个依赖值的具体的、运行时版本。 -注入器依靠 "提供者配置" 来创建依赖的实例,并把该实例注入到组件、指令、管道和其它服务中。 -You must configure an injector with a provider, or it won't know how to create the dependency. -The most obvious way for an injector to create an instance of a service class is with the class itself. -If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with `new`. +## Specifying a provider token -你必须使用提供者来配置注入器,否则注入器就无法知道如何创建此依赖。 -注入器创建服务实例的最简单方法,就是用这个服务类本身来创建它。 -如果你把服务类作为此服务的 DI 令牌,注入器的默认行为就是 `new` 出这个类实例。 +## 指定提供者令牌 -In the following typical example, the `Logger` class itself provides a `Logger` instance. +If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class with `new`. -在下面这个典型的例子中,`Logger` 类自身提供了 `Logger` 的实例。 +如果你把服务类指定为提供者令牌,那么注入器的默认行为是用 `new` 来实例化那个类。 + +In the following example, the `Logger` class provides a `Logger` instance. + +在下面这个例子中,`Logger` 类提供了 `Logger` 的实例。 -You can, however, configure an injector with an alternative provider, -in order to deliver some other object that provides the needed logging functionality. -For instance: +You can, however, configure an injector with an alternative provider in order to deliver some other object that provides the needed logging functionality. 不过,你也可以用一个替代提供者来配置注入器,这样就可以指定另一些同样能提供日志功能的对象。 -比如: -* You can provide a substitute class. +You can configure an injector with a service class, you can provide a substitute class, an object, or a factory function. - 你可以提供一个替代类。 - -* You can provide a logger-like object. - - 你可以提供一个类似于 Logger 的对象。 - -* Your provider can call a logger factory function. - - 你的提供者可以调用一个工厂函数来创建 logger。 +你可以使用服务类来配置注入器,也可以提供一个替代类、一个对象或一个工厂函数。 {@a provide} -## The `Provider` object literal +## Defining providers -## `Provider` 对象字面量 +## 定义提供者 -The class-provider syntax is a shorthand expression that expands -into a provider configuration, defined by the [`Provider` interface](api/core/Provider). -The following code snippets shows how a class that is given as the `providers` value is expanded into a full provider object. +The class provider syntax is a shorthand expression that expands into a provider configuration, defined by the [`Provider` interface](api/core/Provider). +The following example is the class provider syntax for providing a `Logger` class in the `providers` array. 类提供者的语法实际上是一种简写形式,它会扩展成一个由 [`Provider` 接口](api/core/Provider)定义的提供者配置对象。 下面的代码片段展示了 `providers` 中给出的类会如何扩展成完整的提供者配置对象。 @@ -61,6 +48,10 @@ The following code snippets shows how a class that is given as the `providers` v +Angular expands the `providers` value into a full provider object as follows. + +Angular 把这个 `providers` 值扩展为一个完整的提供者对象,如下所示。 + @@ -68,12 +59,11 @@ The expanded provider configuration is an object literal with two properties. 扩展的提供者配置是一个具有两个属性的对象字面量。 -* The `provide` property holds the [token](guide/dependency-injection#token) -that serves as the key for both locating a dependency value and configuring the injector. +1. The `provide` property holds the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value and configuring the injector. `provide` 属性存有[令牌](guide/dependency-injection#token),它作为一个 key,在定位依赖值和配置注入器时使用。 -* The second property is a provider definition object, which tells the injector how to create the dependency value. +2. The second property is a provider definition object, which tells the injector how to create the dependency value. The provider-definition key can be `useClass`, as in the example. It can also be `useExisting`, `useValue`, or `useFactory`. Each of these keys provides a different type of dependency, as discussed below. @@ -85,289 +75,252 @@ Each of these keys provides a different type of dependency, as discussed below. {@a class-provider} -## Alternative class providers +## Configuring the injector to use alternative class providers -## 替代类提供者 +## 配置该注入器以使用替代类提供者 -Different classes can provide the same service. -For example, the following code tells the injector -to return a `BetterLogger` instance when the component asks for a logger -using the `Logger` token. +To configure the injector to return a different class that provides the same service, you can use the `useClass` property. +In this example, the injector returns a `BetterLogger` instance when using the `Logger` token. -不同的类都可用于提供相同的服务。 -比如,下面的代码告诉注入器,当组件使用 `Logger` 令牌请求日志对象时,给它返回一个 `BetterLogger` 实例。 +为了让注入器能够返回提供同一服务的另一个类,你可以使用 `useClass` 属性。在这个例子中,当使用 `Logger` 令牌时,注入器会返回一个 `BetterLogger` 的实例。 {@a class-provider-dependencies} -### Class providers with dependencies +### Configuring class providers with dependencies -### 带依赖的类提供者 +### 配置带依赖的类提供者 -Another class, `EvenBetterLogger`, might display the user name in the log message. +If the alternative class providers have their own dependencies, specify both providers in the `providers` metadata property of the parent module or component. + +如果替代类提供者有自己的依赖,那就在父模块或组件的元数据属性 `providers` 中指定那些依赖。 + + + +In this example, `EvenBetterLogger` displays the user name in the log message. This logger gets the user from an injected `UserService` instance. -另一个类 `EvenBetterLogger` 可能要在日志信息里显示用户名。 +在这个例子中,`EvenBetterLogger` 会在日志信息里显示用户名。 这个 logger 要从注入的 `UserService` 实例中来获取该用户。 -The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component. +The injector needs providers for both this new logging service and its dependent `UserService`. 注入器需要提供这个新的日志服务以及该服务所依赖的 `UserService` 对象。 -使用 `useClass` 作为提供者定义对象的 key,来配置一个 logger 的替代品,比如 `BetterLogger`。 -下面的数组同时在父模块和组件的 `providers` 元数据选项中指定了这些提供者。 - - {@a aliased-class-providers} -### Aliased class providers +### Aliasing class providers ### 别名类提供者 -Suppose an old component depends upon the `OldLogger` class. -`OldLogger` has the same interface as `NewLogger`, but for some reason -you can't update the old component to use it. +To alias a class provider, specify the alias and the class provider in the `providers` array with the `useExisting` property. -假设老的组件依赖于 `OldLogger` 类。`OldLogger` 和 `NewLogger` 的接口相同,但是由于某种原因,我们没法修改老的组件来使用 `NewLogger`。 +要为类提供者设置别名,请在 `providers` 数组中使用 `useExisting` 属性指定别名和类提供程序。 -When the old component logs a message with `OldLogger`, -you want the singleton instance of `NewLogger` to handle it instead. -In this case, the dependency injector should inject that singleton instance -when a component asks for either the new or the old logger. -`OldLogger` should be an *alias* for `NewLogger`. +In the following example, the injector injects the singleton instance of `NewLogger` when the component asks for either the new or the old logger. +In this way, `OldLogger` is an alias for `NewLogger`. -当老的组件要使用 `OldLogger` 记录信息时,你可能希望改用 `NewLogger` 的单例来处理它。 -在这种情况下,无论某个组件请求老的 logger 还是新的 logger,依赖注入器都应该注入这个 `NewLogger` 的单例。 -也就是说 `OldLogger` 应该是 `NewLogger` 的*别名*。 - -If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app. - -如果你试图用 `useClass` 为 `OldLogger` 指定一个别名 `NewLogger`,就会在应用中得到 `NewLogger` 的两个不同的实例。 - - - -To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option. - -要确保只有一个 `NewLogger` 实例,就要用 `useExisting` 来为 `OldLogger` 指定别名。 +在下面的例子中,当组件请求新的或旧的记录器时,注入器都会注入一个 `NewLogger` 的实例。 +通过这种方式, `OldLogger` 就成了 `NewLogger` 的别名。 +Be sure you don't alias `OldLogger` to `NewLogger` with `useClass`, as this creates two different `NewLogger` instances. + +请确保你没有使用 `useClass` 来把 `OldLogger` 设为 `NewLogger` 的别名,因为如果这样做它就会创建两个不同的 `NewLogger` 实例。 + +{@a provideparent} + +## Aliasing a class interface + +## 为类接口指定别名 + +Generally, writing variations of the same parent alias provider uses [forwardRef](guide/dependency-injection-in-action#forwardref) as follows. + +通常,编写同一个父组件别名提供者的变体时会使用[forwardRef](guide/dependency-injection-in-action#forwardref),如下所示。 + + + +To streamline your code, you can extract that logic into a helper function using the `provideParent()` helper function. + +为简化你的代码,可以使用辅助函数 `provideParent()` 来把这个逻辑提取到一个辅助函数中。 + + + +Now you can add a parent provider to your components that's easier to read and understand. + +现在,你可以为组件添加一个更容易阅读和理解的父提供者。 + + + +### Aliasing multiple class interfaces + +### 为多个类接口指定别名 + +To alias multiple parent types, each with its own class interface token, configure `provideParent()` to accept more arguments. + +要为多个父类型指定别名(每个类型都有自己的类接口令牌),请配置 `provideParent()` 以接受更多的参数。 + +Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface. + +这是一个修订版本,默认值为 `parent` 但同时也接受另一个父类接口作为可选的第二参数。 + + + +Next, to use `provideParent()` with a different parent type, provide a second argument, here `DifferentParent`. + +接下来,要使用 `provideParent()` ,请传入第二参数,这里是 `DifferentParent` 。 + + + + {@a value-provider} -## Value providers +## Injecting an object -## 值提供者 - -Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. -To inject an object you have already created, -configure the injector with the `useValue` option - -有时候,提供一个现成的对象会比要求注入器从类去创建更简单一些。 -如果要注入一个你已经创建过的对象,请使用 `useValue` 选项来配置该注入器。 - -The following code defines a variable that creates such an object to play the logger role. - -下面的代码定义了一个变量,用来创建这样一个能扮演 logger 角色的对象。 - - +## 注入一个对象 +To inject an object, configure the injector with the `useValue` option. The following provider object uses the `useValue` key to associate the variable with the `Logger` token. +要注入一个对象,可以用 `useValue` 选项来配置注入器。 下面的提供者定义对象使用 `useValue` 作为 key 来把该变量与 `Logger` 令牌关联起来。 +In this example, `SilentLogger` is an object that fulfills the logger role. + +在这个例子中,`SilentLogger` 是一个充当记录器角色的对象。 + + + {@a non-class-dependencies} -### Non-class dependencies +### Injecting a configuration object -### 非类依赖 +### 注入一个配置对象 -Not all dependencies are classes. -Sometimes you want to inject a string, function, or object. +A common use case for object literals is a configuration object. +The following configuration object includes the title of the application and the address of a web API endpoint. -并非所有的依赖都是类。 -有时候你会希望注入字符串、函数或对象。 - -Apps often define configuration objects with lots of small facts, -like the title of the application or the address of a web API endpoint. -These configuration objects aren't always instances of a class. -They can be object literals, as shown in the following example. - -应用通常会用大量的小型参数来定义配置对象,比如应用的标题或 Web API 端点的地址。 -这些配置对象不一定总是类的实例。 -它们还可能是对象字面量,如下例所示。 +常用的对象字面量是配置对象。下列配置对象包括应用的标题和 Web API 的端点地址。 -{@a interface-not-valid-token} +To provide and inject the configuration object, specify the object in the `@NgModule()` `providers` array. -**TypeScript interfaces are not valid tokens** +要提供并注入配置对象,请在 `@NgModule()` 的 `providers` 数组中指定该对象。 -**TypeScript 接口不是有效的令牌** + -The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface. -Unfortunately, you cannot use a TypeScript interface as a token. -In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use. +{@a injectiontoken} -`HERO_DI_CONFIG` 常量满足 `AppConfig` 接口的要求。 -不幸的是,你不能用 TypeScript 的接口作为令牌。 -在 TypeScript 中,接口是一个设计期的概念,无法用作 DI 框架在运行期所需的令牌。 +### Using an `InjectionToken` object + +### 使用 `InjectionToken` 对象 + +You can define and use an `InjectionToken` object for choosing a provider token for non-class dependencies. +The following example defines a token, `APP_CONFIG` of the type `InjectionToken`. + +你可以定义和使用一个 `InjectionToken` 对象来为非类的依赖选择一个提供者令牌。下列例子定义了一个类型为 `InjectionToken` 的 `APP_CONFIG` 。 + + + +The optional type parameter, `app.config`, and the token description, `` specify the token's purpose. + +可选的参数 `app.config` 和令牌类型 `` 指出了令牌的用途。 + +Next, register the dependency provider in the component using the `InjectionToken` object of `APP_CONFIG`. + +接着,用 `APP_CONFIG` 这个 `InjectionToken` 对象在组件中注册依赖提供者。 + + + +Now you can inject the configuration object into the constructor with `@Inject()` parameter decorator. + +现在,借助参数装饰器 `@Inject()`,你可以把这个配置对象注入到构造函数中。 + + + +{@a di-and-interfaces} + +#### Interfaces and dependency injection + +#### 接口和依赖注入 + +Though the TypeScript `AppConfig` interface supports typing within the class, the `AppConfig` interface plays no role in dependency injection. +In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation, or token, that the DI framework can use. + +虽然 TypeScript 的 `AppConfig` 接口可以在类中提供类型支持,但它在依赖注入时却没有任何作用。在 TypeScript 中,接口是一项设计期工件,它没有可供 DI 框架使用的运行时表示形式或令牌。 + +When the transpiler changes TypeScript to JavaScript, the interface disappears because JavaScript doesn't have interfaces. + +当转译器把 TypeScript 转换成 JavaScript 时,接口就会消失,因为 JavaScript 没有接口。 + +Since there is no interface for Angular to find at runtime, the interface cannot be a token, nor can you inject it. + +由于 Angular 在运行期没有接口,所以该接口不能作为令牌,也不能注入它。 -
    - -This might seem strange if you're used to dependency injection in strongly typed languages where an interface is the preferred dependency lookup key. -However, JavaScript, doesn't have interfaces, so when TypeScript is transpiled to JavaScript, the interface disappears. -There is no interface type information left for Angular to find at runtime. - -如果你曾经在强类型语言中使用过依赖注入功能,这一点可能看起来有点奇怪,那些语言都优先使用接口作为查找依赖的 key。 -不过,JavaScript 没有接口,所以,当 TypeScript 转译成 JavaScript 时,接口也就消失了。 -在运行期间,没有留下任何可供 Angular 进行查找的接口类型信息。 - -
    - -One alternative is to provide and inject the configuration object in an NgModule like `AppModule`. - -替代方案之一是以类似于 `AppModule` 的方式,在 NgModule 中提供并注入这个配置对象。 - - - -Another solution to choosing a provider token for non-class dependencies is -to define and use an `InjectionToken` object. -The following example shows how to define such a token. - -另一个为非类依赖选择提供者令牌的解决方案是定义并使用 `InjectionToken` 对象。 -下面的例子展示了如何定义那样一个令牌。 - - - -The type parameter, while optional, conveys the dependency's type to developers and tooling. -The token description is another developer aid. - -虽然类型参数在这里是可选的,不过还是能把此依赖的类型信息传达给开发人员和开发工具。 -这个令牌的描述则是开发人员的另一个助力。 - -Register the dependency provider using the `InjectionToken` object: - -使用 `InjectionToken` 对象注册依赖提供者: - - - -Now you can inject the configuration object into any constructor that needs it, with -the help of an `@Inject()` parameter decorator. - -现在,借助参数装饰器 `@Inject()`,你可以把这个配置对象注入到任何需要它的构造函数中。 - - - -
    - -Although the `AppConfig` interface plays no role in dependency injection, -it supports typing of the configuration object within the class. - -虽然 `AppConfig` 接口在依赖注入时没有任何作用,但它可以为该组件类中的这个配置对象指定类型信息。 - -
    - {@a factory-provider} {@a factory-providers} -## Factory providers +## Using factory providers -## 工厂提供者 +## 使用工厂提供者 -Sometimes you need to create a dependent value dynamically, -based on information you won't have until run time. -For example, you might need information that changes repeatedly in the course of the browser session. -Also, your injectable service might not have independent access to the source of the information. +To create a changeable, dependent value based on information unavailable before run time, you can use a factory provider. -有时候你需要动态创建依赖值,创建时需要的信息你要等运行期间才能拿到。 -比如,你可能需要某个在浏览器会话过程中会被反复修改的信息,而且这个可注入服务还不能独立访问这个信息的源头。 +要想根据运行前尚不可用的信息创建可变的依赖值,可以使用工厂提供者。 -In cases like this you can use a *factory provider*. -Factory providers can also be useful when creating an instance of a dependency from a third-party library that wasn't designed to work with DI. +In the following example, only authorized users should see secret heroes in the `HeroService`. +Authorization can change during the course of a single application session, as when a different user logs in . -这种情况下,你可以使用*工厂提供者*。 -当需要从第三方库创建依赖项实例时,工厂提供者也很有用,因为第三方库不是为 DI 而设计的。 +在下面的例子中,只有授权用户才能看到 `HeroService` 中的秘密英雄。授权可能在单个应用会话期间发生变化,比如改用其他用户登录。 -For example, suppose `HeroService` must hide *secret* heroes from normal users. -Only authorized users should see secret heroes. +To keep security-sensitive information in `UserService` and out of `HeroService`, give the `HeroService` constructor a boolean flag to control display of secret heroes. -比如,假设 `HeroService` 必须对普通用户隐藏*秘密*英雄,只有得到授权的用户才能看到他们。 - -Like `EvenBetterLogger`, `HeroService` needs to know if the user is authorized to see secret heroes. -That authorization can change during the course of a single application session, -as when you log in a different user. - -像 `EvenBetterLogger` 一样,`HeroService` 需要知道该用户是否有权查看秘密英雄。 -而认证信息可能会在应用的单个会话中发生变化,比如你改用另一个用户登录。 - -Let's say you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information. -`HeroService` won't have direct access to the user information to decide -who is authorized and who isn't. - -假设你不希望直接把 `UserService` 注入到 `HeroService` 中,因为你不希望把这个服务与那些高度敏感的信息牵扯到一起。 -这样 `HeroService` 就无法直接访问到用户信息,来决定谁有权访问,谁没有。 - -To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes. - -要解决这个问题,我们给 `HeroService` 的构造函数一个逻辑型标志,以控制是否显示秘密英雄。 +要想在 `UserService` 和 `HeroService` 中保存敏感信息,就要给 `HeroService` 的构造函数传一个逻辑标志来控制秘密英雄的显示。 -You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`. +To implement the `isAuthorized` flag, use a factory provider to create a new logger instance for `HeroService`. -你可以注入 `Logger` 但是不能注入 `isAuthorized` 标志。不过你可以改用工厂提供者来为 `HeroService` 创建一个新的 logger 实例。 - -A factory provider needs a factory function. - -工厂提供者需要一个工厂函数。 +要实现 `isAuthorized` 标志,可以用工厂提供者来为 `HeroService` 创建一个新的 logger 实例。 -Although `HeroService` has no access to `UserService`, the factory function does. -You inject both `Logger` and `UserService` into the factory provider -and let the injector pass them along to the factory function. +The factory function has access to `UserService`. +You inject both `Logger` and `UserService` into the factory provider so the injector can pass them along to the factory function. -虽然 `HeroService` 不能访问 `UserService`,但是工厂函数可以。 -你把 `Logger` 和 `UserService` 注入到了工厂提供者中,并让注入器把它们传给这个工厂函数。 +这个工厂函数可以访问 `UserService`。你可以同时把 `Logger` 和 `UserService` 注入到工厂提供者中,这样注入器就可以把它们传给工厂函数了。 -* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`. +* The `useFactory` field specifies that the provider is a factory function whose implementation is `heroServiceFactory`. - `useFactory` 字段告诉 Angular 该提供者是一个工厂函数,该函数的实现代码是 `heroServiceFactory`。 + `useFactory` 字段指定该提供程序是一个工厂函数,其实现代码是 `heroServiceFactory` 。 * The `deps` property is an array of [provider tokens](guide/dependency-injection#token). The `Logger` and `UserService` classes serve as tokens for their own class providers. -The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. +The injector resolves these tokens and injects the corresponding services into the matching `heroServiceFactory` factory function parameters. - `deps` 属性是一个[提供者令牌](guide/dependency-injection#token)数组。 - `Logger` 和 `UserService` 类作为它们自己的类提供者令牌使用。 - 注入器解析这些令牌,并把与之对应的服务注入到相应的工厂函数参数表中。 + `deps` 属性是一个[提供者令牌](guide/dependency-injection#token)数组。 `Logger` 和 `UserService` 类都是自己类提供者的令牌。该注入器解析了这些令牌,并把相应的服务注入到 `heroServiceFactory` 工厂函数的参数中。 -Notice that you captured the factory provider in an exported variable, `heroServiceProvider`. -This extra step makes the factory provider reusable. -You can configure a provider of `HeroService` with this variable wherever you need it. -In this sample, you need it only in `HeroesComponent`, -where `heroServiceProvider` replaces `HeroService` in the metadata `providers` array. +Capturing the factory provider in the exported variable, `heroServiceProvider`, makes the factory provider reusable. -注意,你把这个工厂提供者保存到了一个导出的变量 `heroServiceProvider` 中。 -这个额外的步骤让工厂提供者可被复用。 -你可以在任何需要它的地方用这个变量来配置 `HeroService` 的提供者。 -在这个例子中,你只在 `HeroesComponent` 中用到了它。你在该组件元数据的 `providers` 数组中用 `heroServiceProvider` 替换了 `HeroService`。 +通过把工厂提供者导出为变量 `heroServiceProvider`,就能让工厂提供者变得可复用。 -The following shows the new and the old implementations side-by-side. +The following side-by-side example shows how `heroServiceProvider` replaces `HeroService` in the `providers` array. -下面并列显示了新旧实现。 +下面这两个并排的例子展示了在 `providers` 数组中,如何用 `heroServiceProvider` 替换 `HeroService` @@ -378,154 +331,3 @@ The following shows the new and the old implementations side-by-side. - -## Predefined tokens and multiple providers - -## 预定义令牌与多提供者 - -Angular provides a number of built-in injection-token constants that you can use to customize the behavior of -various systems. - -Angular 提供了一些内置的注入令牌常量,你可以用它们来自定义系统的多种行为。 - -For example, you can use the following built-in tokens as hooks into the framework’s bootstrapping and initialization process. -A provider object can associate any of these injection tokens with one or more callback functions that take app-specific initialization actions. - -比如,你可以使用下列内置令牌来切入 Angular 框架的启动和初始化过程。 -提供者对象可以把任何一个注入令牌与一个或多个用来执行应用初始化操作的回调函数关联起来。 - -* [PLATFORM_INITIALIZER](api/core/PLATFORM_INITIALIZER): Callback is invoked when a platform is initialized. - - [PLATFORM_INITIALIZER](api/core/PLATFORM_INITIALIZER):平台初始化之后调用的回调函数。 - -* [APP_BOOTSTRAP_LISTENER](api/core/APP_BOOTSTRAP_LISTENER): Callback is invoked for each component that is bootstrapped. The handler function receives the ComponentRef instance of the bootstrapped component. - - [APP_BOOTSTRAP_LISTENER](api/core/APP_BOOTSTRAP_LISTENER):每个启动组件启动完成之后调用的回调函数。这个处理器函数会收到这个启动组件的 ComponentRef 实例。 - -* [APP_INITIALIZER](api/core/APP_INITIALIZER): Callback is invoked before an app is initialized. All registered initializers can optionally return a Promise. All initializer functions that return Promises must be resolved before the application is bootstrapped. If one of the initializers fails to resolves, the application is not bootstrapped. - - [APP_INITIALIZER](api/core/APP_INITIALIZER):应用初始化之前调用的回调函数。注册的所有初始化器都可以(可选地)返回一个 Promise。所有返回 Promise 的初始化函数都必须在应用启动之前解析完。如果任何一个初始化器失败了,该应用就不会继续启动。 - -The provider object can have a third option, `multi: true`, which you can use with `APP_INITIALIZER` -to register multiple handlers for the provide event. - -该提供者对象还有第三个选项 `multi: true`,把它和 `APP_INITIALIZER` 一起使用可以为特定的事件注册多个处理器。 - -For example, when bootstrapping an application, you can register many initializers using the same token. - -比如,当启动应用时,你可以使用同一个令牌注册多个初始化器。 - -``` -export const APP_TOKENS = [ - { provide: PLATFORM_INITIALIZER, useFactory: platformInitialized, multi: true }, - { provide: APP_INITIALIZER, useFactory: delayBootstrapping, multi: true }, - { provide: APP_BOOTSTRAP_LISTENER, useFactory: appBootstrapped, multi: true }, -]; -``` - -Multiple providers can be associated with a single token in other areas as well. -For example, you can register a custom form validator using the built-in [NG_VALIDATORS](api/forms/NG_VALIDATORS) token, -and provide multiple instances of a given validator provider by using the `multi: true` property in the provider object. -Angular adds your custom validators to the existing collection. - -在其它地方,多个提供者也同样可以和单个令牌关联起来。 -比如,你可以使用内置的 [NG_VALIDATORS](api/forms/NG_VALIDATORS) 令牌注册自定义表单验证器,还可以在提供者定义对象中使用 `multi: true` 属性来为指定的验证器令牌提供多个验证器实例。 -Angular 会把你的自定义验证器添加到现有验证器的集合中。 - -The Router also makes use of multiple providers associated with a single token. -When you provide multiple sets of routes using [RouterModule.forRoot](api/router/RouterModule#forroot) -and [RouterModule.forChild](api/router/RouterModule#forchild) in a single module, -the [ROUTES](api/router/ROUTES) token combines all the different provided sets of routes into a single value. - -路由器也同样用多个提供者关联到了一个令牌。 -当你在单个模块中用 [RouterModule.forRoot](api/router/RouterModule#forroot) 和 [RouterModule.forChild](api/router/RouterModule#forchild) 提供了多组路由时,[ROUTES](api/router/ROUTES) 令牌会把这些不同的路由组都合并成一个单一值。 - -
    - -Search for [Constants in API documentation](api?type=const) to find more built-in tokens. - -搜索 [API 文档中的常量](api?type=const)以了解更多内置令牌。 - -
    - -{@a tree-shakable-provider} -{@a tree-shakable-providers} - -## Tree-shakable providers - -## 可摇树优化的提供者 - -Tree shaking refers to a compiler option that removes code from the final bundle if the app doesn't reference that code. -When providers are tree-shakable, the Angular compiler removes the associated -services from the final output when it determines that your application doesn't use those services. -This significantly reduces the size of your bundles. - -摇树优化是指一个编译器选项,意思是把应用中未引用过的代码从最终生成的包中移除。 -如果提供者是可摇树优化的,Angular 编译器就会从最终的输出内容中移除应用代码中从未用过的服务。 -这会显著减小你的打包体积。 - -
    - -Ideally, if an application isn't injecting a service, Angular shouldn't include it in the final output. -However, Angular has to be able to identify at build time whether the app will require the service or not. -Because it's always possible to inject a service directly using `injector.get(Service)`, -Angular can't identify all of the places in your code where this injection could happen, -so it has no choice but to include the service in the injector. -Thus, services in the NgModule `providers` array or at component level are not tree-shakable. - -理想情况下,如果应用没有注入服务,它就不应该包含在最终输出中。 -不过,Angular 要能在构建期间识别出该服务是否需要。 -由于还可能用 `injector.get(Service)` 的形式直接注入服务,所以 Angular 无法准确识别出代码中可能发生此注入的全部位置,因此为保险起见,只能把服务包含在注入器中。 -因此,在 NgModule 或 组件级别提供的服务是无法被摇树优化掉的。 - -
    - -The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule. - -下面这个不可摇树优化的 Angular 提供者的例子为 NgModule 注入器配置了一个服务提供者。 - - - -You can then import this module into your application module -to make the service available for injection in your app, -as in the following example. - -你可以把该模块导入到你的应用模块中,以便该服务可注入到你的应用中,例子如下。 - - - -When `ngc` runs, it compiles `AppModule` into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services. - -当运行 `ngc` 时,它会把 `AppModule` 编译到模块工厂中,工厂包含该模块及其导入的所有模块中声明的所有提供者。在运行时,该工厂会变成负责实例化所有这些服务的注入器。 - -Tree-shaking doesn't work here because Angular can't decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself. - -这里摇树优化不起作用,因为 Angular 无法根据是否用到了其它代码块(服务类),来决定是否能排除这块代码(模块工厂中的服务提供者定义)。要让服务可以被摇树优化,关于如何构建该服务实例的信息(即提供者定义),就应该是服务类本身的一部分。 - -### Creating tree-shakable providers - -### 创建可摇树优化的提供者 - -You can make a provider tree-shakable by specifying it in the `@Injectable()` decorator on the service itself, rather than in the metadata for the NgModule or component that depends on the service. - -只要在服务本身的 `@Injectable()` 装饰器中指定,而不是在依赖该服务的 NgModule 或组件的元数据中指定,你就可以制作一个可摇树优化的提供者。 - -The following example shows the tree-shakable equivalent to the `ServiceModule` example above. - -下面的例子展示了与上面的 `ServiceModule` 例子等价的可摇树优化的版本。 - - - -The service can be instantiated by configuring a factory function, as in the following example. - -该服务还可以通过配置工厂函数来实例化,如下例所示。 - - - -
    - -To override a tree-shakable provider, configure the injector of a specific NgModule or component with another provider, using the `providers: []` array syntax of the `@NgModule()` or `@Component()` decorator. - -要想覆盖可摇树优化的提供者,请使用其它提供者来配置指定的 NgModule 或组件的注入器,只要使用 `@NgModule()` 或 `@Component()` 装饰器中的 `providers: []` 数组就可以了。 - -
    diff --git a/aio/content/guide/dependency-injection.md b/aio/content/guide/dependency-injection.md index dcb26895d1..7926264ca8 100644 --- a/aio/content/guide/dependency-injection.md +++ b/aio/content/guide/dependency-injection.md @@ -30,7 +30,7 @@ In Angular, the DI framework provides declared dependencies to a class when that Start by reviewing this simplified version of the _heroes_ feature from the [The Tour of Heroes](tutorial/). This simple version doesn't use DI; we'll walk through converting it to do so. -我们先看一下[英雄指南](tutorial/)中*英雄管理*特性的简化版。这个简化版不使用 DI,我们将逐步把它转换成使用 DI 的。 +我们先看一下[英雄之旅](tutorial/)中*英雄管理*特性的简化版。这个简化版不使用 DI,我们将逐步把它转换成使用 DI 的。 @@ -88,14 +88,14 @@ it is important to define the service first, and then the component. If you defi 如果你把组件和服务都放在同一个文件中,请务必先定义服务,然后再定义组件。如果在服务之前定义组件,则会在运行时收到一个空引用错误。 -It is possible to define the component first with the help of the `forwardRef()` method as explained in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html). +It is possible to define the component first with the help of the `forwardRef()` method as explained in this [blog post](https://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html). 也可以借助 `forwardRef()` 方法来先定义组件,就像[这个博客](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html)中解释的那样。 You can also use forward references to break circular dependencies. See an example in the [DI Cookbook](guide/dependency-injection-in-action#forwardref). -你还可以使用前向引用来打破循环依赖,参见 [DI 一章](guide/dependency-injection-in-action#forwardref)中的例子。 +你还可以使用前向引用来打破循环依赖,参阅 [DI 一章](guide/dependency-injection-in-action#forwardref)中的例子。 @@ -170,11 +170,11 @@ from the injector of its parent NgModule, or from the `root` injector. * Learn more about the [different kinds of providers](guide/dependency-injection-providers). - 更多知识,参见 [提供者的不同类型](guide/dependency-injection-providers)。 + 更多知识,参阅 [提供者的不同类型](guide/dependency-injection-providers)。 * Learn more about how the [injector hierarchy](guide/hierarchical-dependency-injection) works. - 更多知识,参见[层次化注入器](guide/hierarchical-dependency-injection)的工作原理。 + 更多知识,参阅[层次化注入器](guide/hierarchical-dependency-injection)的工作原理。 @@ -211,7 +211,7 @@ Components are directives, and the `providers` option is inherited from `@Direct Learn more about [where to configure providers](guide/hierarchical-dependency-injection). -欲知详情,参见[该在哪里配置提供者](guide/hierarchical-dependency-injection)。 +欲知详情,参阅[该在哪里配置提供者](guide/hierarchical-dependency-injection)。 @@ -317,7 +317,7 @@ under test. Learn more in the [Testing](guide/testing) guide. -欲知详情,参见[测试](guide/testing)一章。 +欲知详情,参阅[测试](guide/testing)一章。 @@ -364,7 +364,7 @@ If Angular can't find that parameter information, it throws an error. Angular can only find the parameter information _if the class has a decorator of some kind_. The `@Injectable()` decorator is the standard decorator for service classes. -当 Angular 创建一个构造函数中有参数的类时,它会查找有关这些参数的类型,和供注入使用的元数据,以便找到正确的服务。 +当 Angular 创建一个构造函数中有参数的类时,它会查找关于这些参数的类型,和供注入使用的元数据,以便找到正确的服务。 如果 Angular 无法找到参数信息,它就会抛出一个错误。 *只有当类具有某种装饰器时*,Angular 才能找到参数信息。 `@Injectable()` 装饰器是所有服务类的标准装饰器。 @@ -421,7 +421,7 @@ Many dependency values are provided by classes, but not all. The expanded *provi * Learn more about [different kinds of providers](guide/dependency-injection-providers). - 欲知详情,参见[不同种类的提供者](guide/dependency-injection-providers)。 + 欲知详情,参阅[不同种类的提供者](guide/dependency-injection-providers)。 {@a optional} @@ -460,7 +460,7 @@ value of `logger` to null. Learn more about parameter decorators in [Hierarchical Dependency Injectors](guide/hierarchical-dependency-injection). -欲知详情,参见[多级注入器](guide/hierarchical-dependency-injection)。 +欲知详情,参阅[多级注入器](guide/hierarchical-dependency-injection)。 @@ -483,7 +483,7 @@ Dive deeper into the capabilities and advanced feature of the Angular DI system * Learn more about nested injectors in [Hierarchical Dependency Injection](guide/hierarchical-dependency-injection). - 要深入了解嵌套注入器,参见[多级依赖注入](guide/hierarchical-dependency-injection) + 要深入了解嵌套注入器,参阅[多级依赖注入](guide/hierarchical-dependency-injection) * Learn more about [DI tokens and providers](guide/dependency-injection-providers). diff --git a/aio/content/guide/deployment.md b/aio/content/guide/deployment.md index 9c2d0e03d3..1c70c3c662 100644 --- a/aio/content/guide/deployment.md +++ b/aio/content/guide/deployment.md @@ -147,7 +147,7 @@ For the simplest deployment, create a production build and copy the output direc -2. Copy _everything_ within the output folder (`dist/` by default) to a folder on the server. +2. Copy _everything_ within the output folder (`dist/project-name/` by default) to a folder on the server. 把输出目录(默认为 `dist/`)下的*每个文件*都复制到到服务器上的某个目录下。 @@ -155,7 +155,7 @@ For the simplest deployment, create a production build and copy the output direc Learn more about server-side redirects [below](#fallback). 配置服务器,让缺失的文件都重定向到 `index.html` 上。 - 欲知详情,参见[稍后](#fallback)的服务端重定向部分。 + 欲知详情,参阅[稍后](#fallback)的服务端重定向部分。 This is the simplest production-ready deployment of your application. @@ -205,7 +205,7 @@ You can see your deployed page at `https://.github.io// Check out [angular-cli-ghpages](https://github.com/angular-buch/angular-cli-ghpages), a full featured package that does all this for you and has extra functionality. - 参见 [angular-cli-ghpages](https://github.com/angular-buch/angular-cli-ghpages),这个包用到了全部这些特性,还提供了一些额外功能。 + 参阅 [angular-cli-ghpages](https://github.com/angular-buch/angular-cli-ghpages),这个包用到了全部这些特性,还提供了一些额外功能。 @@ -284,7 +284,7 @@ The list is by no means exhaustive, but should provide you with a good starting 这个列表虽然不够详尽,但可以为你提供一个良好的起点。 * [Apache](https://httpd.apache.org/): add a -[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown +[rewrite rule](https://httpd.apache.org/docs/current/mod/mod_rewrite.html) to the `.htaccess` file as shown (https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/): [Apache](https://httpd.apache.org/):在 `.htaccess` 文件中添加一个[重写规则](http://httpd.apache.org/docs/current/mod/mod_rewrite.html), @@ -300,7 +300,7 @@ The list is by no means exhaustive, but should provide you with a good starting RewriteRule ^ /index.html -* [Nginx](http://nginx.org/): use `try_files`, as described in +* [Nginx](https://nginx.org/): use `try_files`, as described in [Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps), modified to serve `index.html`: @@ -325,19 +325,18 @@ modified to serve `index.html`: # -- public - # |-- dist - + # |-- project-name # |-- index.html get '/' do - folderDir = settings.public_folder + '/dist' # ng build output folder + folderDir = settings.public_folder + '/project-name' # ng build output folder send_file File.join(folderDir, 'index.html') end ``` * [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown -[here](http://stackoverflow.com/a/26152011/2116927): +[here](https://stackoverflow.com/a/26152011): [IIS](https://www.iis.net/):往 `web.config` 中添加一条重写规则,类似于[这里](http://stackoverflow.com/a/26152011/2116927): @@ -403,11 +402,11 @@ Angular 开发者在向与该应用的宿主服务器不同域的服务器发起 There isn't anything the client application can do about these errors. The server must be configured to accept the application's requests. Read about how to enable CORS for specific servers at -enable-cors.org. +enable-cors.org. 客户端应用对这种错误无能为力。 服务器必须配置成可以接受来自该应用的请求。 -要了解如何对特定的服务器开启 CORS,参见enable-cors.org。 +要了解如何对特定的服务器开启 CORS,参阅enable-cors.org
    @@ -447,7 +446,7 @@ The `--prod` _meta-flag_ engages the following build optimization features. See [`ng build`](cli/build) for more about CLI build options and what they do. -要了解关于 CLI 构建选项及其作用的更多知识,参见 [`ng build`](cli/build)。 +要了解关于 CLI 构建选项及其作用的更多知识,参阅 [`ng build`](cli/build)。 {@a enable-prod-mode} @@ -485,11 +484,11 @@ absolutely must be present when the app starts. 通过只加载应用启动时绝对必须的那些模块,你可以极大缩短应用启动的时间。 Configure the Angular Router to defer loading of all other modules (and their associated code), either by -[waiting until the app has launched](guide/router#preloading "Preloading") +[waiting until the app has launched](guide/router-tutorial-toh#preloading "Preloading") or by [_lazy loading_](guide/router#lazy-loading "Lazy loading") them on demand. -可以配置 Angular 的路由器,来推迟所有其它模块(及其相关代码)的加载时机,方法有[一直等到应用启动完毕](guide/router#preloading "Preloading"),或者当用到时才按需[*惰性加载*](guide/router#asynchronous-routing "Lazy loading")。 +可以配置 Angular 的路由器,来推迟所有其它模块(及其相关代码)的加载时机,方法有[一直等到应用启动完毕](guide/router-tutorial-toh#preloading "Preloading"),或者当用到时才按需[*惰性加载*](guide/router#lazy-loading "Lazy loading")。
    @@ -497,7 +496,7 @@ them on demand.
    不要急性(eagerly)导入来自惰性加载模块中的任何东西
    -If you mean to lazy-load a module, be careful not import it +If you mean to lazy-load a module, be careful not to import it in a file that's eagerly loaded when the app starts (such as the root `AppModule`). If you do that, the module will be loaded immediately. @@ -584,13 +583,13 @@ Build your app for production _including the source maps_ -List the generated bundles in the `dist/` folder. +List the generated bundles in the `dist/project-name/` folder. 在 `dist/` 目录下列出生成的包。 - ls dist/*.bundle.js + ls dist/project-name/*.js @@ -602,7 +601,7 @@ The following example displays the graph for the _main_ bundle. - node_modules/.bin/source-map-explorer dist/main.*.bundle.js + node_modules/.bin/source-map-explorer dist/project-name/main* @@ -641,7 +640,7 @@ HTML 的 [_<base href="..."/>_](/guide/router) 标签指定了用于解析 See also the [*APP_BASE_HREF*](api/common/APP_BASE_HREF "API: APP_BASE_HREF") alternative. -另一种方式参见 [*APP_BASE_HREF*](api/common/APP_BASE_HREF "API: APP_BASE_HREF")。 +另一种方式参阅 [*APP_BASE_HREF*](api/common/APP_BASE_HREF "API: APP_BASE_HREF")。
    @@ -721,7 +720,7 @@ The following configurations determine your requirements. The Browserslist configuration file is included in your application [project structure](guide/file-structure#application-configuration-files) and provides the minimum browsers your application supports. See the [Browserslist spec](https://github.com/browserslist/browserslist) for complete configuration options. - `browserslist` 配置文件包含在应用的[项目结构中](guide/file-structure#application-configuration-files),它提供了本应用打算支持的最低浏览器版本。有关完整的配置选项,请参阅 [Browserslist 规范](https://github.com/browserslist/browserslist)。 + `browserslist` 配置文件包含在应用的[项目结构中](guide/file-structure#application-configuration-files),它提供了本应用打算支持的最低浏览器版本。关于完整的配置选项,请参阅 [Browserslist 规范](https://github.com/browserslist/browserslist)。 * TypeScript configuration @@ -747,7 +746,7 @@ For a development build, the output produced by `ng build` is simpler and easier For a production build, your configuration determines which bundles are created for deployment of your application. When needed, the `index.html` file is also modified during the build process to include script tags that enable differential loading, as shown in the following example. -对于生产版本,你的配置将决定创建哪些捆绑软件来部署你的应用程序。必要时,还会在构建过程中修改 `index.html` 文件,以包括启用差异化加载的脚本标签,如以下示例所示。 +对于生产版本,你的配置将决定创建哪些捆绑软件来部署你的应用程序。必要时,还会在构建过程中修改 `index.html` 文件,以包括启用差异化加载的脚本标签,如以下范例所示。 <body> @@ -773,7 +772,7 @@ Each script tag has a `type="module"` or `nomodule` attribute. Browsers with nat Some legacy browsers still download both bundles, but only execute the appropriate scripts based on the attributes mentioned above. You can read more on the issue [here](https://github.com/philipwalton/webpack-esnext-boilerplate/issues/1). - 一些旧版浏览器仍会下载两个捆绑包,但只会根据上述属性执行适当的脚本。你可以在[此处](https://github.com/philipwalton/webpack-esnext-boilerplate/issues/1)阅读有关此问题的更多[信息](https://github.com/philipwalton/webpack-esnext-boilerplate/issues/1)。 + 一些旧版浏览器仍会下载两个捆绑包,但只会根据上述属性执行适当的脚本。你可以在[此处](https://github.com/philipwalton/webpack-esnext-boilerplate/issues/1)阅读关于此问题的更多[信息](https://github.com/philipwalton/webpack-esnext-boilerplate/issues/1)。 @@ -785,12 +784,12 @@ To include differential loading in your application builds, you must configure t 要想在构建应用时包含差异化加载特性,你必须修改项目中的 Browserslist 和 TypeScript 配置文件。 -The following examples show a `browserlistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015. +The following examples show a `.browserslistrc` and `tsconfig.json` file for a newly created Angular application. In this configuration, legacy browsers such as IE 9-11 are ignored, and the compilation target is ES2015. 下面的例子展示了新创建的 Angular 应用的 `browserlistrc` 和 `tsconfig.json` 文件。 在这份配置中,老式浏览器(比如 IE 9-11)都被忽略了,其编译目标是 ES2015。 - + # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. # For additional information regarding the format and rule options, please see: # https://github.com/browserslist/browserslist#queries @@ -804,10 +803,10 @@ The following examples show a `browserlistrc` and `tsconfig.json` file for a new last 1 Chrome version last 1 Firefox version last 2 Edge major versions -last 2 Safari major version +last 2 Safari major versions last 2 iOS major versions Firefox ESR -not IE 9-11 # For IE 9-11 support, remove 'not'. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. @@ -863,6 +862,7 @@ If your Browserslist configuration includes support for any legacy browsers, the | ES5 support enabled | es5 | Single build w/conditional polyfills for ES5 only | | 启用 ES5 支持 | es5 | 单一构建,按需附带只供 ES5 使用的腻子脚本 | | ES5 support enabled | es2015 | Differential loading (two builds w/conditional polyfills) | +| 弃用 ES5 支持 | es2015 | 差异化加载 (按需附带两种构建需要的腻子脚本) | {@a test-and-serve} diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index d04e00777b..32385531d7 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -19,11 +19,11 @@ This guide contains a summary of all Angular APIs and features that are currentl Features and APIs that were deprecated in v6 or earlier are candidates for removal in version 9 or any later major version. For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices"). -v6 或更早版本中已弃用的特性和 API 将会在版本 9 或更高级版本中删除。要了解 Angular 中关于弃用和删除的实践,参见[Angular 发布实践](guide/releases#deprecation-practices "Angular 发布实践:弃用实践")。 +v6 或更早版本中已弃用的特性和 API 将会在版本 9 或更高级版本中删除。要了解 Angular 中关于弃用和删除的实践,参阅[Angular 发布实践](guide/releases#deprecation-practices "Angular 发布实践:弃用实践")。 For step-by-step instructions on how to update to the latest Angular release, use the interactive update guide at [update.angular.io](https://update.angular.io). -有关如何更新到最新 Angular 版本的分步说明,参见 [update.angular.io](https://update.angular.io) 上的交互式更新指南。 +关于如何更新到最新 Angular 版本的分步说明,参阅 [update.angular.io](https://update.angular.io) 上的交互式更新指南。 @@ -43,28 +43,25 @@ v6 - v9 v7 - v10 v8 - v11 v9 - v12 +v10 - v13 +v11 - v14 +v12 - v15 --> | Area | API or Feature | May be removed in | | ---- | -------------- | ----------------- | | 区域 | API 或特性 | 可能会在什么时候移除 | -| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 | | `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | v11 | | `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | v11 | -| `@angular/core` | [`CollectionChangeRecord`](#core) | v11 | | `@angular/core` | [`DefaultIterableDiffer`](#core) | v11 | | `@angular/core` | [`ReflectiveKey`](#core) | v11 | | `@angular/core` | [`RenderComponentType`](#core) | v11 | -| `@angular/core` | [`ViewEncapsulation.Native`](#core) | v11 | | `@angular/core` | [`WrappedValue`](#core) | v12 | | `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | v11 | | `@angular/forms` | [响应式表单中的 `ngModel`](#ngmodel-reactive) | v11 | -| `@angular/router` | [`preserveQueryParams`](#router) | v11 | | `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | v11 | | `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | v11 | | `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | v11 | -| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | v11  | -| `@angular/platform-webworker` | [所有入口点](api/platform-webworker) | v11 | | template syntax | [`](#template-tag) | v11 | | 模板语法 | [`](#template-tag) | v11 | | polyfills | [reflect-metadata](#reflect-metadata) | v11 | @@ -76,17 +73,18 @@ v9 - v12 | `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | v11 | | `@angular/router` | [`loadChildren` string syntax](#loadChildren) | v11 | | `@angular/router` | [`loadChildren` 字符串语法](#loadChildren) | v11 | -| `@angular/core/testing` | [`TestBed.get`](#testing) | v12 | -| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified | -| `@angular/router` | [`ActivatedRoute` 参数和 `queryParams` 属性](#activatedroute-props) | 未定 | -| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | unspecified | -| 模板语法 | [`/deep/`,`>>>` 和 `::ng-deep`](#deep-component-style-selector) | 未定 | -| browser support | [`IE 9 and 10, IE mobile`](#ie-9-10-and-mobile) | v11 | -| 浏览器支持 | [`IE 9、10 和 IE mobile`](#ie-9-10-and-mobile) | v11 | +| `@angular/core/testing` | [`TestBed.get`](#testing) | v12 | +| `@angular/core/testing` | [`async`](#testing) | v12 | +| `@angular/forms` | [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | v14 | +| `@angular/forms` | [`FormBuilder.group` 老式选项参数](api/forms/FormBuilder#group) | v14 | +| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified | +| `@angular/router` | [`ActivatedRoute` 参数和 `queryParams` 属性](#activatedroute-props) | 未定 | +| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | unspecified | +| 模板语法 | [`/deep/`, `>>>`, 和 `::ng-deep`](#deep-component-style-selector) | 未定 | For information about Angular CDK and Angular Material deprecations, see the [changelog](https://github.com/angular/components/blob/master/CHANGELOG.md). -要了解 Angular CDK 和 Angular Material 的弃用情况,参见[变更记录](https://github.com/angular/components/blob/master/CHANGELOG.md)。 +要了解 Angular CDK 和 Angular Material 的弃用情况,参阅[变更记录](https://github.com/angular/components/blob/master/CHANGELOG.md)。 ## Deprecated APIs @@ -119,24 +117,22 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | API | Replacement | Deprecation announced | Notes | | --- | ----------- | --------------------- | ----- | | API | 替代品 | 宣布弃用 | 备注 | -| [`CollectionChangeRecord`](api/core/CollectionChangeRecord) | [`IterableChangeRecord`](api/core/IterableChangeRecord) | v4 | none | -| [`CollectionChangeRecord`](api/core/CollectionChangeRecord) | [`IterableChangeRecord`](api/core/IterableChangeRecord) | v4 | 无 | | [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | n/a | v4 | Not part of public API. | | [`DefaultIterableDiffer`](api/core/DefaultIterableDiffer) | 不适用 | v4 | 不属于公共 API。| | [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | See [`ReflectiveInjector`](#reflectiveinjector) | -| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | 参见 [`ReflectiveInjector`](#reflectiveinjector) | +| [`ReflectiveInjector`](api/core/ReflectiveInjector) | [`Injector.create`](api/core/Injector#create) | v5 | 参阅 [`ReflectiveInjector`](#reflectiveinjector) | | [`ReflectiveKey`](api/core/ReflectiveKey) | none | v5 | none | | [`ReflectiveKey`](api/core/ReflectiveKey) | 无 | v5 | 无 | -| [`ViewEncapsulation.Native`](api/core/ViewEncapsulation#Native) | [`ViewEncapsulation.ShadowDom`](api/core/ViewEncapsulation#ShadowDom) | v6 | Use the native encapsulation mechanism of the renderer. See [view.ts](https://github.com/angular/angular/blob/3e992e18ebf51d6036818f26c3d77b52d3ec48eb/packages/core/src/metadata/view.ts#L32). | -| [`ViewEncapsulation.Native`](api/core/ViewEncapsulation#Native) | [`ViewEncapsulation.ShadowDom`](api/core/ViewEncapsulation#ShadowDom) | v6 | 使用渲染器的原生封装机制。参见 [view.ts。](https://github.com/angular/angular/blob/3e992e18ebf51d6036818f26c3d77b52d3ec48eb/packages/core/src/metadata/view.ts#L32) | | [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. | | [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | 仅在生成的代码中使用。任何源代码都不应依赖此 API。| | [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) | -| [`entryComponents`](api/core/NgModule#entryComponents) | 无 | v9 | 参见 [`entryComponents`](#entryComponents) | +| [`entryComponents`](api/core/NgModule#entryComponents) | 无 | v9 | 参阅 [`entryComponents`](#entryComponents) | | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | -| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | 无 | v9 | 参见 [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | +| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | 无 | v9 | 参阅 [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | | [`WrappedValue`](api/core/WrappedValue) | none | v10 | See [removing `WrappedValue`](#wrapped-value) | -| [`WrappedValue`](api/core/WrappedValue) | 无 | v10 | 参见[移除 `WrappedValue`](#wrapped-value) | +| [`WrappedValue`](api/core/WrappedValue) | 无 | v10 | 参阅[移除 `WrappedValue`](#wrapped-value) | +| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | The `async` function from `@angular/core/testing` has been renamed to `waitForAsync` in order to avoid confusion with the native JavaScript `async` syntax. The existing function is deprecated and will be removed in a future version. | +| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | `@angular/core/testing` 中的 `async` 函数已经改名为 `waitForAsync` 以免与 JavaScript 原生 `async` 语法混淆。现有函数已经标记为弃用,并将在未来版本中移除。| {@a testing} ### @angular/core/testing @@ -146,6 +142,9 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | API | 替代品 | 宣布弃用 | 备注 | | [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | v9 | Same behavior, but type safe. | | [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | v9 | 行为相同,但类型安全。| +| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v10 | Same behavior, but rename to avoid confusion. | +| [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v10 | 行为相同,只是改名以免混淆。 | + {@a forms} ### @angular/forms @@ -154,34 +153,10 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | --- | ----------- | --------------------- | ----- | | API | 替代品 | 宣布弃用 | 备注 | | [`ngModel` with reactive forms](#ngmodel-reactive) | [`FormControlDirective`](api/forms/FormControlDirective) | v6 | none | -| [响应式表单中的 `ngModel`](#ngmodel-reactive) | 参见 [FormControlDirective 使用说明](api/forms/FormControlDirective) | v6 | 无 | +| [响应式表单中的 `ngModel`](#ngmodel-reactive) | 参阅 [FormControlDirective 使用说明](api/forms/FormControlDirective) | v6 | 无 | +| [`FormBuilder.group` legacy options parameter](api/forms/FormBuilder#group) | [`AbstractControlOptions` parameter value](api/forms/AbstractControlOptions) | v11 | none | +| [`FormBuilder.group` 老式选项参数](api/forms/FormBuilder#group) | [`AbstractControlOptions` 参数值](api/forms/AbstractControlOptions) | v11 | 无 | -{@a router} -### @angular/router - -| API | Replacement | Deprecation announced | Notes | -| --- | ----------- | --------------------- | ----- | -| API | 替代品 | 宣布弃用 | 备注 | -| [`preserveQueryParams`](api/router/NavigationExtras#preserveQueryParams) | [`queryParamsHandling`](api/router/NavigationExtras#queryParamsHandling) | v4 | none | -| [`preserveQueryParams`](api/router/NavigationExtras#preserveQueryParams) | [`queryParamsHandling`](api/router/NavigationExtras#queryParamsHandling) | v4 | 无 | - -{@a platform-webworker} -### @angular/platform-webworker - -| API | Replacement | Deprecation announced | Notes | -| --- | ----------- | --------------------- | ----- | -| API | 替代品 | 宣布弃用 | 备注 | -| [All entry points](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | See [Upgrading from AngularJS](guide/upgrade). | -| [所有入口点](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | 参见[从 AngularJS 升级](guide/upgrade)。| - -{@a platform-webworker-dynamic} -### @angular/platform-webworker-dynamic - -| API | Replacement | Deprecation announced | Notes | -| --- | ----------- | --------------------- | ----- | -| API | 替代品 | 宣布弃用 | 备注 | -| [All entry points](api/platform-webworker-dynamic) | none | v8 | See [platform-webworker](#webworker-apps). | -| [所有入口点](api/platform-webworker-dynamic) | 无 | v8 | 参见 [platform-webworker](#webworker-apps). | {@a upgrade} ### @angular/upgrade @@ -190,7 +165,7 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | --- | ----------- | --------------------- | ----- | | API | 替代品 | 宣布弃用 | 备注 | | [All entry points](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | See [Upgrading from AngularJS](guide/upgrade). | -| [所有入口点](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | 参见 [从 AngularJS 升级](guide/upgrade)。| +| [所有入口点](api/upgrade) | [`@angular/upgrade/static`](api/upgrade/static) | v5 | 参阅 [从 AngularJS 升级](guide/upgrade)。| {@a upgrade-static} ### @angular/upgrade/static @@ -199,9 +174,9 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | --- | ----------- | --------------------- | ----- | | API | 替代品 | 宣布弃用 | 备注 | | [`getAngularLib`](api/upgrade/static/getAngularLib) | [`getAngularJSGlobal`](api/upgrade/static/getAngularJSGlobal) | v5 | See [Upgrading from AngularJS](guide/upgrade). | -| [`getAngularLib`](api/upgrade/static/getAngularLib) | [`getAngularJSGlobal`](api/upgrade/static/getAngularJSGlobal) | v5 | 参见[从 AngularJS 升级](guide/upgrade)。| +| [`getAngularLib`](api/upgrade/static/getAngularLib) | [`getAngularJSGlobal`](api/upgrade/static/getAngularJSGlobal) | v5 | 参阅[从 AngularJS 升级](guide/upgrade)。| | [`setAngularLib`](api/upgrade/static/setAngularLib) | [`setAngularJSGlobal`](api/upgrade/static/setAngularJSGlobal) | v5 | See [Upgrading from AngularJS](guide/upgrade). | -| [`setAngularLib`](api/upgrade/static/setAngularLib) | [`setAngularJSGlobal`](api/upgrade/static/setAngularJSGlobal) | v5 | 参见[从 AngularJS 升级](guide/upgrade)。| +| [`setAngularLib`](api/upgrade/static/setAngularLib) | [`setAngularJSGlobal`](api/upgrade/static/setAngularJSGlobal) | v5 | 参阅[从 AngularJS 升级](guide/upgrade)。| {@a deprecated-features} @@ -222,7 +197,7 @@ Bazel builder and schematics were introduced in Angular Labs to let users try ou This feature has been deprecated. For more information, please refer to the [migration doc](https://github.com/angular/angular/blob/master/packages/bazel/src/schematics/README.md). Bazel 构建器及其原理图曾经被引入到 Angular Labs 中,以便让用户尝试 Bazel,而不用管理 Bazel 的版本和 BUILD 文件。 -该特性已经弃用了。欲知详情,参见[迁移文档](https://github.com/angular/angular/blob/master/packages/bazel/src/schematics/README.md)。 +该特性已经弃用了。欲知详情,参阅[迁移文档](https://github.com/angular/angular/blob/master/packages/bazel/src/schematics/README.md)。 {@a wtf} ### Web Tracing Framework integration @@ -250,9 +225,11 @@ For more information, see [/deep/, >>>, and ::ng-deep](guide/component-styles#de {@a template-tag} ### <template> tag +### <template> 标签 + The `