Compare commits
114 Commits
Author | SHA1 | Date | |
---|---|---|---|
61b1425aed | |||
71fb99f062 | |||
7ca17a6249 | |||
3cce723370 | |||
b7bf5258ce | |||
823200c6c1 | |||
cd8ec3d902 | |||
02b87505ee | |||
32a8e72a14 | |||
7827d3ee73 | |||
38db976617 | |||
d8632ed541 | |||
10db99588f | |||
8fd59dabba | |||
d8e313bb6d | |||
c602c9bdc6 | |||
4a5f17cd8e | |||
02405f1406 | |||
94d7ef3909 | |||
4fd70a2bf3 | |||
af170384b4 | |||
95fcb4164f | |||
f90c1f77d8 | |||
6efbc8b58c | |||
33c7d23fdd | |||
670fac0ace | |||
9baa85f83b | |||
72cdd42278 | |||
591fa51a6f | |||
1623f5d817 | |||
4207cff04f | |||
f591fc8d53 | |||
c558c02e9e | |||
2664338582 | |||
5db7b4c354 | |||
e08d02145e | |||
dae3a77c43 | |||
02e75df3e7 | |||
f752ab9367 | |||
9c875b30dc | |||
8fa78d10ab | |||
26988f0d62 | |||
6acf117386 | |||
8b731e374d | |||
5378f78e79 | |||
495c40e31c | |||
9963c5d8f7 | |||
13c4a7b1da | |||
70e85d226c | |||
dfb129dbfa | |||
1a1407aef6 | |||
da71f76a6a | |||
0c9b76e9e7 | |||
db34b23ab5 | |||
df0941554b | |||
5221df8e92 | |||
bc35c6a8ed | |||
23fc2b43ac | |||
c16995eb59 | |||
3487d5a13a | |||
1efc75cd72 | |||
c4190e3142 | |||
5d98834585 | |||
529ff73200 | |||
791205005c | |||
2d37e47e95 | |||
27c8066641 | |||
952fd8662c | |||
42be9047d8 | |||
52a0c6b36e | |||
8913aee527 | |||
7628c36f49 | |||
b417dd7b9c | |||
8cf16b4815 | |||
806aed63f4 | |||
eb34037cd7 | |||
ba0df5250f | |||
c4553bbed9 | |||
cc13b37446 | |||
166df5d2ca | |||
a5b474580c | |||
9f132d0d93 | |||
8dee378b3e | |||
0845d1148f | |||
7cfa57a5f7 | |||
f80c22002b | |||
7aa12412f3 | |||
072b707b38 | |||
223b80cb7d | |||
416403fc63 | |||
878e2f0deb | |||
4c30f5135b | |||
6791cd79af | |||
f50313f54d | |||
30433a0710 | |||
86ab9f92b4 | |||
42f9679376 | |||
bee10574d8 | |||
afce0f5038 | |||
bb19e61848 | |||
50e83e2566 | |||
d80c4890be | |||
4f8b716c13 | |||
cb85d69450 | |||
4a446878fa | |||
cb80f46c64 | |||
5199d55d45 | |||
bb11fd9058 | |||
771f7318f0 | |||
5d584b7728 | |||
1b5f6ee7a6 | |||
669e07580c | |||
965249d1da | |||
3a01856e7c |
4
.bazelrc
@ -74,8 +74,8 @@ test --test_output=errors
|
||||
# Trick bazel into treating BUILD files under integration/bazel as being regular files
|
||||
# This lets us glob() up all the files inside this integration test to make them inputs to tests
|
||||
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
|
||||
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
||||
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/test/e2e
|
||||
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
|
||||
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
|
||||
|
||||
################################
|
||||
# Temporary Settings for Ivy #
|
||||
|
@ -1,3 +1,3 @@
|
||||
3.2.0
|
||||
3.5.1
|
||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
||||
|
@ -14,8 +14,8 @@ build --repository_cache=/home/circleci/bazel_repository_cache
|
||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
|
||||
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
|
||||
build --local_cpu_resources=8
|
||||
build --local_ram_resources=14336
|
||||
build --local_cpu_resources=20
|
||||
build --local_ram_resources=32768
|
||||
|
||||
# All build executed remotely should be done using our RBE configuration.
|
||||
build:remote --google_default_credentials
|
||||
|
@ -6,13 +6,9 @@
|
||||
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
|
||||
try-import %workspace%/.circleci/bazel.common.rc
|
||||
|
||||
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
|
||||
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
|
||||
build --repository_cache=C:/Users/circleci/bazel_repository_cache
|
||||
|
||||
# Manually set the local resources used in windows CI runs
|
||||
build --local_ram_resources=13500
|
||||
build --local_cpu_resources=4
|
||||
build --local_ram_resources=120000
|
||||
build --local_cpu_resources=32
|
||||
|
||||
# All windows jobs run on master and should use http caching
|
||||
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
|
||||
|
@ -27,8 +27,6 @@ var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum
|
||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||
# be slower due to its growing size.
|
||||
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
||||
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
||||
|
||||
# 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.
|
||||
@ -80,7 +78,7 @@ executors:
|
||||
|
||||
windows-executor:
|
||||
working_directory: ~/ng
|
||||
resource_class: windows.medium
|
||||
resource_class: windows.2xlarge
|
||||
# CircleCI windows VMs do have the GitBash shell available:
|
||||
# https://github.com/CircleCI-Public/windows-preview-docs#shells
|
||||
# But in this specific case we really should not use it because Bazel must not be ran from
|
||||
@ -183,23 +181,12 @@ commands:
|
||||
setup_win:
|
||||
description: Setup windows node environment
|
||||
steps:
|
||||
# Use the Linux workspace directly, as it already has checkout, rebased and node modules.
|
||||
- custom_attach_workspace
|
||||
- checkout
|
||||
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
|
||||
- run: ./.circleci/windows-env.ps1
|
||||
- run: node --version
|
||||
- run: yarn --version
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *cache_key_win
|
||||
- *cache_key_win_fallback
|
||||
# Reinstall to get windows binaries.
|
||||
- run: yarn install --frozen-lockfile --non-interactive
|
||||
# Install @bazel/bazelisk globally and use that for the first run.
|
||||
# Workaround for https://github.com/bazelbuild/rules_nodejs/issues/894
|
||||
# NB: the issue was for @bazel/bazel but the same problem applies to @bazel/bazelisk
|
||||
- run: yarn global add @bazel/bazelisk@$env:BAZELISK_VERSION
|
||||
- run: bazelisk info
|
||||
|
||||
notify_webhook_on_fail:
|
||||
description: Notify a webhook about failure
|
||||
@ -273,6 +260,7 @@ jobs:
|
||||
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
|
||||
- run: yarn -s ts-circular-deps:check
|
||||
- run: yarn -s ng-dev pullapprove verify
|
||||
- run: yarn -s ng-dev ngbot verify
|
||||
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
|
||||
|
||||
test:
|
||||
@ -755,25 +743,26 @@ jobs:
|
||||
steps:
|
||||
- setup_win
|
||||
- run:
|
||||
# Ran into a command parsing problem where `-browser:chromium-local` was converted to
|
||||
# `-browser: chromium-local` (a space was added) in https://circleci.com/gh/angular/angular/357511.
|
||||
# Probably a powershell command parsing thing. There's no problem using a yarn script though.
|
||||
command: yarn circleci-win-ve
|
||||
no_output_timeout: 45m
|
||||
# Save bazel repository cache to use on subsequent runs.
|
||||
# We don't save node_modules because it's faster to use the linux workspace and reinstall.
|
||||
- save_cache:
|
||||
key: *cache_key_win
|
||||
paths:
|
||||
- "C:/Users/circleci/bazel_repository_cache"
|
||||
name: Build all windows CI targets
|
||||
command: yarn bazel build --build_tag_filters=-ivy-only //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: yarn bazel test --test_tag_filters="-ivy-only,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
no_output_timeout: 15m
|
||||
|
||||
test_ivy_aot_win:
|
||||
executor: windows-executor
|
||||
steps:
|
||||
- setup_win
|
||||
- run:
|
||||
command: yarn circleci-win-ivy
|
||||
no_output_timeout: 45m
|
||||
name: Build all windows CI targets
|
||||
command: yarn bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
no_output_timeout: 15m
|
||||
- run:
|
||||
name: Test all windows CI targets
|
||||
command: yarn bazel test --config=ivy --test_tag_filters="-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/...
|
||||
no_output_timeout: 15m
|
||||
|
||||
|
||||
workflows:
|
||||
@ -857,26 +846,25 @@ workflows:
|
||||
- build-npm-packages
|
||||
- build-ivy-npm-packages
|
||||
- legacy-unit-tests-saucelabs
|
||||
- components-repo-unit-tests:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
# Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in
|
||||
# rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very
|
||||
# difficult and time consuming to resolve and involve patching @angular/bazel in components repo such
|
||||
# as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to
|
||||
# 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes
|
||||
# 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot
|
||||
# 3) update angular/angular to the landed components commit and re-enable these tests
|
||||
# - components-repo-unit-tests:
|
||||
# requires:
|
||||
# - build-npm-packages
|
||||
- test_zonejs:
|
||||
requires:
|
||||
- setup
|
||||
# Windows Jobs
|
||||
# These are very slow so we run them on non-PRs only for now.
|
||||
# TODO: remove the filter when CircleCI makes Windows FS faster.
|
||||
# The Windows jobs are only run after their non-windows counterparts finish successfully.
|
||||
# This isn't strictly necessary as there is no artifact dependency, but helps economize
|
||||
# CI resources by not attempting to build when we know should fail.
|
||||
- test_win:
|
||||
<<: *skip_on_pull_requests
|
||||
requires:
|
||||
- test
|
||||
- setup
|
||||
- test_ivy_aot_win:
|
||||
<<: *skip_on_pull_requests
|
||||
requires:
|
||||
- test_ivy_aot
|
||||
- setup
|
||||
|
||||
monitoring:
|
||||
jobs:
|
||||
|
@ -2,8 +2,8 @@
|
||||
# https://docs.bazel.build/versions/master/install-windows.html
|
||||
# https://docs.bazel.build/versions/master/windows.html
|
||||
# Install MSYS2 and packages
|
||||
choco install msys2 --version 20180531.0.0 --no-progress --package-parameters "/NoUpdate"
|
||||
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils git"
|
||||
choco install msys2 --version 20200903.0.0 --no-progress --package-parameters "/NoUpdate"
|
||||
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils"
|
||||
|
||||
# Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile.
|
||||
# https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)
|
||||
@ -41,7 +41,8 @@ copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
|
||||
####################################################################################################
|
||||
# Install specific version of node.
|
||||
####################################################################################################
|
||||
choco install nodejs --version 12.14.1 --no-progress
|
||||
nvm install 12.14.1
|
||||
nvm use 12.14.1
|
||||
|
||||
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
|
||||
# choco install yarn --version 1.16.0 --no-progress
|
||||
|
27
.github/angular-robot.yml
vendored
@ -38,6 +38,7 @@ merge:
|
||||
- "modules/benchmarks/**"
|
||||
- "modules/system.d.ts"
|
||||
- "packages/**"
|
||||
- "dev-infra/benchmark/driver-utilities/**"
|
||||
# list of patterns to ignore for the files changed by the PR
|
||||
exclude:
|
||||
- "packages/*"
|
||||
@ -47,8 +48,10 @@ merge:
|
||||
- "packages/bazel/src/ng_package/**"
|
||||
- "packages/bazel/src/protractor/**"
|
||||
- "packages/bazel/src/schematics/**"
|
||||
- "packages/compiler-cli/src/ngcc/**"
|
||||
- "packages/compiler-cli/linker/**"
|
||||
- "packages/compiler-cli/ngcc/**"
|
||||
- "packages/compiler-cli/src/ngtsc/sourcemaps/**",
|
||||
- "packages/compiler-cli/src/ngtsc/sourcemaps/**"
|
||||
- "packages/docs/**"
|
||||
- "packages/elements/schematics/**"
|
||||
- "packages/examples/**"
|
||||
@ -56,6 +59,8 @@ merge:
|
||||
- "packages/localize/**"
|
||||
- "packages/private/**"
|
||||
- "packages/service-worker/**"
|
||||
- "packages/common/locales/**"
|
||||
- "packages/http/**"
|
||||
- "**/.gitignore"
|
||||
- "**/.gitkeep"
|
||||
- "**/yarn.lock"
|
||||
@ -137,24 +142,28 @@ triage:
|
||||
# arrays of labels that determine if an issue has been fully triaged
|
||||
l2TriageLabels:
|
||||
-
|
||||
- "type: bug/fix"
|
||||
- "severity*"
|
||||
- "freq*"
|
||||
- "P0"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: feature"
|
||||
- "P1"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: refactor"
|
||||
- "P2"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: RFC / Discussion / question"
|
||||
- "P3"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: confusing"
|
||||
- "P4"
|
||||
- "comp: *"
|
||||
-
|
||||
- "type: use-case"
|
||||
- "P5"
|
||||
- "comp: *"
|
||||
-
|
||||
- "feature"
|
||||
- "comp: *"
|
||||
-
|
||||
- "discussion"
|
||||
- "comp: *"
|
||||
|
||||
# options for the triage PR plugin
|
||||
|
@ -9,11 +9,11 @@ export const caretaker: CaretakerConfig = {
|
||||
},
|
||||
{
|
||||
name: 'Merge Assistance Queue',
|
||||
query: `is:pr is:open status:success label:"action: merge-assistance"`,
|
||||
query: `is:pr is:open label:"action: merge-assistance"`,
|
||||
},
|
||||
{
|
||||
name: 'Primary Triage Queue',
|
||||
query: `is:open is:issue no:milestone`,
|
||||
name: 'Initial Triage Queue',
|
||||
query: `is:open no:milestone`,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ export const release: ReleaseConfig = {
|
||||
],
|
||||
// TODO: Implement release package building here.
|
||||
buildPackages: async () => [],
|
||||
// TODO: This can be removed once there is a org-wide tool for changelog generation.
|
||||
// TODO: This can be removed once there is an org-wide tool for changelog generation.
|
||||
generateReleaseNotesForHead: async () => {
|
||||
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
|
||||
},
|
||||
|
@ -186,6 +186,7 @@ groups:
|
||||
- IgorMinar # Igor Minar
|
||||
- jbogarthyde # Judy Bogart
|
||||
- jelbourn # Jeremy Elbourn
|
||||
- jessicajaniuk # Jessica Janiuk
|
||||
- JiaLiPassion # Jia Li
|
||||
- JoostK # Joost Koehoorn
|
||||
- josephperrott # Joey Perrott
|
||||
@ -325,7 +326,10 @@ groups:
|
||||
'aio/content/guide/component-interaction.md',
|
||||
'aio/content/examples/component-interaction/**',
|
||||
'aio/content/images/guide/component-interaction/**',
|
||||
'aio/content/guide/component-overview.md',
|
||||
'aio/content/examples/component-overview/**',
|
||||
'aio/content/guide/component-styles.md',
|
||||
'aio/content/guide/view-encapsulation.md',
|
||||
'aio/content/examples/component-styles/**',
|
||||
'aio/content/guide/dependency-injection.md',
|
||||
'aio/content/examples/dependency-injection/**',
|
||||
@ -378,6 +382,7 @@ groups:
|
||||
'aio/content/examples/binding-syntax/**',
|
||||
'aio/content/guide/property-binding.md',
|
||||
'aio/content/examples/property-binding/**',
|
||||
'aio/content/guide/property-binding-best-practices.md',
|
||||
'aio/content/guide/attribute-binding.md',
|
||||
'aio/content/examples/attribute-binding/**',
|
||||
'aio/content/guide/two-way-binding.md',
|
||||
@ -406,6 +411,7 @@ groups:
|
||||
'aio/content/guide/structural-directives.md',
|
||||
'aio/content/examples/structural-directives/**',
|
||||
'aio/content/guide/svg-in-templates.md',
|
||||
'aio/content/guide/style-precedence.md',
|
||||
'aio/content/images/guide/structural-directives/**',
|
||||
'aio/content/guide/template-statements.md',
|
||||
'aio/content/guide/user-input.md',
|
||||
@ -1115,11 +1121,13 @@ groups:
|
||||
'docs/DEBUG.md',
|
||||
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
||||
'docs/DEVELOPER.md',
|
||||
'docs/FIXUP_COMMITS.md',
|
||||
'docs/GITHUB_PROCESS.md',
|
||||
'docs/PR_REVIEW.md',
|
||||
'docs/SAVED_REPLIES.md',
|
||||
'docs/TOOLS.md',
|
||||
'docs/TRIAGE_AND_LABELS.md',
|
||||
'docs/images/**',
|
||||
'goldens/*',
|
||||
'modules/*',
|
||||
'packages/*',
|
||||
|
@ -47,3 +47,9 @@ filegroup(
|
||||
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||
],
|
||||
)
|
||||
|
||||
# Detect if the build is running under --stamp
|
||||
config_setting(
|
||||
name = "stamp",
|
||||
values = {"stamp": "true"},
|
||||
)
|
||||
|
57
CHANGELOG.md
@ -1,3 +1,60 @@
|
||||
<a name="10.2.0"></a>
|
||||
# 10.2.0 (2020-10-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** guard reading of global `ngDevMode` for undefined. ([#36055](https://github.com/angular/angular/issues/36055)) ([02405f1](https://github.com/angular/angular/commit/02405f1))
|
||||
* **platform-server:** Resolve absolute URL from baseUrl ([#39334](https://github.com/angular/angular/issues/39334)) ([71fb99f](https://github.com/angular/angular/commit/71fb99f))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **platform-server:** If you use `useAbsoluteUrl` to setup `platform-server`, you now need to
|
||||
also specify `baseUrl`.
|
||||
We are intentionally making this a breaking change in a minor release,
|
||||
because if `useAbsoluteUrl` is set to `true` then the behavior of the
|
||||
application could be unpredictable, resulting in issues that are hard to
|
||||
discover but could be affecting production environments.
|
||||
|
||||
|
||||
|
||||
<a name="10.1.6"></a>
|
||||
## 10.1.6 (2020-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** incorrectly encapsulating [@import](https://github.com/import) containing colons and semicolons ([#38716](https://github.com/angular/angular/issues/38716)) ([52a0c6b](https://github.com/angular/angular/commit/52a0c6b)), closes [#38587](https://github.com/angular/angular/issues/38587)
|
||||
* **compiler-cli:** support namespaced query types in directives ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([f752ab9](https://github.com/angular/angular/commit/f752ab9))
|
||||
* **elements:** detect matchesSelector prototype without IIFE ([#37799](https://github.com/angular/angular/issues/37799)) ([952fd86](https://github.com/angular/angular/commit/952fd86)), closes [#24551](https://github.com/angular/angular/issues/24551)
|
||||
* **ngcc:** ensure that "inline exports" can be interpreted correctly ([#39272](https://github.com/angular/angular/issues/39272)) ([e08d021](https://github.com/angular/angular/commit/e08d021))
|
||||
* **ngcc:** handle aliases in UMD export declarations ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([9963c5d](https://github.com/angular/angular/commit/9963c5d)), closes [#38947](https://github.com/angular/angular/issues/38947)
|
||||
* **ngcc:** map `exports` to the current module in UMD files ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([13c4a7b](https://github.com/angular/angular/commit/13c4a7b))
|
||||
* **ngcc:** support inline export declarations in UMD files ([#38959](https://github.com/angular/angular/issues/38959)) ([#39272](https://github.com/angular/angular/issues/39272)) ([9c875b3](https://github.com/angular/angular/commit/9c875b3)), closes [#38947](https://github.com/angular/angular/issues/38947)
|
||||
|
||||
|
||||
### build
|
||||
|
||||
* upgrade angular build, integration/bazel and [@angular](https://github.com/angular)/bazel package to rule_nodejs 2.2.0 ([#39182](https://github.com/angular/angular/issues/39182)) ([7628c36](https://github.com/angular/angular/commit/7628c36))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **ngcc:** do not rescan program source files when referenced from multiple root files ([#39254](https://github.com/angular/angular/issues/39254)) ([5221df8](https://github.com/angular/angular/commit/5221df8)), closes [#39240](https://github.com/angular/angular/issues/39240)
|
||||
|
||||
|
||||
<a name="10.1.5"></a>
|
||||
## 10.1.5 (2020-10-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **router:** update getRouteGuards to check if the context outlet is activated ([#39049](https://github.com/angular/angular/issues/39049)) ([771f731](https://github.com/angular/angular/commit/771f731)), closes [#39030](https://github.com/angular/angular/issues/39030)
|
||||
* **compiler:** Recover on malformed keyed reads and keyed writes ([#39004](https://github.com/angular/angular/issues/39004)) ([f50313f](https://github.com/angular/angular/commit/f50313f)), closes [#38596](https://github.com/angular/angular/issues/38596)
|
||||
|
||||
|
||||
|
||||
<a name="10.1.4"></a>
|
||||
## 10.1.4 (2020-09-30)
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
Communication through any of Angular's channels (GitHub, Discord, Gitter, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -32,7 +32,7 @@ Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
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].
|
||||
If you would like to chat about the question in real-time, you can reach out via [our Discord server][discord].
|
||||
|
||||
|
||||
## <a name="issue"></a> Found a Bug?
|
||||
@ -107,7 +107,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
git commit --all
|
||||
```
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
|
||||
@ -119,20 +119,55 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
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):
|
||||
#### Addressing review feedback
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
If we ask for changes via code reviews then:
|
||||
|
||||
1. Make the required updates to the code.
|
||||
|
||||
2. Re-run the Angular test suites to ensure tests are still passing.
|
||||
|
||||
3. Create a fixup commit and push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git commit --all --fixup HEAD
|
||||
git push
|
||||
```
|
||||
|
||||
For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md).
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
|
||||
##### Updating the commit message
|
||||
|
||||
A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines](#commit)).
|
||||
In order to update the commit message of the last commit on your branch:
|
||||
|
||||
1. Check out your branch:
|
||||
|
||||
```shell
|
||||
git checkout my-fix-branch
|
||||
```
|
||||
|
||||
2. Amend the last commit and modify the commit message:
|
||||
|
||||
```shell
|
||||
git commit --amend
|
||||
```
|
||||
|
||||
3. Push to your GitHub repository:
|
||||
|
||||
```shell
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
> NOTE:<br />
|
||||
> If you need to update the commit message of an earlier commit, you can use `git rebase` in interactive mode.
|
||||
> See the [git docs](https://git-scm.com/docs/git-rebase#_interactive_mode) for more details.
|
||||
|
||||
|
||||
#### 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:
|
||||
@ -213,7 +248,7 @@ Any line of the commit message cannot be longer than 100 characters.
|
||||
│ 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
|
||||
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
|
||||
```
|
||||
|
||||
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional.
|
||||
@ -276,7 +311,7 @@ There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
* `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`).
|
||||
* none/empty string: useful for `test` and `refactor` changes that are done across all packages (e.g. `test: add missing unit tests`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
|
||||
|
||||
|
||||
##### Summary
|
||||
@ -349,7 +384,7 @@ The following documents can help you sort out issues with GitHub accounts and mu
|
||||
[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
|
||||
[discord]: https://discord.gg/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
|
||||
|
145
README.md
@ -1,26 +1,151 @@
|
||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://www.npmjs.com/@angular/core)
|
||||
<h1 align="center">Angular - One framework. Mobile & desktop.</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="aio/src/assets/images/logos/angular/angular.png" alt="angular-logo" width="120px" height="120px"/>
|
||||
<br>
|
||||
<i>Angular is a development platform for building mobile and desktop web applications
|
||||
<br> using Typescript/JavaScript and other languages.</i>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
# Angular
|
||||
<p align="center">
|
||||
<a href="https://www.angular.io"><strong>www.angular.io</strong></a>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
|
||||
<p align="center">
|
||||
<a href="CONTRIBUTING.md">Contributing Guidelines</a>
|
||||
·
|
||||
<a href="https://github.com/angular/angular/issues">Submit an Issue</a>
|
||||
·
|
||||
<a href="https://blog.angular.io/">Blog</a>
|
||||
<br>
|
||||
<br>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://circleci.com/gh/angular/workflows/angular/tree/master">
|
||||
<img src="https://img.shields.io/circleci/build/github/angular/angular/master.svg?logo=circleci&logoColor=fff&label=CircleCI" alt="CI status" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/@angular/core">
|
||||
<img src="https://img.shields.io/npm/v/@angular/core.svg?logo=npm&logoColor=fff&label=NPM+package&color=limegreen" alt="Angular on npm" />
|
||||
</a>
|
||||
<a href="https://discord.gg/angular">
|
||||
<img src="https://img.shields.io/discord/463752820026376202.svg?logo=discord&logoColor=fff&label=Discord&color=7389d8" alt="Discord conversation" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<hr>
|
||||
|
||||
## Documentation
|
||||
|
||||
Get started with Angular, learn the fundamentals and explore advanced topics on our documentation website.
|
||||
|
||||
- [Getting Started][quickstart]
|
||||
- [Architecture][architecture]
|
||||
- [Components and Templates][componentstemplates]
|
||||
- [Forms][forms]
|
||||
- [API][api]
|
||||
|
||||
### Advanced
|
||||
|
||||
- [Angular Elements][angularelements]
|
||||
- [Server Side Rendering][ssr]
|
||||
- [Schematics][schematics]
|
||||
- [Lazy Loading][lazyloading]
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install [Node.js] which includes [Node Package Manager][npm]
|
||||
|
||||
### Setting Up a Project
|
||||
|
||||
Intall the Angular CLI globally:
|
||||
|
||||
```
|
||||
npm install -g @angular/cli
|
||||
```
|
||||
|
||||
Create workspace:
|
||||
|
||||
```
|
||||
ng new [PROJECT NAME]
|
||||
```
|
||||
|
||||
Run the application:
|
||||
|
||||
```
|
||||
cd [PROJECT NAME]
|
||||
ng serve
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Ecosystem
|
||||
|
||||
<p>
|
||||
<img src="/docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
|
||||
</p>
|
||||
|
||||
- [Angular Command Line (CLI)][cli]
|
||||
- [Angular Material][angularmaterial]
|
||||
|
||||
## Changelog
|
||||
|
||||
[Learn about the latest improvements][changelog].
|
||||
|
||||
## Want to help?
|
||||
## Upgrading
|
||||
|
||||
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).
|
||||
Check out our [upgrade guide](https://update.angular.io/) to find out the best way to upgrade your project.
|
||||
|
||||
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
## Contributing
|
||||
|
||||
### Contributing Guidelines
|
||||
|
||||
Read through our [contributing guidelines][contributing] to learn about our submission process, coding rules and more.
|
||||
|
||||
### 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).
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][codeofconduct].
|
||||
|
||||
## Community
|
||||
|
||||
Join the conversation and help the community.
|
||||
|
||||
- [Twitter][twitter]
|
||||
- [Gitter][gitter]
|
||||
- Find a Local [Meetup][meetup]
|
||||
|
||||
[](https://www.github.com/angular/angular)
|
||||
|
||||
**Love Angular? Give our repo a star :star: :arrow_up:.**
|
||||
|
||||
[contributing]: CONTRIBUTING.md
|
||||
[quickstart]: https://angular.io/start
|
||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
||||
[changelog]: CHANGELOG.md
|
||||
[ng]: https://angular.io
|
||||
[documentation]: https://angular.io/docs
|
||||
[angularmaterial]: https://material.angular.io/
|
||||
[cli]: https://cli.angular.io/
|
||||
[architecture]: https://angular.io/guide/architecture
|
||||
[componentstemplates]: https://angular.io/guide/displaying-data
|
||||
[forms]: https://angular.io/guide/forms-overview
|
||||
[api]: https://angular.io/api
|
||||
[angularelements]: https://angular.io/guide/elements
|
||||
[ssr]: https://angular.io/guide/universal
|
||||
[schematics]: https://angular.io/guide/schematics
|
||||
[lazyloading]: https://angular.io/guide/lazy-loading-ngmodules
|
||||
[node.js]: https://nodejs.org/
|
||||
[npm]: https://www.npmjs.com/get-npm
|
||||
[codeofconduct]: CODE_OF_CONDUCT.md
|
||||
[twitter]: https://www.twitter.com/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[meetup]: https://www.meetup.com/find/?keywords=angular"
|
||||
|
24
WORKSPACE
@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
||||
sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the rules_nodejs version and download npm dependencies
|
||||
@ -17,7 +17,7 @@ http_archive(
|
||||
# assert on that.
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||
|
||||
check_rules_nodejs_version(minimum_version_string = "1.7.0")
|
||||
check_rules_nodejs_version(minimum_version_string = "2.2.0")
|
||||
|
||||
# Setup the Node.js toolchain
|
||||
node_repositories(
|
||||
@ -39,23 +39,18 @@ yarn_install(
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
# Install all bazel dependencies of the @npm npm packages
|
||||
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
|
||||
|
||||
install_bazel_dependencies()
|
||||
|
||||
# Load angular dependencies
|
||||
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
||||
|
||||
rules_angular_dev_dependencies()
|
||||
|
||||
# Load protractor dependencies
|
||||
load("@npm_bazel_protractor//:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
|
||||
npm_bazel_protractor_dependencies()
|
||||
|
||||
# Load karma dependencies
|
||||
load("@npm_bazel_karma//:package.bzl", "npm_bazel_karma_dependencies")
|
||||
load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies")
|
||||
|
||||
npm_bazel_karma_dependencies()
|
||||
|
||||
@ -68,11 +63,6 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
|
||||
browser_repositories()
|
||||
|
||||
# Setup the rules_typescript tooolchain
|
||||
load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
|
||||
|
||||
ts_setup_workspace()
|
||||
|
||||
# Setup the rules_sass toolchain
|
||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||
|
||||
@ -91,8 +81,8 @@ rbe_autoconfig(
|
||||
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
||||
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
||||
# https://github.com/bazelbuild/bazel-toolchains/blob/3.5.1/configs/ubuntu16_04_clang/versions.bzl
|
||||
base_container_digest = "sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1",
|
||||
# Note that if you change the `digest`, you might also need to update the
|
||||
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
||||
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
||||
|
@ -36,8 +36,9 @@ Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
||||
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
||||
|
||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||
* `yarn create-example` - create a new example directory containing initial source files.
|
||||
|
||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||
|
||||
|
2
aio/content/examples/.gitignore
vendored
@ -32,6 +32,8 @@
|
||||
**/karma-test-shim.js
|
||||
**/browser-test-shim.js
|
||||
**/node_modules
|
||||
**/yarn.lock
|
||||
**/package-lock.json
|
||||
|
||||
# built files
|
||||
*.map
|
||||
|
@ -12,7 +12,7 @@ describe('Accessibility example e2e tests', () => {
|
||||
|
||||
it('should take a number and change progressbar width', () => {
|
||||
element(by.css('input')).sendKeys('16');
|
||||
expect(element(by.css('input')).getAttribute('value')).toEqual('016');
|
||||
expect(element(by.css('input')).getAttribute('value')).toEqual('16');
|
||||
expect(element(by.css('app-example-progressbar div')).getCssValue('width')).toBe('48px');
|
||||
});
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<label>
|
||||
Enter an example progress value
|
||||
<input type="number" min="0" max="100"
|
||||
[value]="progress" (input)="progress = $event.target.value">
|
||||
[value]="progress" (input)="setProgress($event)">
|
||||
</label>
|
||||
|
||||
<!-- The user of the progressbar sets an aria-label to communicate what the progress means. -->
|
||||
|
@ -7,4 +7,8 @@ import { Component } from '@angular/core';
|
||||
})
|
||||
export class AppComponent {
|
||||
progress = 0;
|
||||
|
||||
setProgress($event: Event) {
|
||||
this.progress = +($event.target as HTMLInputElement).value;
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
<h2>Attribute binding</h2>
|
||||
<!-- #docregion attrib-binding-colspan -->
|
||||
<table border=1>
|
||||
<!-- #docregion colspan -->
|
||||
<!-- expression calculates colspan=2 -->
|
||||
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
|
||||
<!-- #enddocregion colspan -->
|
||||
|
||||
<!-- ERROR: There is no `colspan` property to set!
|
||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||
@ -32,31 +34,31 @@
|
||||
<!-- #docregion basic-specificity -->
|
||||
<h3>Basic specificity</h3>
|
||||
|
||||
<!-- The `class.special` binding will override any value for the `special` class in `classExpr`. -->
|
||||
<div [class.special]="isSpecial" [class]="classExpr">Some text.</div>
|
||||
<!-- The `class.special` binding overrides any value for the `special` class in `classExpression`. -->
|
||||
<div [class.special]="isSpecial" [class]="classExpression">Some text.</div>
|
||||
|
||||
<!-- The `style.color` binding will override any value for the `color` property in `styleExpr`. -->
|
||||
<div [style.color]="color" [style]="styleExpr">Some text.</div>
|
||||
<!-- The `style.color` binding overrides any value for the `color` property in `styleExpression`. -->
|
||||
<div [style.color]="color" [style]="styleExpression">Some text.</div>
|
||||
<!-- #enddocregion basic-specificity -->
|
||||
|
||||
<!-- #docregion source-specificity -->
|
||||
<h3>Source specificity</h3>
|
||||
|
||||
<!-- The `class.special` template binding will override any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
||||
<!-- The `class.special` template binding overrides any host binding to the `special` class set by `dirWithClassBinding` or `comp-with-host-binding`.-->
|
||||
<comp-with-host-binding [class.special]="isSpecial" dirWithClassBinding>Some text.</comp-with-host-binding>
|
||||
|
||||
<!-- The `style.color` template binding will override any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
||||
<!-- The `style.color` template binding overrides any host binding to the `color` property set by `dirWithStyleBinding` or `comp-with-host-binding`. -->
|
||||
<comp-with-host-binding [style.color]="color" dirWithStyleBinding>Some text.</comp-with-host-binding>
|
||||
<!-- #enddocregion source-specificity -->
|
||||
|
||||
<!-- #docregion dynamic-priority -->
|
||||
<h3>Dynamic vs static</h3>
|
||||
|
||||
<!-- If `classExpr` has a value for the `special` class, this value will override the `class="special"` below -->
|
||||
<div class="special" [class]="classExpr">Some text.</div>
|
||||
<!-- If `classExpression` has a value for the `special` class, this value overrides the `class="special"` below -->
|
||||
<div class="special" [class]="classExpression">Some text.</div>
|
||||
|
||||
<!-- If `styleExpr` has a value for the `color` property, this value will override the `style="color: blue"` below -->
|
||||
<div style="color: blue" [style]="styleExpr">Some text.</div>
|
||||
<!-- If `styleExpression` has a value for the `color` property, this value overrides the `style="color: blue"` below -->
|
||||
<div style="color: blue" [style]="styleExpression">Some text.</div>
|
||||
|
||||
<!-- #enddocregion dynamic-priority -->
|
||||
|
||||
|
@ -9,7 +9,7 @@ export class AppComponent {
|
||||
actionName = 'Go for it';
|
||||
isSpecial = true;
|
||||
canSave = true;
|
||||
classExpr = 'special clearance';
|
||||
styleExpr = 'color: red';
|
||||
classExpression = 'special clearance';
|
||||
styleExpression = 'color: red';
|
||||
color = 'blue';
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ export class CompWithHostBindingComponent {
|
||||
@HostBinding('style.color')
|
||||
color = 'green';
|
||||
|
||||
// #docregion hostbinding
|
||||
@HostBinding('style.width')
|
||||
width = '200px';
|
||||
// #enddocregion hostbinding
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('Component Overview', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it('should display component overview works ', () => {
|
||||
expect(element(by.css('p')).getText()).toEqual('component-overview works!');
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1 @@
|
||||
<app-component-overview></app-component-overview>
|
@ -0,0 +1,31 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'component-overview'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('component-overview');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('component-overview app is running!');
|
||||
});
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'component-overview';
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ComponentOverviewComponent } from './component-overview/component-overview.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
ComponentOverviewComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
@ -0,0 +1,14 @@
|
||||
// #docplaster
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion template
|
||||
@Component({
|
||||
selector: 'app-component-overview',
|
||||
template: '<h1>Hello World!</h1>',
|
||||
})
|
||||
// #enddocregion template
|
||||
|
||||
export class ComponentOverviewComponent {
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
// #docplaster
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion templatebacktick
|
||||
@Component({
|
||||
selector: 'app-component-overview',
|
||||
template: `<h1>Hello World!</h1>
|
||||
<p>This template definition spans
|
||||
multiple lines.</p>`
|
||||
})
|
||||
// #enddocregion templatebacktick
|
||||
|
||||
export class ComponentOverviewComponent {
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
// #docplaster
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion styles
|
||||
@Component({
|
||||
selector: 'app-component-overview',
|
||||
template: '<h1>Hello World!</h1>',
|
||||
styles: ['h1 { font-weight: normal; }']
|
||||
})
|
||||
// #enddocregion styles
|
||||
|
||||
export class ComponentOverviewComponent {
|
||||
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
<p>component-overview works!</p>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ComponentOverviewComponent } from './component-overview.component';
|
||||
|
||||
describe('ComponentOverviewComponent', () => {
|
||||
let component: ComponentOverviewComponent;
|
||||
let fixture: ComponentFixture<ComponentOverviewComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ComponentOverviewComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ComponentOverviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,22 @@
|
||||
// #docplaster
|
||||
// #docregion import
|
||||
import { Component } from '@angular/core';
|
||||
// #enddocregion import
|
||||
|
||||
// #docregion decorator, decorator-skeleton, selector, templateUrl
|
||||
@Component({
|
||||
// #enddocregion decorator-skeleton
|
||||
selector: 'app-component-overview',
|
||||
// #enddocregion selector
|
||||
templateUrl: './component-overview.component.html',
|
||||
// #enddocregion templateUrl
|
||||
styleUrls: ['./component-overview.component.css']
|
||||
// #docregion decorator-skeleton, selector, templateUrl
|
||||
})
|
||||
// #enddocregion decorator, decorator-skeleton, selector, templateUrl
|
||||
|
||||
// #docregion class
|
||||
export class ComponentOverviewComponent {
|
||||
|
||||
}
|
||||
// #enddocregion class
|
13
aio/content/examples/component-overview/src/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ComponentOverview</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
12
aio/content/examples/component-overview/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
8
aio/content/examples/component-overview/stackblitz.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"description": "Component Overview",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["overview", "component"]
|
||||
}
|
@ -7,9 +7,15 @@ import { Component } from '@angular/core';
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
// #docregion item-image
|
||||
itemImageUrl = '../assets/phone.png';
|
||||
// #enddocregion item-image
|
||||
// #docregion boolean
|
||||
isUnchanged = true;
|
||||
// #enddocregion boolean
|
||||
// #docregion directive-property
|
||||
classes = 'special';
|
||||
// #enddocregion directive-property
|
||||
// #docregion parent-data-type
|
||||
parentItem = 'lamp';
|
||||
// #enddocregion parent-data-type
|
||||
|
@ -23,13 +23,13 @@ describe('retry-on-error', () => {
|
||||
docRegionDefault(mockConsole, ajax);
|
||||
expect(mockConsole.log.calls.allArgs()).toEqual([
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Error occurred.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Error occurred.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Error occurred.'],
|
||||
['Subscribed to AJAX'],
|
||||
['Error occured.'],
|
||||
['Error occurred.'],
|
||||
['data: ', []],
|
||||
]);
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ export function docRegionDefault(console, ajax) {
|
||||
const apiData = ajax('/api/data').pipe(
|
||||
map((res: any) => {
|
||||
if (!res.response) {
|
||||
console.log('Error occured.');
|
||||
console.log('Error occurred.');
|
||||
throw new Error('Value expected!');
|
||||
}
|
||||
return res.response;
|
||||
|
@ -89,7 +89,7 @@ This example from the `HeroListComponent` template uses three of these forms.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||
|
||||
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
|
||||
* The `{{hero.name}}` [*interpolation*](guide/interpolation)
|
||||
displays the component's `hero.name` property value within the `<li>` element.
|
||||
|
||||
* The `[hero]` [*property binding*](guide/property-binding) passes the value of
|
||||
@ -166,8 +166,8 @@ The example template uses two built-in structural directives to add application
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" header="src/app/hero-list.component.html (structural)" region="structural"></code-example>
|
||||
|
||||
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
|
||||
* [`*ngFor`](guide/structural-directives#inside-ngfor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||
* [`*ngIf`](guide/structural-directives#ngif-case-study) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
|
||||
|
||||
#### Attribute directives
|
||||
|
||||
|
@ -11,7 +11,7 @@ about the features and tools that can help you develop and deliver Angular appli
|
||||
|
||||
## Application architecture
|
||||
|
||||
* The [Components and templates](guide/displaying-data) guide explains how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application.
|
||||
* The **Main Concepts** section located in the table of contents contains several topics that explain how to connect the application data in your [components](guide/glossary#component) to your page-display [templates](guide/glossary#template), to create a complete interactive application.
|
||||
|
||||
* The [NgModules](guide/ngmodules) guide provides in-depth information on the modular structure of an Angular application.
|
||||
|
||||
@ -21,7 +21,7 @@ about the features and tools that can help you develop and deliver Angular appli
|
||||
|
||||
## Responsive programming
|
||||
|
||||
The **Components and Templates** guide provides guidance and details of the [template syntax](guide/template-syntax) that you use to display your component data when and where you want it within a view, and to collect input from users that you can respond to.
|
||||
The [template syntax](guide/template-syntax) and related topics contain details about how to display your component data when and where you want it within a view, and how to collect input from users that you can respond to.
|
||||
|
||||
Additional pages and sections describe some basic programming techniques for Angular apps.
|
||||
|
||||
@ -52,8 +52,6 @@ For some platforms and applications, you might also want to use the PWA (Progres
|
||||
|
||||
## Support for the development cycle
|
||||
|
||||
The **Development Workflow** section describes the tools and processes you use to compile, test, and deploy Angular applications.
|
||||
|
||||
* [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||
|
||||
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
|
||||
@ -68,7 +66,6 @@ The **Development Workflow** section describes the tools and processes you use t
|
||||
|
||||
* [Accessibility](guide/accessibility): Make your app accessible to all users.
|
||||
|
||||
|
||||
## File structure, configuration, and dependencies
|
||||
|
||||
* [Workspace and file structure](guide/file-structure): Understand the structure of Angular workspace and project folders.
|
||||
|
@ -5,7 +5,7 @@ Angular is written in TypeScript.
|
||||
It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
|
||||
|
||||
The architecture of an Angular application relies on certain fundamental concepts.
|
||||
The basic building blocks are *NgModules*, which provide a compilation context for *components*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*.
|
||||
The basic building blocks of the Angular framework are Angular components that are organized into *NgModules*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*.
|
||||
|
||||
* Components define *views*, which are sets of screen elements that Angular can choose among and modify according to your program logic and data.
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Attribute, class, and style bindings
|
||||
|
||||
The template syntax provides specialized one-way bindings for scenarios less well-suited to property binding.
|
||||
Attribute binding in Angular helps you set values for attributes directly.
|
||||
With attribute binding, you can improve accessibility, style your application dynamically, and manage multiple CSS classes or styles simultaneously.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -8,23 +9,36 @@ See the <live-example></live-example> for a working example containing the code
|
||||
|
||||
</div>
|
||||
|
||||
## Binding to an attribute
|
||||
|
||||
## Attribute binding
|
||||
It is recommended that you set an element property with a [property binding](guide/property-binding) whenever possible.
|
||||
However, sometimes you don't have an element property to bind.
|
||||
In those situations, you can use attribute binding.
|
||||
|
||||
Set the value of an attribute directly with an **attribute binding**. This is the only exception to the rule that a binding sets a target property and the only binding that creates and sets an attribute.
|
||||
For example, [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG) are purely attributes.
|
||||
Neither ARIA nor SVG correspond to element properties and don't set element properties.
|
||||
In these cases, you must use attribute binding because there are no corresponding property targets.
|
||||
|
||||
Usually, setting an element property with a [property binding](guide/property-binding)
|
||||
is preferable to setting the attribute with a string. However, sometimes
|
||||
there is no element property to bind, so attribute binding is the solution.
|
||||
|
||||
Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and
|
||||
[SVG](https://developer.mozilla.org/en-US/docs/Web/SVG). They are purely attributes, don't correspond to element properties, and don't set element properties. In these cases, there are no property targets to bind to.
|
||||
## Syntax
|
||||
|
||||
Attribute binding syntax resembles property binding, but
|
||||
instead of an element property between brackets, start with the prefix `attr`,
|
||||
followed by a dot (`.`), and the name of the attribute.
|
||||
You then set the attribute value, using an expression that resolves to a string,
|
||||
or remove the attribute when the expression resolves to `null`.
|
||||
Attribute binding syntax resembles [property binding](guide/property-binding), but instead of an element property between brackets, you precede the name of the attribute with the prefix `attr`, followed by a dot.
|
||||
Then, you set the attribute value with an expression that resolves to a string.
|
||||
|
||||
<code-example language="html">
|
||||
|
||||
<p [attr.attribute-you-are-targeting]="expression"></p>
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When the expression resolves to `null`, Angular removes the attribute altogether.
|
||||
|
||||
</div>
|
||||
|
||||
## Binding ARIA attributes
|
||||
|
||||
One of the primary use cases for attribute binding
|
||||
is to set ARIA attributes, as in this example:
|
||||
@ -33,32 +47,30 @@ is to set ARIA attributes, as in this example:
|
||||
|
||||
{@a colspan}
|
||||
|
||||
## Binding to `colspan`
|
||||
|
||||
Another common use case for attribute binding is with the `colspan` attribute in tables.
|
||||
Binding to the `colspan` attribute helps you keep your tables programmatically dynamic.
|
||||
Depending on the amount of data that your application populates a table with, the number of columns that a row spans could change.
|
||||
|
||||
To use attribute binding with the `<td>` attribute `colspan`:
|
||||
|
||||
1. Specify the `colspan` attribute by using the following syntax: `[attr.colspan]`.
|
||||
1. Set `[attr.colspan]` equal to an expression.
|
||||
|
||||
In the following example, binds the `colspan` attribute to the expression `1 + 1`.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="colspan" header="src/app/app.component.html"></code-example>
|
||||
|
||||
This binding causes the `<tr>` to span two columns.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
#### `colspan` and `colSpan`
|
||||
Sometimes there are differences between the name of property and an attribute.
|
||||
|
||||
Notice the difference between the `colspan` attribute and the `colSpan` property.
|
||||
|
||||
If you wrote something like this:
|
||||
|
||||
<code-example language="html">
|
||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||
</code-example>
|
||||
|
||||
You'd get this error:
|
||||
|
||||
<code-example language="bash">
|
||||
Template parse errors:
|
||||
Can't bind to 'colspan' since it isn't a known native property
|
||||
</code-example>
|
||||
|
||||
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||||
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
||||
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
||||
|
||||
Instead, you'd use property binding and write it like this:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||
`colspan` is an attribute of `<tr>`, while `colSpan` with a capital "S" is a property.
|
||||
When using attribute binding, use `colspan` with a lowercase "s".
|
||||
For more information on how to bind to the `colSpan` property, see the [`colspan` and `colSpan`](guide/property-binding#colspan) section of [Property Binding](guide/property-binding).
|
||||
|
||||
</div>
|
||||
|
||||
@ -66,28 +78,32 @@ Instead, you'd use property binding and write it like this:
|
||||
|
||||
{@a class-binding}
|
||||
|
||||
## Class binding
|
||||
## Binding to the `class` attribute
|
||||
|
||||
Here's how to set the `class` attribute without a binding in plain HTML:
|
||||
You can use class binding to add and remove CSS class names from an element's `class` attribute.
|
||||
|
||||
```html
|
||||
<!-- standard class attribute setting -->
|
||||
<div class="foo bar">Some text</div>
|
||||
```
|
||||
### Binding to a single CSS `class`
|
||||
|
||||
You can also add and remove CSS class names from an element's `class` attribute with a **class binding**.
|
||||
To create a single class binding, use the prefix `class` followed by a dot and the name of the CSS class—for example, `[class.sale]="onSale"`.
|
||||
Angular adds the class when the bound expression, `onSale` is truthy, and it removes the class when the expression is falsy—with the exception of `undefined`.
|
||||
See [styling delegation](guide/style-precedence#styling-delegation) for more information.
|
||||
|
||||
To create a single class binding, start with the prefix `class` followed by a dot (`.`) and the name of the CSS class (for example, `[class.foo]="hasFoo"`).
|
||||
Angular adds the class when the bound expression is truthy, and it removes the class when the expression is falsy (with the exception of `undefined`, see [styling delegation](#styling-delegation)).
|
||||
### Binding to multiple CSS classes
|
||||
|
||||
To create a binding to multiple classes, use a generic `[class]` binding without the dot (for example, `[class]="classExpr"`).
|
||||
The expression can be a space-delimited string of class names, or you can format it as an object with class names as the keys and truthy/falsy expressions as the values.
|
||||
With object format, Angular will add a class only if its associated value is truthy.
|
||||
To bind to multiple classes, use `[class]` set to an expression—for example, `[class]="classExpression"`.
|
||||
The expression can be a space-delimited string of class names, or an object with class names as the keys and truthy or falsy expressions as the values.
|
||||
With an object format, Angular adds a class only if its associated value is truthy.
|
||||
|
||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||||
Updating the property without changing object identity will have no effect.
|
||||
<div class="alert is-important">
|
||||
|
||||
If there are multiple bindings to the same class name, conflicts are resolved using [styling precedence](#styling-precedence).
|
||||
With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list.
|
||||
Updating the property without changing object identity has no effect.
|
||||
|
||||
</div>
|
||||
|
||||
If there are multiple bindings to the same class name, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use.
|
||||
|
||||
The following table summarizes class binding syntax.
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
@ -118,13 +134,13 @@ If there are multiple bindings to the same class name, conflicts are resolved us
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Single class binding</td>
|
||||
<td><code>[class.foo]="hasFoo"</code></td>
|
||||
<td><code>[class.sale]="onSale"</code></td>
|
||||
<td><code>boolean | undefined | null</code></td>
|
||||
<td><code>true</code>, <code>false</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=3>Multi-class binding</td>
|
||||
<td rowspan=3><code>[class]="classExpr"</code></td>
|
||||
<td rowspan=3><code>[class]="classExpression"</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"my-class-1 my-class-2 my-class-3"</code></td>
|
||||
</tr>
|
||||
@ -138,44 +154,44 @@ If there are multiple bindings to the same class name, conflicts are resolved us
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
The [NgClass](guide/built-in-directives/#ngclass) directive can be used as an alternative to direct `[class]` bindings.
|
||||
However, using the above class binding syntax without `NgClass` is preferred because due to improvements in class binding in Angular, `NgClass` no longer provides significant value, and might eventually be removed in the future.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
## Style binding
|
||||
{@a style-binding}
|
||||
|
||||
Here's how to set the `style` attribute without a binding in plain HTML:
|
||||
## Binding to the style attribute
|
||||
|
||||
```html
|
||||
<!-- standard style attribute setting -->
|
||||
<div style="color: blue">Some text</div>
|
||||
```
|
||||
You can use style binding to set styles dynamically.
|
||||
|
||||
You can also set styles dynamically with a **style binding**.
|
||||
### Binding to a single style
|
||||
|
||||
To create a single style binding, start with the prefix `style` followed by a dot (`.`) and the name of the CSS style property (for example, `[style.width]="width"`).
|
||||
The property will be set to the value of the bound expression, which is normally a string.
|
||||
To create a single style binding, use the prefix `style` followed by a dot and the name of the CSS style property—for example, `[style.width]="width"`.
|
||||
Angular sets the property to the value of the bound expression, which is usually a string.
|
||||
Optionally, you can add a unit extension like `em` or `%`, which requires a number type.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Note that a _style property_ name can be written in either
|
||||
[dash-case](guide/glossary#dash-case), as shown above, or
|
||||
[camelCase](guide/glossary#camelcase), such as `fontSize`.
|
||||
You can write a style property name in either [dash-case](guide/glossary#dash-case), or
|
||||
[camelCase](guide/glossary#camelcase).
|
||||
|
||||
</div>
|
||||
|
||||
If there are multiple styles you'd like to toggle, you can bind to the `[style]` property directly without the dot (for example, `[style]="styleExpr"`).
|
||||
The expression attached to the `[style]` binding is most often a string list of styles like `"width: 100px; height: 100px;"`.
|
||||
### Binding to multiple styles
|
||||
|
||||
You can also format the expression as an object with style names as the keys and style values as the values, like `{width: '100px', height: '100px'}`.
|
||||
It's important to note that with any object-like expression (`object`, `Array`, `Map`, `Set`, etc), the identity of the object must change for the class list to be updated.
|
||||
Updating the property without changing object identity will have no effect.
|
||||
To toggle multiple styles, bind to the `[style]` attribute—for example, `[style]="styleExpression"`).
|
||||
The expression is often a string list of styles such as `"width: 100px; height: 100px;"`.
|
||||
|
||||
If there are multiple bindings to the same style property, conflicts are resolved using [styling precedence rules](#styling-precedence).
|
||||
You can also format the expression as an object with style names as the keys and style values as the values, such as `{width: '100px', height: '100px'}`.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
With any object-like expression—such as `object`, `Array`, `Map`, or `Set`—the identity of the object must change for Angular to update the class list.
|
||||
Updating the property without changing object identity has no effect.
|
||||
|
||||
</div>
|
||||
|
||||
If there are multiple bindings to the same style attribute, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use.
|
||||
|
||||
The following table summarizes style binding syntax.
|
||||
|
||||
<style>
|
||||
td, th {vertical-align: top}
|
||||
@ -219,7 +235,7 @@ If there are multiple bindings to the same style property, conflicts are resolve
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=3>Multi-style binding</td>
|
||||
<td rowspan=3><code>[style]="styleExpr"</code></td>
|
||||
<td rowspan=3><code>[style]="styleExpression"</code></td>
|
||||
<td><code>string</code></td>
|
||||
<td><code>"width: 100px; height: 100px"</code></td>
|
||||
</tr>
|
||||
@ -232,72 +248,3 @@ If there are multiple bindings to the same style property, conflicts are resolve
|
||||
<td><code>['width', '100px']</code></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The [NgStyle](guide/built-in-directives/#ngstyle) directive can be used as an alternative to direct `[style]` bindings.
|
||||
However, using the above style binding syntax without `NgStyle` is preferred because due to improvements in style binding in Angular, `NgStyle` no longer provides significant value, and might eventually be removed in the future.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a styling-precedence}
|
||||
|
||||
## Styling Precedence
|
||||
|
||||
A single HTML element can have its CSS class list and style values bound to multiple sources (for example, host bindings from multiple directives).
|
||||
|
||||
When there are multiple bindings to the same class name or style property, Angular uses a set of precedence rules to resolve conflicts and determine which classes or styles are ultimately applied to the element.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<h4>Styling precedence (highest to lowest)</h4>
|
||||
|
||||
1. Template bindings
|
||||
1. Property binding (for example, `<div [class.foo]="hasFoo">` or `<div [style.color]="color">`)
|
||||
1. Map binding (for example, `<div [class]="classExpr">` or `<div [style]="styleExpr">`)
|
||||
1. Static value (for example, `<div class="foo">` or `<div style="color: blue">`)
|
||||
1. Directive host bindings
|
||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||||
1. Component host bindings
|
||||
1. Property binding (for example, `host: {'[class.foo]': 'hasFoo'}` or `host: {'[style.color]': 'color'}`)
|
||||
1. Map binding (for example, `host: {'[class]': 'classExpr'}` or `host: {'[style]': 'styleExpr'}`)
|
||||
1. Static value (for example, `host: {'class': 'foo'}` or `host: {'style': 'color: blue'}`)
|
||||
|
||||
</div>
|
||||
|
||||
The more specific a class or style binding is, the higher its precedence.
|
||||
|
||||
A binding to a specific class (for example, `[class.foo]`) will take precedence over a generic `[class]` binding, and a binding to a specific style (for example, `[style.bar]`) will take precedence over a generic `[style]` binding.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Specificity rules also apply when it comes to bindings that originate from different sources.
|
||||
It's possible for an element to have bindings in the template where it's declared, from host bindings on matched directives, and from host bindings on matched components.
|
||||
|
||||
Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
||||
|
||||
Directive host bindings are considered less specific because directives can be used in multiple locations, so they have a lower precedence than template bindings.
|
||||
|
||||
Directives often augment component behavior, so host bindings from components have the lowest precedence.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
In addition, bindings take precedence over static attributes.
|
||||
|
||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding will take precedence because it is dynamic.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a styling-delegation}
|
||||
### Delegating to styles with lower precedence
|
||||
|
||||
It is possible for higher precedence styles to "delegate" to lower precedence styles using `undefined` values.
|
||||
Whereas setting a style property to `null` ensures the style is removed, setting it to `undefined` will cause Angular to fall back to the next-highest precedence binding to that style.
|
||||
|
||||
For example, consider the following template:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||||
In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `width` property will fall back to the value of the `comp-with-host-binding` host binding.
|
||||
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|
||||
|
@ -194,7 +194,7 @@ which explains the following:
|
||||
* Using [`<ng-container>`](guide/structural-directives#ngcontainer "<ng-container>")
|
||||
to group elements when there is no suitable host element for the directive.
|
||||
* How to write your own structural directive.
|
||||
* That you can only apply [one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
||||
* Why you [can only apply one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
||||
|
||||
</div>
|
||||
|
||||
@ -281,7 +281,7 @@ You define a block of HTML that defines how a single item should be displayed
|
||||
and then you tell Angular to use that block as a template for rendering each item in the list.
|
||||
The text assigned to `*ngFor` is the instruction that guides the repeater process.
|
||||
|
||||
The following example shows `NgFor` applied to a simple `<div>`. (Don't forget the asterisk (`*`) in front of `ngFor`.)
|
||||
The following example shows `NgFor` applied to a simple `<div>`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
|
182
aio/content/guide/component-overview.md
Normal file
@ -0,0 +1,182 @@
|
||||
# Angular Components Overview
|
||||
|
||||
Components are the main building block for Angular applications. Each component consists of:
|
||||
|
||||
* An HTML template that declares what renders on the page
|
||||
* A Typescript class that defines behavior
|
||||
* A CSS selector that defines how the component is used in a template
|
||||
* Optionally, CSS styles applied to the template
|
||||
|
||||
This topic describes how to create and configure an Angular component.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
To view or download the example code used in this topic, see the <live-example></live-example>.
|
||||
|
||||
</div>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To create a component, verify that you have met the following prerequisites:
|
||||
|
||||
1. Install the Angular CLI.
|
||||
1. Create an Angular project.
|
||||
If you don't have a project, you can create one using `ng new <project-name>`, where `<project-name>` is the name of your Angular application.
|
||||
|
||||
## Creating a component
|
||||
|
||||
The easiest way to create a component is with the Angular CLI. You can also create a component manually.
|
||||
|
||||
### Creating a component using the Angular CLI
|
||||
|
||||
To create a component using the Angular CLI:
|
||||
|
||||
1. From a terminal window, navigate to the directory containing your application.
|
||||
1. Run the `ng generate component <component-name>` command, where `<component-name>` is the name of your new component.
|
||||
|
||||
By default, this command creates the following:
|
||||
|
||||
* A folder named after the component
|
||||
* A component file, `<component-name>.component.ts`
|
||||
* A template file, `<component-name>.component.ts`
|
||||
* A CSS file, `<component-name>.component.css`
|
||||
* A testing specification file, `<component-name>.component.spec.ts`
|
||||
|
||||
Where `<component-name>` is the name of your component.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You can change how `ng generate component` creates new components.
|
||||
For more information, see [ng generate component](cli/generate#component-command) in the Angular CLI documentation.
|
||||
|
||||
</div>
|
||||
|
||||
### Creating a component manually
|
||||
|
||||
Although the Angular CLI is the easiest way to create an Angular component, you can also create a component manually.
|
||||
This section describes how to create the core component file within an existing Angular project.
|
||||
|
||||
To create a new component manually:
|
||||
|
||||
1. Navigate to your Angular project directory.
|
||||
1. Create a new file, `<component-name>.component.ts`.
|
||||
1. At the top of the file, add the following import statement.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="import">
|
||||
</code-example>
|
||||
|
||||
1. After the `import` statement, add a `@Component` decorator.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="decorator-skeleton">
|
||||
</code-example>
|
||||
|
||||
1. Choose a CSS selector for the component.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="selector">
|
||||
</code-example>
|
||||
|
||||
For more information on choosing a selector, see [Specifying a component's selector](#specifying-a-components-css-selector).
|
||||
|
||||
1. Define the HTML template that the component uses to display information.
|
||||
In most cases, this template is a separate HTML file.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="templateUrl">
|
||||
</code-example>
|
||||
|
||||
For more information on defining a component's template, see [Defining a component's template](#defining-a-components-template).
|
||||
|
||||
1. Select the styles for the component's template.
|
||||
In most cases, you define the styles for you component's template in a separate file.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="decorator">
|
||||
</code-example>
|
||||
|
||||
1. Add a `class` statement that includes the code for the component.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="class">
|
||||
</code-example>
|
||||
|
||||
## Specifying a component's CSS selector
|
||||
|
||||
Every component requires a CSS _selector_. A selector instructs Angular to instantiate this component wherever it finds the corresponding tag in template HTML. For example, consider a component, `hello-world.component.ts` that defines its selector as `app-hello-world`. This selector instructs angular to instantiate this component any time the tag, `<app-hellow-world>` in a template.
|
||||
|
||||
To specify a component's selector, add a `selector` statement to the `@Component` decorator.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="selector">
|
||||
</code-example>
|
||||
|
||||
## Defining a component's template
|
||||
|
||||
A template is a block of HTML that tells Angular how to render the component in your application.
|
||||
You can define a template for your component in one of two ways: by referencing an external file, or directly within the component.
|
||||
|
||||
To define a template as an external file, add a `templateUrl` property to the `@Component` decorator.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="templateUrl">
|
||||
</code-example>
|
||||
|
||||
To define a template within the component, add a `template` property to the `@Component` decorator that contains the HTML you want to use.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.1.ts"
|
||||
region="template">
|
||||
</code-example>
|
||||
|
||||
If your want your template to span multiple lines, you can use backticks (<code> ` </code>).
|
||||
For example:
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.2.ts"
|
||||
region="templatebacktick">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
An Angular component requires a template defined using `template` or `templateUrl`. You cannot have both statements in a component.
|
||||
|
||||
</div>
|
||||
|
||||
## Declaring a component's styles
|
||||
|
||||
You can declare component styles uses for its template in one of two ways: by referencing an external file, or directly within the component.
|
||||
|
||||
To declare the styles for a component in a separate file, add a `stylesUrls` property to the `@Component` decorator.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.ts"
|
||||
region="decorator">
|
||||
</code-example>
|
||||
|
||||
To select the styles within the component, add a `styles` property to the `@Component` decorator that contains the styles you want to use.
|
||||
|
||||
<code-example
|
||||
path="component-overview/src/app/component-overview/component-overview.component.3.ts"
|
||||
region="styles">
|
||||
</code-example>
|
||||
|
||||
The `styles` property takes an array of strings that contain the CSS rule declarations.
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
* For an archictural overview of components, see [Introduction to components and templates](guide/architecture-components).
|
||||
* For additional options you can use when creating a component, see [Component](api/core/Component) in the API Reference.
|
||||
* For more information on styling components, see [Component styles](guide/component-styles).
|
||||
* For more information on templates, see [Template syntax](guide/template-syntax).
|
||||
|
@ -119,7 +119,7 @@ The `/deep/` combinator also has the aliases `>>>`, and `::ng-deep`.
|
||||
|
||||
Use `/deep/`, `>>>` and `::ng-deep` only with *emulated* view encapsulation.
|
||||
Emulated is the default and most commonly used view encapsulation. For more information, see the
|
||||
[Controlling view encapsulation](guide/component-styles#view-encapsulation) section.
|
||||
[View Encapsulation](guide/view-encapsulation) section.
|
||||
|
||||
</div>
|
||||
|
||||
@ -267,89 +267,3 @@ as explained in the [CLI wiki](https://github.com/angular/angular-cli/wiki/stori
|
||||
Style strings added to the `@Component.styles` array _must be written in CSS_ because the CLI cannot apply a preprocessor to inline styles.
|
||||
|
||||
</div>
|
||||
|
||||
{@a view-encapsulation}
|
||||
|
||||
## View encapsulation
|
||||
|
||||
As discussed earlier, component CSS styles are encapsulated into the component's view and don't
|
||||
affect the rest of the application.
|
||||
|
||||
To control how this encapsulation happens on a *per
|
||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
|
||||
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
|
||||
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Inspecting generated CSS](guide/component-styles#inspect-generated-css) below.
|
||||
|
||||
* `None` means that Angular does no view encapsulation.
|
||||
Angular adds the CSS to the global styles.
|
||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
||||
This is essentially the same as pasting the component's styles into the HTML.
|
||||
|
||||
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
|
||||
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
|
||||
|
||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
||||
in most cases.
|
||||
|
||||
{@a inspect-generated-css}
|
||||
|
||||
## Inspecting generated CSS
|
||||
|
||||
When using emulated view encapsulation, Angular preprocesses
|
||||
all component styles so that they approximate the standard shadow CSS scoping rules.
|
||||
|
||||
In the DOM of a running Angular application with emulated view
|
||||
encapsulation enabled, each DOM element has some extra attributes
|
||||
attached to it:
|
||||
|
||||
<code-example format="">
|
||||
<hero-details _nghost-pmm-5>
|
||||
<h2 _ngcontent-pmm-5>Mister Fantastic</h2>
|
||||
<hero-team _ngcontent-pmm-5 _nghost-pmm-6>
|
||||
<h3 _ngcontent-pmm-6>Team</h3>
|
||||
</hero-team>
|
||||
</hero-detail>
|
||||
|
||||
</code-example>
|
||||
|
||||
There are two kinds of generated attributes:
|
||||
|
||||
* An element that would be a shadow DOM host in native encapsulation has a
|
||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
||||
* An element within a component's view has a `_ngcontent` attribute
|
||||
that identifies to which host's emulated shadow DOM this element belongs.
|
||||
|
||||
The exact values of these attributes aren't important. They are automatically
|
||||
generated and you never refer to them in application code. But they are targeted
|
||||
by the generated component styles, which are in the `<head>` section of the DOM:
|
||||
|
||||
<code-example format="">
|
||||
[_nghost-pmm-5] {
|
||||
display: block;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
h3[_ngcontent-pmm-6] {
|
||||
background-color: white;
|
||||
border: 1px solid #777;
|
||||
}
|
||||
</code-example>
|
||||
|
||||
These styles are post-processed so that each selector is augmented
|
||||
with `_nghost` or `_ngcontent` attribute selectors.
|
||||
These extra selectors enable the scoping rules described in this page.
|
||||
|
@ -156,7 +156,7 @@ The library must be rebuilt on every change.
|
||||
When linking a library, make sure that the build step runs in watch mode, and that the library's `package.json` configuration points at the correct entry points.
|
||||
For example, `main` should point at a JavaScript file, not a TypeScript file.
|
||||
|
||||
## Use TypeScript path mapping for peer dependencies
|
||||
### Use TypeScript path mapping for peer dependencies
|
||||
|
||||
Angular libraries should list all `@angular/*` dependencies as peer dependencies.
|
||||
This ensures that when modules ask for Angular, they all get the exact same module.
|
||||
|
@ -97,7 +97,7 @@ For the simplest deployment, create a production build and copy the output direc
|
||||
</code-example>
|
||||
|
||||
|
||||
2. Copy _everything_ within the output folder (`dist/` by default) to a folder on the server.
|
||||
2. Copy _everything_ within the output folder (`dist/project-name/` by default) to a folder on the server.
|
||||
|
||||
3. Configure the server to redirect requests for missing files to `index.html`.
|
||||
Learn more about server-side redirects [below](#fallback).
|
||||
@ -211,11 +211,11 @@ modified to serve `index.html`:
|
||||
# .
|
||||
# -- server.rb
|
||||
# -- public
|
||||
# |-- dist
|
||||
# |-- project-name
|
||||
# |-- index.html
|
||||
|
||||
get '/' do
|
||||
folderDir = settings.public_folder + '/dist' # ng build output folder
|
||||
folderDir = settings.public_folder + '/project-name' # ng build output folder
|
||||
send_file File.join(folderDir, 'index.html')
|
||||
end
|
||||
```
|
||||
@ -383,11 +383,11 @@ Build your app for production _including the source maps_
|
||||
|
||||
</code-example>
|
||||
|
||||
List the generated bundles in the `dist/` folder.
|
||||
List the generated bundles in the `dist/project-name/` folder.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
ls dist/*.js
|
||||
ls dist/project-name/*.js
|
||||
|
||||
</code-example>
|
||||
|
||||
@ -396,7 +396,7 @@ The following example displays the graph for the _main_ bundle.
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
|
||||
node_modules/.bin/source-map-explorer dist/main*
|
||||
node_modules/.bin/source-map-explorer dist/project-name/main*
|
||||
|
||||
</code-example>
|
||||
|
||||
|
@ -1,312 +0,0 @@
|
||||
# Displaying data in views
|
||||
|
||||
Angular [components](guide/glossary#component) form the data structure of your application.
|
||||
The HTML [template](guide/glossary#template) associated with a component provides the means to display that data in the context of a web page.
|
||||
Together, a component's class and template form a [view](guide/glossary#view) of your application data.
|
||||
|
||||
The process of combining data values with their representation on the page is called [data binding](guide/glossary#data-binding).
|
||||
You display your data to a user (and collect data from the user) by *binding* controls in the HTML template to the data properties of the component class.
|
||||
|
||||
In addition, you can add logic to the template by including [directives](guide/glossary#directive), which tell Angular how to modify the page as it is rendered.
|
||||
|
||||
Angular defines a *template language* that expands HTML notation with syntax that allows you to define various kinds of data binding and logical directives.
|
||||
When the page is rendered, Angular interprets the template syntax to update the HTML according to your logic and current data state.
|
||||
Before you read the complete [template syntax guide](guide/template-syntax), the exercises on this page give you a quick demonstration of how template syntax works.
|
||||
|
||||
In this demo, you'll create a component with a list of heroes.
|
||||
You'll display the list of hero names and conditionally show a message below the list.
|
||||
The final UI looks like this:
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The <live-example></live-example> demonstrates all of the syntax and code snippets described in this page.
|
||||
|
||||
</div>
|
||||
|
||||
{@a interpolation}
|
||||
|
||||
## Showing component properties with interpolation
|
||||
The easiest way to display a component property is to bind the property name through interpolation.
|
||||
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
|
||||
|
||||
Use the CLI command [`ng new displaying-data`](cli/new) to create a workspace and app named `displaying-data`.
|
||||
|
||||
Delete the <code>app.component.html</code> file. It is not needed for this example.
|
||||
|
||||
Then modify the <code>app.component.ts</code> file by
|
||||
changing the template and the body of the component.
|
||||
|
||||
When you're done, it should look like this:
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
You added two properties to the formerly empty component: `title` and `myHero`.
|
||||
|
||||
The template displays the two component properties using double curly brace
|
||||
interpolation:
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>).
|
||||
The backtick (<code>\`</code>)—which is *not* the same character as a single
|
||||
quote (`'`)—allows you to compose a string over several lines, which makes the
|
||||
HTML more readable.
|
||||
|
||||
</div>
|
||||
|
||||
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
|
||||
inserts those values into the browser. Angular updates the display
|
||||
when these properties change.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
More precisely, the redisplay occurs after some kind of asynchronous event related to
|
||||
the view, such as a keystroke, a timer completion, or a response to an HTTP request.
|
||||
|
||||
</div>
|
||||
|
||||
Notice that you don't call **new** to create an instance of the `AppComponent` class.
|
||||
Angular is creating an instance for you. How?
|
||||
|
||||
The CSS `selector` in the `@Component` decorator specifies an element named `<app-root>`.
|
||||
That element is a placeholder in the body of your `index.html` file:
|
||||
|
||||
<code-example path="displaying-data/src/index.html" header="src/index.html (body)" region="body"></code-example>
|
||||
|
||||
When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<app-root>`
|
||||
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
|
||||
inside the `<app-root>` tag.
|
||||
|
||||
Now run the app. It should display the title and hero name:
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/displaying-data/title-and-hero.png" alt="Title and Hero">
|
||||
</div>
|
||||
|
||||
The next few sections review some of the coding choices in the app.
|
||||
|
||||
|
||||
## Choosing the template source
|
||||
|
||||
The `@Component` metadata tells Angular where to find the component's template.
|
||||
You can store your component's template in one of two places.
|
||||
|
||||
* You can define the template *inline* using the `template` property of the `@Component` decorator. An inline template is useful for a small demo or test.
|
||||
* Alternatively, you can define the template in a separate HTML file and link to that file in the `templateUrl` property of the `@Component` decorator. This configuration is typical for anything more complex than a small test or demo, and is the default when you generate a new component.
|
||||
|
||||
In either style, the template data bindings have the same access to the component's properties.
|
||||
Here the app uses inline HTML because the template is small and the demo is simpler without the additional HTML file.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
By default, the Angular CLI command [`ng generate component`](cli/generate) generates components with a template file.
|
||||
You can override that by adding the "-t" (short for `inlineTemplate=true`) option:
|
||||
|
||||
<code-example hideCopy language="sh" class="code-shell">
|
||||
ng generate component hero -t
|
||||
</code-example>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Initialization
|
||||
|
||||
The following example uses variable assignment to initialize the components.
|
||||
|
||||
<code-example path="displaying-data/src/app/app-ctor.component.1.ts" region="class"></code-example>
|
||||
|
||||
You could instead declare and initialize the properties using a constructor.
|
||||
This app uses more terse "variable assignment" style simply for brevity.
|
||||
|
||||
|
||||
{@a ngFor}
|
||||
|
||||
## Add logic to loop through data
|
||||
|
||||
The `*ngFor` directive (predefined by Angular) lets you loop through data. The following example uses the directive to show all of the values in an array property.
|
||||
|
||||
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
|
||||
Now use the Angular `ngFor` directive in the template to display each item in the `heroes` list.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
This UI uses the HTML unordered list with `<ul>` and `<li>` tags. The `*ngFor`
|
||||
in the `<li>` element is the Angular "repeater" directive.
|
||||
It marks that `<li>` element (and its children) as the "repeater template":
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.2.ts" header="src/app/app.component.ts (li)" region="li"></code-example>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
|
||||
Read more about `ngFor` and `*` in the [ngFor section](guide/built-in-directives#ngfor) of the [Built-in directives](guide/built-in-directives) page.
|
||||
|
||||
</div>
|
||||
|
||||
Notice the `hero` in the `ngFor` double-quoted instruction;
|
||||
it is an example of a template input variable. Read
|
||||
more about template input variables in the [microsyntax](guide/built-in-directives#microsyntax) section of
|
||||
the [Built-in directives](guide/built-in-directives) page.
|
||||
|
||||
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable
|
||||
to the item (the hero) in the current iteration. Angular uses that variable as the
|
||||
context for the interpolation in the double curly braces.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
In this case, `ngFor` is displaying an array, but `ngFor` can
|
||||
repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) object.
|
||||
|
||||
</div>
|
||||
|
||||
Now the heroes appear in an unordered list.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/displaying-data/hero-names-list.png" alt="After ngfor">
|
||||
</div>
|
||||
|
||||
|
||||
## Creating a class for the data
|
||||
|
||||
The app's code defines the data directly inside the component, which isn't best practice.
|
||||
In a simple demo, however, it's fine.
|
||||
|
||||
At the moment, the binding is to an array of strings.
|
||||
In real applications, most bindings are to more specialized objects.
|
||||
|
||||
To convert this binding to use specialized objects, turn the array
|
||||
of hero names into an array of `Hero` objects. For that you'll need a `Hero` class:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate class hero
|
||||
</code-example>
|
||||
|
||||
This command creates the following code.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
||||
|
||||
You've defined a class with a constructor and two properties: `id` and `name`.
|
||||
|
||||
It might not look like the class has properties, but it does.
|
||||
The declaration of the constructor parameters takes advantage of a TypeScript shortcut.
|
||||
|
||||
Consider the first parameter:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/hero.ts" header="src/app/hero.ts (id)" region="id"></code-example>
|
||||
|
||||
That brief syntax does a lot:
|
||||
|
||||
* Declares a constructor parameter and its type.
|
||||
* Declares a public property of the same name.
|
||||
* Initializes that property with the corresponding argument when creating an instance of the class.
|
||||
|
||||
|
||||
### Using the Hero class
|
||||
|
||||
After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ array
|
||||
of `Hero` objects:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (heroes)" region="heroes"></code-example>
|
||||
|
||||
|
||||
|
||||
Next, update the template.
|
||||
At the moment it displays the hero's `id` and `name`.
|
||||
Fix that to display only the hero's `name` property.
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.3.ts" header="src/app/app.component.ts (template)" region="template"></code-example>
|
||||
|
||||
|
||||
The display looks the same, but the code is clearer.
|
||||
|
||||
{@a ngIf}
|
||||
|
||||
## Conditional display with NgIf
|
||||
|
||||
Sometimes an app needs to display a view or a portion of a view only under specific circumstances.
|
||||
|
||||
Let's change the example to display a message if there are more than three heroes.
|
||||
|
||||
The Angular `ngIf` directive inserts or removes an element based on a _truthy/falsy_ condition.
|
||||
To see it in action, add the following paragraph at the bottom of the template:
|
||||
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.ts" header="src/app/app.component.ts (message)" region="message"></code-example>
|
||||
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
|
||||
Read more about `ngIf` and `*` in the [ngIf section](guide/built-in-directives#ngIf) of the [Built-in directives](guide/built-in-directives) page.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
The template expression inside the double quotes,
|
||||
`*ngIf="heroes.length > 3"`, looks and behaves much like TypeScript.
|
||||
When the component's list of heroes has more than three items, Angular adds the paragraph
|
||||
to the DOM and the message appears.
|
||||
If there are three or fewer items, Angular omits the paragraph, so no message appears.
|
||||
|
||||
For more information, see [template expression operators](guide/interpolation#template-expressions).
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM. That improves performance, especially in larger projects when conditionally including or excluding
|
||||
big chunks of HTML with many data bindings.
|
||||
|
||||
</div>
|
||||
|
||||
Try it out. Because the array has four items, the message should appear.
|
||||
Go back into <code>app.component.ts</code> and delete or comment out one of the elements from the heroes array.
|
||||
The browser should refresh automatically and the message should disappear.
|
||||
|
||||
|
||||
## Summary
|
||||
Now you know how to use:
|
||||
|
||||
* **Interpolation** with double curly braces to display a component property.
|
||||
* **ngFor** to display an array of items.
|
||||
* A TypeScript class to shape the **model data** for your component and display properties of that model.
|
||||
* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
|
||||
|
||||
Here's the final code:
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane header="src/app/app.component.ts" path="displaying-data/src/app/app.component.ts" region="final">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/hero.ts" path="displaying-data/src/app/hero.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="src/app/app.module.ts" path="displaying-data/src/app/app.module.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane header="main.ts" path="displaying-data/src/main.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
@ -4,7 +4,7 @@ An entry component is any component that Angular loads imperatively, (which mean
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
||||
To contrast the two types of components, there are components which are included in the template, which are declarative. Additionally, there are components which you load imperatively; that is, entry components.
|
||||
|
||||
</div>
|
||||
|
||||
@ -75,11 +75,10 @@ All router components must be entry components. Because this would require you t
|
||||
|
||||
|
||||
## The `entryComponents` array
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Since 9.0.0 with Ivy, the `entryComponents` property is no longer necessary. See [deprecations guide](guide/deprecations#entryComponents).
|
||||
|
||||
|
||||
Since 9.0.0 with Ivy, the `entryComponents` property is no longer necessary. See [deprecations guide](guide/deprecations#entryComponents).
|
||||
|
||||
</div>
|
||||
|
||||
Though the `@NgModule` decorator has an `entryComponents` array, most of the time
|
||||
|
@ -99,7 +99,7 @@ in the `$event` variable.
|
||||
|
||||
## Template statements have side effects
|
||||
|
||||
Though [template expressions](guide/interpolation#template-expressions) shouldn't have [side effects](guide/property-binding#avoid-side-effects), template
|
||||
Though [template expressions](guide/interpolation#template-expressions) shouldn't have [side effects](guide/property-binding-best-practices#avoid-side-effects), template
|
||||
statements usually do. The `deleteItem()` method does have
|
||||
a side effect: it deletes an item.
|
||||
|
||||
|
@ -627,10 +627,11 @@ The [npm package manager](https://docs.npmjs.com/getting-started/what-is-npm) is
|
||||
|
||||
Learn more about how Angular uses [Npm Packages](guide/npm-packages).
|
||||
|
||||
{@ ngc}
|
||||
{@a ngc}
|
||||
|
||||
## ngc
|
||||
`ngc` is a Typescript-to-Javascript transpiler that processes Angular decorators, metadata, and templates, and emits JavaScript code.
|
||||
The most recent implementation is internally refered to as `ngtsc` because it's a minimalistic wrapper around the TypeScript compiler `tsc` that adds a transform for processing Angular code.
|
||||
The most recent implementation is internally referred to as `ngtsc` because it's a minimalistic wrapper around the TypeScript compiler `tsc` that adds a transform for processing Angular code.
|
||||
|
||||
{@a O}
|
||||
|
||||
|
@ -397,7 +397,7 @@ The following code example binds an observable of message strings
|
||||
## Caching HTTP requests
|
||||
|
||||
To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HTTPClient.get()` method to fetch data from a server.
|
||||
The aynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
||||
The asynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
||||
|
||||
As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
||||
You can also create an impure pipe to make and cache an HTTP request.
|
||||
|
65
aio/content/guide/property-binding-best-practices.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Property binding best practices
|
||||
|
||||
By following a few guidelines, you can use property binding in a way that helps you minimize bugs and keep your code readable.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
See the <live-example name="property-binding"></live-example> for a working example containing the code snippets in this guide.
|
||||
|
||||
</div>
|
||||
|
||||
## Avoid side effects
|
||||
|
||||
Evaluation of a template expression should have no visible side effects.
|
||||
Use the syntax for template expressions to help avoid side effects.
|
||||
In general, the correct syntax prevents you from assigning a value to anything in a property binding expression.
|
||||
The syntax also prevents you from using increment and decrement operators.
|
||||
|
||||
### An example of producing side effects
|
||||
|
||||
If you had an expression that changed the value of something else that you were binding to, that change of value would be a side effect.
|
||||
Angular might or might not display the changed value.
|
||||
If Angular does detect the change, it throws an error.
|
||||
|
||||
As a best practice, use only properties and methods that return values.
|
||||
|
||||
## Return the proper type
|
||||
|
||||
A template expression should evaluate to the type of value that the target property expects.
|
||||
For example, return a string if the target property expects a string, a number if it expects a number, or an object if it expects an object.
|
||||
|
||||
### Passing in a string
|
||||
|
||||
In the following example, the `childItem` property of the `ItemDetailComponent` expects a string.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can confirm this expectation by looking in the `ItemDetailComponent` where the `@Input()` type is `string`:
|
||||
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type)"></code-example>
|
||||
|
||||
The `parentItem` in `AppComponent` is a string, which means that the expression, `parentItem` within `[childItem]="parentItem"`, evaluates to a string.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
If `parentItem` were some other type, you would need to specify `childItem` `@Input()` as that type as well.
|
||||
|
||||
### Passing in an object
|
||||
|
||||
In this example, `ItemListComponent` is a child component of `AppComponent` and the `items` property expects an array of objects.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
|
||||
|
||||
In the `ItemListComponent` the `@Input()`, `items`, has a type of `Item[]`.
|
||||
|
||||
<code-example path="property-binding/src/app/item-list/item-list.component.ts" region="item-input" header="src/app/item-list.component.ts"></code-example>
|
||||
|
||||
Notice that `Item` is an object that it has two properties; an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts"></code-example>
|
||||
|
||||
In `app.component.ts`, `currentItems` is an array of objects in the same shape as the `Item` object in `items.ts`, with an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
|
||||
|
||||
By supplying an object in the same shape, you satisfy the expectations of `items` when Angular evaluates the expression `currentItems`.
|
@ -1,8 +1,8 @@
|
||||
|
||||
# Property binding `[property]`
|
||||
# Property binding
|
||||
|
||||
Use property binding to _set_ properties of target elements or
|
||||
directive `@Input()` decorators.
|
||||
Property binding in Angular helps you set values for properties of HTML elements or directives.
|
||||
With property binding, you can do things such as toggle button functionality, set paths programatically, and share values between components.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -10,36 +10,86 @@ See the <live-example></live-example> for a working example containing the code
|
||||
|
||||
</div>
|
||||
|
||||
## One-way in
|
||||
## Prerequisites
|
||||
|
||||
Property binding flows a value in one direction,
|
||||
from a component's property into a target element property.
|
||||
To get the most out of property binding, you should be familiar with the following:
|
||||
|
||||
You can't use property
|
||||
binding to read or pull values out of target elements. Similarly, you cannot use
|
||||
property binding to call a method on the target element.
|
||||
If the element raises events, you can listen to them with an [event binding](guide/event-binding).
|
||||
* [Basics of components](guide/architecture-components)
|
||||
* [Basics of templates](guide/glossary#template)
|
||||
* [Binding syntax](guide/binding-syntax)
|
||||
|
||||
If you must read a target element property or call one of its methods,
|
||||
see the API reference for [ViewChild](api/core/ViewChild) and
|
||||
[ContentChild](api/core/ContentChild).
|
||||
<hr />
|
||||
|
||||
## Examples
|
||||
## Understanding the flow of data
|
||||
|
||||
The most common property binding sets an element property to a component
|
||||
property value. An example is
|
||||
binding the `src` property of an image element to a component's `itemImageUrl` property:
|
||||
Property binding moves a value in one direction, from a component's property into a target element property.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on listening for events, see [Event binding](guide/event-binding).
|
||||
|
||||
</div>
|
||||
|
||||
To read a target element property or call one of its methods, see the API reference for [ViewChild](api/core/ViewChild) and [ContentChild](api/core/ContentChild).
|
||||
|
||||
## Binding to a property
|
||||
|
||||
To bind to an element's property, enclose it in square brackets, `[]`, which identifies the property as a target property.
|
||||
A target property is the DOM property to which you want to assign a value.
|
||||
For example, the target property in the following code is the image element's `src` property.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Here's an example of binding to the `colSpan` property. Notice that it's not `colspan`,
|
||||
which is the attribute, spelled with a lowercase `s`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||
In most cases, the target name is the name of a property, even when it appears to be the name of an attribute.
|
||||
In this example, `src` is the name of the `<img>` element property.
|
||||
|
||||
For more details, see the [MDN HTMLTableCellElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) documentation.
|
||||
The brackets, `[]`, cause Angular to evaluate the right-hand side of the assignment as a dynamic expression.
|
||||
Without the brackets, Angular treats the the right-hand side as a string literal and sets the property to that static value.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html"></code-example>
|
||||
|
||||
Omitting the brackets renders the string `parentItem`, not the value of `parentItem`.
|
||||
|
||||
## Setting an element property to a component property value
|
||||
|
||||
To bind the `src` property of an `<img>` element to a component's property, place the target, `src`, in square brackets followed by an equal sign and then the property.
|
||||
The property here is `itemImageUrl`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Declare the `itemImageUrl` property in the class, in this case `AppComponent`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="item-image" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
{@a colspan}
|
||||
|
||||
#### `colspan` and `colSpan`
|
||||
|
||||
A common point of confusion is between the attribute, `colspan`, and the property, `colSpan`.
|
||||
Notice that these two names differ by only a single letter.
|
||||
|
||||
If you wrote something like this:
|
||||
|
||||
<code-example language="html">
|
||||
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||||
</code-example>
|
||||
|
||||
You'd get this error:
|
||||
|
||||
<code-example language="bash">
|
||||
Template parse errors:
|
||||
Can't bind to 'colspan' since it isn't a known native property
|
||||
</code-example>
|
||||
|
||||
As the message says, the `<td>` element does not have a `colspan` property. This is true
|
||||
because `colspan` is an attribute—`colSpan`, with a capital `S`, is the
|
||||
corresponding property. Interpolation and property binding can set only *properties*, not attributes.
|
||||
|
||||
Instead, you'd use property binding and write it like this:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="colSpan" header="src/app/app.component.html"></code-example>
|
||||
|
||||
For more information about `colSpan` and `colspan`, see the [Attribute binding](guide/attribute-binding#colspan) guide.
|
||||
|
||||
Another example is disabling a button when the component says that it `isUnchanged`:
|
||||
|
||||
@ -54,175 +104,100 @@ for parent and child components to communicate:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
## Binding targets
|
||||
|
||||
An element property between enclosing square brackets identifies the target property.
|
||||
The target property in the following code is the image element's `src` property.
|
||||
## Toggling button functionality
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding" header="src/app/app.component.html"></code-example>
|
||||
To disable a button's functionality depending on a Boolean value, bind the DOM `disabled` property to a property in the class that is `true` or `false`.
|
||||
|
||||
There's also the `bind-` prefix alternative:
|
||||
<code-example path="property-binding/src/app/app.component.html" region="disabled-button" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="bind-prefix" header="src/app/app.component.html"></code-example>
|
||||
Because the value of the property `isUnchanged` is `true` in the `AppComponent`, Angular disables the button.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="boolean" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
|
||||
In most cases, the target name is the name of a property, even
|
||||
when it appears to be the name of an attribute.
|
||||
So in this case, `src` is the name of the `<img>` element property.
|
||||
## Setting a directive property
|
||||
|
||||
Element properties may be the more common targets,
|
||||
but Angular looks first to see if the name is a property of a known directive,
|
||||
as it is in the following example:
|
||||
To set a property of a directive, place the directive within square brackets , such as `[ngClass]`, followed by an equal sign and the property.
|
||||
Here, the property is `classes`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="class-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Technically, Angular is matching the name to a directive `@Input()`,
|
||||
one of the property names listed in the directive's `inputs` array
|
||||
or a property decorated with `@Input()`.
|
||||
Such inputs map to the directive's own properties.
|
||||
To use the property, you must declare it in the class, which in this example is `AppComponent`.
|
||||
The value of `classes` is `special`.
|
||||
|
||||
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="directive-property" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
Angular applies the class `special` to the `<p>` element so that you can use `special` to apply CSS styles.
|
||||
|
||||
Though the target name is usually the name of a property,
|
||||
there is an automatic attribute-to-property mapping in Angular for
|
||||
several common attributes. These include `class`/`className`, `innerHtml`/`innerHTML`, and
|
||||
`tabindex`/`tabIndex`.
|
||||
## Bind values between components
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Avoid side effects
|
||||
|
||||
Evaluation of a template expression should have no visible side effects.
|
||||
The expression language itself, or the way you write template expressions,
|
||||
helps to a certain extent;
|
||||
you can't assign a value to anything in a property binding expression
|
||||
nor use the increment and decrement operators.
|
||||
|
||||
For example, you could have an expression that invoked a property or method that had
|
||||
side effects. The expression could call something like `getFoo()` where only you
|
||||
know what `getFoo()` does. If `getFoo()` changes something
|
||||
and you happen to be binding to that something,
|
||||
Angular may or may not display the changed value. Angular may detect the
|
||||
change and throw a warning error.
|
||||
As a best practice, stick to properties and to methods that return
|
||||
values and avoid side effects.
|
||||
|
||||
## Return the proper type
|
||||
|
||||
The template expression should evaluate to the type of value
|
||||
that the target property expects.
|
||||
Return a string if the target property expects a string, a number if it
|
||||
expects a number, an object if it expects an object, and so on.
|
||||
|
||||
In the following example, the `childItem` property of the `ItemDetailComponent` expects a string, which is exactly what you're sending in the property binding:
|
||||
To set the model property of a custom component, place the target, here `childItem`, between square brackets `[]` followed by an equal sign and the property.
|
||||
Here, the property is `parentItem`.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="model-property-binding" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can confirm this by looking in the `ItemDetailComponent` where the `@Input` type is set to a string:
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts (setting the @Input() type)"></code-example>
|
||||
To use the target and the property, you must declare them in their respective classes.
|
||||
|
||||
Declare the target of `childItem` in its component class, in this case `ItemDetailComponent`.
|
||||
|
||||
For example, the following code declares the target of `childItem` in its component class, in this case `ItemDetailComponent`.
|
||||
|
||||
Then, the code contains an `@Input()` decorator with the `childItem` property so data can flow into it.
|
||||
|
||||
<code-example path="property-binding/src/app/item-detail/item-detail.component.ts" region="input-type" header="src/app/item-detail/item-detail.component.ts"></code-example>
|
||||
|
||||
Next, the code declares the property of `parentItem` in its component class, in this case `AppComponent`.
|
||||
In this example the type of `childItem` is `string`, so `parentItem` needs to be a string.
|
||||
Here, `parentItem` has the string value of `lamp`.
|
||||
|
||||
As you can see here, the `parentItem` in `AppComponent` is a string, which the `ItemDetailComponent` expects:
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="parent-data-type" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
### Passing in an object
|
||||
With this configuration, the view of `<app-item-detail>` uses the value of `lamp` for `childItem`.
|
||||
|
||||
The previous simple example showed passing in a string. To pass in an object,
|
||||
the syntax and thinking are the same.
|
||||
## Property binding and security
|
||||
|
||||
In this scenario, `ItemListComponent` is nested within `AppComponent` and the `items` property expects an array of objects.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="pass-object" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `items` property is declared in the `ItemListComponent` with a type of `Item` and decorated with `@Input()`:
|
||||
|
||||
<code-example path="property-binding/src/app/item-list/item-list.component.ts" region="item-input" header="src/app/item-list.component.ts"></code-example>
|
||||
|
||||
In this sample app, an `Item` is an object that has two properties; an `id` and a `name`.
|
||||
|
||||
<code-example path="property-binding/src/app/item.ts" region="item-class" header="src/app/item.ts"></code-example>
|
||||
|
||||
While a list of items exists in another file, `mock-items.ts`, you can
|
||||
specify a different item in `app.component.ts` so that the new item will render:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="pass-object" header="src/app.component.ts"></code-example>
|
||||
|
||||
You just have to make sure, in this case, that you're supplying an array of objects because that's the type of `Item` and is what the nested component, `ItemListComponent`, expects.
|
||||
|
||||
In this example, `AppComponent` specifies a different `item` object
|
||||
(`currentItems`) and passes it to the nested `ItemListComponent`. `ItemListComponent` was able to use `currentItems` because it matches what an `Item` object is according to `item.ts`. The `item.ts` file is where
|
||||
`ItemListComponent` gets its definition of an `item`.
|
||||
|
||||
## Remember the brackets
|
||||
|
||||
The brackets, `[]`, tell Angular to evaluate the template expression.
|
||||
If you omit the brackets, Angular treats the string as a constant
|
||||
and *initializes the target property* with that string:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="no-evaluation" header="src/app.component.html"></code-example>
|
||||
|
||||
|
||||
Omitting the brackets will render the string
|
||||
`parentItem`, not the value of `parentItem`.
|
||||
|
||||
## One-time string initialization
|
||||
|
||||
You *should* omit the brackets when all of the following are true:
|
||||
|
||||
* The target property accepts a string value.
|
||||
* The string is a fixed value that you can put directly into the template.
|
||||
* This initial value never changes.
|
||||
|
||||
You routinely initialize attributes this way in standard HTML, and it works
|
||||
just as well for directive and component property initialization.
|
||||
The following example initializes the `prefix` property of the `StringInitComponent` to a fixed string,
|
||||
not a template expression. Angular sets it and forgets about it.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="string-init" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `[item]` binding, on the other hand, remains a live binding to the component's `currentItems` property.
|
||||
|
||||
## Property binding vs. interpolation
|
||||
|
||||
You often have a choice between interpolation and property binding.
|
||||
The following binding pairs do the same thing:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation is a convenient alternative to property binding in
|
||||
many cases. When rendering data values as strings, there is no
|
||||
technical reason to prefer one form to the other, though readability
|
||||
tends to favor interpolation. However, *when setting an element
|
||||
property to a non-string data value, you must use property binding*.
|
||||
|
||||
## Content security
|
||||
|
||||
Imagine the following malicious content.
|
||||
Property binding can help keep content secure.
|
||||
For example, consider the following malicious content.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.ts" region="malicious-content" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
In the component template, the content might be used with interpolation:
|
||||
The component template interpolates the content as follows:
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-interpolated" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Fortunately, Angular data binding is on alert for dangerous HTML. In the above case,
|
||||
the HTML displays as is, and the Javascript does not execute. Angular **does not**
|
||||
allow HTML with script tags to leak into the browser, neither with interpolation
|
||||
nor property binding.
|
||||
|
||||
In the following example, however, Angular [sanitizes](guide/security#sanitization-and-security-contexts)
|
||||
the values before displaying them.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation handles the `<script>` tags differently than
|
||||
property binding but both approaches render the
|
||||
content harmlessly. The following is the browser output
|
||||
of the `evilTitle` examples.
|
||||
The browser doesn't process the HTML and instead displays it raw, as follows.
|
||||
|
||||
<code-example language="bash">
|
||||
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
||||
</code-example>
|
||||
|
||||
|
||||
Angular does not allow HTML with `<script>` tags, neither with [interpolation](guide/interpolation) nor property binding, which prevents the JavaScript from running.
|
||||
|
||||
In the following example, however, Angular [sanitizes](guide/security#sanitization-and-security-contexts) the values before displaying them.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="malicious-content" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Interpolation handles the `<script>` tags differently than property binding, but both approaches render the content harmlessly.
|
||||
The following is the browser output of the sanitized `evilTitle` example.
|
||||
|
||||
<code-example language="bash">
|
||||
"Template Syntax" is the property bound evil title.
|
||||
</code-example>
|
||||
|
||||
## Property binding and interpolation
|
||||
|
||||
Often [interpolation](guide/interpolation) and property binding can achieve the same results.
|
||||
The following binding pairs do the same thing.
|
||||
|
||||
<code-example path="property-binding/src/app/app.component.html" region="property-binding-interpolation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
You can use either form when rendering data values as strings, though interpolation is preferable for readability.
|
||||
However, when setting an element property to a non-string data value, you must use property binding.
|
||||
|
||||
<hr />
|
||||
|
||||
## What's next
|
||||
|
||||
* [Property binding best practices](guide/property-binding-best-practices)
|
||||
|
@ -180,7 +180,7 @@ A form group tracks the status and changes for each of its controls, so if one o
|
||||
|
||||
<code-example path="reactive-forms/src/app/profile-editor/profile-editor.component.1.html" region="formgroup" header="src/app/profile-editor/profile-editor.component.html (template form group)"></code-example>
|
||||
|
||||
Note that just as a form group contains a group of controls, the *profile form* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
||||
Note that just as a form group contains a group of controls, the *profileForm* `FormGroup` is bound to the `form` element with the `FormGroup` directive, creating a communication layer between the model and the form containing the inputs. The `formControlName` input provided by the `FormControlName` directive binds each individual input to the form control defined in `FormGroup`. The form controls communicate with their respective elements. They also communicate changes to the form group instance, which provides the source of truth for the model value.
|
||||
|
||||
**Save form data**
|
||||
|
||||
|
@ -1656,7 +1656,7 @@ _before_ the `AppRoutingModule`:
|
||||
|
||||
</code-tabs>
|
||||
|
||||
Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide teh feature routes.
|
||||
Remove the initial crisis center route from the `app-routing.module.ts` because now the `HeroesModule` and the `CrisisCenter` modules provide the feature routes.
|
||||
|
||||
The `app-routing.module.ts` file retains the top-level application routes such as the default and wildcard routes.
|
||||
|
||||
|
133
aio/content/guide/style-precedence.md
Normal file
@ -0,0 +1,133 @@
|
||||
# Style Precedence
|
||||
|
||||
When there are multiple bindings to the same class name or style attribute, Angular uses a set of precedence rules to determine which classes or styles to apply to the element.
|
||||
These rules specify an order for which style- and class-related bindings have priority.
|
||||
This styling precedence is as follows, from the most specific with the highest priority to least specific with the lowest priorty:
|
||||
|
||||
1. Template bindings are the most specific because they apply to the element directly and exclusively, so they have the highest precedence.
|
||||
<table width="100%">
|
||||
<col width="40%"></col>
|
||||
<col width="60%"></col>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Binding type</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Property binding</td>
|
||||
<td><code><div [class.foo]="hasFoo"></code><br><code><div [style.color]="color"></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Map binding</td>
|
||||
<td><code><div [class]="classExpression"></code><br><code><div [style]="styleExpression"></code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Static value</td>
|
||||
<td><code><div class="foo"></code><br><code><div style="color: blue"></code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
1. Directive host bindings are less specific because you can use directives in multiple locations, so they have a lower precedence than template bindings.
|
||||
<table width="100%">
|
||||
<col width="40%"></col>
|
||||
<col width="60%"></col>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Binding type</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Property binding</td>
|
||||
<td><code>host: {'[class.foo]': 'hasFoo'}</code><br><code>host: {'[style.color]': 'color'}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Map binding</td>
|
||||
<td><code>host: {'[class]': 'classExpr'}</code><br><code>host: {'[style]': 'styleExpr'}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Static value</td>
|
||||
<td><code>host: {'class': 'foo'}</code><br><code>host: {'style': 'color: blue'}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
1. Component host bindings have the lowest precedence.
|
||||
<table width="100%">
|
||||
<col width="40%"></col>
|
||||
<col width="60%"></col>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Binding type</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Property binding</td>
|
||||
<td><code>host: {'[class.foo]': 'hasFoo'}</code><br><code>host: {'[style.color]': 'color'}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Map binding</td>
|
||||
<td><code>host: {'[class]': 'classExpression'}</code><br><code>host: {'[style]': 'styleExpression'}</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Static value</td>
|
||||
<td><code>host: {'class': 'foo'}</code><br><code>host: {'style': 'color: blue'}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Precedence and specificity
|
||||
|
||||
In the following example, binding to a specific class, as in `[class.special]`, takes precedence over a generic `[class]` binding.
|
||||
Similarly, binding to a specific style, as in `[style.color]`, takes precedence over a generic `[style]` binding.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="basic-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
## Precedence and bindings from different sources
|
||||
|
||||
Specificity rules also apply to bindings even when they originate from different sources.
|
||||
An element can have bindings that originate from its own template, from host bindings on matched directives, and from host bindings on matched components.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="source-specificity" header="src/app/app.component.html"></code-example>
|
||||
|
||||
## Precedence of bindings and static attributes
|
||||
|
||||
Bindings take precedence over static attributes because they are dynamic.
|
||||
In the following case, `class` and `[class]` have similar specificity, but the `[class]` binding takes precedence.
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="dynamic-priority" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a styling-delegation}
|
||||
|
||||
## Delegating to styles with lower precedence
|
||||
|
||||
Higher precedence styles can defer to lower precedence styles using `undefined` values.
|
||||
For example, consider the following template:
|
||||
|
||||
<code-example path="attribute-binding/src/app/app.component.html" region="style-delegation" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Imagine that the `dirWithHostBinding` directive and the `comp-with-host-binding` component both have a `[style.width]` host binding.
|
||||
|
||||
<code-example path="attribute-binding/src/app/comp-with-host-binding.component.ts" region="hostbinding" header="src/app/comp-with-host-binding.component.ts and dirWithHostBinding.directive.ts"></code-example>
|
||||
|
||||
If `dirWithHostBinding` sets its binding to `undefined`, the `width` property falls back to the value of the `comp-with-host-binding` host binding.
|
||||
|
||||
<code-example header="dirWithHostBinding directive">
|
||||
@HostBinding('style.width')
|
||||
width = ''; // undefined
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If `dirWithHostBinding` sets its binding to `null`, Angular removes the `width` property entirely.
|
||||
|
||||
<code-example header="dirWithHostBinding">
|
||||
@HostBinding('style.width')
|
||||
width = null;
|
||||
</code-example>
|
||||
|
||||
</div>
|
@ -14,7 +14,7 @@ In the following example, the template statement `deleteHero()` appears in quote
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="context-component-statement" header="src/app/app.component.html"></code-example>
|
||||
|
||||
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` function in the component class.
|
||||
When the user clicks the **Delete hero** button, Angular calls the `deleteHero()` method in the component class.
|
||||
|
||||
You can use template statements with elements, components, or directives in response to events.
|
||||
|
||||
|
@ -479,6 +479,15 @@ using the `downgradeComponent()` method. The result is an AngularJS
|
||||
<code-example path="upgrade-module/src/app/downgrade-static/app.module.ts" region="downgradecomponent" header="app.module.ts">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
By default, Angular change detection will also run on the component for every
|
||||
AngularJS `$digest` cycle. If you wish to only have change detection run when
|
||||
the inputs change, you can set `propagateDigest` to `false` when calling
|
||||
`downgradeComponent()`.
|
||||
|
||||
</div>
|
||||
|
||||
Because `HeroDetailComponent` is an Angular component, you must also add it to the
|
||||
`declarations` in the `AppModule`.
|
||||
|
||||
|
@ -234,8 +234,8 @@ To fix this issue, listen to both the _Enter_ key and the _blur_ event.
|
||||
|
||||
|
||||
## Put it all together
|
||||
The previous page showed how to [display data](guide/displaying-data).
|
||||
This page demonstrated event binding techniques.
|
||||
|
||||
This page demonstrated several event binding techniques.
|
||||
|
||||
Now, put it all together in a micro-app
|
||||
that can display a list of heroes and add new heroes to the list.
|
||||
|
83
aio/content/guide/view-encapsulation.md
Normal file
@ -0,0 +1,83 @@
|
||||
# View encapsulation
|
||||
|
||||
In Angular, component CSS styles are encapsulated into the component's view and don't
|
||||
affect the rest of the application.
|
||||
|
||||
To control how this encapsulation happens on a *per
|
||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
|
||||
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
|
||||
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Inspecting generated CSS](guide/view-encapsulation#inspect-generated-css) below.
|
||||
|
||||
* `None` means that Angular does no view encapsulation.
|
||||
Angular adds the CSS to the global styles.
|
||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
||||
This is essentially the same as pasting the component's styles into the HTML.
|
||||
|
||||
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
|
||||
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" header="src/app/quest-summary.component.ts"></code-example>
|
||||
|
||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
||||
in most cases.
|
||||
|
||||
{@a inspect-generated-css}
|
||||
|
||||
## Inspecting generated CSS
|
||||
|
||||
When using emulated view encapsulation, Angular preprocesses
|
||||
all component styles so that they approximate the standard shadow CSS scoping rules.
|
||||
|
||||
In the DOM of a running Angular application with emulated view
|
||||
encapsulation enabled, each DOM element has some extra attributes
|
||||
attached to it:
|
||||
|
||||
<code-example format="">
|
||||
<hero-details _nghost-pmm-5>
|
||||
<h2 _ngcontent-pmm-5>Mister Fantastic</h2>
|
||||
<hero-team _ngcontent-pmm-5 _nghost-pmm-6>
|
||||
<h3 _ngcontent-pmm-6>Team</h3>
|
||||
</hero-team>
|
||||
</hero-detail>
|
||||
|
||||
</code-example>
|
||||
|
||||
There are two kinds of generated attributes:
|
||||
|
||||
* An element that would be a shadow DOM host in native encapsulation has a
|
||||
generated `_nghost` attribute. This is typically the case for component host elements.
|
||||
* An element within a component's view has a `_ngcontent` attribute
|
||||
that identifies to which host's emulated shadow DOM this element belongs.
|
||||
|
||||
The exact values of these attributes aren't important. They are automatically
|
||||
generated and you never refer to them in application code. But they are targeted
|
||||
by the generated component styles, which are in the `<head>` section of the DOM:
|
||||
|
||||
<code-example format="">
|
||||
[_nghost-pmm-5] {
|
||||
display: block;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
h3[_ngcontent-pmm-6] {
|
||||
background-color: white;
|
||||
border: 1px solid #777;
|
||||
}
|
||||
</code-example>
|
||||
|
||||
These styles are post-processed so that each selector is augmented
|
||||
with `_nghost` or `_ngcontent` attribute selectors.
|
||||
These extra selectors enable the scoping rules described in this page.
|
@ -9,7 +9,7 @@ To understand the benefits of `NgZone`, it is important to have a clear grasp of
|
||||
|
||||
### Displaying and updating data in Angular
|
||||
|
||||
In Angular, you can [display data](guide/displaying-data) by binding controls in an HTML template to the properties of an Angular component.
|
||||
In Angular, you can display data by binding controls in an HTML template to the properties of an Angular component.
|
||||
|
||||
<code-example path="displaying-data/src/app/app.component.1.ts" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
@ -102,13 +102,13 @@ In Angular, this step is unnecessary. Whenever you update the data, your HTML is
|
||||
|
||||
To understand how change detection works, first consider when the application needs to update the HTML. Typically, updates occur for one of the following reasons:
|
||||
|
||||
1. Component initialization. For example, when bootstrapping an Angular application, Angular loads the bootstrap component and triggers the [ApplicationRef.tick()](api/core/ApplicationRef#tick) to call change detection and View Rendering. Just as in the [displaying data](guide/displaying-data) sample, the `AppComponent` is the bootstrap component. This component has the properties `title` and `myHero`, which the application renders in the HTML.
|
||||
1. Component initialization. For example, when bootstrapping an Angular application, Angular loads the bootstrap component and triggers the [ApplicationRef.tick()](api/core/ApplicationRef#tick) to call change detection and View Rendering.
|
||||
|
||||
2. Event listener. The DOM event listener can update the data in an Angular component and also trigger change detection, as in the following example.
|
||||
1. Event listener. The DOM event listener can update the data in an Angular component and also trigger change detection, as in the following example.
|
||||
|
||||
<code-example path="user-input/src/app/click-me.component.ts" region="click-me-component" header="src/app/click-me.component.ts"></code-example>
|
||||
|
||||
3. HTTP Data Request. You can also get data from a server through an HTTP request. For example:
|
||||
1. HTTP Data Request. You can also get data from a server through an HTTP request. For example:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
|
BIN
aio/content/images/bios/fmalcher.jpg
Executable file
After Width: | Height: | Size: 12 KiB |
BIN
aio/content/images/bios/jessicajaniuk.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
aio/content/images/bios/johanneshoppe.jpg
Executable file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 79 KiB |
@ -458,8 +458,8 @@
|
||||
"name": "Sajeetharan Sinnathurai",
|
||||
"picture": "sajee.jpg",
|
||||
"twitter": "kokkisajee",
|
||||
"website": "https://sajeetharan.herokuapp.com/",
|
||||
"bio": "Sajeetharan is a Developer, Top contributor on stackoverflow for #Angular, ng-SriLanka organizer. He makes use of his extensive knowledge over the past years to contribute to community to make the world a better place.",
|
||||
"website": "https://sajeetharan.com/",
|
||||
"bio": "Sajeetharan is a Cloud Architect, Top contributor on stackoverflow for #Angular, ng-SriLanka organizer. He makes use of his extensive knowledge over the past years to contribute to community to make the world a better place.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"lacolaco": {
|
||||
@ -826,5 +826,30 @@
|
||||
"website": "https://thekiba.io",
|
||||
"bio": "Andrew is a software engineer using Angular and .NET. He spends most of his spare time staying up-to-date, helping other people, and experimenting with web tech.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"fmalcher": {
|
||||
"name": "Ferdinand Malcher",
|
||||
"picture": "fmalcher.jpg",
|
||||
"twitter": "fmalcher01",
|
||||
"website": "https://angular.schule",
|
||||
"bio": "Ferdinand is a developer, trainer, and international speaker from Leipzig, Germany. He is co-author of the German Angular book and loves to share knowledge – that's why he writes books and blog posts, speaks at meetups and conferences, does Angular training and works as a consultant in his company \"Angular.Schule\".",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"johanneshoppe": {
|
||||
"name": "Johannes Hoppe",
|
||||
"picture": "johanneshoppe.jpg",
|
||||
"twitter": "johanneshoppe",
|
||||
"website": "https://angular.schule",
|
||||
"bio": "Johannes works as a software architect and consultant for Angular. He is co-author of the German Angular book and a passionate speaker. With his company \"Angular.Schule\" he offers public Angular training and consulting. Johannes is head of the Angular Meetup in Heidelberg, Germany, and is engaged in a couple of open source projects.",
|
||||
"groups": ["GDE"]
|
||||
},
|
||||
"jessicajaniuk": {
|
||||
"name": "Jessica Janiuk",
|
||||
"picture": "jessicajaniuk.jpg",
|
||||
"twitter": "thepunderwoman",
|
||||
"website": "https://github.com/jessicajaniuk",
|
||||
"bio": "Jessica is a software engineer at Google. In her free time she is a historical fencer, astromech droid builder, photographer, pun purveyor, occasional cosplayer, and full time cat mom.",
|
||||
"groups": ["Angular"],
|
||||
"lead": "jelbourn"
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ Most Angular code can be written with just the latest JavaScript, using [types](
|
||||
|
||||
<h3>You can sit with us!</h3>
|
||||
|
||||
We want to hear from you. [Report problems or submit suggestions for future docs.](https://github.com/angular/angular/issues/new/choose "Angular GitHub repository new issue form")
|
||||
We want to hear from you. [Report problems or submit suggestions for future docs](https://github.com/angular/angular/issues/new/choose "Angular GitHub repository new issue form").
|
||||
|
||||
Contribute to Angular docs by creating
|
||||
[pull requests](https://github.com/angular/angular/pulls "Angular Github pull requests")
|
||||
|
@ -101,21 +101,26 @@
|
||||
"title": "Components",
|
||||
"tooltip": "Building dynamic views with data binding",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/component-overview",
|
||||
"title": "Overview",
|
||||
"tooltip": "Overview of how to create Angular components."
|
||||
},
|
||||
{
|
||||
"url": "guide/user-input",
|
||||
"title": "User Input",
|
||||
"tooltip": "User input triggers DOM events. Angular listens to those events with event bindings that funnel updated values back into your app's components and models."
|
||||
},
|
||||
{
|
||||
"url": "guide/pipes",
|
||||
"title": "Pipes",
|
||||
"tooltip": "Pipes transform displayed values within a template."
|
||||
},
|
||||
{
|
||||
"url": "guide/lifecycle-hooks",
|
||||
"title": "Component Lifecycle",
|
||||
"tooltip": "Angular calls lifecycle hook methods on directives and components as it creates, changes, and destroys them."
|
||||
},
|
||||
{
|
||||
"url": "guide/view-encapsulation",
|
||||
"title": "View Encapsulation",
|
||||
"tooltip": "Describes how component CSS styles are encapsulated into a component's view."
|
||||
},
|
||||
{
|
||||
"url": "guide/component-interaction",
|
||||
"title": "Component Interaction",
|
||||
@ -126,6 +131,11 @@
|
||||
"title": "Component Styles",
|
||||
"tooltip": "Add CSS styles that are specific to a component."
|
||||
},
|
||||
{
|
||||
"url": "guide/inputs-outputs",
|
||||
"title": "Inputs and Outputs",
|
||||
"tooltip": "Introductory guide to sharing data between parent and child directives or components."
|
||||
},
|
||||
{
|
||||
"url": "guide/dynamic-component-loader",
|
||||
"title": "Dynamic Components",
|
||||
@ -157,6 +167,11 @@
|
||||
"title": "Template statements",
|
||||
"tooltip": "Introductory guide to statements in templates that respond to events that components, directives, or elements raise."
|
||||
},
|
||||
{
|
||||
"url": "guide/pipes",
|
||||
"title": "Pipes",
|
||||
"tooltip": "Pipes transform displayed values within a template."
|
||||
},
|
||||
{
|
||||
"url": "guide/binding-syntax",
|
||||
"title": "Binding syntax",
|
||||
@ -187,11 +202,6 @@
|
||||
"title": "Template reference variables",
|
||||
"tooltip": "Introductory guide to referring to DOM elements within a template."
|
||||
},
|
||||
{
|
||||
"url": "guide/inputs-outputs",
|
||||
"title": "Inputs and Outputs",
|
||||
"tooltip": "Introductory guide to sharing data between parent and child directives or components."
|
||||
},
|
||||
{
|
||||
"url": "guide/template-expression-operators",
|
||||
"title": "Template expression operators",
|
||||
@ -412,6 +422,11 @@
|
||||
"title": "Keeping Up-to-Date",
|
||||
"tooltip": "Information about updating Angular applications and libraries to the latest version."
|
||||
},
|
||||
{
|
||||
"url": "guide/property-binding-best-practices",
|
||||
"title": "Property Binding Best Practices",
|
||||
"tooltip": "Use property binding efficiently."
|
||||
},
|
||||
{
|
||||
"title": "Testing",
|
||||
"tooltip": "Testing your Angular apps.",
|
||||
@ -604,11 +619,6 @@
|
||||
"title": "Building a Template-driven Form",
|
||||
"tooltip": "Create a template-driven form using directives and Angular template syntax."
|
||||
},
|
||||
{
|
||||
"url": "guide/displaying-data",
|
||||
"title": "Data binding",
|
||||
"tooltip": "Property binding helps show app data in the UI."
|
||||
},
|
||||
{
|
||||
"url": "guide/web-worker",
|
||||
"title": "Web Workers",
|
||||
@ -1004,6 +1014,11 @@
|
||||
"title": "Stack Overflow",
|
||||
"tooltip": "Stack Overflow: where the community answers your technical Angular questions."
|
||||
},
|
||||
{
|
||||
"url": "https://discord.gg/angular",
|
||||
"title": "Join Discord",
|
||||
"tooltip": "Join the discussions at Angular Community Discord server."
|
||||
},
|
||||
{
|
||||
"url": "https://gitter.im/angular/angular",
|
||||
"title": "Gitter",
|
||||
|
@ -180,7 +180,7 @@ give it a `catchError()` operator.
|
||||
</code-example>
|
||||
|
||||
The `catchError()` operator intercepts an **`Observable` that failed**.
|
||||
It passes the error an error handler that can do what it wants with the error.
|
||||
The operator then passes the error to the error handling function.
|
||||
|
||||
The following `handleError()` method reports the error and then returns an
|
||||
innocuous result so that the application keeps working.
|
||||
|
@ -31,6 +31,7 @@
|
||||
{"type": 301, "source": "/guide/quickstart", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started/:rest*", "destination": "/start/:rest*"},
|
||||
{"type": 301, "source": "/guide/displaying-data", "destination": "/start#template-syntax"},
|
||||
|
||||
// Renaming of Getting Started topics
|
||||
{"type": 301, "source": "/start/data", "destination": "/start/start-data"},
|
||||
@ -128,7 +129,8 @@
|
||||
// The below paths are referenced in users projects generated by the CLI
|
||||
{"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"},
|
||||
{"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"},
|
||||
{"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"}
|
||||
{"type": 301, "source": "/config/app-package-json", "destination": "/guide/strict-mode#non-local-side-effects-in-applications"},
|
||||
{"type": 301, "source": "/strict", "destination": "/guide/strict-mode"}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
|
@ -102,6 +102,9 @@
|
||||
"!/guide/cli-quickstart",
|
||||
"!/guide/cli-quickstart.html",
|
||||
"!/guide/cli-quickstart/",
|
||||
"!/guide/displaying-data",
|
||||
"!/guide/displaying-data.html",
|
||||
"!/guide/displaying-data/",
|
||||
"!/guide/learning-angular",
|
||||
"!/guide/learning-angular.html",
|
||||
"!/guide/learning-angular/",
|
||||
@ -148,6 +151,7 @@
|
||||
"!/styleguide/**",
|
||||
"!/testing",
|
||||
"!/testing/**",
|
||||
"!/config/**"
|
||||
"!/config/**",
|
||||
"!/strict"
|
||||
]
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
"build-local-with-viewengine": "yarn ~~build",
|
||||
"prebuild-local-with-viewengine-ci": "node scripts/switch-to-viewengine && yarn setup-local-ci",
|
||||
"build-local-with-viewengine-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js ab97bc382",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js cba6d86ca",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@ -64,13 +64,14 @@
|
||||
"tools-lint": "tslint --config \"tools/tslint.json\" --project \"tools/firebase-test-utils\"",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js && yarn firebase-utils-test",
|
||||
"preserve-and-sync": "yarn docs",
|
||||
"serve-and-sync": "run-p \"start\" \"docs-watch --watch-only\"",
|
||||
"serve-and-sync": "run-p \"docs-watch --watch-only\" \"start {@}\" --",
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
"boilerplate:add:viewengine": "yarn boilerplate:add --viewengine",
|
||||
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
|
||||
"boilerplate:test": "node tools/examples/test.js",
|
||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||
"create-example": "node ./tools/examples/create-example.js",
|
||||
"build-404-page": "node scripts/build-404-page",
|
||||
"update-webdriver": "node ../scripts/webdriver-manager-update.js",
|
||||
"~~audit-web-app": "node scripts/audit-web-app",
|
||||
@ -153,7 +154,7 @@
|
||||
"lunr": "^2.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"protractor": "~5.4.4",
|
||||
"puppeteer": "3.3.0",
|
||||
"puppeteer": "5.1.0",
|
||||
"rehype": "^6.0.0",
|
||||
"rehype-slug": "^2.0.0",
|
||||
"remark": "^9.0.0",
|
||||
@ -172,6 +173,6 @@
|
||||
"unist-util-visit-parents": "^1.1.1",
|
||||
"watchr": "^3.0.1",
|
||||
"xregexp": "^4.0.0",
|
||||
"yargs": "^7.0.2"
|
||||
"yargs": "^16.1.0"
|
||||
}
|
||||
}
|
||||
|
@ -782,7 +782,7 @@ describe('AppComponent', () => {
|
||||
it('should grab focus when the / key is pressed', () => {
|
||||
const searchBox: SearchBoxComponent = fixture.debugElement.query(By.directive(SearchBoxComponent)).componentInstance;
|
||||
spyOn(searchBox, 'focus');
|
||||
window.document.dispatchEvent(new KeyboardEvent('keyup', { 'key': '/' }));
|
||||
window.document.dispatchEvent(new KeyboardEvent('keyup', { key: '/' }));
|
||||
fixture.detectChanges();
|
||||
expect(searchBox.focus).toHaveBeenCalled();
|
||||
});
|
||||
@ -791,7 +791,7 @@ describe('AppComponent', () => {
|
||||
const searchBox: SearchBoxComponent = fixture.debugElement.query(By.directive(SearchBoxComponent)).componentInstance;
|
||||
spyOn(searchBox, 'focus');
|
||||
component.showSearchResults = true;
|
||||
window.document.dispatchEvent(new KeyboardEvent('keyup', { 'key': 'Escape' }));
|
||||
window.document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
|
||||
fixture.detectChanges();
|
||||
expect(searchBox.focus).toHaveBeenCalled();
|
||||
});
|
||||
@ -968,23 +968,23 @@ describe('AppComponent', () => {
|
||||
|
||||
// Initially, `isTransitoning` is true.
|
||||
expect(component.isTransitioning).toBe(true);
|
||||
expect(toolbar.classes['transitioning']).toBe(true);
|
||||
expect(toolbar.classes.transitioning).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(false);
|
||||
expect(toolbar.classes['transitioning']).toBeFalsy();
|
||||
expect(toolbar.classes.transitioning).toBeFalsy();
|
||||
|
||||
// While a document is being rendered, `isTransitoning` is set to true.
|
||||
triggerDocViewerEvent('docReady');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(true);
|
||||
expect(toolbar.classes['transitioning']).toBe(true);
|
||||
expect(toolbar.classes.transitioning).toBe(true);
|
||||
|
||||
triggerDocViewerEvent('docRendered');
|
||||
fixture.detectChanges();
|
||||
expect(component.isTransitioning).toBe(false);
|
||||
expect(toolbar.classes['transitioning']).toBeFalsy();
|
||||
expect(toolbar.classes.transitioning).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update the sidenav state as soon as a new document is inserted (but not before)', () => {
|
||||
@ -1031,15 +1031,15 @@ describe('AppComponent', () => {
|
||||
|
||||
navigateTo('guide/pipes');
|
||||
expect(component.pageId).toEqual('guide-pipes');
|
||||
expect(container.properties['id']).toEqual('guide-pipes');
|
||||
expect(container.properties.id).toEqual('guide-pipes');
|
||||
|
||||
navigateTo('news');
|
||||
expect(component.pageId).toEqual('news');
|
||||
expect(container.properties['id']).toEqual('news');
|
||||
expect(container.properties.id).toEqual('news');
|
||||
|
||||
navigateTo('');
|
||||
expect(component.pageId).toEqual('home');
|
||||
expect(container.properties['id']).toEqual('home');
|
||||
expect(container.properties.id).toEqual('home');
|
||||
});
|
||||
|
||||
it('should not be affected by changes to the query', () => {
|
||||
@ -1050,7 +1050,7 @@ describe('AppComponent', () => {
|
||||
navigateTo('guide/other?search=http');
|
||||
|
||||
expect(component.pageId).toEqual('guide-other');
|
||||
expect(container.properties['id']).toEqual('guide-other');
|
||||
expect(container.properties.id).toEqual('guide-other');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1125,7 +1125,7 @@ describe('AppComponent', () => {
|
||||
|
||||
function checkHostClass(type: string, value: string) {
|
||||
const host = fixture.debugElement;
|
||||
const classes: string = host.properties['className'];
|
||||
const classes: string = host.properties.className;
|
||||
const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0);
|
||||
expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`);
|
||||
expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`);
|
||||
@ -1311,42 +1311,42 @@ class TestHttpClient {
|
||||
|
||||
// tslint:disable:quotemark
|
||||
navJson = {
|
||||
"TopBar": [
|
||||
TopBar: [
|
||||
{
|
||||
"url": "features",
|
||||
"title": "Features"
|
||||
url: 'features',
|
||||
title: 'Features',
|
||||
},
|
||||
{
|
||||
"url": "no-title",
|
||||
"title": "No Title"
|
||||
url: 'no-title',
|
||||
title: 'No Title',
|
||||
},
|
||||
],
|
||||
"SideNav": [
|
||||
SideNav: [
|
||||
{
|
||||
"title": "Core",
|
||||
"tooltip": "Learn the core capabilities of Angular",
|
||||
"children": [
|
||||
title: 'Core',
|
||||
tooltip: 'Learn the core capabilities of Angular',
|
||||
children: [
|
||||
{
|
||||
"url": "guide/pipes",
|
||||
"title": "Pipes",
|
||||
"tooltip": "Pipes transform displayed values within a template."
|
||||
url: 'guide/pipes',
|
||||
title: 'Pipes',
|
||||
tooltip: 'Pipes transform displayed values within a template.',
|
||||
},
|
||||
{
|
||||
"url": "guide/bags",
|
||||
"title": "Bags",
|
||||
"tooltip": "Pack your bags for a code adventure."
|
||||
}
|
||||
]
|
||||
url: 'guide/bags',
|
||||
title: 'Bags',
|
||||
tooltip: 'Pack your bags for a code adventure.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"url": "api",
|
||||
"title": "API",
|
||||
"tooltip": "Details of the Angular classes and values."
|
||||
}
|
||||
url: 'api',
|
||||
title: 'API',
|
||||
tooltip: 'Details of the Angular classes and values.',
|
||||
},
|
||||
],
|
||||
"docVersions": TestHttpClient.docVersions,
|
||||
docVersions: TestHttpClient.docVersions,
|
||||
|
||||
"__versionInfo": TestHttpClient.versionInfo,
|
||||
__versionInfo: TestHttpClient.versionInfo,
|
||||
};
|
||||
|
||||
get(url: string) {
|
||||
|
@ -147,7 +147,7 @@ export class AppComponent implements OnInit {
|
||||
// Compute the version picker list from the current version and the versions in the navigation map
|
||||
combineLatest([
|
||||
this.navigationService.versionInfo,
|
||||
this.navigationService.navigationViews.pipe(map(views => views['docVersions'])),
|
||||
this.navigationService.navigationViews.pipe(map(views => views.docVersions)),
|
||||
]).subscribe(([versionInfo, versions]) => {
|
||||
// TODO(pbd): consider whether we can lookup the stable and next versions from the internet
|
||||
const computedVersions: NavigationNode[] = [
|
||||
@ -167,10 +167,10 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.navigationService.navigationViews.subscribe(views => {
|
||||
this.footerNodes = views['Footer'] || [];
|
||||
this.sideNavNodes = views['SideNav'] || [];
|
||||
this.topMenuNodes = views['TopBar'] || [];
|
||||
this.topMenuNarrowNodes = views['TopBarNarrow'] || this.topMenuNodes;
|
||||
this.footerNodes = views.Footer || [];
|
||||
this.sideNavNodes = views.SideNav || [];
|
||||
this.topMenuNodes = views.TopBar || [];
|
||||
this.topMenuNarrowNodes = views.TopBarNarrow || this.topMenuNodes;
|
||||
});
|
||||
|
||||
this.navigationService.versionInfo.subscribe(vi => this.versionInfo = vi);
|
||||
|
@ -223,77 +223,77 @@ class TestApiService {
|
||||
// tslint:disable:quotemark
|
||||
const apiSections: ApiSection[] = [
|
||||
{
|
||||
"name": "common",
|
||||
"title": "common",
|
||||
"path": "api/common",
|
||||
"deprecated": false,
|
||||
"items": [
|
||||
name: 'common',
|
||||
title: 'common',
|
||||
path: 'api/common',
|
||||
deprecated: false,
|
||||
items: [
|
||||
{
|
||||
"name": "class_1",
|
||||
"title": "Class 1",
|
||||
"path": "api/common/class_1",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false,
|
||||
name: 'class_1',
|
||||
title: 'Class 1',
|
||||
path: 'api/common/class_1',
|
||||
docType: 'class',
|
||||
stability: 'experimental',
|
||||
securityRisk: false,
|
||||
},
|
||||
{
|
||||
"name": "class_2",
|
||||
"title": "Class 2",
|
||||
"path": "api/common/class_2",
|
||||
"docType": "class",
|
||||
"stability": "stable",
|
||||
"securityRisk": false,
|
||||
name: 'class_2',
|
||||
title: 'Class 2',
|
||||
path: 'api/common/class_2',
|
||||
docType: 'class',
|
||||
stability: 'stable',
|
||||
securityRisk: false,
|
||||
},
|
||||
{
|
||||
"name": "directive_1",
|
||||
"title": "Directive 1",
|
||||
"path": "api/common/directive_1",
|
||||
"docType": "directive",
|
||||
"stability": "stable",
|
||||
"securityRisk": true,
|
||||
name: 'directive_1',
|
||||
title: 'Directive 1',
|
||||
path: 'api/common/directive_1',
|
||||
docType: 'directive',
|
||||
stability: 'stable',
|
||||
securityRisk: true,
|
||||
},
|
||||
{
|
||||
"name": "pipe_1",
|
||||
"title": "Pipe 1",
|
||||
"path": "api/common/pipe_1",
|
||||
"docType": "pipe",
|
||||
"stability": "stable",
|
||||
"securityRisk": true,
|
||||
name: 'pipe_1',
|
||||
title: 'Pipe 1',
|
||||
path: 'api/common/pipe_1',
|
||||
docType: 'pipe',
|
||||
stability: 'stable',
|
||||
securityRisk: true,
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "core",
|
||||
"title": "core",
|
||||
"path": "api/core",
|
||||
"deprecated": false,
|
||||
"items": [
|
||||
name: 'core',
|
||||
title: 'core',
|
||||
path: 'api/core',
|
||||
deprecated: false,
|
||||
items: [
|
||||
{
|
||||
"name": "class_3",
|
||||
"title": "Class 3",
|
||||
"path": "api/core/class_3",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false,
|
||||
name: 'class_3',
|
||||
title: 'Class 3',
|
||||
path: 'api/core/class_3',
|
||||
docType: 'class',
|
||||
stability: 'experimental',
|
||||
securityRisk: false,
|
||||
},
|
||||
{
|
||||
"name": "function_1",
|
||||
"title": "Function 1",
|
||||
"path": "api/core/function 1",
|
||||
"docType": "function",
|
||||
"stability": "deprecated",
|
||||
"securityRisk": true,
|
||||
name: 'function_1',
|
||||
title: 'Function 1',
|
||||
path: 'api/core/function 1',
|
||||
docType: 'function',
|
||||
stability: 'deprecated',
|
||||
securityRisk: true,
|
||||
},
|
||||
{
|
||||
"name": "const_1",
|
||||
"title": "Const 1",
|
||||
"path": "api/core/const_1",
|
||||
"docType": "const",
|
||||
"stability": "stable",
|
||||
"securityRisk": false,
|
||||
}
|
||||
]
|
||||
}
|
||||
name: 'const_1',
|
||||
title: 'Const 1',
|
||||
path: 'api/core/const_1',
|
||||
docType: 'const',
|
||||
stability: 'stable',
|
||||
securityRisk: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function getApiSections() { return apiSections; }
|
||||
|
@ -67,7 +67,7 @@ export class ApiService implements OnDestroy {
|
||||
* API sections is an array of Angular top modules and metadata about their API documents (items).
|
||||
* Updates `sections` observable
|
||||
*
|
||||
* @param {string} [src] - Name of the api list JSON file
|
||||
* @param [src] - Name of the api list JSON file
|
||||
*/
|
||||
fetchSections(src?: string) {
|
||||
// TODO: get URL by configuration?
|
||||
|
@ -20,13 +20,13 @@ export class PrettyPrinter {
|
||||
}
|
||||
|
||||
private getPrettyPrintOne(): Promise<PrettyPrintOne> {
|
||||
const ppo = (window as any)['prettyPrintOne'];
|
||||
const ppo = (window as any).prettyPrintOne;
|
||||
return ppo ? Promise.resolve(ppo) :
|
||||
// `prettyPrintOne` is not on `window`, which means `prettify.js` has not been loaded yet.
|
||||
// Import it; ad a side-effect it will add `prettyPrintOne` on `window`.
|
||||
import('assets/js/prettify.js' as any)
|
||||
.then(
|
||||
() => (window as any)['prettyPrintOne'],
|
||||
() => (window as any).prettyPrintOne,
|
||||
err => {
|
||||
const msg = `Cannot get prettify.js from server: ${err.message}`;
|
||||
this.logger.error(new Error(msg));
|
||||
@ -37,9 +37,9 @@ export class PrettyPrinter {
|
||||
|
||||
/**
|
||||
* Format code snippet as HTML
|
||||
* @param {string} code - the code snippet to format; should already be HTML encoded
|
||||
* @param {string} [language] - The language of the code to render (could be javascript, html, typescript, etc)
|
||||
* @param {string|number} [linenums] - Whether to display line numbers:
|
||||
* @param code - the code snippet to format; should already be HTML encoded
|
||||
* @param [language] - The language of the code to render (could be javascript, html, typescript, etc)
|
||||
* @param [linenums] - Whether to display line numbers:
|
||||
* - false: don't display
|
||||
* - true: do display
|
||||
* - number: do display but start at the given number
|
||||
|
@ -63,7 +63,7 @@ describe('ContributorListComponent', () => {
|
||||
it('should set the query to the "GDE" group when user selects "GDE"', () => {
|
||||
component = getComponent();
|
||||
component.selectGroup('GDE');
|
||||
expect(locationService.searchResult['group']).toBe('GDE');
|
||||
expect(locationService.searchResult.group).toBe('GDE');
|
||||
});
|
||||
|
||||
it('should set the query to the first group when user selects unknown name', () => {
|
||||
@ -71,7 +71,7 @@ describe('ContributorListComponent', () => {
|
||||
component.selectGroup('GDE'); // a legit group that isn't the first
|
||||
|
||||
component.selectGroup('foo'); // not a legit group name
|
||||
expect(locationService.searchResult['group']).toBe('Angular');
|
||||
expect(locationService.searchResult.group).toBe('Angular');
|
||||
});
|
||||
|
||||
//// Test Helpers ////
|
||||
|
@ -29,7 +29,7 @@ export class ContributorListComponent implements OnInit {
|
||||
private locationService: LocationService) { }
|
||||
|
||||
ngOnInit() {
|
||||
const groupName = this.locationService.search()['group'] || '';
|
||||
const groupName = this.locationService.search().group || '';
|
||||
// no need to unsubscribe because `contributors` completes
|
||||
this.contributorService.contributors
|
||||
.subscribe(grps => {
|
||||
|
@ -63,7 +63,7 @@ describe('ResourceListComponent', () => {
|
||||
it('should set the query to the "education" category when user selects "education"', () => {
|
||||
component = getComponent();
|
||||
component.selectCategory('education');
|
||||
expect(locationService.searchResult['category']).toBe('education');
|
||||
expect(locationService.searchResult.category).toBe('education');
|
||||
});
|
||||
|
||||
it('should set the query to the first category when user selects unknown name', () => {
|
||||
@ -71,7 +71,7 @@ describe('ResourceListComponent', () => {
|
||||
component.selectCategory('education'); // a legit group that isn't the first
|
||||
|
||||
component.selectCategory('foo'); // not a legit group name
|
||||
expect(locationService.searchResult['category']).toBe('development');
|
||||
expect(locationService.searchResult.category).toBe('development');
|
||||
});
|
||||
|
||||
//// Test Helpers ////
|
||||
|
@ -20,7 +20,7 @@ export class ResourceListComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const category = this.locationService.search()['category'] || '';
|
||||
const category = this.locationService.search().category || '';
|
||||
// Not using async pipe because cats appear twice in template
|
||||
// No need to unsubscribe because categories observable completes.
|
||||
this.resourceService.categories.subscribe(cats => {
|
||||
|
@ -86,62 +86,61 @@ describe('ResourceService', () => {
|
||||
});
|
||||
|
||||
function getTestResources() {
|
||||
// tslint:disable:quotemark
|
||||
return {
|
||||
"Cat 3": {
|
||||
"order": 3,
|
||||
"subCategories": {
|
||||
"Cat3 SubCat1": {
|
||||
"order": 2,
|
||||
"resources": {
|
||||
"Cat3 SubCat1 Res1": {
|
||||
"desc": "Meetup in Barcelona, Spain. ",
|
||||
"title": "Angular Beers",
|
||||
"url": "http://www.meetup.com/AngularJS-Beers/"
|
||||
'Cat 3': {
|
||||
order: 3,
|
||||
subCategories: {
|
||||
'Cat3 SubCat1': {
|
||||
order: 2,
|
||||
resources: {
|
||||
'Cat3 SubCat1 Res1': {
|
||||
desc: 'Meetup in Barcelona, Spain. ',
|
||||
title: 'Angular Beers',
|
||||
url: 'http://www.meetup.com/AngularJS-Beers/',
|
||||
},
|
||||
"Cat3 SubCat1 Res2": {
|
||||
"desc": "Angular Camps in Barcelona, Spain.",
|
||||
"title": "Angular Camp",
|
||||
"url": "http://angularcamp.org/"
|
||||
}
|
||||
}
|
||||
'Cat3 SubCat1 Res2': {
|
||||
desc: 'Angular Camps in Barcelona, Spain.',
|
||||
title: 'Angular Camp',
|
||||
url: 'http://angularcamp.org/',
|
||||
},
|
||||
},
|
||||
},
|
||||
"Cat3 SubCat2": {
|
||||
"order": 1,
|
||||
"resources": {
|
||||
"Cat3 SubCat2 Res1": {
|
||||
"desc": "A community index of components and libraries",
|
||||
"title": "Catalog of Angular Components & Libraries",
|
||||
"url": "https://a/b/c"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"Cat 1": {
|
||||
"order": 1,
|
||||
"subCategories": {
|
||||
"Cat1 SubCat1": {
|
||||
"order": 1,
|
||||
"resources": {
|
||||
"S S S": {
|
||||
"desc": "SSS",
|
||||
"title": "Sssss",
|
||||
"url": "http://s/s/s"
|
||||
'Cat3 SubCat2': {
|
||||
order: 1,
|
||||
resources: {
|
||||
'Cat3 SubCat2 Res1': {
|
||||
desc: 'A community index of components and libraries',
|
||||
title: 'Catalog of Angular Components & Libraries',
|
||||
url: 'https://a/b/c',
|
||||
},
|
||||
"A A A": {
|
||||
"desc": "AAA",
|
||||
"title": "Aaaa",
|
||||
"url": "http://a/a/a"
|
||||
},
|
||||
"Z Z Z": {
|
||||
"desc": "ZZZ",
|
||||
"title": "Zzzzz",
|
||||
"url": "http://z/z/z"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
'Cat 1': {
|
||||
order: 1,
|
||||
subCategories: {
|
||||
'Cat1 SubCat1': {
|
||||
order: 1,
|
||||
resources: {
|
||||
'S S S': {
|
||||
desc: 'SSS',
|
||||
title: 'Sssss',
|
||||
url: 'http://s/s/s',
|
||||
},
|
||||
'A A A': {
|
||||
desc: 'AAA',
|
||||
title: 'Aaaa',
|
||||
url: 'http://a/a/a',
|
||||
},
|
||||
'Z Z Z': {
|
||||
desc: 'ZZZ',
|
||||
title: 'Zzzzz',
|
||||
url: 'http://z/z/z',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ describe('NavigationService', () => {
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
navService.navigationViews.subscribe(views => view = views['sideNav']);
|
||||
navService.navigationViews.subscribe(views => view = views.sideNav);
|
||||
httpMock.expectOne({}).flush({sideNav});
|
||||
});
|
||||
|
||||
@ -254,7 +254,7 @@ describe('NavigationService', () => {
|
||||
{...v, ...{ tooltip: v.title + '.'}})
|
||||
);
|
||||
|
||||
navService.navigationViews.subscribe(views => actualDocVersions = views['docVersions']);
|
||||
navService.navigationViews.subscribe(views => actualDocVersions = views.docVersions);
|
||||
});
|
||||
|
||||
it('should extract the docVersions', () => {
|
||||
|
@ -41,7 +41,7 @@ export class SearchBoxComponent implements AfterViewInit {
|
||||
* When we first show this search box we trigger a search if there is a search query in the URL
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
const query = this.locationService.search()['search'];
|
||||
const query = this.locationService.search().search;
|
||||
if (query) {
|
||||
this.query = this.decodeQuery(query);
|
||||
this.doSearch();
|
||||
|
@ -26,7 +26,7 @@ function createIndex(loadIndexFn: IndexLoader): lunr.Index {
|
||||
// The lunr typings are missing QueryLexer so we have to add them here manually.
|
||||
const queryLexer = (lunr as any as { QueryLexer: { termSeparator: RegExp } }).QueryLexer;
|
||||
queryLexer.termSeparator = lunr.tokenizer.separator = /\s+/;
|
||||
return lunr(/** @this */function() {
|
||||
return lunr(function() {
|
||||
this.ref('path');
|
||||
this.field('topics', { boost: 15 });
|
||||
this.field('titleWords', { boost: 10 });
|
||||
@ -44,7 +44,7 @@ function handleMessage(message: { data: WebWorkerMessage }): void {
|
||||
const payload = message.data.payload;
|
||||
switch (type) {
|
||||
case 'load-index':
|
||||
makeRequest(SEARCH_TERMS_URL, function(searchInfo: PageInfo[]) {
|
||||
makeRequest(SEARCH_TERMS_URL, (searchInfo: PageInfo[]) => {
|
||||
index = createIndex(loadIndex(searchInfo));
|
||||
postMessage({ type, id, payload: true });
|
||||
});
|
||||
@ -94,7 +94,7 @@ function queryIndex(query: string): PageInfo[] {
|
||||
results = index.search(query + ' ' + titleQuery);
|
||||
}
|
||||
// Map the hits into info about each page to be returned as results
|
||||
return results.map(function(hit) { return pages[hit.ref]; });
|
||||
return results.map(hit => pages[hit.ref]);
|
||||
}
|
||||
} catch (e) {
|
||||
// If the search query cannot be parsed the index throws an error
|
||||
|
@ -11,7 +11,7 @@ export class Deployment {
|
||||
* The deployment mode set from the environment provided at build time;
|
||||
* or overridden by the `mode` query parameter: e.g. `...?mode=archive`
|
||||
*/
|
||||
mode: string = this.location.search()['mode'] || environment.mode;
|
||||
mode: string = this.location.search().mode || environment.mode;
|
||||
|
||||
constructor(private location: LocationService) {}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export class GaService {
|
||||
private previousUrl: string;
|
||||
|
||||
constructor(@Inject(WindowToken) private window: Window) {
|
||||
this.ga('create', environment['gaId'] , 'auto');
|
||||
this.ga('create', environment.gaId , 'auto');
|
||||
}
|
||||
|
||||
locationChanged(url: string) {
|
||||
@ -34,7 +34,7 @@ export class GaService {
|
||||
}
|
||||
|
||||
ga(...args: any[]) {
|
||||
const gaFn = (this.window as any)['ga'];
|
||||
const gaFn = (this.window as any).ga;
|
||||
if (gaFn) {
|
||||
gaFn(...args);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ export class LocationService {
|
||||
/**
|
||||
* Handle user's anchor click
|
||||
*
|
||||
* @param anchor {HTMLAnchorElement} - the anchor element clicked
|
||||
* @param anchor The anchor element clicked
|
||||
* @param button Number of the mouse button held down. 0 means left or none
|
||||
* @param ctrlKey True if control key held down
|
||||
* @param metaKey True if command or window key held down
|
||||
|
@ -13,14 +13,13 @@ import { Logger } from 'app/shared/logger.service';
|
||||
* 1. Checks for available ServiceWorker updates once instantiated.
|
||||
* 2. Re-checks every 6 hours.
|
||||
* 3. Whenever an update is available, it activates the update.
|
||||
*
|
||||
* @property
|
||||
* `updateActivated` {Observable<string>} - Emit the version hash whenever an update is activated.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SwUpdatesService implements OnDestroy {
|
||||
private checkInterval = 1000 * 60 * 60 * 6; // 6 hours
|
||||
private onDestroy = new Subject<void>();
|
||||
|
||||
/** Emit the version hash whenever an update is activated. */
|
||||
updateActivated: Observable<string>;
|
||||
|
||||
constructor(appRef: ApplicationRef, private logger: Logger, private swu: SwUpdate) {
|
||||
|
@ -38,7 +38,11 @@
|
||||
margin: 0;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
color: $blue;
|
||||
@include font-size(14);
|
||||
}
|
||||
.show-all {
|
||||
display: initial;
|
||||
}
|
||||
|
@ -105,9 +105,7 @@
|
||||
|
||||
li {
|
||||
box-sizing: border-box;
|
||||
@include font-size(12);
|
||||
@include line-height(16);
|
||||
padding: 3px 0 3px 12px;
|
||||
padding: 7px 0 7px 12px;
|
||||
position: relative;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
@ -129,6 +127,7 @@
|
||||
color: lighten($darkgray, 10);
|
||||
overflow: visible;
|
||||
@include font-size(12);
|
||||
@include line-height(16);
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
@ -168,11 +167,11 @@
|
||||
}
|
||||
|
||||
&:first-child:before {
|
||||
top: 13px;
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
&:last-child:before {
|
||||
bottom: calc(100% - 14px);
|
||||
bottom: calc(100% - 15px);
|
||||
}
|
||||
|
||||
&:not(.active):hover a:before {
|
||||
|
@ -177,6 +177,7 @@
|
||||
/getting-started/forms /start/start-forms
|
||||
/getting-started/deployment /start/start-deployment
|
||||
/guide/cli-quickstart /start
|
||||
/guide/displaying-data /start#template-syntax
|
||||
/guide/learning-angular /start
|
||||
/guide/learning-angular.html /start
|
||||
/guide/metadata /guide/aot-compiler
|
||||
|