Compare commits

..

26 Commits

Author SHA1 Message Date
Nicolas Molina Monroy
63149810c1 docs: translate guide/creating-libraries 2020-08-24 21:19:26 -04:00
Nicolas Molina Monroy
e3bc073f7b Merge branch 'creating-libraries' of github.com:angular-hispano/angular into creating-libraries 2020-08-23 20:06:24 -04:00
Nicolas Molina Monroy
4b9b094c47 docs: translate guide/creating-libraries 2020-08-23 20:03:23 -04:00
Nicolas Molina Monroy
75e59cc837
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:29:18 -04:00
Nicolas Molina Monroy
6ad701074a
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:29:07 -04:00
Nicolas Molina Monroy
fb4df0a54c
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:28:52 -04:00
Nicolas Molina Monroy
7b0fba0983
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:28:40 -04:00
Nicolas Molina Monroy
7bd65e5f1b
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:28:29 -04:00
Nicolas Molina Monroy
c90d9fd468
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:28:18 -04:00
Nicolas Molina Monroy
4bd3debef2
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:28:06 -04:00
Nicolas Molina Monroy
43b1210f86
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:27:53 -04:00
Nicolas Molina Monroy
189686bea1
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:26:38 -04:00
Nicolas Molina Monroy
66592ac5a3
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:26:26 -04:00
Nicolas Molina Monroy
066da01295
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:26:12 -04:00
Nicolas Molina Monroy
03ca38a478
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:26:04 -04:00
Nicolas Molina Monroy
6452285a71
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:25:56 -04:00
Nicolas Molina Monroy
a81e82028a
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:25:14 -04:00
Nicolas Molina Monroy
958d06fa69
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:25:05 -04:00
Nicolas Molina Monroy
15b311ac9f
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:24:56 -04:00
Nicolas Molina Monroy
5717054caa
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:24:44 -04:00
Nicolas Molina Monroy
44cf633cb1
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:24:30 -04:00
Nicolas Molina Monroy
957e152656
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:24:22 -04:00
Nicolas Molina Monroy
9ccf7631c6
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:24:06 -04:00
Nicolas Molina Monroy
225fdf9ac8
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:23:58 -04:00
Nicolas Molina Monroy
b206732b25
Update aio/content/guide/creating-libraries.es.md
Co-authored-by: Andrés Villanueva <andresvillanuevab@gmail.com>
2020-08-23 19:23:35 -04:00
Nicolas Molina Monroy
efef4b7dc1 docs: translate guide/creating-libraries 2020-08-21 03:59:48 -04:00
884 changed files with 14455 additions and 30059 deletions

View File

@ -136,6 +136,15 @@ build:remote --remote_executor=remotebuildexecution.googleapis.com
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times. # retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1 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 # User bazel configuration
# NOTE: This needs to be the *last* entry in the config. # NOTE: This needs to be the *last* entry in the config.

View File

@ -30,6 +30,11 @@ 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_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_win: &cache_key_win_fallback v7-angular-win-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-
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and # Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
# `build-ivy-npm-packages`. # `build-ivy-npm-packages`.
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs # https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
@ -151,6 +156,27 @@ commands:
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
git config --global gc.auto 0 || true git config --global gc.auto 0 || true
init_saucelabs_environment:
description: Sets up a domain that resolves to the local host.
steps:
- run:
name: Preparing environment for running tests on Sauce Labs.
command: |
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
# the tunnel. We do this because devices are sometimes not able to properly resolve
# `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
# resolve to anything on SauceLabs VMs ensures that such requests are always resolved
# through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
# More context can be found in: https://github.com/angular/angular/pull/35171.
setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
- run:
# Sets up a local domain in the machine's host file that resolves to the local
# host. This domain is helpful in Sauce Labs tests where devices are not able to
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
name: Setting up alias domain for local host.
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
# Normally this would be an individual job instead of a command. # Normally this would be an individual job instead of a command.
# But startup and setup time for each individual windows job are high enough to discourage # But startup and setup time for each individual windows job are high enough to discourage
# many small jobs, so instead we use a command for setup unless the gain becomes significant. # many small jobs, so instead we use a command for setup unless the gain becomes significant.
@ -246,8 +272,109 @@ jobs:
- run: yarn -s tslint - run: yarn -s tslint
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check - run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
- run: yarn -s ts-circular-deps:check - run: yarn -s ts-circular-deps:check
- run: yarn -s ng-dev pullapprove verify
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE - run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
test:
executor:
name: default-executor
# Now that large integration tests are running locally in parallel (they can't run on RBE yet
# as they require network access for yarn install), this test is running out of memory
# consistently with the xlarge machine.
# TODO: switch back to xlarge once integration tests are running on remote-exec
resource_class: 2xlarge+
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- install_java
- run:
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
no_output_timeout: 20m
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_aot:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# We need to explicitly specify the --symlink_prefix option because otherwise we would
# not be able to easily find the output bin directory when uploading artifacts for size
# measurements.
- run:
command: yarn test-ivy-aot //... --symlink_prefix=dist/
no_output_timeout: 20m
# Publish bundle artifacts which will be used to calculate the size change. **Note**: Make
# sure that the size plugin from the Angular robot fetches the artifacts from this CircleCI
# job (see .github/angular-robot.yml). Additionally any artifacts need to be stored with the
# following path format: "{projectName}/{context}/{fileName}". This format is necessary
# because otherwise the bot is not able to pick up the artifacts from CircleCI. See:
# https://github.com/angular/github-robot/blob/master/functions/src/plugins/size.ts#L392-L394
- store_artifacts:
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
destination: core/hello_world/bundle
- store_artifacts:
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
destination: core/todo/bundle
- store_artifacts:
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
destination: core/hello_world/bundle.br
- store_artifacts:
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
destination: core/todo/bundle.br
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
saucelabs_view_engine:
executor:
name: default-executor
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Run Bazel tests on Saucelabs with ViewEngine
# See /tools/saucelabs/README.md for more info
command: |
yarn bazel run //tools/saucelabs:sauce_service_setup
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)')
yarn bazel test --config=saucelabs ${TESTS}
yarn bazel run //tools/saucelabs:sauce_service_stop
no_output_timeout: 40m
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
saucelabs_ivy:
executor:
name: default-executor
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Run Bazel tests on Saucelabs with Ivy
# See /tools/saucelabs/README.md for more info
command: |
yarn bazel run //tools/saucelabs:sauce_service_setup
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)')
yarn bazel test --config=saucelabs --config=ivy ${TESTS}
yarn bazel run //tools/saucelabs:sauce_service_stop
no_output_timeout: 40m
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
test_aio: test_aio:
executor: default-executor executor: default-executor
steps: steps:
@ -266,6 +393,10 @@ jobs:
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE - run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Run accessibility tests # Run accessibility tests
- run: yarn --cwd aio test-a11y-score-localhost - run: yarn --cwd aio test-a11y-score-localhost
# Check the bundle sizes.
- run: yarn --cwd aio payload-size
# Run unit tests for Firebase redirects
- run: yarn --cwd aio redirects-test
deploy_aio: deploy_aio:
executor: default-executor executor: default-executor
@ -295,6 +426,8 @@ jobs:
- run: yarn --cwd aio e2e --configuration=ci - run: yarn --cwd aio e2e --configuration=ci
# Run PWA-score tests # Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE - run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Check the bundle sizes.
- run: yarn --cwd aio payload-size aio-local<<# parameters.viewengine >>-viewengine<</ parameters.viewengine >>
test_aio_tools: test_aio_tools:
executor: default-executor executor: default-executor
@ -308,6 +441,26 @@ jobs:
- run: yarn --cwd aio tools-test - run: yarn --cwd aio tools-test
- run: ./aio/aio-builds-setup/scripts/test.sh - run: ./aio/aio-builds-setup/scripts/test.sh
test_docs_examples:
parameters:
viewengine:
type: boolean
default: false
executor:
name: default-executor
resource_class: xlarge
parallelism: 5
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# Install aio
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<</ parameters.viewengine >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`. # This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
aio_preview: aio_preview:
executor: default-executor executor: default-executor
@ -336,6 +489,7 @@ jobs:
name: Wait for preview and run tests name: Wait for preview and run tests
command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE
# The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and # The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and
# tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh). # tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh).
# Having multiple jobs that independently build in this manner duplicates some work; we build # Having multiple jobs that independently build in this manner duplicates some work; we build
@ -347,7 +501,7 @@ jobs:
build-npm-packages: build-npm-packages:
executor: executor:
name: default-executor name: default-executor
resource_class: medium resource_class: xlarge
steps: steps:
- custom_attach_workspace - custom_attach_workspace
- init_environment - init_environment
@ -369,6 +523,246 @@ jobs:
- "~/bazel_repository_cache" - "~/bazel_repository_cache"
- "~/.cache/bazelisk" - "~/.cache/bazelisk"
# Build the ivy npm packages.
build-ivy-npm-packages:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- run: node scripts/build/build-ivy-npm-packages.js
# Save the npm packages from //packages/... for other workflow jobs to read
- persist_to_workspace:
root: *workspace_location
paths:
- ng/dist/packages-dist-ivy-aot
- ng/dist/zone.js-dist-ivy-aot
# This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as
# build artifacts. This makes it easy to try out changes from a PR build for testing purposes.
# More info CircleCI build artifacts: https://circleci.com/docs/2.0/artifacts
#
# NOTE: Currently, this job only runs for PR builds. See `publish_snapshot` for non-PR builds.
publish_packages_as_artifacts:
executor: default-executor
environment:
NG_PACKAGES_DIR: &ng_packages_dir 'dist/packages-dist'
NG_PACKAGES_ARCHIVES_DIR: &ng_packages_archives_dir 'dist/packages-dist-archives'
ZONEJS_PACKAGES_DIR: &zonejs_packages_dir 'dist/zone.js-dist'
ZONEJS_PACKAGES_ARCHIVES_DIR: &zonejs_packages_archives_dir 'dist/zone.js-dist-archives'
steps:
- custom_attach_workspace
- init_environment
# Publish `@angular/*` packages.
- run:
name: Create artifacts for @angular/* packages
command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $NG_PACKAGES_DIR $NG_PACKAGES_ARCHIVES_DIR
- store_artifacts:
path: *ng_packages_archives_dir
destination: angular
# 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
- store_artifacts:
path: *zonejs_packages_archives_dir
destination: zone.js
# This job updates the content of repos like github.com/angular/core-builds
# for every green build on angular/angular.
publish_snapshot:
executor: default-executor
steps:
# See below - ideally this job should not trigger for non-upstream builds.
# But since it does, we have to check this condition.
- run:
name: Skip this job for Pull Requests and Fork builds
# Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this
# step can be run before `init_environment`.
command: >
if [[ -n "${CIRCLE_PR_NUMBER}" ]] ||
[[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] ||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
circleci step halt
fi
- custom_attach_workspace
- init_environment
# CircleCI has a config setting to force SSH for all github connections
# This is not compatible with our mechanism of using a Personal Access Token
# Clear the global setting
- run: git config --global --unset "url.ssh://git@github.com.insteadof"
- run:
name: Decrypt github credentials
# We need ensure that the same default digest is used for encoding and decoding with
# OpenSSL. OpenSSL versions might have different default digests which can cause
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
- run: ./scripts/ci/publish-build-artifacts.sh
aio_monitoring_stable:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- run: setPublicVar_CI_STABLE_BRANCH
- run:
name: Check out `aio/` and yarn from the stable branch
command: |
git fetch origin $CI_STABLE_BRANCH
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
# branch and there could be a node version skew, which is acceptable in this monitoring job.
- run: yarn config set ignore-engines true
- run:
name: Run tests against https://angular.io/
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
aio_monitoring_next:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- run:
name: Run tests against https://next.angular.io/
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
legacy-unit-tests-saucelabs:
executor:
name: default-executor
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
# container for this job. This is necessary because we launch a lot of browsers concurrently
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Starting Saucelabs tunnel service
command: ./tools/saucelabs/sauce-service.sh run
background: true
- run: yarn tsc -p packages
- run: yarn tsc -p modules
- run: yarn bazel build //packages/zone.js:npm_package
- 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
command: ./tools/saucelabs/sauce-service.sh ready-wait
- run:
name: Running tests on Saucelabs.
command: |
browsers=$(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
yarn karma start ./karma-js.conf.js --single-run --browsers=${browsers}
- run:
name: Stop Saucelabs tunnel service
command: ./tools/saucelabs/sauce-service.sh stop
# Job that runs all unit tests of the `angular/components` repository.
components-repo-unit-tests:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
# Restore the cache before cloning the repository because the clone script re-uses
# the restored repository if present. This reduces the amount of times the components
# repository needs to be cloned (this is slow and increases based on commits in the repo).
- restore_cache:
keys:
- *components_repo_unit_tests_cache_key
# Whenever the `angular/components` SHA is updated, the cache key will no longer
# match. The fallback cache will still match, and CircleCI will restore the most
# recently cached repository folder. Without the fallback cache, we'd need to download
# the repository from scratch and it would slow down the job. This is because we can't
# clone the repository with reduced `--depth`, but rather need to clone the whole
# repository to be able to support arbitrary SHAs.
- *components_repo_unit_tests_cache_key_fallback
- run:
name: "Fetching angular/components repository"
command: ./scripts/ci/clone_angular_components_repo.sh
- run:
# Run yarn install to fetch the Bazel binaries as used in the components repo.
name: Installing dependencies.
# TODO: remove this once the repo has been updated to use NodeJS v12 and Yarn 1.19.1.
# We temporarily ignore the "engines" because the Angular components repository has
# minimum dependency on NodeJS v12 and Yarn 1.19.1, but the framework repository uses
# older versions.
command: yarn --ignore-engines --cwd ${COMPONENTS_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
- save_cache:
key: *components_repo_unit_tests_cache_key
paths:
# Temporary directory must be kept in sync with the `$COMPONENTS_REPO_TMP_DIR` env
# variable. It needs to be hardcoded here, because env variables interpolation is
# not supported.
- "/tmp/angular-components-repo"
- run:
# Updates the `angular/components` `package.json` file to refer to the release output
# inside the `packages-dist` directory. Note that it's not necessary to perform a yarn
# install as Bazel runs Yarn automatically when needed.
name: Setting up release packages.
command: node scripts/ci/update-deps-to-dist-packages.js ${COMPONENTS_REPO_TMP_DIR}/package.json dist/packages-dist/
- run:
name: "Running `angular/components` unit tests"
command: ./scripts/ci/run_angular_components_unit_tests.sh
test_zonejs:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisetest
- run: yarn --cwd packages/zone.js promisefinallytest
- 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
# Windows jobs
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
test_win:
executor: windows-executor
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"
test_ivy_aot_win:
executor: windows-executor
steps:
- setup_win
- run:
command: yarn circleci-win-ivy
no_output_timeout: 45m
workflows: workflows:
version: 2 version: 2
@ -381,9 +775,21 @@ workflows:
- lint: - lint:
requires: requires:
- setup - setup
- test:
requires:
- setup
- test_ivy_aot:
requires:
- setup
- build-npm-packages: - build-npm-packages:
requires: requires:
- setup - setup
- build-ivy-npm-packages:
requires:
- setup
- legacy-unit-tests-saucelabs:
requires:
- setup
- test_aio: - test_aio:
requires: requires:
- setup - setup
@ -393,9 +799,22 @@ workflows:
- test_aio_local: - test_aio_local:
requires: requires:
- build-npm-packages - build-npm-packages
- test_aio_local:
name: test_aio_local_viewengine
viewengine: true
requires:
- build-npm-packages
- test_aio_tools: - test_aio_tools:
requires: requires:
- build-npm-packages - build-npm-packages
- test_docs_examples:
requires:
- build-npm-packages
- test_docs_examples:
name: test_docs_examples_viewengine
viewengine: true
requires:
- build-npm-packages
- aio_preview: - aio_preview:
# Only run on PR builds. (There can be no previews for non-PR builds.) # Only run on PR builds. (There can be no previews for non-PR builds.)
<<: *only_on_pull_requests <<: *only_on_pull_requests
@ -404,3 +823,78 @@ workflows:
- test_aio_preview: - test_aio_preview:
requires: requires:
- aio_preview - aio_preview
- publish_packages_as_artifacts:
requires:
- build-npm-packages
- publish_snapshot:
# Note: no filters on this job because we want it to run for all upstream branches
# We'd really like to filter out pull requests here, but not yet available:
# https://discuss.circleci.com/t/workflows-pull-request-filter/14396/4
# Instead, the job just exits immediately at the first step.
requires:
# Only publish if tests and integration tests pass
- test
- test_ivy_aot
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
- test_aio_local
- test_aio_local_viewengine
- test_docs_examples
- test_docs_examples_viewengine
# Get the artifacts to publish from the build-packages-dist job
# since the publishing script expects the legacy outputs layout.
- build-npm-packages
- build-ivy-npm-packages
- legacy-unit-tests-saucelabs
- 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
- test_ivy_aot_win:
<<: *skip_on_pull_requests
requires:
- test_ivy_aot
monitoring:
jobs:
- setup
- aio_monitoring_stable:
requires:
- setup
- aio_monitoring_next:
requires:
- setup
- saucelabs_ivy:
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
# each karma_web_test target is provisioning and tearing down browsers which is adding
# a lot of overhead. Running once daily on master only to avoid wasting resources and
# slowing down CI for PRs.
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
# share provisioned browsers and we can remove the legacy saucelabs job.
requires:
- setup
- saucelabs_view_engine:
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
# each karma_web_test target is provisioning and tearing down browsers which is adding
# a lot of overhead. Running once daily on master only to avoid wasting resources and
# slowing down CI for PRs.
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
# share provisioned browsers and we can remove the legacy saucelabs job.
requires:
- setup
triggers:
- schedule:
<<: *only_on_master
# Runs monitoring jobs at 10:00AM every day.
cron: "0 10 * * *"

View File

@ -17,7 +17,7 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
#################################################################################################### ####################################################################################################
setPublicVar CI "$CI" setPublicVar CI "$CI"
setPublicVar PROJECT_ROOT "$projectDir"; setPublicVar PROJECT_ROOT "$projectDir";
setPublicVar CI_AIO_MIN_PWA_SCORE "62"; setPublicVar CI_AIO_MIN_PWA_SCORE "95";
# This is the branch being built; e.g. `pull/12345` for PR builds. # This is the branch being built; e.g. `pull/12345` for PR builds.
setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
@ -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_URL "https://github.com/angular/components.git"
setPublicVar COMPONENTS_REPO_BRANCH "master" setPublicVar COMPONENTS_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`. # **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
setPublicVar COMPONENTS_REPO_COMMIT "09e68db8ed5b1253f2fe38ff954ef0df019fc25a" setPublicVar COMPONENTS_REPO_COMMIT "f428c00465dfcf8a020237f22532480eedbd2cb6"
#################################################################################################### ####################################################################################################

View File

@ -1,12 +1,12 @@
## Lista de Verificación del PR ## Lista de Verificación del PR
Comprueba si tu PR cumple los siguientes requisitos: Comprueba si tu PR cumple los siguientes requisitos:
- [ ] El mensaje de commit esta conforme con [nuestras reglas](https://github.com/angular-hispano/angular/blob/master/CONTRIBUTING.md#-formato-para-el-mensaje-de-los-commits) - [ ] El mensaje de commit conforme con [nuestras reglas](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit)
- [ ] Probe los cambios que agregué (arreglo de bugs / funcionalidades) - [ ] Prueba los cambios que agregaste (arreglo de bugs / funcionalidades)
- [ ] Revisé previamente las traducciones o cambios de contenido - [ ] Revisa tus traducciones o cambios de contenido
- [ ] Consulté el [diccionario de términos](https://github.com/angular-hispano/angular/blob/master/aio/diccionario-de-términos.md) en español - [ ] Consulté el [diccionario de términos](https://github.com/angular-hispano/angular/issues/9) en español
- [ ] He creado dos archivos con la extensión correspondiente(.en.md para el archivo en inglés y .md para el Archivo en español) - [ ] Se han creado dos archivos con la extensión correspondiente(.en.md para el archivo en inglés y .md para el Archivo en español)
- [ ] He enlazado el commit con el issue correspondiente <!-- ejemplo Fixes #X --> - [ ] PR enlazada con el issue correspondiente
## Tipo de PR ## Tipo de PR
@ -16,13 +16,13 @@ Comprueba si tu PR cumple los siguientes requisitos:
- [ ] Bugfix - [ ] Bugfix
- [ ] Funcionalidad - [ ] Funcionalidad
- [ ] Actualización de el estilo del código (formato, variables locales) - [ ] Actualizar el estilo del código (formato, variables locales)
- [ ] Refactorización (no cambios en la funcionalidad, no cambios en el api) - [ ] Refactorización (no cambios en la funcionalidad, no cambios en el api)
- [ ] Cambios relacionados al build - [ ] Cambios relacionados al build
- [ ] Cambios relacionados al CI (Integración continua) - [ ] Cambios relacionados al CI (Integración continua)
- [ ] Cambios en el contenido de la documentación - [ ] Cambios en el contenido de la documentación
- [ ] Cambios en la aplicación / infraestructura de angular.io - [ ] Cambios en la aplicación / infrastructura de angular.io
- [ ] Otro... Por favor describe la: - [ ] Otro... Por favor describela:
## ¿Cuál es el comportamiento actual? ## ¿Cuál es el comportamiento actual?
<!-- Describe el comportamiento actual que está modificando o vincule a un problema relevante. <!-- Describe el comportamiento actual que está modificando o vincule a un problema relevante.

View File

@ -6,7 +6,7 @@ import {CommitMessageConfig} from '../dev-infra/commit-message/config';
export const commitMessage: CommitMessageConfig = { export const commitMessage: CommitMessageConfig = {
maxLineLength: 120, maxLineLength: 120,
minBodyLength: 20, minBodyLength: 20,
minBodyLengthTypeExcludes: ['docs', 'upstream'], minBodyLengthTypeExcludes: ['docs'],
types: [ types: [
'build', 'build',
'ci', 'ci',
@ -18,7 +18,6 @@ export const commitMessage: CommitMessageConfig = {
'release', 'release',
'style', 'style',
'test', 'test',
'upstream',
], ],
scopes: [ scopes: [
'animations', 'animations',

View File

@ -67,25 +67,6 @@ version: 3
# Meta field that goes unused by PullApprove to allow for defining aliases to be # Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config. # used throughout the config.
meta: meta:
# The following groups have no file based conditions and will be initially `active` on all PRs
# - `global-approvers`
# - `global-docs-approvers`
# - `required-minimum-review`
#
# By checking the number of active/pending/rejected groups when these are excluded, we can determine
# if any other groups are matched.
#
# Note: Because all inactive groups start as pending, we are only checking pending and rejected active groups.
#
# Also note that the ordering of groups matters in this file. The only groups visible to the current
# one are those that appear above it.
no-groups-above-this-pending: &no-groups-above-this-pending
len(groups.active.pending.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
no-groups-above-this-rejected: &no-groups-above-this-rejected
len(groups.active.rejected.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
no-groups-above-this-active: &no-groups-above-this-active
len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved" can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved" can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
defaults: &defaults defaults: &defaults
@ -882,7 +863,6 @@ groups:
- *can-be-global-docs-approved - *can-be-global-docs-approved
- > - >
contains_any_globs(files, [ contains_any_globs(files, [
'aio/content/guide/roadmap.md',
'aio/content/marketing/**', 'aio/content/marketing/**',
'aio/content/images/bios/**', 'aio/content/images/bios/**',
'aio/content/images/marketing/**', 'aio/content/images/marketing/**',
@ -1110,7 +1090,6 @@ groups:
'dev-infra/**', 'dev-infra/**',
'docs/BAZEL.md', 'docs/BAZEL.md',
'docs/CARETAKER.md', 'docs/CARETAKER.md',
'docs/CODING_STANDARDS.md',
'docs/COMMITTER.md', 'docs/COMMITTER.md',
'docs/DEBUG.md', 'docs/DEBUG.md',
'docs/DEBUG_COMPONENTS_REPO_IVY.md', 'docs/DEBUG_COMPONENTS_REPO_IVY.md',
@ -1163,8 +1142,6 @@ groups:
public-api: public-api:
<<: *defaults <<: *defaults
conditions: conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved - *can-be-global-approved
- > - >
contains_any_globs(files, [ contains_any_globs(files, [
@ -1178,16 +1155,14 @@ groups:
]) ])
reviewers: reviewers:
users: users:
- AndrewKushnir
- IgorMinar - IgorMinar
- alxhub - alxhub
- atscott
- jelbourn - jelbourn
- petebacondarwin - petebacondarwin
- pkozlowski-opensource - pkozlowski-opensource
reviews: reviews:
request: 4 # Request reviews from four people request: -1 # request reviews from everyone
required: 3 # Require that three people approve required: 3 # require at least 3 approvals
reviewed_for: required reviewed_for: required
@ -1197,8 +1172,6 @@ groups:
size-tracking: size-tracking:
<<: *defaults <<: *defaults
conditions: conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved - *can-be-global-approved
- > - >
contains_any_globs(files, [ contains_any_globs(files, [
@ -1206,16 +1179,14 @@ groups:
]) ])
reviewers: reviewers:
users: users:
- AndrewKushnir
- IgorMinar - IgorMinar
- alxhub - alxhub
- atscott
- jelbourn - jelbourn
- petebacondarwin - petebacondarwin
- pkozlowski-opensource - pkozlowski-opensource
reviews: reviews:
request: 4 # Request reviews from four people request: -1 # request reviews from everyone
required: 2 # Require that two people approve required: 2 # require at least 2 approvals
reviewed_for: required reviewed_for: required
@ -1225,8 +1196,6 @@ groups:
circular-dependencies: circular-dependencies:
<<: *defaults <<: *defaults
conditions: conditions:
- *no-groups-above-this-pending
- *no-groups-above-this-rejected
- *can-be-global-approved - *can-be-global-approved
- > - >
contains_any_globs(files, [ contains_any_globs(files, [
@ -1234,11 +1203,9 @@ groups:
]) ])
reviewers: reviewers:
users: users:
- AndrewKushnir
- IgorMinar - IgorMinar
- alxhub
- atscott
- jelbourn - jelbourn
- josephperrott
- petebacondarwin - petebacondarwin
- pkozlowski-opensource - pkozlowski-opensource
@ -1260,10 +1227,7 @@ groups:
]) ])
reviewers: reviewers:
users: users:
- AndrewKushnir
- IgorMinar - IgorMinar
- alxhub
- atscott
- jelbourn - jelbourn
- josephperrott - josephperrott
- mhevery - mhevery
@ -1293,7 +1257,14 @@ groups:
# `global-approvers` can still approve PRs that match this `fallback` rule, # `global-approvers` can still approve PRs that match this `fallback` rule,
# but that should be an exception and not an expectation. # but that should be an exception and not an expectation.
conditions: conditions:
- *no-groups-above-this-active # The following groups have no file based conditions and will be initially `active` on all PRs
# - `global-approvers`
# - `global-docs-approvers`
# - `required-minimum-review`
#
# By checking the number of active groups when these are excluded, we can determine
# if any other groups are matched.
- len(groups.active.exclude("required-minimum-review").exclude("global-approvers").exclude("global-docs-approvers")) == 0
# When any of the `global-*` groups is approved, they cause other groups to deactivate. # When any of the `global-*` groups is approved, they cause other groups to deactivate.
# In those cases, the condition above would evaluate to `true` while in reality, only a global # In those cases, the condition above would evaluate to `true` while in reality, only a global
# approval has been provided. To ensure we don't activate the fallback group in such cases, # approval has been provided. To ensure we don't activate the fallback group in such cases,

View File

@ -1,95 +1,3 @@
<a name="10.1.0-next.4"></a>
# 10.1.0-next.4 (2020-08-04)
### Bug Fixes
* **common:** narrow `NgIf` context variables in template type checker ([#36627](https://github.com/angular/angular/issues/36627)) ([9c8bc4a](https://github.com/angular/angular/commit/9c8bc4a))
* **compiler:** mark `NgModuleFactory` construction as not side effectful ([#38147](https://github.com/angular/angular/issues/38147)) ([7f8c222](https://github.com/angular/angular/commit/7f8c222))
### Features
* **core:** rename async to waitForAsync to avoid confusing ([#37583](https://github.com/angular/angular/issues/37583)) ([8f07429](https://github.com/angular/angular/commit/8f07429))
* **core:** update reference and doc to change `async` to `waitAsync`. ([#37583](https://github.com/angular/angular/issues/37583)) ([8fbf40b](https://github.com/angular/angular/commit/8fbf40b))
<a name="10.0.8"></a>
## 10.0.8 (2020-08-04)
### Bug Fixes
* **compiler:** add PURE annotation to getInheritedFactory calls ([#38291](https://github.com/angular/angular/issues/38291)) ([03d8e31](https://github.com/angular/angular/commit/03d8e31))
* **compiler:** update unparsable character reference entity error messages ([#38319](https://github.com/angular/angular/issues/38319)) ([cea4678](https://github.com/angular/angular/commit/cea4678)), closes [#26067](https://github.com/angular/angular/issues/26067)
<a name="10.0.7"></a>
## 10.0.7 (2020-07-30)
### Bug Fixes
* **compiler:** Metadata should not include methods on Object.prototype ([#38292](https://github.com/angular/angular/issues/38292)) ([879ff08](https://github.com/angular/angular/commit/879ff08))
<a name="10.1.0-next.3"></a>
# 10.1.0-next.3 (2020-07-28)
### Bug Fixes
* **elements:** run strategy methods in correct zone ([#37814](https://github.com/angular/angular/issues/37814)) ([8df888d](https://github.com/angular/angular/commit/8df888d)), closes [#24181](https://github.com/angular/angular/issues/24181)
<a name="10.0.6"></a>
## 10.0.6 (2020-07-28)
### Bug Fixes
* **compiler:** share identical stylesheets between components in the same file ([#38213](https://github.com/angular/angular/issues/38213)) ([264950b](https://github.com/angular/angular/commit/264950b)), closes [#38204](https://github.com/angular/angular/issues/38204)
* **compiler-cli:** Add support for string literal class members ([#38226](https://github.com/angular/angular/issues/38226)) ([b1e7775](https://github.com/angular/angular/commit/b1e7775))
* **core:** `Attribute` decorator `attributeName` is mandatory ([#38131](https://github.com/angular/angular/issues/38131)) ([1c4fcce](https://github.com/angular/angular/commit/1c4fcce)), closes [#32658](https://github.com/angular/angular/issues/32658)
* **core:** unify the signature between ngZone and noopZone ([#37581](https://github.com/angular/angular/issues/37581)) ([d5264f5](https://github.com/angular/angular/commit/d5264f5))
<a name="10.1.0-next.2"></a>
# 10.1.0-next.2 (2020-07-22)
### Bug Fixes
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#35464](https://github.com/angular/angular/issues/35464)) ([737506e](https://github.com/angular/angular/commit/737506e)), closes [#30497](https://github.com/angular/angular/issues/30497)
### Features
* **common:** add ReadonlyMap in place of Map in keyValuePipe ([#37311](https://github.com/angular/angular/issues/37311)) ([3373453](https://github.com/angular/angular/commit/3373453)), closes [#37308](https://github.com/angular/angular/issues/37308)
* **forms:** AbstractControl to store raw validators in addition to combined validators function ([#37881](https://github.com/angular/angular/issues/37881)) ([ad7046b](https://github.com/angular/angular/commit/ad7046b))
* **localize:** allow duplicate messages to be handled during extraction ([#38082](https://github.com/angular/angular/issues/38082)) ([cf9a47b](https://github.com/angular/angular/commit/cf9a47b)), closes [#38077](https://github.com/angular/angular/issues/38077)
<a name="10.0.5"></a>
## 10.0.5 (2020-07-22)
### Bug Fixes
* **compiler:** properly associate source spans for implicitly closed elements ([#38126](https://github.com/angular/angular/issues/38126)) ([e80278c](https://github.com/angular/angular/commit/e80278c)), closes [#36118](https://github.com/angular/angular/issues/36118)
* **compiler-cli:** ensure file_system handles mixed Windows drives ([#38030](https://github.com/angular/angular/issues/38030)) ([dba4023](https://github.com/angular/angular/commit/dba4023)), closes [#36777](https://github.com/angular/angular/issues/36777)
* **core:** Allow modification of lifecycle hooks any time before bootstrap ([#38119](https://github.com/angular/angular/issues/38119)) ([14b4718](https://github.com/angular/angular/commit/14b4718)), closes [#30497](https://github.com/angular/angular/issues/30497)
* **core:** error due to integer overflow when there are too many host bindings ([#38014](https://github.com/angular/angular/issues/38014)) ([7b6e73c](https://github.com/angular/angular/commit/7b6e73c)), closes [#37876](https://github.com/angular/angular/issues/37876) [#37876](https://github.com/angular/angular/issues/37876)
* **core:** incorrectly validating properties on ng-content and ng-container ([#37773](https://github.com/angular/angular/issues/37773)) ([17ddab9](https://github.com/angular/angular/commit/17ddab9))
<a name="10.0.4"></a> <a name="10.0.4"></a>
## 10.0.4 (2020-07-15) ## 10.0.4 (2020-07-15)

View File

@ -1,72 +1,12 @@
# Código de Conducta # Contributor Code of Conduct
## Version 0.3b-angular
## 1. Propósito As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
“Prometemos brindar cortesía y respeto a cualquier persona involucrada en esta comunidad, sin importar el género con el que se identifique, su orientación sexual, limitación física, edad, raza, etnia, religión o nivel de conocimiento. Esperamos que cualquiera que desee contribuir en este proyecto brinde el mismo comportamiento” Communication through any of Angular's channels (GitHub, Gitter, IRC, mailing lists, Google+, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Bajo ese principio queremos enfocar esta comunidad, una comunidad de respeto por el otro, donde cualquiera que sienta pasión por Angular y desee involucrarse con cualquier tipo de actividad deberá ayudar a mantener una atmósfera de cortesía por el otro, respetando los pensamientos, acciones, ideales y propuestas del otro. We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
## 2. Comportamiento esperado If any member of the community violates this code of conduct, the maintainers of the Angular project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
- Evitar usar expresiones o gestos insultantes, humillantes o intimidatorios para referirnos a otros. If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [conduct@angular.io](mailto:conduct@angular.io).
- Ejercita la consideración y el respeto en tus comunicaciones y acciones.
- Absténte de adoptar una conducta y un lenguaje degradantes, discriminatorios, abusivos o acosadores.
- Evitar comportamientos que agredan las expresiones de identidad, género, creencias religiosas, acciones que lastimen o los aíslen a otros.
- Evitar comentarios sobre ideas políticas.
- No se toleran conductas físicas y actitudes dirigidas al descrédito personal, físico o emocional de cualquier persona.
- No se toleran chistes y comentarios excluyentes, sexistas y racistas.
- Se rechaza actitudes de hostigamiento.
- Se rechaza el acoso: este se entiende como cualquier tipo de comentario verbal que refuerce discriminación por género, identidad y expresión de género, orientación sexual, discapacidad, apariencia física, tamaño corporal, raza, edad o religión en contextos laborales o sociales.
- No se tolera el contacto físico y/o la atención sexual no deseada.
- Promover la igualdad de oportunidades de formación, educación, intercambio y retroalimentación antes, durante y después de los eventos.
- Intenta colaborar en lugar de generar conflicto.
- Sé consciente de tu entorno y de tus compañeros participantes. Alerta a líderes de la comunidad si notas una situación peligrosa, alguien en apuros, o violaciones de este Código de Conducta, incluso si parecen intrascendentes.
## 3. Comportamiento inaceptable
Comportamientos inaceptables incluyen: intimidación, acoso, abuso, discriminación, comunicación despectiva o degradante o acciones por cualquier participante en nuestra comunidad ya sea virtual, o en las comunicaciones uno-a-uno que se realizan en el contexto de la comunidad. Por favor ser respetuoso con todos.
El acoso incluye: comentarios nocivos o perjudiciales, verbales o escritos relacionados con el género, la orientación sexual, raza, religión, discapacidad; uso inadecuado de desnudos y / o imágenes sexuales en espacios públicos (incluyendo la presentación diapositivas); intimidación deliberada, acecho o seguimiento; fotografías o grabaciones acosadoras; interrupción sostenida de charlas y otros eventos; contacto físico inapropiado, y atención sexual no deseada.
## 4. Consecuencias de comportamiento inaceptable
Se espera que personas a quienes se les solicite que detengan su comportamiento inaceptable lo hagan de manera inmediata esto será válido para cualquier miembro de la comunidad.
En caso de presentarse una violación al código de conducta de manera repetida se tendrá cero tolerancia a este comportamiento por parte de los organizadores.
Si un miembro de la comunidad participa en una conducta inaceptable, los organizadores pueden tomar cualquier acción que consideren apropiada, hasta e incluyendo una prohibición temporal o expulsión permanente de la comunidad, sin previo aviso.
Priorizamos la seguridad de las personas marginadas sobre la comodidad de las personas privilegiadas. Nos reservamos el derecho de no actuar sobre las quejas relacionadas con:
- El "racismo inverso", "sexismo inverso" y "cisfobia".
- Comunicación razonable de límites, como "déjame en paz", "vete" o "no estoy discutiendo esto contigo".
- Al comunicarse en un "tono", no se encuentra agradable.
- Identificando comportamientos o suposiciones racistas, sexistas, cissexistas u opresivas.
## 5. ¿Qué hacer si se incumple el código de conducta?
Si eres víctima o testigo de una conducta inaceptable, o tienes cualquier inquietud, por favor comunícate con el equipo organizador lo antes posible:
- Mensaje directo: Michael Prentice (@splaktar)
- Mensaje directo: Jorge Cano (@jorgeucano)
- Mensaje directo: Andrés Villanueva (@villanuevand)
- Email: [soporte@angular.lat](mailto:soporte@angular.lat)
### ¿Qué pasa después?
- Una vez haya sido notificada el no cumplimiento de la norma, el equipo de liderazgo se reunirán y analizarán el caso.
- Los incidentes presentados se manejarán con discreción.
- Los organizadores se comunicarán con la persona que incumplió la norma y tomarán las medidas respectivas.
- Se realizará un acompañamiento a la persona agredida y se apoyará.
- Si el incidente se hace público, Angular Hispano no se hace responsable de los perjuicios que esto pueda ocasionar en el agresor o el agredido.
## 6. Alcance
Esperamos que todos los participantes de la comunidad (organizadores, voluntarios, contribuyentes pagados o de otro modo; patrocinadores; y otros invitados) se atengan a este Código de Conducta en todos los espacios virtuales y presenciales, así como en todas las comunicaciones de uno-a-uno pertinentes la comunidad.
### Colaboradores
- Alejandra Giraldo
- Vanessa Marely
- Mariano Alvarez
- Andrés Villanueva
- Michael Prentice
- javascript&friends
- Angular
- She Codes Angular
- Colombia dev

View File

@ -1,359 +0,0 @@
# Contributing to Angular
We would love for you to contribute to Angular and help make it even better than it is today!
As a contributor, here are the guidelines we would like you to follow:
- [Code of Conduct](#coc)
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
- [Coding Rules](#rules)
- [Commit Message Guidelines](#commit)
- [Signing the CLA](#cla)
## <a name="coc"></a> Code of Conduct
Help us keep Angular open and inclusive.
Please read and follow our [Code of Conduct][coc].
## <a name="question"></a> Got a Question or Problem?
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
Stack Overflow is a much better place to ask questions since:
- there are thousands of people willing to help on Stack Overflow
- questions and answers stay available for public viewing so your question/answer might help someone else
- Stack Overflow's voting system assures that the best answers are prominently visible.
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
## <a name="issue"></a> Found a Bug?
If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github].
Even better, you can [submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Missing a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed:
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Submitting an Issue
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
In order to reproduce bugs, we require that you provide a minimal reproduction.
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs.
Often, developers find coding problems themselves while preparing a minimal reproduction.
We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
You don't want to duplicate existing efforts.
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discussing the design upfront helps to ensure that we're ready to accept your work.
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without a signed CLA.
Make sure you author all contributed Git commits with email address associated with your CLA signature.
4. Fork the angular/angular repo.
5. Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
6. Create your patch, **including appropriate test cases**.
7. Follow our [Coding Rules](#rules).
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
10. Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
11. In GitHub, send a pull request to `angular:master`.
If we ask for changes via code reviews then:
* Make the required updates.
* Re-run the Angular test suites to ensure tests are still passing.
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell
git rebase master -i
git push -f
```
That's it! Thank you for your contribution!
#### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell
git push origin --delete my-fix-branch
```
* Check out the master branch:
```shell
git checkout master -f
```
* Delete the local branch:
```shell
git branch -D my-fix-branch
```
* Update your master with the latest upstream version:
```shell
git pull --ff upstream master
```
## <a name="rules"></a> Coding Rules
To ensure consistency throughout the source code, keep these rules in mind as you are working:
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
* All public API methods **must be documented**.
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
## <a name="commit"></a> Commit Message Format
*This specification is inspired and supersedes the [AngularJS commit message format][commit-message-format].*
We have very precise rules over how our Git commit messages must be formatted.
This format leads to **easier to read commit history**.
Each commit message consists of a **header**, a **body**, and a **footer**.
```
<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
The `body` is mandatory for all commits except for those of scope "docs".
When the body is required it must be at least 20 characters long.
The `footer` is optional.
Any line of the commit message cannot be longer than 100 characters.
#### <a href="commit-header"></a>Commit Message Header
```
<type>(<scope>): <short summary>
│ │ │
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
│ │
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
│ elements|forms|http|language-service|localize|platform-browser|
│ platform-browser-dynamic|platform-server|platform-webworker|
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
```
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
##### Type
Must be one of the following:
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
* **docs**: Documentation only changes
* **feat**: A new feature
* **fix**: A bug fix
* **perf**: A code change that improves performance
* **refactor**: A code change that neither fixes a bug nor adds a feature
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **test**: Adding missing tests or correcting existing tests
##### Scope
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
The following is the list of supported scopes:
* `animations`
* `bazel`
* `benchpress`
* `common`
* `compiler`
* `compiler-cli`
* `core`
* `elements`
* `forms`
* `http`
* `language-service`
* `localize`
* `platform-browser`
* `platform-browser-dynamic`
* `platform-server`
* `platform-webworker`
* `platform-webworker-dynamic`
* `router`
* `service-worker`
* `upgrade`
* `zone.js`
There are currently a few exceptions to the "use package name" rule:
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
* `changelog`: used for updating the release notes in CHANGELOG.md
* `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
* `migrations`: used for changes to the `ng update` migrations.
* `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
* `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
##### Summary
Use the summary field to provide a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end
#### Commit Message Body
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
#### Commit Message Footer
The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
```
BREAKING CHANGE: <breaking change summary>
<BLANK LINE>
<breaking change description + migration instructions>
<BLANK LINE>
<BLANK LINE>
Fixes #<issue number>
```
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
### Revert commits
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
The content of the commit message body should contain:
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
- a clear description of the reason for reverting the commit message.
## <a name="cla"></a> Signing the CLA
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
* For individuals, we have a [simple click-through form][individual-cla].
* For corporations, we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
* https://help.github.com/articles/setting-your-commit-email-address-in-git/
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
* https://help.github.com/articles/about-commit-email-addresses/
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
[angular-group]: https://groups.google.com/forum/#!forum/angular
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
[github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: http://jsfiddle.net
[plunker]: http://plnkr.co/edit
[runnable]: http://runnable.com
[stackoverflow]: http://stackoverflow.com/questions/tagged/angular

View File

@ -1,243 +1,243 @@
# Contribuye a Angular # Contributing to Angular
¡Nos encantaría que contribuyeras a Angular y que ayudaras a hacerlo aún mejor de lo que es hoy! We would love for you to contribute to Angular and help make it even better than it is today!
Como colaborador, estos son los lineamientos que nos gustaría que siguieras: As a contributor, here are the guidelines we would like you to follow:
- [Código de conducta](#coc) - [Code of Conduct](#coc)
- [¿Preguntas o problemas?](#question) - [Question or Problem?](#question)
- [_Issues_ y _bugs_](#issue) - [Issues and Bugs](#issue)
- [Solicitud de funcionalidades](#feature) - [Feature Requests](#feature)
- [Guía para la creación de issues y PRs](#submit) - [Submission Guidelines](#submit)
- [Reglas del código](#rules) - [Coding Rules](#rules)
- [Convención para el mensaje de los _commits_](#commit) - [Commit Message Guidelines](#commit)
- [Firma del Acuerdo de Licencia de Colaborador (CLA)](#cla) - [Signing the CLA](#cla)
## <a name="coc"></a> Código de conducta ## <a name="coc"></a> Code of Conduct
Ayúdanos a mantener Angular abierto e inclusivo. Help us keep Angular open and inclusive.
Por favor lee y sigue nuestro [Código de conducta][coc]. Please read and follow our [Code of Conduct][coc].
## <a name="question"></a> ¿Tienes alguna pregunta o problema? ## <a name="question"></a> Got a Question or Problem?
No abras *issues* para preguntas de soporte general ya que queremos mantener los *issues* de GitHub para reporte de *bugs* y solicitud de funcionalidades. Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests.
En su lugar, recomendamos utilizar [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) para hacer preguntas relacionadas con soporte. Al crear una nueva pregunta en Stack Overflow, asegúrate de agregar el etiqueta (tag) de `angular`. Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
Stack Overflow es mucho mejor para hacer preguntas ya que: Stack Overflow is a much better place to ask questions since:
- Hay miles de personas dispuestas a ayudar en preguntas y respuestas de Stack Overflow - there are thousands of people willing to help on Stack Overflow
que permanecen disponibles para el público, por lo que tu pregunta o respuesta podría ayudar a otra persona. - questions and answers stay available for public viewing so your question/answer might help someone else
- El sistema de votación de Stack Overflow asegura que las mejores respuestas sobresalgan y sean visibles. - Stack Overflow's voting system assures that the best answers are prominently visible.
Para ahorrar tu tiempo y el nuestro, cerraremos sistemáticamente todos los *issues* que sean solicitudes de soporte general y redirigiremos a las personas a Stack Overflow. To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
Si deseas chatear sobre alguna pregunta en tiempo real, puedes hacerlo a través de nuestro [canal de Gitter][gitter]. If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
## <a name="issue"></a> ¿Encontraste un Bug? ## <a name="issue"></a> Found a Bug?
Si encontraste un error en el código fuente, puedes ayudarnos [creando un *issue*](#submit-issue) en nuestro [repositorio de GitHub][github]. If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github].
O incluso mejor, puedes [crear un *Pull Request*](#submit-pr) con la solución. Even better, you can [submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> ¿Falta alguna funcionalidad? ## <a name="feature"></a> Missing a Feature?
Puedes solicitar una nueva funcionalidad [creando un *issue*](#submit-issue) en nuestro repositorio de GitHub. You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository.
Si deseas implementar una nueva funcionalidad, por favor considera el tamaño del cambio para determinar los pasos correctos para continuar: If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed:
* Para un **cambio significativo**, primero abre un *issue* y describe tu propuesta para que pueda ser discutida. * For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed.
Este proceso nos permite coordinar mejor nuestros esfuerzos, evitar trabajo duplicado y ayudarte a diseñar el cambio para que sea aceptado con éxito en el proyecto. This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
**Nota**: Agregar un nuevo tema a la documentación o reescribir significativamente un tema, también cuenta como *cambio significativo*. **Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
* **Cambios pequeños** pueden ser elaborados y directamente [creados como un _pull request_](#submit-pr). * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="submit"></a> Guía para la creación de issues y PRs ## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Creación de _issues_ ### <a name="submit-issue"></a> Submitting an Issue
Antes de crear un *issue*, por favor busca en el el *issue tracker*, quizá un *issue* para tu problema ya existe y la discusión puede informarte sobre soluciones alternativas disponibles. Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
Queremos solucionar todos los problemas lo antes posible, pero antes de corregir un bug necesitamos reproducirlo y confirmarlo. We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it.
Para reproducir errores, requerimos que proporciones una reproducción mínima. In order to reproduce bugs, we require that you provide a minimal reproduction.
Tener un escenario reproducible mínimo nos brinda una gran cantidad de información importante sin tener que ir y venir con preguntas adicionales. Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
Una reproducción mínima nos permite confirmar rápidamente un bug (o señalar un problema de código), así también confirmar que estamos solucionando el problema correcto. A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
Requerimos una reproducción mínima para ahorrar tiempo a los encargados del mantenimiento y en última instancia, poder corregir más bugs. We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs.
A menudo los desarrolladores encuentran problemas de código mientras preparan una reproducción mínima. Often, developers find coding problems themselves while preparing a minimal reproduction.
Entendemos que a veces puede ser difícil extraer porciones esenciales de código de un código más grande, pero realmente necesitamos aislar el problema antes de poder solucionarlo. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
Desafortunadamente no podemos investigar/corregir errores sin una reproducción mínima, por lo que si no tenemos tu retroalimentación del bug, vamos a cerrar el *issue* ya que no tiene suficiente información para reproducirse. Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
Puedes presentar nuevos *issues* seleccionando nuestra [plantilla de _issues_](https://github.com/angular/angular/issues/new/choose) y complentando la plantilla. You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
### <a name="submit-pr"></a> Creación de un Pull Requests (PR) ### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Antes de crear tu Pull Request (PR) considera los siguientes lineamientos: Before you submit your Pull Request (PR) consider the following guidelines:
1. Busca en [GitHub](https://github.com/angular/angular/pulls) PRs que estén abiertos o cerrados y que estén relacionados con el que vas a crear. 1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
No deseas duplicar los esfuerzos existentes. You don't want to duplicate existing efforts.
2. Asegúrate de que el PR describa el problema que estás solucionando o que documente el diseño de la funcionalidad que deseas agregar. 2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
Discutir el diseño por adelantado ayuda a garantizar que estemos listos para aceptar tu trabajo. Discussing the design upfront helps to ensure that we're ready to accept your work.
3. Por favor firma nuestro [Acuerdo de Licencia de Colaborador (CLA)](#cla) antes de crear PRs. 3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
No podemos aceptar el código sin el Acuerdo de Licencia de Colaborador (CLA) firmado. We cannot accept code without a signed CLA.
Asegúrate de crear todas las contribuciones de Git con la dirección de correo electrónico asociada con tu firma del Acuerdo de Licencia de Colaborador (CLA). Make sure you author all contributed Git commits with email address associated with your CLA signature.
4. Haz *fork* del repositorio angular/angular. 4. Fork the angular/angular repo.
5. Haz tus cambios en una nueva rama de Git: 5. Make your changes in a new git branch:
```shell ```shell
git checkout -b my-fix-branch master git checkout -b my-fix-branch master
``` ```
6. Crea tu correción, **incluyendo casos de prueba apropiados**. 6. Create your patch, **including appropriate test cases**.
7. Sigue nuestras [Reglas de código](#rules). 7. Follow our [Coding Rules](#rules).
8. Ejecuta todo el conjunto de pruebas de Angular, tal como está descrito en la [documentación del desarrollador][dev-doc], y asegúrate de que todas las pruebas pasen. 8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass.
9. Crea un commit de tus cambios utilizando un mensaje de commit descriptivo que siga nuestra [convención para el mensaje de los commits](#commit). 9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit).
Es necesario cumplir con estas convenciones porque las notas de las versiones se generan automáticamente a partir de estos mensajes. Adherence to these conventions is necessary because release notes are automatically generated from these messages.
```shell ```shell
git commit -a git commit -a
``` ```
Nota: la opción de la línea de comandos de Git `-a` automaticamente hará "add" y "rm" a los archivos editados. Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
10. Haz push de tu rama a GitHub: 10. Push your branch to GitHub:
```shell ```shell
git push origin my-fix-branch git push origin my-fix-branch
``` ```
11. En GitHub, crea un pull request a `angular:master`. 11. In GitHub, send a pull request to `angular:master`.
Si solicitamos cambios a través de revisiones de código, sigue las siguientes indicaciones: If we ask for changes via code reviews then:
* Haz los cambios requeridos. * Make the required updates.
* Ejecuta nuevamente el conjunto de pruebas de Angular para asegurar que todas las pruebas aún están pasando. * Re-run the Angular test suites to ensure tests are still passing.
* Haz rebase de tu rama a la rama master y haz push con la opción `-f` a tu repositorio de Github (esto actualizará tu Pull Request): * Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
```shell ```shell
git rebase master -i git rebase master -i
git push -f git push -f
``` ```
¡Es todo! ¡Muchas gracias por tu contribución! That's it! Thank you for your contribution!
#### Después del merge de tu pull request #### After your pull request is merged
Después de que se hizo merge de tu pull request, puedes eliminar de forma segura tu rama y hacer pull de los cambios del repositorio principal (upstream): After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository:
* Elimina la rama remota en GitHub a través de la interfaz de usuario web de GitHub o en tu línea de comandos de la siguiente manera: * Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
```shell ```shell
git push origin --delete my-fix-branch git push origin --delete my-fix-branch
``` ```
* Muévete a la rama master: * Check out the master branch:
```shell ```shell
git checkout master -f git checkout master -f
``` ```
* Elimina tu rama local: * Delete the local branch:
```shell ```shell
git branch -D my-fix-branch git branch -D my-fix-branch
``` ```
* Actualiza tu rama master con la última versión del fork (upstream): * Update your master with the latest upstream version:
```shell ```shell
git pull --ff upstream master git pull --ff upstream master
``` ```
## <a name="rules"></a> Reglas del código ## <a name="rules"></a> Coding Rules
Para garantizar la coherencia en todo el código fuente, ten en cuenta estas reglas mientras trabajas: To ensure consistency throughout the source code, keep these rules in mind as you are working:
* Todas las funcionalidades o solución de bugs **deben ser probadas** por una o más pruebas (pruebas unitarias). * All features or bug fixes **must be tested** by one or more specs (unit-tests).
* Todos los métodos públicos del API **deben ser documentados**. * All public API methods **must be documented**.
* Seguimos la [guía de estilo JavaScript de Google][js-style-guide], pero cada línea no debe exceder **100 caracteres**. * We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**.
Un formateador automatizado está disponible, revisar [DEVELOPER.md](docs/DEVELOPER.md#clang-format). An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
## <a name="commit"></a> Formato para el mensaje de los commits ## <a name="commit"></a> Commit Message Format
*Esta especificación está inspirada y reemplaza el [Formato de mensaje de commits de AngularJS][commit-message-format].* *This specification is inspired and supersedes the [AngularJS commit message format][commit-message-format].*
Tenemos reglas muy precisas sobre cómo deben formatearse nuestros mensajes de los commits de Git. We have very precise rules over how our Git commit messages must be formatted.
Este formato permite tener **un historial de commits más facil de leer**. This format leads to **easier to read commit history**.
Cada mensaje de un commit consta del **header**, el **body**, y el **footer**. Each commit message consists of a **header**, a **body**, and a **footer**.
``` ```
<header> <header>
<LINEA VACIA> <BLANK LINE>
<body> <body>
<LINEA VACIA> <BLANK LINE>
<footer> <footer>
``` ```
El `header` es obligatorio y debe ajustarse al formato del [mensaje del header del commit](#commit-header). The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format.
El `body` es obligatorio para todos los commits excepto los que tenga scope "docs". The `body` is mandatory for all commits except for those of scope "docs".
Cuando el body es requerido debe tener al menos 20 caracteres. When the body is required it must be at least 20 characters long.
El `footer` es opcional. The `footer` is optional.
Cualquier línea del mensaje del commit no puede tener más de 100 caracteres. Any line of the commit message cannot be longer than 100 characters.
#### <a href="commit-header"></a>Mensaje del header del commit #### <a href="commit-header"></a>Commit Message Header
``` ```
<tipo>(<alcance>): <resumen> <type>(<scope>): <short summary>
│ │ │ │ │ │
│ │ └─⫸ Resumen corto escrito en modo imperativo, tiempo presente. Sin mayúsculas. Sin punto final. │ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
│ │ │ │
│ └─⫸ Alcance del commit: animations|bazel|benchpress|common|compiler|compiler-cli|core| │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
│ elements|forms|http|language-service|localize|platform-browser| │ elements|forms|http|language-service|localize|platform-browser|
│ platform-browser-dynamic|platform-server|platform-webworker| │ platform-browser-dynamic|platform-server|platform-webworker|
│ platform-webworker-dynamic|router|service-worker|upgrade|zone.js| │ platform-webworker-dynamic|router|service-worker|upgrade|zone.js|
│ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve │ packaging|changelog|dev-infra|docs-infra|migrations|ngcc|ve
└─⫸ Tipo de commit: build|ci|docs|feat|fix|perf|refactor|style|test └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
``` ```
El `<tipo>` y `<resumen>` son obligatorios, el `(<alcance>)` es opcional. The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
##### Tipo ##### Type
El tipo debe ser uno de los siguientes: Must be one of the following:
* **build**: cambios que afectan el sistema de compilación o dependencias externas (ejemplos de scopes: gulp, broccoli, npm) * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
* **ci**: cambios en nuestros archivos y scripts de configuración de CI (ejemplos de scopes: Circle, BrowserStack, SauceLabs) * **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
* **docs**: cambios en la documentación * **docs**: Documentation only changes
* **feat**: una nueva funcionalidad * **feat**: A new feature
* **fix**: una solución de un bug * **fix**: A bug fix
* **perf**: un cambio de código que mejora el rendimiento. * **perf**: A code change that improves performance
* **refactor**: un cambio de código que no corrige ningún error ni agrega ninguna funcionalidad * **refactor**: A code change that neither fixes a bug nor adds a feature
* **style**: cambios que no afectan el significado del código (espacios en blanco, formato, falta de punto y coma, etc.) * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **test**: se agregan pruebas faltantes o se corrigen pruebas existentes * **test**: Adding missing tests or correcting existing tests
##### Alcance ##### Scope
El alcance debe ser el nombre del paquete npm afectado (tal como lo percibe la persona que lee el registro de cambios generado a partir de los mensajes de commit). The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
La siguiente es la lista de alcances permitidos: The following is the list of supported scopes:
* `animations` * `animations`
* `bazel` * `bazel`
@ -261,73 +261,80 @@ La siguiente es la lista de alcances permitidos:
* `upgrade` * `upgrade`
* `zone.js` * `zone.js`
Actualmente hay algunas excepciones a la regla "usar el nombre de paquete": There are currently a few exceptions to the "use package name" rule:
* `packaging`: usado para cambios que cambian el diseño de los paquetes de npm en todos nuestros paquetes. Ejemplos: cambios de la ruta públic, package.json cambios hechos a todos los paquetes, cambios a archivos o formatos d.ts, cambios a bundles, etc. * `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
* `changelog`: utilizado para actualizar las notas de la versión en CHANGELOG.md * `changelog`: used for updating the release notes in CHANGELOG.md
* `dev-infra`: utilizado para cambios relacionados con dev-infra dentro de los directorios /scripts, /tools y /dev-infra * `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
* `docs-infra`: utilizado para cambios relacionados con la documentación (angular.io) dentro del directorio /aio del repositorio * `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
* `migrations`: utilizado para los cambios en las migraciones `ng update`. * `migrations`: used for changes to the `ng update` migrations.
* `ngcc`: usado para los cambios del [Compilador de compatibilidad de Angular](./packages/compiler-cli/ngcc/README.md) * `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
* `ve`: utilizado para cambios específicos de ViewEngine (legacy compiler/renderer). * `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
* alcance vacío: útil para cambios de `style`, `test` y `refactor` que se realizan en todos los paquetes (ejemplo: `style: add missing semicolons`) y para cambios de la documentación que no están relacionados a un paquete en específico(ejemplo: `docs: corrige error gramatical en el tutorial`). * none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
##### Resumen ##### Summary
Usa el campo resumen para proporcionar una descripción breve del cambio: Use the summary field to provide a succinct description of the change:
* usa el modo imperativo, tiempo presente: "cambia" no "cambió" o "cambios" * use the imperative, present tense: "change" not "changed" nor "changes"
* no debe de contener ninguna letra mayúscula * don't capitalize the first letter
* no debe de conter punto (.) al final * no dot (.) at the end
#### Mensaje del cuerpo del commit #### Commit Message Body
Tal como en el resumen, usa el modo imperativo, tiempo presente: "cambia" no "cambió" o "cambios". Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
Explica la razón del cambio en el el mensaje del cuerpo del commit. Este mensaje de confirmación debe explicar _por qué_ está realizando el cambio. Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
Puedes incluir una comparación del comportamiento anterior con el nuevo comportamiento para ilustrar el impacto del cambio. You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
#### Mensaje del footer del commit #### Commit Message Footer
El footer puede contener información sobre cambios significativos y también es el lugar para hacer referencia a issues de GitHub, tickets de Jira y otros PRs que están relacionados con el commit. The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
``` ```
CAMBIO SIGNIFICATIVO: <resumen del cambio significativo> BREAKING CHANGE: <breaking change summary>
<LINEA VACIA> <BLANK LINE>
<descripción del cambio significativo + instrucciones para la migración> <breaking change description + migration instructions>
<LINEA VACIA> <BLANK LINE>
<LINEA VACIA> <BLANK LINE>
Fix #<issue número> Fixes #<issue number>
``` ```
La sección de cambios significativos debería comenzar con la frase "CAMBIO SIGNIFICATIVO: " seguido de un resumen del cambio significativo, una línea en blanco y una descripción detallada del cambio significativo que también incluya instrucciones de migración. Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
### Revirtiendo commits ### Revert commits
Si el commit revierte un commit previo, el commit debería comenzar con `revert: `, seguido por el header del commit revertido. If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
El contenido del mensaje del commit debería contener: The content of the commit message body should contain:
- Información sobre el SHA del commit que se revierte en el siguiente formato: `Esto revierte el commit <SHA>`, - information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
- Una descripción clara de la razón para revertir el mensaje del _commit_. - a clear description of the reason for reverting the commit message.
## <a name="cla"></a> Firma del Acuerdo de Licencia de Colaborador (CLA) ## <a name="cla"></a> Signing the CLA
Por favor firma nuestro Acuerdo de Licencia de Colaborador (CLA) cuando creas tu primer pull request. Para que cualquier cambio de código sea aceptado, el Acuerdo de Licencia de Colaborador (CLA) debe ser firmado. Es un proceso rápido con nuestro CLA assistant que está integrado con nuestro CI. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
Los siguientes documentos pueden ayudarte a resolver problemas con cuentas de GitHub y múltiples direcciones de correo electrónico: * For individuals, we have a [simple click-through form][individual-cla].
* For corporations, we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
* https://help.github.com/articles/setting-your-commit-email-address-in-git/ * https://help.github.com/articles/setting-your-commit-email-address-in-git/
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean * https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean

View File

@ -1,26 +0,0 @@
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/workflows/angular/tree/master)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://www.npmjs.com/@angular/core)
# Angular
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
## Quickstart
[Get started in 5 minutes][quickstart].
## Changelog
[Learn about the latest improvements][changelog].
## Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
[quickstart]: https://angular.io/start
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
[ng]: https://angular.io

View File

@ -1,25 +1,25 @@
# Angular en español [![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/workflows/angular/tree/master)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://www.npmjs.com/@angular/core)
Angular es una plataforma de desarrollo para construir aplicaciones web y móviles que usa
TypeScript/JavaScript y otros lenguajes de programación.
## ¿Quieres ayudar? # Angular
### Documentación en español Angular es una plataforma de desarrollo para construir aplicaciones web y móviles que usa TypeScript/JavaScript y otros lenguajes de programación.
¿Quieres mejorar la documentación? ¡Excelente! Lee nuestras pautas para
[colaborar](CONTRIBUTING.md) y luego revisa algunos de nuestras
[issues](https://github.com/angular-hispano/angular/issues).
### El framework
La colaboración para corregir errores y agregar funciones en el framework debe realizarse en inglés a través
del repositorio [angular/angular](https://github.com/angular/angular) upstream.
## Guía rápida ## Guía rápida
[Comienza a usarlo en 5 minutos](https://docs.angular.lat/start). [Comienza a usarlo en 5 minutos][quickstart].
## Registro de cambios (Changelog) ## Registro de cambios (Changelog)
[Últimas mejoras realizadas](CHANGELOG.md). [Últimas mejoras realizadas][changelog].
## ¿Quieres ayudar?
¿Quieres encontrar fallos, colaborar con código, o mejorar la documentación? ¡Excelente! Lee nuestras
pautas para [colaborar][contributing] y luego revisa algunos de nuestras incidencias (issues) en [ayuda comunitaria](https://github.com/angular-hispano/angular/labels/ayuda%20comunitaria).
[contributing]: https://github.com/angular-hispano/angular/blob/master/CONTRIBUTING.md
[quickstart]: https://docs.angular.lat/start
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md

View File

@ -1,131 +1,140 @@
# Proyecto de documentación Angular (https://docs.angular.lat) # Angular documentation project (https://angular.io)
Todo en esta carpeta es parte del proyecto de documentación. Esto incluye: Everything in this folder is part of the documentation project. This includes
* El sitio web para mostrar la documentación * the web site for displaying the documentation
* La configuración de dgeni para convertir los archivos de origen a archivos renderizados que se pueden vizualizar en el sitio web. * the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
* Las herramientas para establecer ejemplos para el desarrollo; y generar archivos en tiempo real y archivos zip desde los ejemplos. * the tooling for setting up examples for development; and generating live-example and zip files from the examples.
## Tareas de desarrollador ## Developer tasks
Nosotros usamos [Yarn](https://yarnpkg.com) para gestionar las dependencias y crear tareas de compilación. We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
Debes ejecutar todas estas tareas desde la carpeta `angular/aio`. You should run all these tasks from the `angular/aio` folder.
Aquí están las tareas más importantes que podrías necesitar usar: Here are the most important tasks you might need to use:
* `yarn` - instalar todas las dependencias. * `yarn` - install all the dependencies.
* `yarn setup` - instalar todas las dependencias, boilerplate, stackblitz, zips y ejecuta dgeni en los documentos. * `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
* `yarn setup-local` - igual que `setup`, pero crea los paquetes de Angular a partir del código y usa estas versiones construidas localmente (en lugar de las recuperadas desde npm) para aio y ejemplos de documentos boilerplate. * `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate.
* `yarn build` - crear una compilación de producción de la aplicación (después de instalar dependencias, boilerplate, etc). * `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - igual que `build`, pero usa `setup-local` en lugar de `setup`. * `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn build-local-with-viewengine` - igual que `build-local`, pero además también enciende el modo `ViewEngine` (pre-Ivy) en aio. * `yarn build-local-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` (pre-Ivy) mode in aio.
(Nota: Encender el modo `ViewEngine` en ejemplos de documentos, ver `yarn boilerplate:add:viewengine` abajo.) (Note: To turn on `ViewEngine` mode in docs examples, see `yarn boilerplate:add:viewengine` below.)
* `yarn start` - ejecutar un servidor web de desarrollo que observa los archivos; luego crea el doc-viewer y vuelve a cargar la página, según sea necesario. * `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - ejecutar ambos, el `docs-watch` y `start` en la misma consola. * `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
* `yarn lint` - comprobar que el código del doc-viewer sigue nuestras reglas de estilo. * `yarn lint` - check that the doc-viewer code follows our style rules.
* `yarn test` - observar todos los archivos de origen, para el doc-viewer, y ejecuta todas las pruebas unitarias cuando haya algún cambio. * `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
* `yarn test --watch=false` -ejecutar todas las pruebas unitarias una sola vez. * `yarn test --watch=false` - run all the unit tests once.
* `yarn e2e` - ejecutar todas las pruebas de e2e para el doc-viewer. * `yarn e2e` - run all the e2e tests for the doc-viewer.
* `yarn docs` - generar toda la documentación desde los archivos fuente. * `yarn docs` - generate all the docs from the source files.
* `yarn docs-watch` - observar el código Angular, los archivos de documentación y ejecutar un 'doc-gen' en corto circuito para los documentos que fueron cambiados. * `yarn docs-watch` - watch the Angular source and the docs files and run a short-circuited doc-gen for the docs that changed.
* `yarn docs-lint` - comprobar que el código del documento generado sigue nuestras reglas de estilo. * `yarn docs-lint` - check that the doc gen code follows our style rules.
* `yarn docs-test` - ejecutar las pruebas unitarias para el código de generación de doc. * `yarn docs-test` - run the unit tests for the doc generation code.
* `yarn boilerplate:add` - generar todo el código boilerplate para los ejemplos, para que puedan ejecutarse localmente. * `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
* `yarn boilerplate:add:viewengine` - igual que `boilerplate:add` pero también enciende el modo `ViewEngine` (pre-Ivy). * `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
* `yarn boilerplate:remove` - eliminar todo el código boilerplate que fue añadido a través`yarn boilerplate:add`. * `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn generate-stackblitz` - generar los archivos stackblitz que están usados por la etiqueta `live-example` en documentos. * `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generar los archivos zip desde los ejemplos. Zip está disponible a través de la etiqueta `live-example` en los documentos. * `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn example-e2e` - ejecutar todas las pruebas e2e para ejemplos. Opciones disponibles: * `yarn example-e2e` - run all e2e tests for examples. Available options:
- `--setup`: generar boilerplate, forzar la actualización del controlador web y otras configuraciones, luego ejecutar las pruebas. - `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
- `--local`: ejecutar pruebas e2e con la versión local de Angular contenida en la carpeta "dist". - `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
_Requiere `--setup` para que surta efecto._ _Requires `--setup` in order to take effect._
- `--viewengine`: ejecutar pruebas e2e en modo `ViewEngine` (pre-Ivy). - `--viewengine`: run e2e tests in `ViewEngine` (pre-Ivy) mode.
- `--filter=foo`: limitar pruebas e2e a las que contienen la palabra "foo". - `--filter=foo`: limit e2e tests to those containing the word "foo".
> **Nota para usuarios Windows** > **Note for Windows users**
> >
> Configurar los ejemplos implica crear algunos [enlaces simbólicos](https://es.wikipedia.org/wiki/Enlace_simb%C3%B3lico) (ver [Aquí](./tools/examples/README.md#symlinked-node_modules) para más detalles). En Windows, esto requiere tener [Habilitado el Modo de desarrollador ](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (compatible con Windows 10 o más reciente) o ejecutar los comandos de configuración cómo administrador. > Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
> >
> Los comandos afectados son: > The affected commands are:
> - `yarn setup` / `yarn setup-*` > - `yarn setup` / `yarn setup-*`
> - `yarn build` / `yarn build-*` > - `yarn build` / `yarn build-*`
> - `yarn boilerplate:add` > - `yarn boilerplate:add`
> - `yarn example-e2e --setup` > - `yarn example-e2e --setup`
## Usando ServiceWorker localmente ## Using ServiceWorker locally
Ejecutando `yarn start` (incluso cuando se apunta explícitamente al modo de producción) no configura el Running `yarn start` (even when explicitly targeting production mode) does not set up the
ServiceWorker. Si quieres probar el ServiceWorker localmente, puedes usar `yarn build` y luego ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
ejecutar los archivos en `dist/` con `yarn http-server dist -p 4200`. serve the files in `dist/` with `yarn http-server dist -p 4200`.
## Guía de autoría ## Guide to authoring
Existen dos tipos de contenido en la documentación:
* **Documentación de API**: descripciones de los módulos, clases, interfaces, decoradores, etc que son parte de la plataforma Angular. There are two types of content in the documentation:
La documentacion de API está generada directamente desde el código fuente.
El código fuente está contenido en archivos TypeScript , almacenados en la carpeta `angular/packages`.
Cada elemento de la API puede tener un comentario anterior, el cual contiene etiquetas y contenido de estilos JSDoc.
El contenido está escrito en markdown.
* **Otro contenido**: guias, tutoriales, y otro material de marketing. * **API docs**: descriptions of the modules, classes, interfaces, decorators, etc that make up the Angular platform.
Todos los demás contenidos se escriben utilizando markdown en archivos de texto, ubicados en la carpeta `angular/aio/content`. API docs are generated directly from the source code.
Más específicamente, hay subcarpetas que contienen tipos particulares de contenido: guías, tutoriales y marketing. The source code is contained in TypeScript files, located in the `angular/packages` folder.
Each API item may have a preceding comment, which contains JSDoc style tags and content.
The content is written in markdown.
* **Ejempos de código**: los ejemplos de código deben ser comprobables para garantizar su precisión. * **Other content**: guides, tutorials, and other marketing material.
Además, nuestros ejemplos tienen un aspecto específico y permiten al usuario copiar el código fuente. Para mayor All other content is written using markdown in text files, located in the `angular/aio/content` folder.
ejemplos se representan en una interfaz con pestañas (e.g. template, HTML, y TypeScript en pestañas separadas). Adicionalmente, algunos son ejemplos en acción, que proporcionan enlaces donde se puede editar el código, ejecutar, y/o descargar. Para obtener más detalles sobre cómo trabajar con ejemplos de código, lea los [fragmentos de código](https://docs.angular.lat/guide/docs-style-guide#code-snippets), [código fuente de marcado ](https://docs.angular.lat/guide/docs-style-guide#source-code-markup), y [ ejemplos en acción ](https://docs.angular.lat/guide/docs-style-guide#live-examples) paginas de los [ autores de guías de estilo ](https://docs.angular.lat/guide/docs-style-guide). More specifically, there are sub-folders that contain particular types of content: guides, tutorial and marketing.
Usamos la herramienta [dgeni](https://github.com/angular/dgeni) para convertir estos archivos en docs que se pueden ver en el doc-viewer. * **Code examples**: code examples need to be testable to ensure their accuracy.
Las [guías de estilo para Autores](https://docs.angular.lat/guide/docs-style-guide) prescriben pautas para Also, our examples have a specific look and feel and allow the user to copy the source code. For larger
escribir páginas de guía, explica cómo usar la documentación de clases y componentes, y cómo marcar el código fuente para producir fragmentos de código. examples they are rendered in a tabbed interface (e.g. template, HTML, and TypeScript on separate
tabs). Additionally, some are live examples, which provide links where the code can be edited, executed, and/or downloaded. For details on working with code examples, please read the [Code snippets](https://angular.io/guide/docs-style-guide#code-snippets), [Source code markup](https://angular.io/guide/docs-style-guide#source-code-markup), and [Live examples](https://angular.io/guide/docs-style-guide#live-examples) pages of the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
### Generando documentos completos We use the [dgeni](https://github.com/angular/dgeni) tool to convert these files into docs that can be viewed in the doc-viewer.
La principal tarea para generar los documentos es `yarn docs`. Esto procesará todos los archivos fuente (API y otros), extrayendo la documentación y generando archivos JSON que pueden ser consumidos por el doc-viewer. The [Authors Style Guide](https://angular.io/guide/docs-style-guide) prescribes guidelines for
writing guide pages, explains how to use the documentation classes and components, and how to markup sample source code to produce code snippets.
### Generación parcial de doc para editores ### Generating the complete docs
La generación completa de documentos puede llevar hasta un minuto. Eso es demasiado lento para la creación y edición eficiente de documentos. The main task for generating the docs is `yarn docs`. This will process all the source files (API and other),
extracting the documentation and generating JSON files that can be consumed by the doc-viewer.
Puedes ealizar pequeños cambios en un editor inteligente que muestre un markdown con formato: ### Partial doc generation for editors
>En VS Code, _Cmd-K, V_ abre la vista previa de markdown en el panel lateral; _Cmd-B_ alterna la barra izquierda
Puedes también mirar los cambios que se muestran correctamente en el doc-viewer Full doc generation can take up to one minute. That's too slow for efficient document creation and editing.
con un tiempo de ciclo rápido de edición / visualización.
Para este propósito, usa la tarea `yarn docs-watch`, que observa los cambios en los archivos de origen y solo vuelve a procesar los archivos necesarios para generar los documentos relacionados con el archivo que ha cambiado. You can make small changes in a smart editor that displays formatted markdown:
Dado que esta tarea tiene accesos directos, es mucho más rápido (a menudo menos de 1 segundo) pero no producirá contenido de fidelidad completa. Por ejemplo, los enlaces a otros documentoss y ejemplos de código pueden no mostrarse correctamente. Esto se nota especialmente en los enlaces a otros documentos y en los ejemplos incrustados, que no siempre se representan correctamente. >In VS Code, _Cmd-K, V_ opens markdown preview in side pane; _Cmd-B_ toggles left sidebar
La configuración general es la siguiente: You also want to see those changes displayed properly in the doc viewer
with a quick, edit/view cycle time.
* Abrir una terminal, estar seguro que las dependencias están instaladas; ejecutar una generación inicial del doc; luego iniciar el doc-viewer: For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
re-processes the files necessary to generate the docs that are related to the file that has changed.
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
fidelity content. For example, links to other docs and code examples may not render correctly. This is
most particularly noticed in links to other docs and in the embedded examples, which may not always render
correctly.
The general setup is as follows:
* Open a terminal, ensure the dependencies are installed; run an initial doc generation; then start the doc-viewer:
```bash ```bash
yarn setup yarn setup
yarn start yarn start
``` ```
* Abrir una segunda terminal e iniciar el observador de documentos. * Open a second terminal and start watching the docs
```bash ```bash
yarn docs-watch yarn docs-watch
``` ```
>Alternativamente, prueba el comando fusionado `serve-and-sync` que crea, observa y ejecuta en la misma ventana de la terminal >Alternatively, try the consolidated `serve-and-sync` command that builds, watches and serves in the same terminal window
```bash ```bash
yarn serve-and-sync yarn serve-and-sync
``` ```
* Abre un navegador con la siguiente dirección https://localhost:4200/ y navega hasta el documento en el que quieras trabajar. * Open a browser at https://localhost:4200/ and navigate to the document on which you want to work.
Puedes automáticamente abrir el navegador utilizando `yarn start -o` en la primera terminal. You can automatically open the browser by using `yarn start -o` in the first terminal.
* Realiza cambios en la página de documentación asociada o en los archivos de ejemplo. Cada vez que un archivo es guardado, la documentación se regenerará, la aplicación se reconstruirá y la página se volverá a cargar. * Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
be regenerated, the app will rebuild and the page will reload.
*Si recibes un error de compilación acerca de los ejemplos o cualquier otro error, asegúrate de consultar las * If you get a build error complaining about examples or any other odd behavior, be sure to consult
[guías de estilo para Autores](https://angular.io/guide/docs-style-guide) para más información. the [Authors Style Guide](https://angular.io/guide/docs-style-guide).

View File

@ -18,7 +18,6 @@
**/src/karma.conf.js **/src/karma.conf.js
**/.angular-cli.json **/.angular-cli.json
**/.editorconfig **/.editorconfig
**/.gitignore
**/angular.json **/angular.json
**/tsconfig.json **/tsconfig.json
**/bs-config.e2e.json **/bs-config.e2e.json

View File

@ -1,3 +1,5 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Accessibility example e2e tests', () => { describe('Accessibility example e2e tests', () => {
@ -6,11 +8,11 @@ describe('Accessibility example e2e tests', () => {
browser.get(''); browser.get('');
}); });
it('should display Accessibility Example', () => { it('should display Accessibility Example', function () {
expect(element(by.css('h1')).getText()).toEqual('Accessibility Example'); expect(element(by.css('h1')).getText()).toEqual('Accessibility Example');
}); });
it('should take a number and change progressbar width', () => { it('should take a number and change progressbar width', function () {
element(by.css('input')).sendKeys('16'); element(by.css('input')).sendKeys('16');
expect(element(by.css('input')).getAttribute('value')).toEqual('016'); expect(element(by.css('input')).getAttribute('value')).toEqual('016');
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px'); expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');

View File

@ -1,4 +1,3 @@
// tslint:disable: no-host-metadata-property
// #docregion progressbar-component // #docregion progressbar-component
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';

View File

@ -1,18 +1,20 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('AngularJS to Angular Quick Reference Tests', () => { describe('AngularJS to Angular Quick Reference Tests', function () {
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should display no poster images after bootstrap', () => { it('should display no poster images after bootstrap', function () {
testImagesAreDisplayed(false); testImagesAreDisplayed(false);
}); });
it('should display proper movie data', () => { it('should display proper movie data', function () {
// We check only a few samples // We check only a few samples
const expectedSamples: any[] = [ let expectedSamples: any[] = [
{row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true}, {row: 0, column: 0, element: 'img', attr: 'src', value: 'images/hero.png', contains: true},
{row: 0, column: 2, value: 'Celeritas'}, {row: 0, column: 2, value: 'Celeritas'},
{row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format {row: 1, column: 3, matches: /Dec 1[678], 2015/}, // absorb timezone dif; we care about date format
@ -23,17 +25,18 @@ describe('AngularJS to Angular Quick Reference Tests', () => {
]; ];
// Go through the samples // Go through the samples
const movieRows = getMovieRows(); let movieRows = getMovieRows();
for (const sample of expectedSamples) { for (let i = 0; i < expectedSamples.length; i++) {
const tableCell = movieRows.get(sample.row) let sample = expectedSamples[i];
let tableCell = movieRows.get(sample.row)
.all(by.tagName('td')).get(sample.column); .all(by.tagName('td')).get(sample.column);
// Check the cell or its nested element // Check the cell or its nested element
const elementToCheck = sample.element let elementToCheck = sample.element
? tableCell.element(by.tagName(sample.element)) ? tableCell.element(by.tagName(sample.element))
: tableCell; : tableCell;
// Check element attribute or text // Check element attribute or text
const valueToCheck = sample.attr let valueToCheck = sample.attr
? elementToCheck.getAttribute(sample.attr) ? elementToCheck.getAttribute(sample.attr)
: elementToCheck.getText(); : elementToCheck.getText();
@ -48,42 +51,42 @@ describe('AngularJS to Angular Quick Reference Tests', () => {
} }
}); });
it('should display images after Show Poster', () => { it('should display images after Show Poster', function () {
testPosterButtonClick('Show Poster', true); testPosterButtonClick('Show Poster', true);
}); });
it('should hide images after Hide Poster', () => { it('should hide images after Hide Poster', function () {
testPosterButtonClick('Hide Poster', false); testPosterButtonClick('Hide Poster', false);
}); });
it('should display no movie when no favorite hero is specified', () => { it('should display no movie when no favorite hero is specified', function () {
testFavoriteHero(null, 'Please enter your favorite hero.'); testFavoriteHero(null, 'Please enter your favorite hero.');
}); });
it('should display no movie for Magneta', () => { it('should display no movie for Magneta', function () {
testFavoriteHero('Magneta', 'No movie, sorry!'); testFavoriteHero('Magneta', 'No movie, sorry!');
}); });
it('should display a movie for Dr Nice', () => { it('should display a movie for Dr Nice', function () {
testFavoriteHero('Dr Nice', 'Excellent choice!'); testFavoriteHero('Dr Nice', 'Excellent choice!');
}); });
function testImagesAreDisplayed(isDisplayed: boolean) { function testImagesAreDisplayed(isDisplayed: boolean) {
const expectedMovieCount = 3; let expectedMovieCount = 3;
const movieRows = getMovieRows(); let movieRows = getMovieRows();
expect(movieRows.count()).toBe(expectedMovieCount); expect(movieRows.count()).toBe(expectedMovieCount);
for (let i = 0; i < expectedMovieCount; i++) { for (let i = 0; i < expectedMovieCount; i++) {
const movieImage = movieRows.get(i).element(by.css('td > img')); let movieImage = movieRows.get(i).element(by.css('td > img'));
expect(movieImage.isDisplayed()).toBe(isDisplayed); expect(movieImage.isDisplayed()).toBe(isDisplayed);
} }
} }
function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) { function testPosterButtonClick(expectedButtonText: string, isDisplayed: boolean) {
const posterButton = element(by.css('app-movie-list tr > th > button')); let posterButton = element(by.css('app-movie-list tr > th > button'));
expect(posterButton.getText()).toBe(expectedButtonText); expect(posterButton.getText()).toBe(expectedButtonText);
posterButton.click().then(() => { posterButton.click().then(function () {
testImagesAreDisplayed(isDisplayed); testImagesAreDisplayed(isDisplayed);
}); });
} }
@ -93,12 +96,12 @@ describe('AngularJS to Angular Quick Reference Tests', () => {
} }
function testFavoriteHero(heroName: string, expectedLabel: string) { function testFavoriteHero(heroName: string, expectedLabel: string) {
const movieListComp = element(by.tagName('app-movie-list')); let movieListComp = element(by.tagName('app-movie-list'));
const heroInput = movieListComp.element(by.tagName('input')); let heroInput = movieListComp.element(by.tagName('input'));
const favoriteHeroLabel = movieListComp.element(by.tagName('h3')); let favoriteHeroLabel = movieListComp.element(by.tagName('h3'));
const resultLabel = movieListComp.element(by.css('span > p')); let resultLabel = movieListComp.element(by.css('span > p'));
heroInput.clear().then(() => { heroInput.clear().then(function () {
heroInput.sendKeys(heroName || ''); heroInput.sendKeys(heroName || '');
expect(resultLabel.getText()).toBe(expectedLabel); expect(resultLabel.getText()).toBe(expectedLabel);
if (heroName) { if (heroName) {

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { MovieListComponent } from './movie-list.component'; import { MovieListComponent } from './movie-list.component';

View File

@ -1,8 +1,8 @@
// #docregion // #docregion
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@NgModule({ @NgModule({
imports: [ BrowserModule ], imports: [ BrowserModule ],

View File

@ -1,9 +1,9 @@
// #docregion // #docregion
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; 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 { MovieListComponent } from './movie-list.component';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';

View File

@ -1,3 +1,5 @@
'use strict'; // necessary for es6 output in node
import { browser, ExpectedConditions as EC } from 'protractor'; import { browser, ExpectedConditions as EC } from 'protractor';
import { logging } from 'selenium-webdriver'; import { logging } from 'selenium-webdriver';
import * as openClose from './open-close.po'; import * as openClose from './open-close.po';

View File

@ -34,7 +34,7 @@ export class AppComponent {
// #docregion prepare-router-outlet // #docregion prepare-router-outlet
prepareRoute(outlet: RouterOutlet) { prepareRoute(outlet: RouterOutlet) {
return outlet && outlet.activatedRouteData && outlet.activatedRouteData.animation; return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
} }
// #enddocregion prepare-router-outlet // #enddocregion prepare-router-outlet

View File

@ -1,6 +1,4 @@
// tslint:disable: variable-name
// #docplaster // #docplaster
// #docregion
import { Component, HostBinding, OnInit } from '@angular/core'; import { Component, HostBinding, OnInit } from '@angular/core';
import { trigger, transition, animate, style, query, stagger } from '@angular/animations'; import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
@ -54,11 +52,13 @@ export class HeroListPageComponent implements OnInit {
@HostBinding('@pageAnimations') @HostBinding('@pageAnimations')
public animatePage = true; public animatePage = true;
_heroes = [];
// #docregion filter-animations // #docregion filter-animations
heroTotal = -1; heroTotal = -1;
// #enddocregion filter-animations // #enddocregion filter-animations
get heroes() { return this._heroes; } get heroes() {
private _heroes = []; return this._heroes;
}
ngOnInit() { ngOnInit() {
this._heroes = HEROES; this._heroes = HEROES;

View File

@ -8,7 +8,8 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
// #docregion trigger, trigger-wildcard1, trigger-transition // #docregion trigger, trigger-wildcard1, trigger-transition
animations: [ animations: [
trigger('openClose', [ trigger('openClose', [
// #docregion state1 // #enddocregion events1
// #docregion state1, events1
// ... // ...
// #enddocregion events1 // #enddocregion events1
state('open', style({ state('open', style({
@ -33,7 +34,8 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
transition('closed => open', [ transition('closed => open', [
animate('0.5s') animate('0.5s')
]), ]),
// #enddocregion transition2, trigger, component // #enddocregion trigger, component
// #enddocregion transition2
// #docregion trigger-wildcard1 // #docregion trigger-wildcard1
transition('* => closed', [ transition('* => closed', [
animate('1s') animate('1s')
@ -68,9 +70,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
}) })
// #docregion events // #docregion events
export class OpenCloseComponent { export class OpenCloseComponent {
// #enddocregion events1, events, component // #enddocregion events1, events
@Input() logging = false;
// #docregion component
isOpen = true; isOpen = true;
toggle() { toggle() {
@ -78,8 +78,9 @@ export class OpenCloseComponent {
} }
// #enddocregion component // #enddocregion component
@Input() logging = false;
// #docregion events1, events // #docregion events1, events
onAnimationEvent( event: AnimationEvent ) { onAnimationEvent ( event: AnimationEvent ) {
// #enddocregion events1, events // #enddocregion events1, events
if (!this.logging) { if (!this.logging) {
return; return;

View File

@ -1,3 +1,5 @@
'use strict'; // necessary for es6 output in node
import { protractor, browser, element, by, ElementFinder } from 'protractor'; import { protractor, browser, element, by, ElementFinder } from 'protractor';
const nameSuffix = 'X'; const nameSuffix = 'X';
@ -19,7 +21,7 @@ describe('Architecture', () => {
}); });
it(`has h2 '${expectedH2}'`, () => { it(`has h2 '${expectedH2}'`, () => {
const h2 = element.all(by.css('h2')).map((elt: any) => elt.getText()); let h2 = element.all(by.css('h2')).map((elt: any) => elt.getText());
expect(h2).toEqual(expectedH2); expect(h2).toEqual(expectedH2);
}); });
@ -32,42 +34,42 @@ function heroTests() {
const targetHero: Hero = { id: 2, name: 'Dr Nice' }; const targetHero: Hero = { id: 2, name: 'Dr Nice' };
it('has the right number of heroes', () => { it('has the right number of heroes', () => {
const page = getPageElts(); let page = getPageElts();
expect(page.heroes.count()).toEqual(3); expect(page.heroes.count()).toEqual(3);
}); });
it('has no hero details initially', () => { it('has no hero details initially', function () {
const page = getPageElts(); let page = getPageElts();
expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail'); expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
}); });
it('shows selected hero details', async () => { it('shows selected hero details', async () => {
await element(by.cssContainingText('li', targetHero.name)).click(); await element(by.cssContainingText('li', targetHero.name)).click();
const page = getPageElts(); let page = getPageElts();
const hero = await heroFromDetail(page.heroDetail); let hero = await heroFromDetail(page.heroDetail);
expect(hero.id).toEqual(targetHero.id); expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(targetHero.name); expect(hero.name).toEqual(targetHero.name);
}); });
it(`shows updated hero name in details`, async () => { it(`shows updated hero name in details`, async () => {
const input = element.all(by.css('input')).first(); let input = element.all(by.css('input')).first();
input.sendKeys(nameSuffix); input.sendKeys(nameSuffix);
const page = getPageElts(); let page = getPageElts();
const hero = await heroFromDetail(page.heroDetail); let hero = await heroFromDetail(page.heroDetail);
const newName = targetHero.name + nameSuffix; let newName = targetHero.name + nameSuffix;
expect(hero.id).toEqual(targetHero.id); expect(hero.id).toEqual(targetHero.id);
expect(hero.name).toEqual(newName); expect(hero.name).toEqual(newName);
}); });
} }
function salesTaxTests() { function salesTaxTests() {
it('has no sales tax initially', () => { it('has no sales tax initially', function () {
const page = getPageElts(); let page = getPageElts();
expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info'); expect(page.salesTaxDetail.isPresent()).toBeFalsy('no sales tax info');
}); });
it('shows sales tax', async () => { it('shows sales tax', async function () {
const page = getPageElts(); let page = getPageElts();
page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER); page.salesTaxAmountInput.sendKeys('10', protractor.Key.ENTER);
expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00'); expect(page.salesTaxDetail.getText()).toEqual('The sales tax is $1.00');
}); });
@ -86,11 +88,13 @@ function getPageElts() {
async function heroFromDetail(detail: ElementFinder): Promise<Hero> { async function heroFromDetail(detail: ElementFinder): Promise<Hero> {
// Get hero id from the first <div> // Get hero id from the first <div>
const id = await detail.all(by.css('div')).first().getText(); // let _id = await detail.all(by.css('div')).first().getText();
let _id = await detail.all(by.css('div')).first().getText();
// Get name from the h2 // Get name from the h2
const name = await detail.element(by.css('h4')).getText(); // let _name = await detail.element(by.css('h4')).getText();
let _name = await detail.element(by.css('h4')).getText();
return { return {
id: +id.substr(id.indexOf(' ') + 1), id: +_id.substr(_id.indexOf(' ') + 1),
name: name.substr(0, name.lastIndexOf(' ')), name: _name.substr(0, _name.lastIndexOf(' '))
}; };
} }

View File

@ -1,15 +1,15 @@
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
// #docregion imports // #docregion imports
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
// #enddocregion imports // #enddocregion imports
import { HeroDetailComponent } from './hero-detail.component'; import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component'; import { HeroListComponent } from './hero-list.component';
import { SalesTaxComponent } from './sales-tax.component'; import { SalesTaxComponent } from './sales-tax.component';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
import { BackendService } from './backend.service'; import { BackendService } from './backend.service';
import { Logger } from './logger.service'; import { Logger } from './logger.service';
@NgModule({ @NgModule({
imports: [ imports: [

View File

@ -18,7 +18,7 @@ export class BackendService {
// TODO: get from the database // TODO: get from the database
return Promise.resolve<Hero[]>(HEROES); return Promise.resolve<Hero[]>(HEROES);
} }
const err = new Error('Cannot get object of this type'); let err = new Error('Cannot get object of this type');
this.logger.error(err); this.logger.error(err);
throw err; throw err;
} }

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
// #docregion metadata, providers // #docregion metadata, providers
@Component({ @Component({

View File

@ -22,7 +22,7 @@ export class AppComponent {
} }
// #docregion module // #docregion module
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
// #docregion import-browser-module // #docregion import-browser-module
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
// #enddocregion import-browser-module // #enddocregion import-browser-module

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SalesTaxService } from './sales-tax.service'; import { SalesTaxService } from './sales-tax.service';
import { TaxRateService } from './tax-rate.service'; import { TaxRateService } from './tax-rate.service';
@Component({ @Component({
selector: 'app-sales-tax', selector: 'app-sales-tax',

View File

@ -7,7 +7,7 @@ export class SalesTaxService {
constructor(private rateService: TaxRateService) { } constructor(private rateService: TaxRateService) { }
getVAT(value: string | number) { getVAT(value: string | number) {
const amount = (typeof value === 'string') ? let amount = (typeof value === 'string') ?
parseFloat(value) : value; parseFloat(value) : value;
return (amount || 0) * this.rateService.getRate('VAT'); return (amount || 0) * this.rateService.getRate('VAT');
} }

View File

@ -1,32 +1,34 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Attribute binding example', () => { describe('Attribute binding example', function () {
beforeEach(() => { beforeEach(function () {
browser.get(''); browser.get('');
}); });
it('should display Property Binding with Angular', () => { it('should display Property Binding with Angular', function () {
expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings'); expect(element(by.css('h1')).getText()).toEqual('Attribute, class, and style bindings');
}); });
it('should display a table', () => { it('should display a table', function() {
expect(element.all(by.css('table')).isPresent()).toBe(true); expect(element.all(by.css('table')).isPresent()).toBe(true);
}); });
it('should display an Aria button', () => { it('should display an Aria button', function () {
expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria'); expect(element.all(by.css('button')).get(0).getText()).toBe('Go for it with Aria');
}); });
it('should display a blue background on div', () => { 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)'); 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', () => { 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)'); 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', () => { 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('special');
expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance'); expect(element.all(by.css('div')).get(1).getAttribute('class')).toContain('clearance');
}); });

View File

@ -1,16 +1,16 @@
import { Component, HostBinding } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'comp-with-host-binding', selector: 'comp-with-host-binding',
template: 'I am a component!', template: 'I am a component!',
host: {
'[class.special]': 'isSpecial',
'[style.color]': 'color',
'[style.width]': 'width'
}
}) })
export class CompWithHostBindingComponent { export class CompWithHostBindingComponent {
@HostBinding('class.special')
isSpecial = false; isSpecial = false;
@HostBinding('style.color')
color = 'green'; color = 'green';
@HostBinding('style.width')
width = '200px'; width = '200px';
} }

View File

@ -1,15 +1,17 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Attribute directives', () => { describe('Attribute directives', () => {
const title = 'My First Attribute Directive'; let _title = 'My First Attribute Directive';
beforeAll(() => { beforeAll(() => {
browser.get(''); browser.get('');
}); });
it(`should display correct title: ${title}`, () => { it(`should display correct title: ${_title}`, () => {
expect(element(by.css('h1')).getText()).toEqual(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', () => {

View File

@ -3,7 +3,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; 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 HLD1 } from './highlight.directive.1';
import { HighlightDirective as HLD2 } from './highlight.directive.2'; import { HighlightDirective as HLD2 } from './highlight.directive.2';
import { HighlightDirective as HLD3 } from './highlight.directive.3'; import { HighlightDirective as HLD3 } from './highlight.directive.3';

View File

@ -3,55 +3,57 @@ import { logging } from 'selenium-webdriver';
describe('Binding syntax e2e tests', () => { describe('Binding syntax e2e tests', () => {
beforeEach(() => browser.get('')); beforeEach(function () {
browser.get('');
});
// helper function used to test what's logged to the console // helper function used to test what's logged to the console
async function logChecker(button, contents) { async function logChecker(button, contents) {
const logs = await browser.manage().logs().get(logging.Type.BROWSER); const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false); const message = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
expect(messages.length).toBeGreaterThan(0); expect(message.length).toBeGreaterThan(0);
} }
it('should display Binding syntax', () => { it('should display Binding syntax', function () {
expect(element(by.css('h1')).getText()).toEqual('Binding syntax'); expect(element(by.css('h1')).getText()).toEqual('Binding syntax');
}); });
it('should display Save button', () => { it('should display Save button', function () {
expect(element.all(by.css('button')).get(0).getText()).toBe('Save'); expect(element.all(by.css('button')).get(0).getText()).toBe('Save');
}); });
it('should display 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'); expect(element.all(by.css('h2')).get(1).getText()).toBe('HTML attributes and DOM properties');
}); });
it('should display 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'); expect(element.all(by.css('p')).get(0).getText()).toContain('1. Use the inspector');
}); });
it('should display Disabled property vs. attribute', () => { it('should display Disabled property vs. attribute', function () {
expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute'); expect(element.all(by.css('h3')).get(0).getText()).toBe('Disabled property vs. attribute');
}); });
it('should log a message including Sarah', async () => { it('should log a message including Sarah', async () => {
const attributeButton = element.all(by.css('button')).get(1); let attributeButton = element.all(by.css('button')).get(1);
await attributeButton.click(); await attributeButton.click();
const contents = 'Sarah'; const contents = 'Sarah';
logChecker(attributeButton, contents); logChecker(attributeButton, contents);
}); });
it('should log a message including Sarah for DOM property', async () => { it('should log a message including Sarah for DOM property', async () => {
const DOMPropertyButton = element.all(by.css('button')).get(2); let DOMPropertyButton = element.all(by.css('button')).get(2);
await DOMPropertyButton.click(); await DOMPropertyButton.click();
const contents = 'Sarah'; const contents = 'Sarah';
logChecker(DOMPropertyButton, contents); logChecker(DOMPropertyButton, contents);
}); });
it('should log a message including Sally for DOM property', async () => { it('should log a message including Sally for DOM property', async () => {
const DOMPropertyButton = element.all(by.css('button')).get(2); let DOMPropertyButton = element.all(by.css('button')).get(2);
const input = element(by.css('input')); let input = element(by.css('input'));
input.sendKeys('Sally'); input.sendKeys('Sally');
await DOMPropertyButton.click(); await DOMPropertyButton.click();
const contents = 'Sally'; const contents = 'Sally';
@ -59,14 +61,14 @@ describe('Binding syntax e2e tests', () => {
}); });
it('should log a message that Test Button works', async () => { it('should log a message that Test Button works', async () => {
const testButton = element.all(by.css('button')).get(3); let testButton = element.all(by.css('button')).get(3);
await testButton.click(); await testButton.click();
const contents = 'Test'; const contents = 'Test';
logChecker(testButton, contents); logChecker(testButton, contents);
}); });
it('should toggle Test Button disabled', async () => { it('should toggle Test Button disabled', async () => {
const toggleButton = element.all(by.css('button')).get(4); let toggleButton = element.all(by.css('button')).get(4);
await toggleButton.click(); await toggleButton.click();
const contents = 'true'; const contents = 'true';
logChecker(toggleButton, contents); logChecker(toggleButton, contents);

View File

@ -26,7 +26,7 @@ export class AppComponent {
toggleDisabled(): any { toggleDisabled(): any {
const testButton = document.getElementById('testButton') as HTMLInputElement; let testButton = <HTMLInputElement> document.getElementById('testButton');
testButton.disabled = !testButton.disabled; testButton.disabled = !testButton.disabled;
console.warn(testButton.disabled); console.warn(testButton.disabled);
} }

View File

@ -1,19 +1,21 @@
'use strict';
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Built-in Directives', () => { describe('Built-in Directives', function () {
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should have title Built-in Directives', () => { it('should have title Built-in Directives', function () {
const title = element.all(by.css('h1')).get(0); let title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Directives'); expect(title.getText()).toEqual('Built-in Directives');
}); });
it('should change first Teapot header', async () => { it('should change first Teapot header', async () => {
const firstLabel = element.all(by.css('p')).get(0); let firstLabel = element.all(by.css('p')).get(0);
const firstInput = element.all(by.css('input')).get(0); let firstInput = element.all(by.css('input')).get(0);
expect(firstLabel.getText()).toEqual('Current item name: Teapot'); expect(firstLabel.getText()).toEqual('Current item name: Teapot');
firstInput.sendKeys('abc'); firstInput.sendKeys('abc');
@ -21,49 +23,49 @@ describe('Built-in Directives', () => {
}); });
it('should modify sentence when modified checkbox checked', () => { it('should modify sentence when modified checkbox checked', function () {
const modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1); let modifiedChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(1);
const modifiedSentence = element.all(by.css('div')).get(1); let modifiedSentence = element.all(by.css('div')).get(1);
modifiedChkbxLabel.click(); modifiedChkbxLabel.click();
expect(modifiedSentence.getText()).toContain('modified'); expect(modifiedSentence.getText()).toContain('modified');
}); });
it('should modify sentence when normal checkbox checked', () => { it('should modify sentence when normal checkbox checked', function () {
const normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4); let normalChkbxLabel = element.all(by.css('input[type="checkbox"]')).get(4);
const normalSentence = element.all(by.css('div')).get(7); let normalSentence = element.all(by.css('div')).get(7);
normalChkbxLabel.click(); normalChkbxLabel.click();
expect(normalSentence.getText()).toContain('normal weight and, extra large'); expect(normalSentence.getText()).toContain('normal weight and, extra large');
}); });
it('should toggle app-item-detail', () => { it('should toggle app-item-detail', function () {
const toggleButton = element.all(by.css('button')).get(3); let toggleButton = element.all(by.css('button')).get(3);
const toggledDiv = element.all(by.css('app-item-detail')).get(0); let toggledDiv = element.all(by.css('app-item-detail')).get(0);
toggleButton.click(); toggleButton.click();
expect(toggledDiv.isDisplayed()).toBe(true); expect(toggledDiv.isDisplayed()).toBe(true);
}); });
it('should hide app-item-detail', () => { it('should hide app-item-detail', function () {
const hiddenMessage = element.all(by.css('p')).get(11); let hiddenMessage = element.all(by.css('p')).get(11);
const hiddenDiv = element.all(by.css('app-item-detail')).get(2); let hiddenDiv = element.all(by.css('app-item-detail')).get(2);
expect(hiddenMessage.getText()).toContain('in the DOM'); expect(hiddenMessage.getText()).toContain('in the DOM');
expect(hiddenDiv.isDisplayed()).toBe(true); expect(hiddenDiv.isDisplayed()).toBe(true);
}); });
it('should have 10 lists each containing the string Teapot', () => { it('should have 10 lists each containing the string Teapot', function () {
const listDiv = element.all(by.cssContainingText('.box', 'Teapot')); let listDiv = element.all(by.cssContainingText('.box', 'Teapot'));
expect(listDiv.count()).toBe(10); expect(listDiv.count()).toBe(10);
}); });
it('should switch case', () => { it('should switch case', function () {
const tvRadioButton = element.all(by.css('input[type="radio"]')).get(3); let tvRadioButton = element.all(by.css('input[type="radio"]')).get(3);
const tvDiv = element(by.css('app-lost-item')); let tvDiv = element(by.css('app-lost-item'));
const fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4); let fishbowlRadioButton = element.all(by.css('input[type="radio"]')).get(4);
const fishbowlDiv = element(by.css('app-unknown-item')); let fishbowlDiv = element(by.css('app-unknown-item'));
tvRadioButton.click(); tvRadioButton.click();
expect(tvDiv.getText()).toContain('Television'); expect(tvDiv.getText()).toContain('Television');

View File

@ -30,14 +30,6 @@ export class AppComponent implements OnInit {
itemsWithTrackByCountReset = 0; itemsWithTrackByCountReset = 0;
itemIdIncrement = 1; itemIdIncrement = 1;
// #docregion setClasses
currentClasses: {};
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
// #enddocregion setStyles
ngOnInit() { ngOnInit() {
this.resetItems(); this.resetItems();
this.setCurrentClasses(); this.setCurrentClasses();
@ -49,18 +41,20 @@ export class AppComponent implements OnInit {
this.currentItem.name = name.toUpperCase(); this.currentItem.name = name.toUpperCase();
} }
// #docregion setClasses // #docregion setClasses
currentClasses: {};
setCurrentClasses() { setCurrentClasses() {
// CSS classes: added/removed per current state of component properties // CSS classes: added/removed per current state of component properties
this.currentClasses = { this.currentClasses = {
saveable: this.canSave, 'saveable': this.canSave,
modified: !this.isUnchanged, 'modified': !this.isUnchanged,
special: this.isSpecial 'special': this.isSpecial
}; };
} }
// #enddocregion setClasses // #enddocregion setClasses
// #docregion setStyles // #docregion setStyles
currentStyles: {};
setCurrentStyles() { setCurrentStyles() {
// CSS styles: set per current state of component properties // CSS styles: set per current state of component properties
this.currentStyles = { this.currentStyles = {
@ -76,7 +70,11 @@ export class AppComponent implements OnInit {
} }
giveNullCustomerValue() { giveNullCustomerValue() {
this.nullCustomer = 'Kelly'; !(this.nullCustomer = null) ? (this.nullCustomer = 'Kelly') : (this.nullCustomer = null);
}
resetNullItem() {
this.nullCustomer = null;
} }
resetItems() { resetItems() {
@ -86,7 +84,7 @@ export class AppComponent implements OnInit {
} }
resetList() { resetList() {
this.resetItems(); this.resetItems()
this.itemsWithTrackByCountReset = 0; this.itemsWithTrackByCountReset = 0;
this.itemsNoTrackByCount = ++this.itemsNoTrackByCount; this.itemsNoTrackByCount = ++this.itemsNoTrackByCount;
} }
@ -109,7 +107,7 @@ export class AppComponent implements OnInit {
trackByItems(index: number, item: Item): number { return item.id; } trackByItems(index: number, item: Item): number { return item.id; }
// #enddocregion trackByItems // #enddocregion trackByItems
trackById(index: number, item: any): number { return item.id; } trackById(index: number, item: any): number { return item['id']; }
} }

View File

@ -1,17 +1,19 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Built Template Functions Example', () => { describe('Built Template Functions Example', function () {
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should have title Built-in Template Functions', () => { it('should have title Built-in Template Functions', function () {
const title = element.all(by.css('h1')).get(0); let title = element.all(by.css('h1')).get(0);
expect(title.getText()).toEqual('Built-in Template Functions'); expect(title.getText()).toEqual('Built-in Template Functions');
}); });
it('should display $any( ) in h2', () => { it('should display $any( ) in h2', function () {
const header = element(by.css('h2')); let header = element(by.css('h2'));
expect(header.getText()).toContain('$any( )'); expect(header.getText()).toContain('$any( )');
}); });

View File

@ -1,85 +1,87 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Component Communication Cookbook Tests', () => { describe('Component Communication Cookbook Tests', function () {
// Note: '?e2e' which app can read to know it is running in protractor // Note: '?e2e' which app can read to know it is running in protractor
// e.g. `if (!/e2e/.test(location.search)) { ...` // e.g. `if (!/e2e/.test(location.search)) { ...`
beforeAll(() => { beforeAll(function () {
browser.get('?e2e'); browser.get('?e2e');
}); });
describe('Parent-to-child communication', () => { describe('Parent-to-child communication', function() {
// #docregion parent-to-child // #docregion parent-to-child
// ... // ...
const heroNames = ['Dr IQ', 'Magneta', 'Bombasto']; let _heroNames = ['Dr IQ', 'Magneta', 'Bombasto'];
const masterName = 'Master'; let _masterName = 'Master';
it('should pass properties to children properly', () => { it('should pass properties to children properly', function () {
const parent = element.all(by.tagName('app-hero-parent')).get(0); let parent = element.all(by.tagName('app-hero-parent')).get(0);
const heroes = parent.all(by.tagName('app-hero-child')); let heroes = parent.all(by.tagName('app-hero-child'));
for (let i = 0; i < heroNames.length; i++) { for (let i = 0; i < _heroNames.length; i++) {
const childTitle = heroes.get(i).element(by.tagName('h3')).getText(); let childTitle = heroes.get(i).element(by.tagName('h3')).getText();
const childDetail = heroes.get(i).element(by.tagName('p')).getText(); let childDetail = heroes.get(i).element(by.tagName('p')).getText();
expect(childTitle).toEqual(heroNames[i] + ' says:'); expect(childTitle).toEqual(_heroNames[i] + ' says:');
expect(childDetail).toContain(masterName); expect(childDetail).toContain(_masterName);
} }
}); });
// ... // ...
// #enddocregion parent-to-child // #enddocregion parent-to-child
}); });
describe('Parent-to-child communication with setter', () => { describe('Parent-to-child communication with setter', function() {
// #docregion parent-to-child-setter // #docregion parent-to-child-setter
// ... // ...
it('should display trimmed, non-empty names', () => { it('should display trimmed, non-empty names', function () {
const nonEmptyNameIndex = 0; let _nonEmptyNameIndex = 0;
const nonEmptyName = '"Dr IQ"'; let _nonEmptyName = '"Dr IQ"';
const parent = element.all(by.tagName('app-name-parent')).get(0); let parent = element.all(by.tagName('app-name-parent')).get(0);
const hero = parent.all(by.tagName('app-name-child')).get(nonEmptyNameIndex); let hero = parent.all(by.tagName('app-name-child')).get(_nonEmptyNameIndex);
const displayName = hero.element(by.tagName('h3')).getText(); let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(nonEmptyName); expect(displayName).toEqual(_nonEmptyName);
}); });
it('should replace empty name with default name', () => { it('should replace empty name with default name', function () {
const emptyNameIndex = 1; let _emptyNameIndex = 1;
const defaultName = '"<no name set>"'; let _defaultName = '"<no name set>"';
const parent = element.all(by.tagName('app-name-parent')).get(0); let parent = element.all(by.tagName('app-name-parent')).get(0);
const hero = parent.all(by.tagName('app-name-child')).get(emptyNameIndex); let hero = parent.all(by.tagName('app-name-child')).get(_emptyNameIndex);
const displayName = hero.element(by.tagName('h3')).getText(); let displayName = hero.element(by.tagName('h3')).getText();
expect(displayName).toEqual(defaultName); expect(displayName).toEqual(_defaultName);
}); });
// ... // ...
// #enddocregion parent-to-child-setter // #enddocregion parent-to-child-setter
}); });
describe('Parent-to-child communication with ngOnChanges', () => { describe('Parent-to-child communication with ngOnChanges', function() {
// #docregion parent-to-child-onchanges // #docregion parent-to-child-onchanges
// ... // ...
// Test must all execute in this exact order // Test must all execute in this exact order
it('should set expected initial values', () => { it('should set expected initial values', function () {
const actual = getActual(); let actual = getActual();
const initialLabel = 'Version 1.23'; let initialLabel = 'Version 1.23';
const initialLog = 'Initial value of major set to 1, Initial value of minor set to 23'; let initialLog = 'Initial value of major set to 1, Initial value of minor set to 23';
expect(actual.label).toBe(initialLabel); expect(actual.label).toBe(initialLabel);
expect(actual.count).toBe(1); expect(actual.count).toBe(1);
expect(actual.logs.get(0).getText()).toBe(initialLog); expect(actual.logs.get(0).getText()).toBe(initialLog);
}); });
it('should set expected values after clicking \'Minor\' twice', () => { it('should set expected values after clicking \'Minor\' twice', function () {
const repoTag = element(by.tagName('app-version-parent')); let repoTag = element(by.tagName('app-version-parent'));
const newMinorButton = repoTag.all(by.tagName('button')).get(0); let newMinorButton = repoTag.all(by.tagName('button')).get(0);
newMinorButton.click().then(() => { newMinorButton.click().then(function() {
newMinorButton.click().then(() => { newMinorButton.click().then(function() {
const actual = getActual(); let actual = getActual();
const labelAfter2Minor = 'Version 1.25'; let labelAfter2Minor = 'Version 1.25';
const logAfter2Minor = 'minor changed from 24 to 25'; let logAfter2Minor = 'minor changed from 24 to 25';
expect(actual.label).toBe(labelAfter2Minor); expect(actual.label).toBe(labelAfter2Minor);
expect(actual.count).toBe(3); expect(actual.count).toBe(3);
@ -88,15 +90,15 @@ describe('Component Communication Cookbook Tests', () => {
}); });
}); });
it('should set expected values after clicking \'Major\' once', () => { it('should set expected values after clicking \'Major\' once', function () {
const repoTag = element(by.tagName('app-version-parent')); let repoTag = element(by.tagName('app-version-parent'));
const newMajorButton = repoTag.all(by.tagName('button')).get(1); let newMajorButton = repoTag.all(by.tagName('button')).get(1);
newMajorButton.click().then(() => { newMajorButton.click().then(function() {
const actual = getActual(); let actual = getActual();
const labelAfterMajor = 'Version 2.0'; let labelAfterMajor = 'Version 2.0';
const logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0'; let logAfterMajor = 'major changed from 1 to 2, minor changed from 25 to 0';
expect(actual.label).toBe(labelAfterMajor); expect(actual.label).toBe(labelAfterMajor);
expect(actual.count).toBe(4); expect(actual.count).toBe(4);
@ -105,14 +107,14 @@ describe('Component Communication Cookbook Tests', () => {
}); });
function getActual() { function getActual() {
const versionTag = element(by.tagName('app-version-child')); let versionTag = element(by.tagName('app-version-child'));
const label = versionTag.element(by.tagName('h3')).getText(); let label = versionTag.element(by.tagName('h3')).getText();
const ul = versionTag.element((by.tagName('ul'))); let ul = versionTag.element((by.tagName('ul')));
const logs = ul.all(by.tagName('li')); let logs = ul.all(by.tagName('li'));
return { return {
label, label: label,
logs, logs: logs,
count: logs.count() count: logs.count()
}; };
} }
@ -121,30 +123,30 @@ describe('Component Communication Cookbook Tests', () => {
}); });
describe('Child-to-parent communication', () => { describe('Child-to-parent communication', function() {
// #docregion child-to-parent // #docregion child-to-parent
// ... // ...
it('should not emit the event initially', () => { it('should not emit the event initially', function () {
const voteLabel = element(by.tagName('app-vote-taker')) let voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 0, Disagree: 0'); expect(voteLabel).toBe('Agree: 0, Disagree: 0');
}); });
it('should process Agree vote', () => { it('should process Agree vote', function () {
const agreeButton1 = element.all(by.tagName('app-voter')).get(0) let agreeButton1 = element.all(by.tagName('app-voter')).get(0)
.all(by.tagName('button')).get(0); .all(by.tagName('button')).get(0);
agreeButton1.click().then(() => { agreeButton1.click().then(function() {
const voteLabel = element(by.tagName('app-vote-taker')) let voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 0'); expect(voteLabel).toBe('Agree: 1, Disagree: 0');
}); });
}); });
it('should process Disagree vote', () => { it('should process Disagree vote', function () {
const agreeButton1 = element.all(by.tagName('app-voter')).get(1) let agreeButton1 = element.all(by.tagName('app-voter')).get(1)
.all(by.tagName('button')).get(1); .all(by.tagName('button')).get(1);
agreeButton1.click().then(() => { agreeButton1.click().then(function() {
const voteLabel = element(by.tagName('app-vote-taker')) let voteLabel = element(by.tagName('app-vote-taker'))
.element(by.tagName('h3')).getText(); .element(by.tagName('h3')).getText();
expect(voteLabel).toBe('Agree: 1, Disagree: 1'); expect(voteLabel).toBe('Agree: 1, Disagree: 1');
}); });
@ -155,31 +157,31 @@ describe('Component Communication Cookbook Tests', () => {
// Can't run timer tests in protractor because // Can't run timer tests in protractor because
// interaction w/ zones causes all tests to freeze & timeout. // interaction w/ zones causes all tests to freeze & timeout.
xdescribe('Parent calls child via local var', () => { xdescribe('Parent calls child via local var', function() {
countDownTimerTests('countdown-parent-lv'); countDownTimerTests('countdown-parent-lv');
}); });
xdescribe('Parent calls ViewChild', () => { xdescribe('Parent calls ViewChild', function() {
countDownTimerTests('countdown-parent-vc'); countDownTimerTests('countdown-parent-vc');
}); });
function countDownTimerTests(parentTag: string) { function countDownTimerTests(parentTag: string) {
// #docregion countdown-timer-tests // #docregion countdown-timer-tests
// ... // ...
it('timer and parent seconds should match', () => { it('timer and parent seconds should match', function () {
const parent = element(by.tagName(parentTag)); let parent = element(by.tagName(parentTag));
const message = parent.element(by.tagName('app-countdown-timer')).getText(); let message = parent.element(by.tagName('app-countdown-timer')).getText();
browser.sleep(10); // give `seconds` a chance to catchup with `message` browser.sleep(10); // give `seconds` a chance to catchup with `message`
const seconds = parent.element(by.className('seconds')).getText(); let seconds = parent.element(by.className('seconds')).getText();
expect(message).toContain(seconds); expect(message).toContain(seconds);
}); });
it('should stop the countdown', () => { it('should stop the countdown', function () {
const parent = element(by.tagName(parentTag)); let parent = element(by.tagName(parentTag));
const stopButton = parent.all(by.tagName('button')).get(1); let stopButton = parent.all(by.tagName('button')).get(1);
stopButton.click().then(() => { stopButton.click().then(function() {
const message = parent.element(by.tagName('app-countdown-timer')).getText(); let message = parent.element(by.tagName('app-countdown-timer')).getText();
expect(message).toContain('Holding'); expect(message).toContain('Holding');
}); });
}); });
@ -188,39 +190,39 @@ describe('Component Communication Cookbook Tests', () => {
} }
describe('Parent and children communicate via a service', () => { describe('Parent and children communicate via a service', function() {
// #docregion bidirectional-service // #docregion bidirectional-service
// ... // ...
it('should announce a mission', () => { it('should announce a mission', function () {
const missionControl = element(by.tagName('app-mission-control')); let missionControl = element(by.tagName('app-mission-control'));
const announceButton = missionControl.all(by.tagName('button')).get(0); let announceButton = missionControl.all(by.tagName('button')).get(0);
announceButton.click().then(() => { announceButton.click().then(function () {
const history = missionControl.all(by.tagName('li')); let history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(1); expect(history.count()).toBe(1);
expect(history.get(0).getText()).toMatch(/Mission.* announced/); expect(history.get(0).getText()).toMatch(/Mission.* announced/);
}); });
}); });
it('should confirm the mission by Lovell', () => { it('should confirm the mission by Lovell', function () {
testConfirmMission(1, 2, 'Lovell'); testConfirmMission(1, 2, 'Lovell');
}); });
it('should confirm the mission by Haise', () => { it('should confirm the mission by Haise', function () {
testConfirmMission(3, 3, 'Haise'); testConfirmMission(3, 3, 'Haise');
}); });
it('should confirm the mission by Swigert', () => { it('should confirm the mission by Swigert', function () {
testConfirmMission(2, 4, 'Swigert'); testConfirmMission(2, 4, 'Swigert');
}); });
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) { function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
const confirmedLog = ' confirmed the mission'; let _confirmedLog = ' confirmed the mission';
const missionControl = element(by.tagName('app-mission-control')); let missionControl = element(by.tagName('app-mission-control'));
const confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex); let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
confirmButton.click().then(() => { confirmButton.click().then(function () {
const history = missionControl.all(by.tagName('li')); let history = missionControl.all(by.tagName('li'));
expect(history.count()).toBe(expectedLogCount); expect(history.count()).toBe(expectedLogCount);
expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + confirmedLog); expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
}); });
} }
// ... // ...

View File

@ -1,5 +1,5 @@
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { AstronautComponent } from './astronaut.component'; import { AstronautComponent } from './astronaut.component';
@ -15,7 +15,7 @@ import { VersionParentComponent } from './version-parent.component';
import { VoterComponent } from './voter.component'; import { VoterComponent } from './voter.component';
import { VoteTakerComponent } from './votetaker.component'; import { VoteTakerComponent } from './votetaker.component';
const directives: any[] = [ let directives: any[] = [
AppComponent, AppComponent,
AstronautComponent, AstronautComponent,
CountdownTimerComponent, CountdownTimerComponent,
@ -30,7 +30,7 @@ const directives: any[] = [
VoteTakerComponent VoteTakerComponent
]; ];
const schemas: any[] = []; let schemas: any[] = [];
// Include Countdown examples // Include Countdown examples
// unless in e2e tests which they break. // unless in e2e tests which they break.
@ -49,6 +49,6 @@ if (!/e2e/.test(location.search)) {
], ],
declarations: directives, declarations: directives,
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],
schemas schemas: schemas
}) })
export class AppModule { } export class AppModule { }

View File

@ -2,7 +2,7 @@
import { Component, Input, OnDestroy } from '@angular/core'; import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service'; import { MissionService } from './mission.service';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'app-astronaut', selector: 'app-astronaut',

View File

@ -2,8 +2,8 @@
// #docregion vc // #docregion vc
import { AfterViewInit, ViewChild } from '@angular/core'; import { AfterViewInit, ViewChild } from '@angular/core';
// #docregion lv // #docregion lv
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CountdownTimerComponent } from './countdown-timer.component'; import { CountdownTimerComponent } from './countdown-timer.component';
// #enddocregion lv // #enddocregion lv
// #enddocregion vc // #enddocregion vc

View File

@ -12,6 +12,6 @@ import { Hero } from './hero';
}) })
export class HeroChildComponent { export class HeroChildComponent {
@Input() hero: Hero; @Input() hero: Hero;
@Input('master') masterName: string; // tslint:disable-line: no-input-rename @Input('master') masterName: string;
} }
// #enddocregion // #enddocregion

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@Injectable() @Injectable()
export class MissionService { export class MissionService {

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MissionService } from './mission.service'; import { MissionService } from './mission.service';
@Component({ @Component({
selector: 'app-mission-control', selector: 'app-mission-control',
@ -34,7 +34,7 @@ export class MissionControlComponent {
} }
announce() { announce() {
const mission = this.missions[this.nextMission++]; let mission = this.missions[this.nextMission++];
this.missionService.announceMission(mission); this.missionService.announceMission(mission);
this.history.push(`Mission "${mission}" announced`); this.history.push(`Mission "${mission}" announced`);
if (this.nextMission >= this.missions.length) { this.nextMission = 0; } if (this.nextMission >= this.missions.length) { this.nextMission = 0; }

View File

@ -1,4 +1,3 @@
// tslint:disable: variable-name
// #docregion // #docregion
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
@ -7,11 +6,13 @@ import { Component, Input } from '@angular/core';
template: '<h3>"{{name}}"</h3>' template: '<h3>"{{name}}"</h3>'
}) })
export class NameChildComponent { export class NameChildComponent {
private _name = '';
@Input() @Input()
get name(): string { return this._name; }
set name(name: string) { set name(name: string) {
this._name = (name && name.trim()) || '<no name set>'; this._name = (name && name.trim()) || '<no name set>';
} }
private _name = '';
get name(): string { return this._name; }
} }
// #enddocregion // #enddocregion

View File

@ -18,14 +18,14 @@ export class VersionChildComponent implements OnChanges {
changeLog: string[] = []; changeLog: string[] = [];
ngOnChanges(changes: {[propKey: string]: SimpleChange}) { ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
const log: string[] = []; let log: string[] = [];
for (const propName in changes) { for (let propName in changes) {
const changedProp = changes[propName]; let changedProp = changes[propName];
const to = JSON.stringify(changedProp.currentValue); let to = JSON.stringify(changedProp.currentValue);
if (changedProp.isFirstChange()) { if (changedProp.isFirstChange()) {
log.push(`Initial value of ${propName} set to ${to}`); log.push(`Initial value of ${propName} set to ${to}`);
} else { } else {
const from = JSON.stringify(changedProp.previousValue); let from = JSON.stringify(changedProp.previousValue);
log.push(`${propName} changed from ${from} to ${to}`); log.push(`${propName} changed from ${from} to ${to}`);
} }
} }

View File

@ -1,5 +1,5 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'app-vote-taker', selector: 'app-vote-taker',

View File

@ -1,14 +1,16 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Component Style Tests', () => { describe('Component Style Tests', function () {
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('scopes component styles to component view', () => { it('scopes component styles to component view', function() {
const componentH1 = element(by.css('app-root > h1')); let componentH1 = element(by.css('app-root > h1'));
const externalH1 = element(by.css('body > h1')); let externalH1 = element(by.css('body > h1'));
// Note: sometimes webdriver returns the fontWeight as "normal", // Note: sometimes webdriver returns the fontWeight as "normal",
// other times as "400", both of which are equal in CSS terms. // other times as "400", both of which are equal in CSS terms.
@ -17,49 +19,49 @@ describe('Component Style Tests', () => {
}); });
it('allows styling :host element', () => { it('allows styling :host element', function() {
const host = element(by.css('app-hero-details')); let host = element(by.css('app-hero-details'));
expect(host.getCssValue('borderWidth')).toEqual('1px'); expect(host.getCssValue('borderWidth')).toEqual('1px');
}); });
it('supports :host() in function form', () => { it('supports :host() in function form', function() {
const host = element(by.css('app-hero-details')); let host = element(by.css('app-hero-details'));
host.element(by.buttonText('Activate')).click(); host.element(by.buttonText('Activate')).click();
expect(host.getCssValue('borderWidth')).toEqual('3px'); expect(host.getCssValue('borderWidth')).toEqual('3px');
}); });
it('allows conditional :host-context() styling', () => { it('allows conditional :host-context() styling', function() {
const h2 = element(by.css('app-hero-details h2')); let h2 = element(by.css('app-hero-details h2'));
expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
}); });
it('styles both view and content children with /deep/', () => { it('styles both view and content children with /deep/', function() {
const viewH3 = element(by.css('app-hero-team h3')); let viewH3 = element(by.css('app-hero-team h3'));
const contentH3 = element(by.css('app-hero-controls h3')); let contentH3 = element(by.css('app-hero-controls h3'));
expect(viewH3.getCssValue('fontStyle')).toEqual('italic'); expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
expect(contentH3.getCssValue('fontStyle')).toEqual('italic'); expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
}); });
it('includes styles loaded with CSS @import', () => { it('includes styles loaded with CSS @import', function() {
const host = element(by.css('app-hero-details')); let host = element(by.css('app-hero-details'));
expect(host.getCssValue('padding')).toEqual('10px'); expect(host.getCssValue('padding')).toEqual('10px');
}); });
it('processes template inline styles', () => { it('processes template inline styles', function() {
const button = element(by.css('app-hero-controls button')); let button = element(by.css('app-hero-controls button'));
const externalButton = element(by.css('body > button')); let externalButton = element(by.css('body > button'));
expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)'); expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
}); });
it('processes template <link>s', () => { it('processes template <link>s', function() {
const li = element(by.css('app-hero-team li:first-child')); let li = element(by.css('app-hero-team li:first-child'));
const externalLi = element(by.css('body > ul li')); let externalLi = element(by.css('body > ul li'));
expect(li.getCssValue('listStyleType')).toEqual('square'); expect(li.getCssValue('listStyleType')).toEqual('square');
expect(externalLi.getCssValue('listStyleType')).not.toEqual('square'); expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');

View File

@ -1,74 +1,76 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Dependency Injection Cookbook', () => { describe('Dependency Injection Cookbook', function () {
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should render Logged in User example', () => { it('should render Logged in User example', function () {
const loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0); let loggedInUser = element.all(by.xpath('//h3[text()="Logged in user"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('"Bombasto" should be the logged in user', () => { it('"Bombasto" should be the logged in user', function () {
const loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0); let loggedInUser = element.all(by.xpath('//div[text()="Name: Bombasto"]')).get(0);
expect(loggedInUser).toBeDefined(); expect(loggedInUser).toBeDefined();
}); });
it('should render sorted heroes', () => { it('should render sorted heroes', function () {
const sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0); let sortedHeroes = element.all(by.xpath('//h3[text()="Sorted Heroes" and position()=1]')).get(0);
expect(sortedHeroes).toBeDefined(); expect(sortedHeroes).toBeDefined();
}); });
it('Dr Nice should be in sorted heroes', () => { it('Dr Nice should be in sorted heroes', function () {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Dr Nice" and position()=2]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('RubberMan should be in sorted heroes', () => { it('RubberMan should be in sorted heroes', function () {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="RubberMan" and position()=3]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('Magma should be in sorted heroes', () => { it('Magma should be in sorted heroes', function () {
const sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0); let sortedHero = element.all(by.xpath('//sorted-heroes/[text()="Magma"]')).get(0);
expect(sortedHero).toBeDefined(); expect(sortedHero).toBeDefined();
}); });
it('should render Hero of the Month', () => { it('should render Hero of the Month', function () {
const heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0); let heroOfTheMonth = element.all(by.xpath('//h3[text()="Hero of the month"]')).get(0);
expect(heroOfTheMonth).toBeDefined(); expect(heroOfTheMonth).toBeDefined();
}); });
it('should render Hero Bios', () => { it('should render Hero Bios', function () {
const heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0); let heroBios = element.all(by.xpath('//h3[text()="Hero Bios"]')).get(0);
expect(heroBios).toBeDefined(); expect(heroBios).toBeDefined();
}); });
it('should render Magma\'s description in Hero Bios', () => { it('should render Magma\'s description in Hero Bios', function () {
const magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0); let magmaText = element.all(by.xpath('//textarea[text()="Hero of all trades"]')).get(0);
expect(magmaText).toBeDefined(); expect(magmaText).toBeDefined();
}); });
it('should render Magma\'s phone in Hero Bios and Contacts', () => { it('should render Magma\'s phone in Hero Bios and Contacts', function () {
const magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0); let magmaPhone = element.all(by.xpath('//div[text()="Phone #: 555-555-5555"]')).get(0);
expect(magmaPhone).toBeDefined(); expect(magmaPhone).toBeDefined();
}); });
it('should render Hero-of-the-Month runner-ups', () => { it('should render Hero-of-the-Month runner-ups', function () {
const runnersUp = element(by.id('rups1')).getText(); let runnersUp = element(by.id('rups1')).getText();
expect(runnersUp).toContain('RubberMan, Dr Nice'); expect(runnersUp).toContain('RubberMan, Dr Nice');
}); });
it('should render DateLogger log entry in Hero-of-the-Month', () => { it('should render DateLogger log entry in Hero-of-the-Month', function () {
const logs = element.all(by.id('logs')).get(0).getText(); let logs = element.all(by.id('logs')).get(0).getText();
expect(logs).toContain('INFO: starting up at'); expect(logs).toContain('INFO: starting up at');
}); });
it('should highlight Hero Bios and Contacts container when mouseover', () => { it('should highlight Hero Bios and Contacts container when mouseover', function () {
const target = element(by.css('div[appHighlight="yellow"]')); let target = element(by.css('div[appHighlight="yellow"]'));
const yellow = 'rgba(255, 255, 0, 1)'; let yellow = 'rgba(255, 255, 0, 1)';
expect(target.getCssValue('background-color')).not.toEqual(yellow); expect(target.getCssValue('background-color')).not.toEqual(yellow);
@ -79,25 +81,25 @@ describe('Dependency Injection Cookbook', () => {
browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000); browser.wait(() => target.getCssValue('background-color').then(c => c === yellow), 2000);
}); });
describe('in Parent Finder', () => { describe('in Parent Finder', function () {
const cathy1 = element(by.css('alex cathy')); let cathy1 = element(by.css('alex cathy'));
const craig1 = element(by.css('alex craig')); let craig1 = element(by.css('alex craig'));
const carol1 = element(by.css('alex carol p')); let carol1 = element(by.css('alex carol p'));
const carol2 = element(by.css('barry carol p')); let carol2 = element(by.css('barry carol p'));
it('"Cathy" should find "Alex" via the component class', () => { it('"Cathy" should find "Alex" via the component class', function () {
expect(cathy1.getText()).toContain('Found Alex via the component'); expect(cathy1.getText()).toContain('Found Alex via the component');
}); });
it('"Craig" should not find "Alex" via the base class', () => { it('"Craig" should not find "Alex" via the base class', function () {
expect(craig1.getText()).toContain('Did not find Alex via the base'); expect(craig1.getText()).toContain('Did not find Alex via the base');
}); });
it('"Carol" within "Alex" should have "Alex" parent', () => { it('"Carol" within "Alex" should have "Alex" parent', function () {
expect(carol1.getText()).toContain('Alex'); expect(carol1.getText()).toContain('Alex');
}); });
it('"Carol" within "Barry" should have "Barry" parent', () => { it('"Carol" within "Barry" should have "Barry" parent', function () {
expect(carol2.getText()).toContain('Barry'); expect(carol2.getText()).toContain('Barry');
}); });
}); });

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
const routes: Routes = []; const routes: Routes = [];

View File

@ -2,9 +2,9 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
// #docregion import-services // #docregion import-services
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
import { UserContextService } from './user-context.service'; import { UserContextService } from './user-context.service';
import { UserService } from './user.service'; import { UserService } from './user.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',

View File

@ -1,26 +1,26 @@
// #docregion // #docregion
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
// import { AppRoutingModule } from './app-routing.module'; // import { AppRoutingModule } from './app-routing.module';
import { LocationStrategy, import { LocationStrategy,
HashLocationStrategy } from '@angular/common'; HashLocationStrategy } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { HeroData } from './hero-data'; import { HeroData } from './hero-data';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api'; import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HeroBioComponent } from './hero-bio.component'; import { HeroBioComponent } from './hero-bio.component';
import { HeroBiosComponent, import { HeroBiosComponent,
HeroBiosAndContactsComponent } from './hero-bios.component'; HeroBiosAndContactsComponent } from './hero-bios.component';
import { HeroOfTheMonthComponent } from './hero-of-the-month.component'; import { HeroOfTheMonthComponent } from './hero-of-the-month.component';
import { HeroContactComponent } from './hero-contact.component'; import { HeroContactComponent } from './hero-contact.component';
import { HeroesBaseComponent, import { HeroesBaseComponent,
SortedHeroesComponent } from './sorted-heroes.component'; SortedHeroesComponent } from './sorted-heroes.component';
import { HighlightDirective } from './highlight.directive'; import { HighlightDirective } from './highlight.directive';
import { ParentFinderComponent, import { ParentFinderComponent,
AlexComponent, AlexComponent,
AliceComponent, AliceComponent,
@ -30,8 +30,8 @@ import { ParentFinderComponent,
CathyComponent, CathyComponent,
BarryComponent, BarryComponent,
BethComponent, BethComponent,
BobComponent } from './parent-finder.component'; BobComponent } from './parent-finder.component';
import { StorageComponent } from './storage.component'; import { StorageComponent } from './storage.component';
const declarations = [ const declarations = [
AppComponent, AppComponent,
@ -42,11 +42,11 @@ const declarations = [
ParentFinderComponent, ParentFinderComponent,
]; ];
const componentListA = [ AliceComponent, AlexComponent ]; const a_components = [AliceComponent, AlexComponent ];
const componentListB = [ BarryComponent, BethComponent, BobComponent ]; const b_components = [ BarryComponent, BethComponent, BobComponent ];
const componentListC = [ const c_components = [
CarolComponent, ChrisComponent, CraigComponent, CarolComponent, ChrisComponent, CraigComponent,
CathyComponent CathyComponent
]; ];
@ -61,9 +61,9 @@ const componentListC = [
], ],
declarations: [ declarations: [
declarations, declarations,
componentListA, a_components,
componentListB, b_components,
componentListC, c_components,
StorageComponent, StorageComponent,
], ],
bootstrap: [ AppComponent ], bootstrap: [ AppComponent ],

View File

@ -1,6 +1,6 @@
/* tslint:disable:one-line*/ /* tslint:disable:one-line*/
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { HeroCacheService } from './hero-cache.service'; import { HeroCacheService } from './hero-cache.service';
// #docregion component // #docregion component
@Component({ @Component({

View File

@ -1,9 +1,9 @@
// #docplaster // #docplaster
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
//////// HeroBiosComponent //// //////// HeroBiosComponent ////
// #docregion simple // #docregion simple

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
// #docregion service // #docregion service

View File

@ -3,7 +3,7 @@
import { Component, Host, Optional } from '@angular/core'; import { Component, Host, Optional } from '@angular/core';
import { HeroCacheService } from './hero-cache.service'; import { HeroCacheService } from './hero-cache.service';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
// #docregion component // #docregion component
@Component({ @Component({

View File

@ -3,7 +3,7 @@ import { Hero } from './hero';
export class HeroData { export class HeroData {
createDb() { createDb() {
const heroes = [ let heroes = [
new Hero(1, 'Windstorm'), new Hero(1, 'Windstorm'),
new Hero(2, 'Bombasto'), new Hero(2, 'Bombasto'),
new Hero(3, 'Magneta'), new Hero(3, 'Magneta'),

View File

@ -1,8 +1,8 @@
// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent // Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent
// Injecting with the MinimalLogger "interface-class" // Injecting with the MinimalLogger "interface-class"
import { Component, NgModule } from '@angular/core'; import { Component, NgModule } from '@angular/core';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service'; import { MinimalLogger } from './minimal-logger.service';
// #docregion // #docregion
@Component({ @Component({

View File

@ -10,12 +10,12 @@ export const TITLE = new InjectionToken<string>('title');
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { DateLoggerService } from './date-logger.service'; import { DateLoggerService } from './date-logger.service';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service'; import { MinimalLogger } from './minimal-logger.service';
import { RUNNERS_UP, import { RUNNERS_UP,
runnersUpFactory } from './runners-up'; runnersUpFactory } from './runners-up';
// #enddocregion hero-of-the-month // #enddocregion hero-of-the-month
// #docregion some-hero // #docregion some-hero

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -1,4 +1,5 @@
// tslint:disable: component-selector space-before-function-paren /* tslint:disable:no-unused-variable component-selector-name one-line check-open-brace */
/* tslint:disable:*/
// #docplaster // #docplaster
// #docregion // #docregion
import { Component, forwardRef, Optional, SkipSelf } from '@angular/core'; import { Component, forwardRef, Optional, SkipSelf } from '@angular/core';
@ -19,7 +20,8 @@ const DifferentParent = Parent;
// The `parentType` defaults to `Parent` when omitting the second parameter. // The `parentType` defaults to `Parent` when omitting the second parameter.
// #docregion provide-the-parent // #docregion provide-the-parent
export function provideParent export function provideParent
// #enddocregion provide-the-parent // #enddocregion provide-parent, provide-the-parent
// #docregion provide-parent
(component: any, parentType?: any) { (component: any, parentType?: any) {
return { provide: parentType || Parent, useExisting: forwardRef(() => component) }; return { provide: parentType || Parent, useExisting: forwardRef(() => component) };
} }

View File

@ -2,7 +2,7 @@
// #docregion // #docregion
import { InjectionToken } from '@angular/core'; import { InjectionToken } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
// #docregion runners-up // #docregion runners-up
@ -22,5 +22,5 @@ export function runnersUpFactory(take: number) {
.join(', '); .join(', ');
// #docregion factory-synopsis // #docregion factory-synopsis
}; };
} };
// #enddocregion factory-synopsis // #enddocregion factory-synopsis

View File

@ -2,8 +2,8 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
/////// HeroesBaseComponent ///// /////// HeroesBaseComponent /////
// #docregion heroes-base, injection // #docregion heroes-base, injection

View File

@ -1,9 +1,9 @@
// #docplaster // #docplaster
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service'; import { LoggerService } from './logger.service';
import { UserService } from './user.service'; import { UserService } from './user.service';
// #docregion injectables, injectable // #docregion injectables, injectable
@Injectable({ @Injectable({
@ -24,7 +24,7 @@ export class UserContextService {
// #enddocregion ctor, injectables // #enddocregion ctor, injectables
loadUser(userId: number) { loadUser(userId: number) {
const user = this.userService.getUserById(userId); let user = this.userService.getUserById(userId);
this.name = user.name; this.name = user.name;
this.role = user.role; this.role = user.role;

View File

@ -1,196 +1,202 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by, ElementFinder } from 'protractor'; import { browser, element, by, ElementFinder } from 'protractor';
describe('Dependency Injection Tests', () => { describe('Dependency Injection Tests', function () {
let expectedMsg: string; let expectedMsg: string;
let expectedMsgRx: RegExp; let expectedMsgRx: RegExp;
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
describe('Cars:', () => { describe('Cars:', function() {
it('DI car displays as expected', () => { it('DI car displays as expected', function () {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#di')).getText()).toEqual(expectedMsg); expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
}); });
it('No DI car displays as expected', () => { it('No DI car displays as expected', function () {
expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg); expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
}); });
it('Injector car displays as expected', () => { it('Injector car displays as expected', function () {
expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#injector')).getText()).toEqual(expectedMsg); expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
}); });
it('Factory car displays as expected', () => { it('Factory car displays as expected', function () {
expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#factory')).getText()).toEqual(expectedMsg); expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
}); });
it('Simple car displays as expected', () => { it('Simple car displays as expected', function () {
expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.'; expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#simple')).getText()).toEqual(expectedMsg); expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
}); });
it('Super car displays as expected', () => { it('Super car displays as expected', function () {
expectedMsg = 'Super car with 12 cylinders and Flintstone tires.'; expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
expect(element(by.css('#super')).getText()).toEqual(expectedMsg); expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
}); });
it('Test car displays as expected', () => { it('Test car displays as expected', function () {
expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.'; expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
expect(element(by.css('#test')).getText()).toEqual(expectedMsg); expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('Other Injections:', () => { describe('Other Injections:', function() {
it('DI car displays as expected', () => { it('DI car displays as expected', function () {
expectedMsg = 'DI car with 4 cylinders and Flintstone tires.'; expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
expect(element(by.css('#car')).getText()).toEqual(expectedMsg); expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
}); });
it('Hero displays as expected', () => { it('Hero displays as expected', function () {
expectedMsg = 'Dr Nice'; expectedMsg = 'Dr Nice';
expect(element(by.css('#hero')).getText()).toEqual(expectedMsg); expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
}); });
it('Optional injection displays as expected', () => { it('Optional injection displays as expected', function () {
expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!'; expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg); expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('Tests:', () => { describe('Tests:', function() {
it('Tests display as expected', () => { it('Tests display as expected', function () {
expectedMsgRx = /Tests passed/; expectedMsgRx = /Tests passed/;
expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
}); });
}); });
describe('Provider variations:', () => { describe('Provider variations:', function() {
it('P1 (class) displays as expected', () => { it('P1 (class) displays as expected', function () {
expectedMsg = 'Hello from logger provided with Logger class'; expectedMsg = 'Hello from logger provided with Logger class';
expect(element(by.css('#p1')).getText()).toEqual(expectedMsg); expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
}); });
it('P3 (provide) displays as expected', () => { it('P3 (provide) displays as expected', function () {
expectedMsg = 'Hello from logger provided with useClass:Logger'; expectedMsg = 'Hello from logger provided with useClass:Logger';
expect(element(by.css('#p3')).getText()).toEqual(expectedMsg); expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
}); });
it('P4 (useClass:BetterLogger) displays as expected', () => { it('P4 (useClass:BetterLogger) displays as expected', function () {
expectedMsg = 'Hello from logger provided with useClass:BetterLogger'; expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
expect(element(by.css('#p4')).getText()).toEqual(expectedMsg); expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
}); });
it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', () => { it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
expectedMsg = 'Message to Bob: Hello from EvenBetterlogger'; expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
expect(element(by.css('#p5')).getText()).toEqual(expectedMsg); expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
}); });
it('P6a (no alias) displays as expected', () => { it('P6a (no alias) displays as expected', function () {
expectedMsg = 'Hello OldLogger (but we want NewLogger)'; expectedMsg = 'Hello OldLogger (but we want NewLogger)';
expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg); expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
}); });
it('P6b (alias) displays as expected', () => { it('P6b (alias) displays as expected', function () {
expectedMsg = 'Hello from NewLogger (via aliased OldLogger)'; expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg); expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
}); });
it('P7 (useValue) displays as expected', () => { it('P7 (useValue) displays as expected', function () {
expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"'; expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
expect(element(by.css('#p7')).getText()).toEqual(expectedMsg); expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
}); });
it('P8 (useFactory) displays as expected', () => { it('P8 (useFactory) displays as expected', function () {
expectedMsg = 'Hero service injected successfully via heroServiceProvider'; expectedMsg = 'Hero service injected successfully via heroServiceProvider';
expect(element(by.css('#p8')).getText()).toEqual(expectedMsg); expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
}); });
it('P9 (InjectionToken) displays as expected', () => { it('P9 (InjectionToken) displays as expected', function () {
expectedMsg = 'APP_CONFIG Application title is Dependency Injection'; expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
expect(element(by.css('#p9')).getText()).toEqual(expectedMsg); expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
}); });
it('P10 (optional dependency) displays as expected', () => { it('P10 (optional dependency) displays as expected', function () {
expectedMsg = 'Optional logger was not available'; expectedMsg = 'Optional logger was not available';
expect(element(by.css('#p10')).getText()).toEqual(expectedMsg); expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
}); });
}); });
describe('User/Heroes:', () => { describe('User/Heroes:', function() {
it('User is Bob - unauthorized', () => { it('User is Bob - unauthorized', function () {
expectedMsgRx = /Bob, is not authorized/; expectedMsgRx = /Bob, is not authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('should have button', () => { it('should have button', function () {
expect(element.all(by.cssContainingText('button', 'Next User')) expect(element.all(by.cssContainingText('button', 'Next User'))
.get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed'); .get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
}); });
it('unauthorized user should have multiple unauthorized heroes', () => { it('unauthorized user should have multiple unauthorized heroes', function () {
const heroes = element.all(by.css('#unauthorized app-hero-list div')); let heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('unauthorized user should have no secret heroes', () => { it('unauthorized user should have no secret heroes', function () {
const heroes = element.all(by.css('#unauthorized app-hero-list div')); let heroes = element.all(by.css('#unauthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => { let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
return elem.getText().then((text: string) => /secret/.test(text)); return elem.getText().then((text: string) => {
return /secret/.test(text);
});
}); });
expect(filteredHeroes.count()).toEqual(0); expect(filteredHeroes.count()).toEqual(0);
}); });
it('unauthorized user should have no authorized heroes listed', () => { it('unauthorized user should have no authorized heroes listed', function () {
expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0); expect(element.all(by.css('#authorized app-hero-list div')).count()).toEqual(0);
}); });
describe('after button click', () => { describe('after button click', function() {
beforeAll((done: any) => { beforeAll(function (done: any) {
const buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0); let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
buttonEle.click().then(done, done); buttonEle.click().then(done, done);
}); });
it('User is Alice - authorized', () => { it('User is Alice - authorized', function () {
expectedMsgRx = /Alice, is authorized/; expectedMsgRx = /Alice, is authorized/;
expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx); expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
}); });
it('authorized user should have multiple authorized heroes ', () => { it('authorized user should have multiple authorized heroes ', function () {
const heroes = element.all(by.css('#authorized app-hero-list div')); let heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', () => { it('authorized user should have multiple authorized heroes with tree-shakeable HeroesService', function () {
const heroes = element.all(by.css('#tspAuthorized app-hero-list div')); let heroes = element.all(by.css('#tspAuthorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have secret heroes', () => { it('authorized user should have secret heroes', function () {
const heroes = element.all(by.css('#authorized app-hero-list div')); let heroes = element.all(by.css('#authorized app-hero-list div'));
expect(heroes.count()).toBeGreaterThan(0); expect(heroes.count()).toBeGreaterThan(0);
const filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => { let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number) {
return elem.getText().then((text: string) => /secret/.test(text)); return elem.getText().then(function(text: string) {
return /secret/.test(text);
});
}); });
expect(filteredHeroes.count()).toBeGreaterThan(0); expect(filteredHeroes.count()).toBeGreaterThan(0);
}); });
it('authorized user should have no unauthorized heroes listed', () => { it('authorized user should have no unauthorized heroes listed', function () {
expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0); expect(element.all(by.css('#unauthorized app-hero-list div')).count()).toEqual(0);
}); });
}); });

View File

@ -2,7 +2,7 @@
// #docregion imports // #docregion imports
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config'; import { APP_CONFIG, AppConfig } from './app.config';
// #enddocregion imports // #enddocregion imports
@Component({ @Component({

View File

@ -1,8 +1,8 @@
// #docplaster // #docplaster
import { NgModule } from '@angular/core'; 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 { AppComponent } from './app.component';
import { CarComponent } from './car/car.component'; import { CarComponent } from './car/car.component';
import { HeroesComponent } from './heroes/heroes.component'; import { HeroesComponent } from './heroes/heroes.component';

View File

@ -7,7 +7,7 @@ import { Car, Engine, Tires } from './car';
export function simpleCar() { export function simpleCar() {
// #docregion car-ctor-instantiation // #docregion car-ctor-instantiation
// Simple car with 4 cylinders and Flintstone tires. // Simple car with 4 cylinders and Flintstone tires.
const car = new Car(new Engine(), new Tires()); let car = new Car(new Engine(), new Tires());
// #enddocregion car-ctor-instantiation // #enddocregion car-ctor-instantiation
car.description = 'Simple'; car.description = 'Simple';
return car; return car;
@ -16,31 +16,30 @@ export function simpleCar() {
///////// example 2 //////////// ///////// example 2 ////////////
// #docregion car-ctor-instantiation-with-param // #docregion car-ctor-instantiation-with-param
class Engine2 { class Engine2 {
constructor(public cylinders: number) { } constructor(public cylinders: number) { }
} }
// #enddocregion car-ctor-instantiation-with-param // #enddocregion car-ctor-instantiation-with-param
export function superCar() { export function superCar() {
// #docregion car-ctor-instantiation-with-param // #docregion car-ctor-instantiation-with-param
// Super car with 12 cylinders and Flintstone tires. // Super car with 12 cylinders and Flintstone tires.
const bigCylinders = 12; let bigCylinders = 12;
const car = new Car(new Engine2(bigCylinders), new Tires()); let car = new Car(new Engine2(bigCylinders), new Tires());
// #enddocregion car-ctor-instantiation-with-param // #enddocregion car-ctor-instantiation-with-param
car.description = 'Super'; car.description = 'Super';
return car; return car;
} }
/////////// example 3 ////////// /////////// example 3 //////////
// #docregion car-ctor-instantiation-with-mocks // #docregion car-ctor-instantiation-with-mocks
class MockEngine extends Engine { cylinders = 8; } class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; } class MockTires extends Tires { make = 'YokoGoodStone'; }
// #enddocregion car-ctor-instantiation-with-mocks // #enddocregion car-ctor-instantiation-with-mocks
export function testCar() { export function testCar() {
// #docregion car-ctor-instantiation-with-mocks // #docregion car-ctor-instantiation-with-mocks
// Test car with 8 cylinders and YokoGoodStone tires. // Test car with 8 cylinders and YokoGoodStone tires.
const car = new Car(new MockEngine(), new MockTires()); let car = new Car(new MockEngine(), new MockTires());
// #enddocregion car-ctor-instantiation-with-mocks // #enddocregion car-ctor-instantiation-with-mocks
car.description = 'Test'; car.description = 'Test';
return car; return car;

View File

@ -4,7 +4,7 @@ import { Engine, Tires, Car } from './car';
// BAD pattern! // BAD pattern!
export class CarFactory { export class CarFactory {
createCar() { createCar() {
const car = new Car(this.createEngine(), this.createTires()); let car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory'; car.description = 'Factory';
return car; return car;
} }

View File

@ -1,7 +1,7 @@
import { Injector } from '@angular/core'; import { Injector } from '@angular/core';
import { Car, Engine, Tires } from './car'; import { Car, Engine, Tires } from './car';
import { Logger } from '../logger.service'; import { Logger } from '../logger.service';
// #docregion injector // #docregion injector
export function useInjector() { export function useInjector() {
@ -26,14 +26,14 @@ export function useInjector() {
] ]
}); });
// #docregion injector-call // #docregion injector-call
const car = injector.get(Car); let car = injector.get(Car);
// #enddocregion injector-call, injector-create-and-call // #enddocregion injector-call, injector-create-and-call
car.description = 'Injector'; car.description = 'Injector';
injector = Injector.create({ injector = Injector.create({
providers: [{ provide: Logger, deps: [] }] providers: [{ provide: Logger, deps: [] }]
}); });
const logger = injector.get(Logger); let logger = injector.get(Logger);
logger.log('Injector car.drive() said: ' + car.drive()); logger.log('Injector car.drive() said: ' + car.drive());
return car; return car;
} }

View File

@ -1,15 +1,15 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Car, Engine, Tires } from './car'; import { Car, Engine, Tires } from './car';
import { Car as CarNoDi } from './car-no-di'; import { Car as CarNoDi } from './car-no-di';
import { CarFactory } from './car-factory'; import { CarFactory } from './car-factory';
import { testCar, import { testCar,
simpleCar, simpleCar,
superCar } from './car-creations'; superCar } from './car-creations';
import { useInjector } from './car-injector'; import { useInjector } from './car-injector';
@Component({ @Component({
@ -27,9 +27,9 @@ import { useInjector } from './car-injector';
providers: [Car, Engine, Tires] providers: [Car, Engine, Tires]
}) })
export class CarComponent { export class CarComponent {
factoryCar = (new CarFactory()).createCar(); factoryCar = (new CarFactory).createCar();
injectorCar = useInjector(); injectorCar = useInjector();
noDiCar = new CarNoDi(); noDiCar = new CarNoDi;
simpleCar = simpleCar(); simpleCar = simpleCar();
superCar = superCar(); superCar = superCar();
testCar = testCar(); testCar = testCar();

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
@Component({ @Component({
selector: 'app-hero-list', selector: 'app-hero-list',

View File

@ -1,7 +1,7 @@
// #docplaster // #docplaster
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
// #enddocregion // #enddocregion
import { HeroService } from './hero.service.1'; import { HeroService } from './hero.service.1';
/* /*

View File

@ -1,7 +1,7 @@
/* tslint:disable:one-line */ /* tslint:disable:one-line */
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Hero } from './hero'; import { Hero } from './hero';
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
@Component({ @Component({

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service'; import { Logger } from '../logger.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',

View File

@ -1,11 +1,11 @@
/* tslint:disable:one-line */ /* tslint:disable:one-line */
// #docregion // #docregion
import { HeroService } from './hero.service'; import { HeroService } from './hero.service';
import { Logger } from '../logger.service'; import { Logger } from '../logger.service';
import { UserService } from '../user.service'; import { UserService } from '../user.service';
// #docregion factory // #docregion factory
const heroServiceFactory = (logger: Logger, userService: UserService) => { let heroServiceFactory = (logger: Logger, userService: UserService) => {
return new HeroService(logger, userService.user.isAuthorized); return new HeroService(logger, userService.user.isAuthorized);
}; };
// #enddocregion factory // #enddocregion factory

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes'; import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service'; import { Logger } from '../logger.service';
import { UserService } from '../user.service'; import { UserService } from '../user.service';
@Injectable({ @Injectable({
@ -17,7 +17,7 @@ export class HeroService {
private isAuthorized: boolean) { } private isAuthorized: boolean) { }
getHeroes() { getHeroes() {
const auth = this.isAuthorized ? 'authorized ' : 'unauthorized'; let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`); this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret); return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
} }

View File

@ -1,5 +1,5 @@
// #docregion // #docregion
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { heroServiceProvider } from './hero.service.provider'; import { heroServiceProvider } from './hero.service.provider';
@Component({ @Component({

View File

@ -2,11 +2,11 @@
// #docregion // #docregion
import { Component, Injector, OnInit } from '@angular/core'; import { Component, Injector, OnInit } from '@angular/core';
import { Car, Engine, Tires } from './car/car'; import { Car, Engine, Tires } from './car/car';
import { Hero } from './heroes/hero'; import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service'; import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider'; import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service'; import { Logger } from './logger.service';
// #docregion injector // #docregion injector
@Component({ @Component({
@ -36,7 +36,7 @@ export class InjectorComponent implements OnInit {
} }
get rodent() { get rodent() {
const rousDontExist = `R.O.U.S.'s? I don't think they exist!`; let rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
return this.injector.get(ROUS, rousDontExist); return this.injector.get(ROUS, rousDontExist);
} }
} }

View File

@ -18,7 +18,7 @@ const template = '{{log}}';
@Component({ @Component({
selector: 'provider-1', selector: 'provider-1',
template, template: template,
// #docregion providers-1, providers-logger // #docregion providers-1, providers-logger
providers: [Logger] providers: [Logger]
// #enddocregion providers-1, providers-logger // #enddocregion providers-1, providers-logger
@ -35,7 +35,7 @@ export class Provider1Component {
@Component({ @Component({
selector: 'provider-3', selector: 'provider-3',
template, template: template,
providers: providers:
// #docregion providers-3 // #docregion providers-3
[{ provide: Logger, useClass: Logger }] [{ provide: Logger, useClass: Logger }]
@ -54,7 +54,7 @@ export class BetterLogger extends Logger {}
@Component({ @Component({
selector: 'provider-4', selector: 'provider-4',
template, template: template,
providers: providers:
// #docregion providers-4 // #docregion providers-4
[{ provide: Logger, useClass: BetterLogger }] [{ provide: Logger, useClass: BetterLogger }]
@ -76,7 +76,7 @@ export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); } constructor(private userService: UserService) { super(); }
log(message: string) { log(message: string) {
const name = this.userService.user.name; let name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`); super.log(`Message to ${name}: ${message}`);
} }
} }
@ -84,7 +84,7 @@ export class EvenBetterLogger extends Logger {
@Component({ @Component({
selector: 'provider-5', selector: 'provider-5',
template, template: template,
providers: providers:
// #docregion providers-5 // #docregion providers-5
[ UserService, [ UserService,
@ -107,12 +107,12 @@ export class OldLogger {
logs: string[] = []; logs: string[] = [];
log(message: string) { log(message: string) {
throw new Error('Should not call the old logger!'); throw new Error('Should not call the old logger!');
} };
} }
@Component({ @Component({
selector: 'provider-6a', selector: 'provider-6a',
template, template: template,
providers: providers:
// #docregion providers-6a // #docregion providers-6a
[ NewLogger, [ NewLogger,
@ -135,7 +135,7 @@ export class Provider6aComponent {
@Component({ @Component({
selector: 'provider-6b', selector: 'provider-6b',
template, template: template,
providers: providers:
// #docregion providers-6b // #docregion providers-6b
[ NewLogger, [ NewLogger,
@ -168,7 +168,7 @@ export const SilentLogger = {
@Component({ @Component({
selector: 'provider-7', selector: 'provider-7',
template, template: template,
providers: providers:
// #docregion providers-7 // #docregion providers-7
[{ provide: Logger, useValue: SilentLogger }] [{ provide: Logger, useValue: SilentLogger }]
@ -186,7 +186,7 @@ export class Provider7Component {
@Component({ @Component({
selector: 'provider-8', selector: 'provider-8',
template, template: template,
providers: [heroServiceProvider, Logger, UserService] providers: [heroServiceProvider, Logger, UserService]
}) })
export class Provider8Component { export class Provider8Component {
@ -202,7 +202,7 @@ export class Provider8Component {
@Component({ @Component({
selector: 'provider-9', selector: 'provider-9',
template, template: template,
/* /*
// #docregion providers-9-interface // #docregion providers-9-interface
// FAIL! Can't use interface as provider token // FAIL! Can't use interface as provider token
@ -237,11 +237,11 @@ export class Provider9Component implements OnInit {
import { Optional } from '@angular/core'; import { Optional } from '@angular/core';
// #enddocregion import-optional // #enddocregion import-optional
const someMessage = 'Hello from the injected logger'; let some_message = 'Hello from the injected logger';
@Component({ @Component({
selector: 'provider-10', selector: 'provider-10',
template, template: template,
providers: [{ provide: Logger, useValue: null }] providers: [{ provide: Logger, useValue: null }]
}) })
export class Provider10Component implements OnInit { export class Provider10Component implements OnInit {
@ -249,7 +249,7 @@ export class Provider10Component implements OnInit {
// #docregion provider-10-ctor // #docregion provider-10-ctor
constructor(@Optional() private logger?: Logger) { constructor(@Optional() private logger?: Logger) {
if (this.logger) { if (this.logger) {
this.logger.log(someMessage); this.logger.log(some_message);
} }
} }
// #enddocregion provider-10-ctor // #enddocregion provider-10-ctor

View File

@ -43,7 +43,7 @@ var testResults: {pass: string; message: string};
function expect(actual: any) { function expect(actual: any) {
return { return {
toEqual: (expected: any) => { toEqual: function(expected: any){
testResults = actual === expected ? testResults = actual === expected ?
{pass: 'passed', message: testName} : {pass: 'passed', message: testName} :
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`}; {pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};

View File

@ -1,6 +1,6 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { ServiceModule } from './service-and-module'; import { ServiceModule } from './service-and-module';
// #docregion // #docregion

View File

@ -8,8 +8,8 @@ export class User {
} }
// TODO: get the user; don't 'new' it. // TODO: get the user; don't 'new' it.
const alice = new User('Alice', true); let alice = new User('Alice', true);
const bob = new User('Bob', false); let bob = new User('Bob', false);
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'

View File

@ -1,27 +1,29 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Displaying Data Tests', () => { describe('Displaying Data Tests', function () {
const title = 'Tour of Heroes'; let _title = 'Tour of Heroes';
const defaultHero = 'Windstorm'; let _defaultHero = 'Windstorm';
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should display correct title: ' + title, () => { it('should display correct title: ' + _title, function () {
expect(element(by.css('h1')).getText()).toEqual(title); expect(element(by.css('h1')).getText()).toEqual(_title);
}); });
it('should have correct default hero: ' + defaultHero, () => { it('should have correct default hero: ' + _defaultHero, function () {
expect(element(by.css('h2')).getText()).toContain(defaultHero); expect(element(by.css('h2')).getText()).toContain(_defaultHero);
}); });
it('should have heroes', () => { it('should have heroes', function () {
const heroEls = element.all(by.css('li')); let heroEls = element.all(by.css('li'));
expect(heroEls.count()).not.toBe(0, 'should have heroes'); expect(heroEls.count()).not.toBe(0, 'should have heroes');
}); });
it('should display "there are many heroes!"', () => { it('should display "there are many heroes!"', function () {
expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!'); expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
}); });
}); });

View File

@ -1,6 +1,6 @@
// #docregion // #docregion
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';

View File

@ -1,13 +1,15 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
describe('Docs Style Guide', () => { describe('Docs Style Guide', function () {
const title = 'Authors Style Guide Sample'; let _title = 'Authors Style Guide Sample';
beforeAll(() => { beforeAll(function () {
browser.get(''); browser.get('');
}); });
it('should display correct title: ' + title, () => { it('should display correct title: ' + _title, function () {
expect(element(by.css('h1')).getText()).toEqual(title); expect(element(by.css('h1')).getText()).toEqual(_title);
}); });
}); });

View File

@ -1,9 +1,9 @@
// #docregion // #docregion
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; 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 // #docregion class
@NgModule({ @NgModule({

View File

@ -1,16 +1,18 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor'; import { browser, element, by } from 'protractor';
/* tslint:disable:quotemark */ /* tslint:disable:quotemark */
describe('Dynamic Component Loader', () => { describe('Dynamic Component Loader', function () {
beforeEach(() => { beforeEach(function () {
browser.get(''); browser.get('');
}); });
it('should load ad banner', () => { it('should load ad banner', function () {
const headline = element(by.xpath("//h4[text()='Featured Hero Profile']")); let headline = element(by.xpath("//h4[text()='Featured Hero Profile']"));
const name = element(by.xpath("//h3[text()='Bombasto']")); let name = element(by.xpath("//h3[text()='Bombasto']"));
const bio = element(by.xpath("//p[text()='Brave as they come']")); let bio = element(by.xpath("//p[text()='Brave as they come']"));
expect(name).toBeDefined(); expect(name).toBeDefined();
expect(headline).toBeDefined(); expect(headline).toBeDefined();

View File

@ -2,7 +2,7 @@
import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core'; import { Component, Input, OnInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { AdDirective } from './ad.directive'; import { AdDirective } from './ad.directive';
import { AdItem } from './ad-item'; import { AdItem } from './ad-item';
import { AdComponent } from './ad.component'; import { AdComponent } from './ad.component';
@Component({ @Component({
@ -11,7 +11,7 @@ import { AdComponent } from './ad.component';
template: ` template: `
<div class="ad-banner-example"> <div class="ad-banner-example">
<h3>Advertisements</h3> <h3>Advertisements</h3>
<ng-template adHost></ng-template> <ng-template ad-host></ng-template>
</div> </div>
` `
// #enddocregion ad-host // #enddocregion ad-host
@ -43,8 +43,8 @@ export class AdBannerComponent implements OnInit, OnDestroy {
const viewContainerRef = this.adHost.viewContainerRef; const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory); const componentRef = viewContainerRef.createComponent(componentFactory);
componentRef.instance.data = adItem.data; (<AdComponent>componentRef.instance).data = adItem.data;
} }
getAds() { getAds() {

View File

@ -1,9 +1,8 @@
// tslint:disable: directive-selector
// #docregion // #docregion
import { Directive, ViewContainerRef } from '@angular/core'; import { Directive, ViewContainerRef } from '@angular/core';
@Directive({ @Directive({
selector: '[adHost]', selector: '[ad-host]',
}) })
export class AdDirective { export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { } constructor(public viewContainerRef: ViewContainerRef) { }

View File

@ -1,9 +1,9 @@
// #docregion // #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 { HeroProfileComponent } from './hero-profile.component';
import { AdItem } from './ad-item'; import { AdItem } from './ad-item';
@Injectable() @Injectable()
export class AdService { export class AdService {

View File

@ -1,8 +1,8 @@
// #docregion // #docregion
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { AdService } from './ad.service'; import { AdService } from './ad.service';
import { AdItem } from './ad-item'; import { AdItem } from './ad-item';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',

View File

@ -1,12 +1,12 @@
// #docregion // #docregion
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { HeroJobAdComponent } from './hero-job-ad.component'; import { HeroJobAdComponent } from './hero-job-ad.component';
import { AdBannerComponent } from './ad-banner.component'; import { AdBannerComponent } from './ad-banner.component';
import { HeroProfileComponent } from './hero-profile.component'; import { HeroProfileComponent } from './hero-profile.component';
import { AdDirective } from './ad.directive'; import { AdDirective } from './ad.directive';
import { AdService } from './ad.service'; import { AdService } from './ad.service';
@NgModule({ @NgModule({
imports: [ BrowserModule ], imports: [ BrowserModule ],

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component'; import { AdComponent } from './ad.component';
@Component({ @Component({
template: ` template: `

View File

@ -1,7 +1,7 @@
// #docregion // #docregion
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component'; import { AdComponent } from './ad.component';
@Component({ @Component({
template: ` template: `

Some files were not shown because too many files have changed in this diff Show More