Compare commits
1 Commits
6.1.3
...
api-overlo
Author | SHA1 | Date | |
---|---|---|---|
8dfcc78c08 |
@ -3,10 +3,7 @@
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
|
||||
# Don't be spammy in the logs
|
||||
# TODO(gmagolan): Hide progress again once build performance improves
|
||||
# Presently, CircleCI can timeout during bazel test ... with the following
|
||||
# error: Too long with no output (exceeded 10m0s)
|
||||
# build --noshow_progress
|
||||
build --noshow_progress
|
||||
|
||||
# Don't run manual tests
|
||||
test --test_tag_filters=-manual
|
||||
|
7
.github/angular-robot.yml
vendored
7
.github/angular-robot.yml
vendored
@ -62,13 +62,6 @@ merge:
|
||||
|
||||
# list of checks that will determine if the merge label can be added
|
||||
checks:
|
||||
|
||||
# require that the PR has reviews from all requested reviewers
|
||||
#
|
||||
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
|
||||
# Rather than merging PRs with pending reviews, if all PullApprove requirements are satisfied and additional reviews are not needed pending reviewers should be removed via GitHub UI (this also leaves an audit trail behind these decisions).
|
||||
requireReviews: true,
|
||||
|
||||
# whether the PR shouldn't have a conflict with the base branch
|
||||
noConflict: true
|
||||
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
|
||||
|
197
.pullapprove.yml
197
.pullapprove.yml
@ -8,7 +8,6 @@
|
||||
# alexeagle - Alex Eagle
|
||||
# alxhub - Alex Rickabaugh
|
||||
# andrewseguin - Andrew Seguin
|
||||
# benlesh - Ben Lesh
|
||||
# brandonroberts - Brandon Roberts
|
||||
# brocco - Mike Brocchi
|
||||
# filipesilva - Filipe Silva
|
||||
@ -16,7 +15,7 @@
|
||||
# hansl - Hans Larsen
|
||||
# IgorMinar - Igor Minar
|
||||
# jasonaden - Jason Aden
|
||||
# jenniferfell - Jennifer Fell
|
||||
# kapunahelewong - Kapunahele Wong
|
||||
# kara - Kara Erickson
|
||||
# kyliau - Keen Yee Liau
|
||||
# matsko - Matias Niemelä
|
||||
@ -24,6 +23,7 @@
|
||||
# petebacondarwin - Pete Bacon Darwin
|
||||
# pkozlowski-opensource - Pawel Kozlowski
|
||||
# robwormald - Rob Wormald
|
||||
# tinayuangao - Tina Gao
|
||||
# vicb - Victor Berchet
|
||||
# vikerman - Vikram Subramanian
|
||||
|
||||
@ -92,7 +92,6 @@ groups:
|
||||
- "*.bzl"
|
||||
- "packages/bazel/*"
|
||||
- "tools/bazel.rc"
|
||||
- "/docs/BAZEL.md"
|
||||
users:
|
||||
- alexeagle #primary
|
||||
- kyliau
|
||||
@ -132,113 +131,42 @@ groups:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/core/*"
|
||||
- "aio/content/guide/bootstrapping.md"
|
||||
- "aio/content/examples/bootstrapping/*"
|
||||
- "aio/content/guide/attribute-directives.md"
|
||||
- "aio/content/examples/attribute-directives/*"
|
||||
- "aio/content/images/guide/attribute-directives/*"
|
||||
- "aio/content/guide/structural-directives.md"
|
||||
- "aio/content/examples/structural-directives/*"
|
||||
- "aio/content/images/guide/structural-directives/*"
|
||||
- "aio/content/guide/dynamic-component-loader.md"
|
||||
- "aio/content/examples/dynamic-component-loader/*"
|
||||
- "aio/content/images/guide/dynamic-component-loader/*"
|
||||
- "aio/content/guide/template-syntax.md"
|
||||
- "aio/content/examples/template-syntax/*"
|
||||
- "aio/content/images/guide/template-syntax/*"
|
||||
- "aio/content/guide/dependency-injection.md"
|
||||
- "aio/content/examples/dependency-injection/*"
|
||||
- "aio/content/images/guide/dependency-injection/*"
|
||||
- "aio/content/guide/dependency-injection-in-action.md"
|
||||
- "aio/content/examples/dependency-injection-in-action/*"
|
||||
- "aio/content/images/guide/dependency-injection-in-action/*"
|
||||
- "aio/content/guide/hierarchical-dependency-injection.md"
|
||||
- "aio/content/examples/hierarchical-dependency-injection/*"
|
||||
- "aio/content/guide/singleton-services.md"
|
||||
- "aio/content/guide/dependency-injection-pattern.md"
|
||||
- "aio/content/guide/providers.md"
|
||||
- "aio/content/examples/providers/*"
|
||||
- "aio/content/guide/component-interaction.md"
|
||||
- "aio/content/examples/component-interaction/*"
|
||||
- "aio/content/images/guide/component-interaction/*"
|
||||
- "aio/content/guide/component-styles.md"
|
||||
- "aio/content/examples/component-styles/*"
|
||||
- "aio/content/guide/lifecycle-hooks.md"
|
||||
- "aio/content/examples/lifecycle-hooks/*"
|
||||
- "aio/content/images/guide/lifecycle-hooks/*"
|
||||
- "aio/content/examples/ngcontainer/*"
|
||||
- "aio/content/images/guide/ngcontainer/*"
|
||||
- "aio/content/guide/pipes.md"
|
||||
- "aio/content/examples/pipes/*"
|
||||
- "aio/content/images/guide/pipes/*"
|
||||
- "aio/content/guide/entry-components.md"
|
||||
- "aio/content/guide/set-document-title.md"
|
||||
- "aio/content/examples/set-document-title/*"
|
||||
- "aio/content/images/guide/set-document-title/*"
|
||||
- "aio/content/guide/ngmodules.md"
|
||||
- "aio/content/examples/ngmodules/*"
|
||||
- "aio/content/examples/ngmodule/*"
|
||||
- "aio/content/images/guide/ngmodule/*"
|
||||
- "aio/content/guide/ngmodule-faq.md"
|
||||
- "aio/content/examples/ngmodule-faq/*"
|
||||
- "aio/content/guide/module-types.md"
|
||||
- "aio/content/guide/sharing-ngmodules.md"
|
||||
- "aio/content/guide/frequent-ngmodules.md"
|
||||
- "aio/content/images/guide/frequent-ngmodules/*"
|
||||
- "aio/content/guide/ngmodule-api.md"
|
||||
- "aio/content/guide/ngmodule-vs-jsmodule.md"
|
||||
- "aio/content/guide/feature-modules.md"
|
||||
- "aio/content/examples/feature-modules/*"
|
||||
- "aio/content/images/guide/feature-modules/*"
|
||||
- "aio/content/guide/lazy-loading-ngmodules.md"
|
||||
- "aio/content/examples/lazy-loading-ngmodules/*"
|
||||
- "aio/content/images/guide/lazy-loading-ngmodules"
|
||||
users:
|
||||
- mhevery #primary
|
||||
- jasonaden
|
||||
- kara
|
||||
- vicb
|
||||
- IgorMinar
|
||||
- jenniferfell #docs only
|
||||
- IgorMinar #fallback
|
||||
|
||||
animations:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/animations/*"
|
||||
- "packages/platform-browser/animations/*"
|
||||
- "aio/content/guide/animations.md"
|
||||
- "aio/content/examples/animations/*"
|
||||
- "aio/content/images/guide/animations/*"
|
||||
users:
|
||||
- matsko #primary
|
||||
- mhevery #fallback
|
||||
- IgorMinar #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
compiler/i18n:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler/src/i18n/*"
|
||||
- "aio/content/guide/i18n.md"
|
||||
- "aio/content/examples/i18n/*"
|
||||
users:
|
||||
- vicb #primary
|
||||
- alxhub
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
compiler:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler/*"
|
||||
- "aio/content/guide/aot-compiler.md"
|
||||
users:
|
||||
- alxhub #primary
|
||||
- vicb
|
||||
- mhevery
|
||||
- IgorMinar #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
compiler-cli/ngtools:
|
||||
conditions:
|
||||
@ -247,6 +175,7 @@ groups:
|
||||
users:
|
||||
- hansl
|
||||
- filipesilva #fallback
|
||||
- brocco #fallback
|
||||
- IgorMinar #fallback
|
||||
|
||||
compiler-cli:
|
||||
@ -282,97 +211,57 @@ groups:
|
||||
files:
|
||||
- "packages/forms/*"
|
||||
- "aio/content/guide/forms.md"
|
||||
- "aio/content/examples/forms/*"
|
||||
- "aio/content/images/guide/forms/*"
|
||||
- "aio/content/guide/form-validation.md"
|
||||
- "aio/content/examples/form-validation/*"
|
||||
- "aio/content/images/guide/form-validation/*"
|
||||
- "aio/content/guide/dynamic-form.md"
|
||||
- "aio/content/examples/dynamic-form/*"
|
||||
- "aio/content/images/guide/dynamic-form/*"
|
||||
- "aio/content/guide/reactive-forms.md"
|
||||
- "aio/content/examples/forms/*"
|
||||
- "aio/content/examples/form-validation/*"
|
||||
- "aio/content/examples/reactive-forms/*"
|
||||
- "aio/content/images/guide/reactive-forms/*"
|
||||
users:
|
||||
- kara #primary
|
||||
- tinayuangao #secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
http:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/common/http/*"
|
||||
- "packages/http/*"
|
||||
- "aio/content/guide/http.md"
|
||||
- "aio/content/examples/http/*"
|
||||
- "aio/content/images/guide/http/*"
|
||||
users:
|
||||
- alxhub #primary
|
||||
- IgorMinar
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
language-service:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/language-service/*"
|
||||
- "aio/content/guide/language-service.md"
|
||||
- "aio/content/images/guide/language-service/*"
|
||||
users:
|
||||
- kyliau #primary
|
||||
# needs secondary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
router:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/router/*"
|
||||
- "aio/content/guide/router.md"
|
||||
- "aio/content/examples/router/*"
|
||||
- "aio/content/images/guide/router/*"
|
||||
users:
|
||||
- jasonaden #primary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
testing:
|
||||
conditions:
|
||||
files:
|
||||
- "*/testing/*"
|
||||
- "aio/content/guide/testing.md"
|
||||
- "aio/content/examples/testing/*"
|
||||
- "aio/content/images/guide/testing/*"
|
||||
users:
|
||||
- vikerman
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
upgrade:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/upgrade/*"
|
||||
- "aio/content/guide/upgrade.md"
|
||||
- "aio/content/examples/upgrade-module/*"
|
||||
- "aio/content/images/guide/upgrade/*"
|
||||
- "aio/content/examples/upgrade-phonecat-1-typescript/*"
|
||||
- "aio/content/examples/upgrade-phonecat-2-hybrid/*"
|
||||
- "aio/content/examples/upgrade-phonecat-3-final/*"
|
||||
- "aio/content/guide/upgrade-performance.md"
|
||||
- "aio/content/guide/ajs-quick-reference.md"
|
||||
- "aio/content/examples/ajs-quick-reference/*"
|
||||
users:
|
||||
- petebacondarwin #primary
|
||||
- gkalpak
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
platform-browser:
|
||||
conditions:
|
||||
@ -388,15 +277,12 @@ groups:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/platform-server/*"
|
||||
- "aio/content/guide/universal.md"
|
||||
- "aio/content/examples/universal/*"
|
||||
users:
|
||||
- vikerman #primary
|
||||
- alxhub #secondary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
platform-webworker:
|
||||
conditions:
|
||||
@ -412,34 +298,22 @@ groups:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/service-worker/*"
|
||||
- "aio/content/guide/service-worker-getting-started.md"
|
||||
- "aio/content/examples/service-worker-getting-started/*"
|
||||
- "aio/content/guide/service-worker-communications.md"
|
||||
- "aio/content/guide/service-worker-config.md"
|
||||
- "aio/content/guide/service-worker-devops.md"
|
||||
- "aio/content/guide/service-worker-intro.md"
|
||||
- "aio/content/images/guide/service-worker/*"
|
||||
users:
|
||||
- gkalpak #primary
|
||||
- alxhub
|
||||
- IgorMinar
|
||||
- alxhub #primary
|
||||
- gkalpak
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
elements:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/elements/*"
|
||||
- "aio/content/examples/elements/*"
|
||||
- "aio/content/images/guide/elements/*"
|
||||
- "aio/content/guide/elements.md"
|
||||
users:
|
||||
- andrewseguin #primary
|
||||
- gkalpak
|
||||
- robwormald
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
||||
benchpress:
|
||||
conditions:
|
||||
@ -451,7 +325,7 @@ groups:
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
docs-infra:
|
||||
angular.io:
|
||||
conditions:
|
||||
files:
|
||||
include:
|
||||
@ -464,7 +338,7 @@ groups:
|
||||
- gkalpak
|
||||
- mhevery #fallback
|
||||
|
||||
docs/guide-and-tutorial:
|
||||
angular.io-guide-and-tutorial:
|
||||
conditions:
|
||||
files:
|
||||
include:
|
||||
@ -474,20 +348,19 @@ groups:
|
||||
- "aio/content/navigation.json"
|
||||
- "aio/content/license.md"
|
||||
users:
|
||||
- kapunahelewong
|
||||
- stephenfluin
|
||||
- jenniferfell
|
||||
- brandonroberts
|
||||
- petebacondarwin
|
||||
- gkalpak
|
||||
- IgorMinar
|
||||
- brandonroberts
|
||||
- mhevery #fallback
|
||||
|
||||
docs/marketing:
|
||||
angular.io-marketing:
|
||||
conditions:
|
||||
files:
|
||||
include:
|
||||
- "aio/content/marketing/*"
|
||||
- "aio/content/images/marketing/*"
|
||||
- "aio/content/navigation.json"
|
||||
- "aio/content/license.md"
|
||||
users:
|
||||
@ -497,43 +370,3 @@ groups:
|
||||
- IgorMinar
|
||||
- robwormald
|
||||
- mhevery #fallback
|
||||
|
||||
docs/observables:
|
||||
conditions:
|
||||
files:
|
||||
- "aio/content/examples/observables/*"
|
||||
- "aio/content/images/guide/observables/*"
|
||||
- "aio/content/guide/observables.md"
|
||||
- "aio/content/guide/comparing-observables.md"
|
||||
- "aio/content/examples/observables-in-angular/*"
|
||||
- "aio/content/images/guide/observables-in-angular/*"
|
||||
- "aio/content/guide/observables-in-angular.md"
|
||||
- "aio/content/examples/practical-observable-usage/*"
|
||||
- "aio/content/guide/practical-observable-usage.md"
|
||||
- "aio/content/examples/rx-library/*"
|
||||
- "aio/content/guide/rx-library.md"
|
||||
users:
|
||||
- jasonaden
|
||||
- benlesh
|
||||
- IgorMinar
|
||||
- mhevery
|
||||
- jenniferfell #docs only
|
||||
|
||||
docs/packaging:
|
||||
conditions:
|
||||
files:
|
||||
- "aio/content/guide/npm-packages.md"
|
||||
- "aio/content/guide/browser-support.md"
|
||||
- "aio/content/guide/typescript-configuration.md"
|
||||
- "aio/content/guide/setup-systemjs-anatomy.md"
|
||||
- "aio/content/examples/setup/*"
|
||||
- "aio/content/guide/setup.md"
|
||||
- "aio/content/guide/deployment.md"
|
||||
- "aio/content/guide/releases.md"
|
||||
- "aio/content/guide/updating.md"
|
||||
users:
|
||||
- IgorMinar #primary
|
||||
- alexeagle
|
||||
- hansl
|
||||
- mhevery #fallback
|
||||
- jenniferfell #docs only
|
||||
|
44
BUILD.bazel
44
BUILD.bazel
@ -5,7 +5,6 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "node_modules_filegroup")
|
||||
exports_files([
|
||||
"tsconfig.json",
|
||||
"LICENSE",
|
||||
"protractor-perf.conf.js",
|
||||
])
|
||||
|
||||
# Developers should always run `bazel run :install`
|
||||
@ -15,19 +14,40 @@ alias(
|
||||
actual = "@nodejs//:yarn",
|
||||
)
|
||||
|
||||
alias(
|
||||
node_modules_filegroup(
|
||||
name = "node_modules",
|
||||
actual = "@angular_deps//:node_modules",
|
||||
packages = [
|
||||
"bytebuffer",
|
||||
"hammerjs",
|
||||
"jasmine",
|
||||
"minimist",
|
||||
"protobufjs",
|
||||
"protractor",
|
||||
"reflect-metadata",
|
||||
"source-map-support",
|
||||
"tsickle",
|
||||
"tslib",
|
||||
"tsutils",
|
||||
"typescript",
|
||||
"zone.js",
|
||||
"@angular-devkit/core",
|
||||
"@angular-devkit/schematics",
|
||||
"@types",
|
||||
"@webcomponents/custom-elements",
|
||||
],
|
||||
patterns = [
|
||||
"node_modules/protractor/**",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "web_test_bootstrap_scripts",
|
||||
# do not sort
|
||||
srcs = [
|
||||
"@angular_deps//:node_modules/reflect-metadata/Reflect.js",
|
||||
"@angular_deps//:node_modules/zone.js/dist/zone.js",
|
||||
"@angular_deps//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"@angular_deps//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"//:node_modules/reflect-metadata/Reflect.js",
|
||||
"//:node_modules/zone.js/dist/zone.js",
|
||||
"//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"//:test-events.js",
|
||||
],
|
||||
)
|
||||
@ -35,11 +55,9 @@ filegroup(
|
||||
filegroup(
|
||||
name = "angularjs_scripts",
|
||||
srcs = [
|
||||
"@angular_deps//:node_modules/angular-1.5/angular.js",
|
||||
"@angular_deps//:node_modules/angular-1.6/angular.js",
|
||||
"@angular_deps//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||
"@angular_deps//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||
"@angular_deps//:node_modules/angular-mocks/angular-mocks.js",
|
||||
"@angular_deps//:node_modules/angular/angular.js",
|
||||
"//:node_modules/angular-1.5/angular.js",
|
||||
"//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||
"//:node_modules/angular-mocks/angular-mocks.js",
|
||||
"//:node_modules/angular/angular.js",
|
||||
],
|
||||
)
|
||||
|
265
CHANGELOG.md
265
CHANGELOG.md
@ -1,134 +1,3 @@
|
||||
<a name="6.1.3"></a>
|
||||
## [6.1.3](https://github.com/angular/angular/compare/6.1.2...6.1.3) (2018-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **service-worker:** `Cache-Control: no-cache` on assets breaks service worker ([#25408](https://github.com/angular/angular/issues/25408)) ([1319ff4](https://github.com/angular/angular/commit/1319ff4)), closes [#25442](https://github.com/angular/angular/issues/25442)
|
||||
|
||||
|
||||
|
||||
<a name="6.1.2"></a>
|
||||
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ae9b4e6](https://github.com/angular/angular/commit/ae9b4e6)), closes [#20061](https://github.com/angular/angular/issues/20061)
|
||||
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([054fbbe](https://github.com/angular/angular/commit/054fbbe))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.1"></a>
|
||||
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** correct tsickle dependency version to fix typescript 2.9 compatibility ([fec29fa](https://github.com/angular/angular/commit/317c7087c56b72aa74cd6d6a8f719e6e7fec29fa))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0"></a>
|
||||
# [6.1.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0) (2018-07-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([dc4a3d0](https://github.com/angular/angular/commit/dc4a3d0))
|
||||
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||
* **animations:** do not throw errors when a destroyed component is animated ([#23836](https://github.com/angular/angular/issues/23836)) ([d2a8687](https://github.com/angular/angular/commit/d2a8687))
|
||||
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([b492b9e](https://github.com/angular/angular/commit/b492b9e))
|
||||
* **animations:** properly clean up queried element styles in safari/edge ([#23633](https://github.com/angular/angular/issues/23633)) ([da9ff25](https://github.com/angular/angular/commit/da9ff25))
|
||||
* **animations:** retain state styling for nodes that are moved around ([#23534](https://github.com/angular/angular/issues/23534)) ([65211f4](https://github.com/angular/angular/commit/65211f4))
|
||||
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([8db928d](https://github.com/angular/angular/commit/8db928d))
|
||||
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([282d351](https://github.com/angular/angular/commit/282d351))
|
||||
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
|
||||
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([a527c69](https://github.com/angular/angular/commit/a527c69)), closes [#24384](https://github.com/angular/angular/issues/24384)
|
||||
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([0b4d85e](https://github.com/angular/angular/commit/0b4d85e)), closes [#24831](https://github.com/angular/angular/issues/24831)
|
||||
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([ff84c5c](https://github.com/angular/angular/commit/ff84c5c)), closes [#24155](https://github.com/angular/angular/issues/24155)
|
||||
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([dcabb05](https://github.com/angular/angular/commit/dcabb05)), closes [#24609](https://github.com/angular/angular/issues/24609)
|
||||
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||
* **compiler:** fix a few non-tree-shakeable code patterns ([#24677](https://github.com/angular/angular/issues/24677)) ([50d4a4f](https://github.com/angular/angular/commit/50d4a4f))
|
||||
* **compiler:** i18n_extractor now outputs the correct source file name ([#24885](https://github.com/angular/angular/issues/24885)) ([c8ad965](https://github.com/angular/angular/commit/c8ad965)), closes [#24884](https://github.com/angular/angular/issues/24884)
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
|
||||
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
|
||||
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
|
||||
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
|
||||
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([0d5f2d3](https://github.com/angular/angular/commit/0d5f2d3))
|
||||
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
|
||||
* **core:** stop reusing provider definitions across NgModuleRef instances ([#25022](https://github.com/angular/angular/issues/25022)) ([6b859da](https://github.com/angular/angular/commit/6b859da)), closes [#25018](https://github.com/angular/angular/issues/25018)
|
||||
* **core:** mark NgModule as not the root if APP_ROOT is set to false ([#24814](https://github.com/angular/angular/issues/24814)) ([1089261](https://github.com/angular/angular/commit/1089261))
|
||||
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([0922228](https://github.com/angular/angular/commit/0922228)), closes [#22939](https://github.com/angular/angular/issues/22939)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
|
||||
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
|
||||
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
|
||||
* **docs-infra:** fix table header layout in API pages ([#24919](https://github.com/angular/angular/issues/24919)) ([3cd9645](https://github.com/angular/angular/commit/3cd9645))
|
||||
* **elements:** always check to create strategy ([#23825](https://github.com/angular/angular/issues/23825)) ([b1cda36](https://github.com/angular/angular/commit/b1cda36))
|
||||
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
|
||||
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
|
||||
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([6881404](https://github.com/angular/angular/commit/6881404)), closes [#21420](https://github.com/angular/angular/issues/21420)
|
||||
* **platform-browser:** add missing deps for HammerGesturesPlugin ([#24682](https://github.com/angular/angular/issues/24682)) ([13d60ea](https://github.com/angular/angular/commit/13d60ea))
|
||||
* **platform-browser:** mark Meta and Title services as tree shakable providers ([#24815](https://github.com/angular/angular/issues/24815)) ([197387d](https://github.com/angular/angular/commit/197387d))
|
||||
* **platform-browser:** workaround wrong import path generated by ngc for DOCUMENT ([#24830](https://github.com/angular/angular/issues/24830)) ([7d27ecc](https://github.com/angular/angular/commit/7d27ecc))
|
||||
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([b96a3c8](https://github.com/angular/angular/commit/b96a3c8))
|
||||
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([60aa943](https://github.com/angular/angular/commit/60aa943)), closes [#23023](https://github.com/angular/angular/issues/23023)
|
||||
* **platform-server:** don't reflect innerHTML property to attribute ([#24213](https://github.com/angular/angular/issues/24213)) ([6a663a4](https://github.com/angular/angular/commit/6a663a4)), closes [#19278](https://github.com/angular/angular/issues/19278)
|
||||
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
|
||||
* **router:** Fix _lastPathIndex in deeply nested empty paths ([#22394](https://github.com/angular/angular/issues/22394)) ([968f153](https://github.com/angular/angular/commit/968f153))
|
||||
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([86d254d](https://github.com/angular/angular/commit/86d254d)), closes [#21468](https://github.com/angular/angular/issues/21468)
|
||||
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
||||
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
|
||||
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
|
||||
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
|
||||
* **service-worker:** don't include sourceMappingURL in ngsw-worker ([#24877](https://github.com/angular/angular/issues/24877)) ([8620373](https://github.com/angular/angular/commit/8620373)), closes [#23596](https://github.com/angular/angular/issues/23596)
|
||||
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([52d43a9](https://github.com/angular/angular/commit/52d43a9))
|
||||
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
||||
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
|
||||
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
|
||||
* **service-worker:** correctly handle requests with empty `clientId` ([#23625](https://github.com/angular/angular/issues/23625)) ([e0ed59e](https://github.com/angular/angular/commit/e0ed59e)), closes [#23526](https://github.com/angular/angular/issues/23526)
|
||||
* **service-worker:** deprecate `versionedFiles` in asset-group resources ([#23584](https://github.com/angular/angular/issues/23584)) ([1d378e2](https://github.com/angular/angular/commit/1d378e2))
|
||||
|
||||
### Features
|
||||
|
||||
* **bazel:** Initial commit of protractor_web_test_suite ([#24787](https://github.com/angular/angular/issues/24787)) ([71e0df0](https://github.com/angular/angular/commit/71e0df0))
|
||||
* **bazel:** protractor_web_test_suite for release ([#24787](https://github.com/angular/angular/issues/24787)) ([161ff5c](https://github.com/angular/angular/commit/161ff5c))
|
||||
* **common:** introduce KeyValuePipe ([#24319](https://github.com/angular/angular/issues/24319)) ([2b49bf7](https://github.com/angular/angular/commit/2b49bf7))
|
||||
* **compiler:** support `// ...` and `// TODO` in mock compiler expectations ([#23441](https://github.com/angular/angular/issues/23441)) ([c6b206e](https://github.com/angular/angular/commit/c6b206e))
|
||||
* **compiler-cli:** update `tsickle` to `0.29.x` ([#24233](https://github.com/angular/angular/issues/24233)) ([f69ac67](https://github.com/angular/angular/commit/f69ac67))
|
||||
* **core:** export defaultKeyValueDiffers to private api ([#24319](https://github.com/angular/angular/issues/24319)) ([92b278c](https://github.com/angular/angular/commit/92b278c))
|
||||
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
|
||||
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
|
||||
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
|
||||
(https://github.com/angular/angular/commit/328971f)), closes [#24616](https://github.com/angular/angular/issues/24616)
|
||||
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
|
||||
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
|
||||
* **platform-server:** use EventManagerPlugin on the server ([#24132](https://github.com/angular/angular/issues/24132)) ([d6595eb](https://github.com/angular/angular/commit/d6595eb))
|
||||
* **router:** add urlUpdateStrategy allow updating the browser URL at the beginning of navigation ([#24820](https://github.com/angular/angular/issues/24820)) ([328971f]
|
||||
* **router:** add navigation execution context info to activation hooks ([#24204](https://github.com/angular/angular/issues/24204)) ([20c463e](https://github.com/angular/angular/commit/20c463e)), closes [#24202](https://github.com/angular/angular/issues/24202)
|
||||
* **router:** implement scrolling restoration service ([#20030](https://github.com/angular/angular/issues/20030)) ([49c5234](https://github.com/angular/angular/commit/49c5234)), closes [#13636](https://github.com/angular/angular/issues/13636) [#10929](https://github.com/angular/angular/issues/10929) [#7791](https://github.com/angular/angular/issues/7791) [#6595](https://github.com/angular/angular/issues/6595)
|
||||
* **service-worker:** add support for `?` in SW config globbing ([#24105](https://github.com/angular/angular/issues/24105)) ([250527c](https://github.com/angular/angular/commit/250527c))
|
||||
* typescript 2.9 support ([#24652](https://github.com/angular/angular/issues/24652)) ([e3064d5](https://github.com/angular/angular/commit/e3064d5))
|
||||
|
||||
### build
|
||||
|
||||
* **bazel:** turn on preserve-symlinks ([#24881](https://github.com/angular/angular/issues/24881)) ([c438b5e](https://github.com/angular/angular/commit/c438b5e))
|
||||
|
||||
### Angular Labs (experimental feature) breaking change
|
||||
|
||||
* **bazel:** Use of @angular/bazel rules now requires calling ng_setup_workspace() in your WORKSPACE file.
|
||||
|
||||
For example:
|
||||
|
||||
local_repository(
|
||||
name = "angular",
|
||||
path = "node_modules/@angular/bazel",
|
||||
)
|
||||
|
||||
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||
|
||||
ng_setup_workspace()
|
||||
|
||||
<a name="6.0.9"></a>
|
||||
## [6.0.9](https://github.com/angular/angular/compare/6.0.8...6.0.9) (2018-07-11)
|
||||
|
||||
@ -137,6 +6,37 @@ For example:
|
||||
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([3c93d07](https://github.com/angular/angular/commit/3c93d07)), closes [#24831](https://github.com/angular/angular/issues/24831)
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0-rc.0"></a>
|
||||
# [6.1.0-rc.0](https://github.com/angular/angular/compare/6.1.0-beta.3...6.1.0-rc.0) (2018-07-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([a527c69](https://github.com/angular/angular/commit/a527c69)), closes [#24384](https://github.com/angular/angular/issues/24384)
|
||||
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([ff84c5c](https://github.com/angular/angular/commit/ff84c5c)), closes [#24155](https://github.com/angular/angular/issues/24155)
|
||||
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([dcabb05](https://github.com/angular/angular/commit/dcabb05)), closes [#24609](https://github.com/angular/angular/issues/24609)
|
||||
* **compiler:** fix a few non-tree-shakeable code patterns ([#24677](https://github.com/angular/angular/issues/24677)) ([50d4a4f](https://github.com/angular/angular/commit/50d4a4f))
|
||||
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([0d5f2d3](https://github.com/angular/angular/commit/0d5f2d3))
|
||||
* **core:** mark NgModule as not the root if APP_ROOT is set to false ([#24814](https://github.com/angular/angular/issues/24814)) ([1089261](https://github.com/angular/angular/commit/1089261))
|
||||
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([0922228](https://github.com/angular/angular/commit/0922228)), closes [#22939](https://github.com/angular/angular/issues/22939)
|
||||
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([6881404](https://github.com/angular/angular/commit/6881404)), closes [#21420](https://github.com/angular/angular/issues/21420)
|
||||
* **platform-browser:** add missing deps for HammerGesturesPlugin ([#24682](https://github.com/angular/angular/issues/24682)) ([13d60ea](https://github.com/angular/angular/commit/13d60ea))
|
||||
* **platform-browser:** mark Meta and Title services as tree shakable providers ([#24815](https://github.com/angular/angular/issues/24815)) ([197387d](https://github.com/angular/angular/commit/197387d))
|
||||
* **platform-browser:** workaround wrong import path generated by ngc for DOCUMENT ([#24830](https://github.com/angular/angular/issues/24830)) ([7d27ecc](https://github.com/angular/angular/commit/7d27ecc))
|
||||
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([86d254d](https://github.com/angular/angular/commit/86d254d)), closes [#21468](https://github.com/angular/angular/issues/21468)
|
||||
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([52d43a9](https://github.com/angular/angular/commit/52d43a9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
|
||||
* **core:** add support for using async/await with Jasmine ([#24637](https://github.com/angular/angular/issues/24637)) ([71100e6](https://github.com/angular/angular/commit/71100e6))
|
||||
* typescript 2.9 support ([#24652](https://github.com/angular/angular/issues/24652)) ([e3064d5](https://github.com/angular/angular/commit/e3064d5))
|
||||
* **service-worker:** add support for `?` in SW config globbing ([#24105](https://github.com/angular/angular/issues/24105)) ([250527c](https://github.com/angular/angular/commit/250527c))
|
||||
|
||||
|
||||
|
||||
<a name="6.0.8"></a>
|
||||
## [6.0.8](https://github.com/angular/angular/compare/6.0.7...6.0.8) (2018-07-11)
|
||||
|
||||
@ -157,6 +57,15 @@ For example:
|
||||
|
||||
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([6c55a13](https://github.com/angular/angular/commit/6c55a13))
|
||||
|
||||
|
||||
<a name="6.1.0-beta.3"></a>
|
||||
# [6.1.0-beta.3](https://github.com/angular/angular/compare/6.1.0-beta.2...6.1.0-beta.3) (2018-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||
|
||||
<a name="6.0.7"></a>
|
||||
## [6.0.7](https://github.com/angular/angular/compare/6.0.6...6.0.7) (2018-06-27)
|
||||
|
||||
@ -166,6 +75,18 @@ For example:
|
||||
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0-beta.2"></a>
|
||||
# [6.1.0-beta.2](https://github.com/angular/angular/compare/6.1.0-beta.1...6.1.0-beta.2) (2018-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
|
||||
|
||||
|
||||
<a name="6.0.6"></a>
|
||||
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)
|
||||
|
||||
@ -175,6 +96,39 @@ For example:
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([e543c73](https://github.com/angular/angular/commit/e543c73)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([f5b3661](https://github.com/angular/angular/commit/f5b3661))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0-beta.1"></a>
|
||||
# [6.1.0-beta.1](https://github.com/angular/angular/compare/6.1.0-beta.0...6.1.0-beta.1) (2018-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([dc4a3d0](https://github.com/angular/angular/commit/dc4a3d0))
|
||||
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([282d351](https://github.com/angular/angular/commit/282d351))
|
||||
* **docs-infra:** use script nomodule to load IE polyfills, skip other polyfills ([#24317](https://github.com/angular/angular/issues/24317)) ([8be6892](https://github.com/angular/angular/commit/8be6892)), closes [#23647](https://github.com/angular/angular/issues/23647)
|
||||
* **ivy:** compute transitive scopes from NgModuleDef only ([#24334](https://github.com/angular/angular/issues/24334)) ([1135563](https://github.com/angular/angular/commit/1135563))
|
||||
* **ivy:** correctly handle queries with embedded views ([#24418](https://github.com/angular/angular/issues/24418)) ([014949f](https://github.com/angular/angular/commit/014949f))
|
||||
* **ivy:** remove debugger statement ([#24480](https://github.com/angular/angular/issues/24480)) ([70ef061](https://github.com/angular/angular/commit/70ef061))
|
||||
* **ivy:** special case [style] and [class] bindings for future use ([#23232](https://github.com/angular/angular/issues/23232)) ([1b253e1](https://github.com/angular/angular/commit/1b253e1))
|
||||
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
||||
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** introduce KeyValuePipe ([#24319](https://github.com/angular/angular/issues/24319)) ([2b49bf7](https://github.com/angular/angular/commit/2b49bf7))
|
||||
* **core:** export defaultKeyValueDiffers to private api ([#24319](https://github.com/angular/angular/issues/24319)) ([92b278c](https://github.com/angular/angular/commit/92b278c))
|
||||
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
|
||||
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
|
||||
* **ivy:** a generic visitor which allows prefixing nodes for ngtsc ([#24230](https://github.com/angular/angular/issues/24230)) ([ca79e11](https://github.com/angular/angular/commit/ca79e11))
|
||||
* **ivy:** add support of ApplicationRef.bootstrapModuleFactory ([#23811](https://github.com/angular/angular/issues/23811)) ([e3759f7](https://github.com/angular/angular/commit/e3759f7))
|
||||
* **ivy:** namespaced attributes added to output instructions ([#24386](https://github.com/angular/angular/issues/24386)) ([82c5313](https://github.com/angular/angular/commit/82c5313))
|
||||
* **ivy:** now supports SVG and MathML elements ([#24377](https://github.com/angular/angular/issues/24377)) ([8c1ac28](https://github.com/angular/angular/commit/8c1ac28))
|
||||
* **router:** implement scrolling restoration service ([#20030](https://github.com/angular/angular/issues/20030)) ([49c5234](https://github.com/angular/angular/commit/49c5234)), closes [#13636](https://github.com/angular/angular/issues/13636) [#10929](https://github.com/angular/angular/issues/10929) [#7791](https://github.com/angular/angular/issues/7791) [#6595](https://github.com/angular/angular/issues/6595)
|
||||
|
||||
|
||||
|
||||
<a name="6.0.5"></a>
|
||||
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
|
||||
|
||||
@ -184,6 +138,53 @@ For example:
|
||||
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([d20877b](https://github.com/angular/angular/commit/d20877b)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
||||
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([ea2987c](https://github.com/angular/angular/commit/ea2987c)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0-beta.0"></a>
|
||||
# [6.1.0-beta.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0-beta.0) (2018-06-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not throw errors when a destroyed component is animated ([#23836](https://github.com/angular/angular/issues/23836)) ([d2a8687](https://github.com/angular/angular/commit/d2a8687))
|
||||
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([b492b9e](https://github.com/angular/angular/commit/b492b9e))
|
||||
* **animations:** properly clean up queried element styles in safari/edge ([#23633](https://github.com/angular/angular/issues/23633)) ([da9ff25](https://github.com/angular/angular/commit/da9ff25))
|
||||
* **animations:** retain state styling for nodes that are moved around ([#23534](https://github.com/angular/angular/issues/23534)) ([65211f4](https://github.com/angular/angular/commit/65211f4))
|
||||
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([8db928d](https://github.com/angular/angular/commit/8db928d))
|
||||
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
|
||||
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
|
||||
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
|
||||
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
|
||||
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
|
||||
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
|
||||
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
|
||||
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
|
||||
* **elements:** always check to create strategy ([#23825](https://github.com/angular/angular/issues/23825)) ([b1cda36](https://github.com/angular/angular/commit/b1cda36))
|
||||
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
|
||||
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
|
||||
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([b96a3c8](https://github.com/angular/angular/commit/b96a3c8))
|
||||
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([60aa943](https://github.com/angular/angular/commit/60aa943)), closes [#23023](https://github.com/angular/angular/issues/23023)
|
||||
* **platform-server:** don't reflect innerHTML property to attibute ([#24213](https://github.com/angular/angular/issues/24213)) ([6a663a4](https://github.com/angular/angular/commit/6a663a4)), closes [#19278](https://github.com/angular/angular/issues/19278)
|
||||
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
|
||||
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
|
||||
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
|
||||
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
|
||||
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
|
||||
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
|
||||
* **service-worker:** correctly handle requests with empty `clientId` ([#23625](https://github.com/angular/angular/issues/23625)) ([e0ed59e](https://github.com/angular/angular/commit/e0ed59e)), closes [#23526](https://github.com/angular/angular/issues/23526)
|
||||
* **service-worker:** deprecate `versionedFiles` in asset-group resources ([#23584](https://github.com/angular/angular/issues/23584)) ([1d378e2](https://github.com/angular/angular/commit/1d378e2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** support `// ...` and `// TODO` in mock compiler expectations ([#23441](https://github.com/angular/angular/issues/23441)) ([c6b206e](https://github.com/angular/angular/commit/c6b206e))
|
||||
* **compiler-cli:** update `tsickle` to `0.29.x` ([#24233](https://github.com/angular/angular/issues/24233)) ([f69ac67](https://github.com/angular/angular/commit/f69ac67))
|
||||
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
|
||||
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
|
||||
* **platform-server:** use EventManagerPlugin on the server ([#24132](https://github.com/angular/angular/issues/24132)) ([d6595eb](https://github.com/angular/angular/commit/d6595eb))
|
||||
* **router:** add navigation execution context info to activation hooks ([#24204](https://github.com/angular/angular/issues/24204)) ([20c463e](https://github.com/angular/angular/commit/20c463e)), closes [#24202](https://github.com/angular/angular/issues/24202)
|
||||
|
||||
|
||||
<a name="6.0.4"></a>
|
||||
## [6.0.4](https://github.com/angular/angular/compare/6.0.3...6.0.4) (2018-06-06)
|
||||
|
||||
@ -262,6 +263,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **animations:** only use the WA-polyfill alongside AnimationBuilder ([#22143](https://github.com/angular/angular/issues/22143)) ([b2f366b](https://github.com/angular/angular/commit/b2f366b)), closes [#17496](https://github.com/angular/angular/issues/17496)
|
||||
* **animations:** expose `element` and `params` within transition matchers ([#22693](https://github.com/angular/angular/issues/22693)) ([58b94e6](https://github.com/angular/angular/commit/58b94e6))
|
||||
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
|
||||
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
|
||||
* **common:** export functions to format numbers, percents, currencies & dates ([#22423](https://github.com/angular/angular/issues/22423)) ([4180912](https://github.com/angular/angular/commit/4180912)), closes [#20536](https://github.com/angular/angular/issues/20536)
|
||||
* **compiler:** lower @NgModule ids if needed ([#23031](https://github.com/angular/angular/issues/23031)) ([bd024c0](https://github.com/angular/angular/commit/bd024c0))
|
||||
* **compiler:** implement "enableIvy" compiler option ([#21427](https://github.com/angular/angular/issues/21427)) ([64d16de](https://github.com/angular/angular/commit/64d16de))
|
||||
@ -301,6 +303,7 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
|
||||
* **animations:** avoid animation insertions during router back/refresh ([#21977](https://github.com/angular/angular/issues/21977)) ([f88fba0](https://github.com/angular/angular/commit/f88fba0)), closes [#19712](https://github.com/angular/angular/issues/19712)
|
||||
* **animations:** treat numeric state name values as strings ([#22923](https://github.com/angular/angular/issues/22923)) ([e5e1b0d](https://github.com/angular/angular/commit/e5e1b0d))
|
||||
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
|
||||
* **animations:** fix increment/decrement aliases example ([#18323](https://github.com/angular/angular/issues/18323)) ([d2aa8ac](https://github.com/angular/angular/commit/d2aa8ac))
|
||||
* **common:** NgClass should properly take className changes into account ([#21937](https://github.com/angular/angular/issues/21937)) ([4a42669](https://github.com/angular/angular/commit/4a42669)), closes [#21932](https://github.com/angular/angular/issues/21932)
|
||||
* **common:** fix the titlecase pipe ([#22600](https://github.com/angular/angular/issues/22600)) ([7966744](https://github.com/angular/angular/commit/7966744))
|
||||
|
62
WORKSPACE
62
WORKSPACE
@ -6,30 +6,23 @@ workspace(name = "angular")
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.4.zip"],
|
||||
strip_prefix = "rules_nodejs-0.11.4",
|
||||
sha256 = "c31c4ead696944a50fad2b3ee9dfbbeffe31a8dcca0b21b9bf5b3e6c6b069801",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
|
||||
strip_prefix = "bazel-skylib-0.3.1",
|
||||
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
|
||||
strip_prefix = "rules_nodejs-0.10.1",
|
||||
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_webtesting",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
|
||||
strip_prefix = "rules_webtesting-0.2.1",
|
||||
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
|
||||
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
|
||||
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
|
||||
strip_prefix = "rules_typescript-0.16.0",
|
||||
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.1.zip",
|
||||
strip_prefix = "rules_typescript-0.15.1",
|
||||
sha256 = "3792cc20ef13bb1d1d8b1760894c3320f02a87843e3a04fed7e8e454a75328b6",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -78,22 +71,6 @@ http_archive(
|
||||
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
|
||||
)
|
||||
|
||||
#
|
||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||
#
|
||||
|
||||
local_repository(
|
||||
name = "rxjs",
|
||||
path = "node_modules/rxjs/src",
|
||||
)
|
||||
|
||||
# Point to the integration test workspace just so that Bazel doesn't descend into it
|
||||
# when expanding the //... pattern
|
||||
local_repository(
|
||||
name = "bazel_integration_test",
|
||||
path = "integration/bazel",
|
||||
)
|
||||
|
||||
#
|
||||
# Load and install our dependencies downloaded above.
|
||||
#
|
||||
@ -101,10 +78,7 @@ local_repository(
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||
|
||||
check_bazel_version("0.15.0")
|
||||
node_repositories(
|
||||
package_json = ["//:package.json"],
|
||||
preserve_symlinks = True,
|
||||
)
|
||||
node_repositories(package_json = ["//:package.json"])
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||
|
||||
@ -123,9 +97,21 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||
|
||||
ts_setup_workspace()
|
||||
|
||||
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||
#
|
||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||
#
|
||||
|
||||
ng_setup_workspace()
|
||||
local_repository(
|
||||
name = "rxjs",
|
||||
path = "node_modules/rxjs/src",
|
||||
)
|
||||
|
||||
# Point to the integration test workspace just so that Bazel doesn't descend into it
|
||||
# when expanding the //... pattern
|
||||
local_repository(
|
||||
name = "bazel_integration_test",
|
||||
path = "integration/bazel",
|
||||
)
|
||||
|
||||
#
|
||||
# Ask Bazel to manage these toolchain dependencies for us.
|
||||
|
@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
|
||||
|
||||
## Developer tasks
|
||||
|
||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
||||
We use `yarn` to manage the dependencies and to run build tasks.
|
||||
You should run all these tasks from the `angular/aio` folder.
|
||||
Here are the most important tasks you might need to use:
|
||||
|
||||
@ -43,17 +43,6 @@ Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
||||
|
||||
## Developing on Windows
|
||||
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
|
||||
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
|
||||
|
||||
> Hint: The following steps require administration rights or [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) enabled!
|
||||
|
||||
To fix this problem, run `scripts/windows/create-symlinks.sh`. This command creates temporary files where the symlinks used to be. Make sure not to commit those files with your documentation changes.
|
||||
When you are done making and testing your documentation changes, you can restore the original symlinks and delete the temporary files by running `scripts/windows/remove-symlinks.sh`.
|
||||
|
||||
It's necessary to remove the temporary files, because otherwise they're displayed as local changes in your git working copy and certain operations are blocked.
|
||||
|
||||
## Using ServiceWorker locally
|
||||
|
||||
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
// #docregion directive-import
|
||||
@ -24,7 +24,7 @@ import { ItemDirective } from './item.directive';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule
|
||||
HttpModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -1,7 +1,7 @@
|
||||
// #docregion
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
// import { AppRoutingModule } from './app-routing.module';
|
||||
import { LocationStrategy,
|
||||
@ -54,7 +54,7 @@ const c_components = [
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
HttpModule,
|
||||
InMemoryWebApiModule.forRoot(HeroData)
|
||||
// AppRoutingModule TODO: add routes
|
||||
],
|
||||
|
@ -1,38 +1,26 @@
|
||||
import { Injector } from '@angular/core';
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
|
||||
import { Car, Engine, Tires } from './car';
|
||||
import { Logger } from '../logger.service';
|
||||
|
||||
// #docregion injector
|
||||
export function useInjector() {
|
||||
let injector: Injector;
|
||||
let injector: ReflectiveInjector;
|
||||
// #enddocregion injector
|
||||
/*
|
||||
// #docregion injector-no-new
|
||||
// Cannot instantiate an Injector like this!
|
||||
let injector = new Injector([
|
||||
{ provide: Car, deps: [Engine, Tires] },
|
||||
{ provide: Engine, deps: [] },
|
||||
{ provide: Tires, deps: [] }
|
||||
]);
|
||||
// Cannot instantiate an ReflectiveInjector like this!
|
||||
let injector = new ReflectiveInjector([Car, Engine, Tires]);
|
||||
// #enddocregion injector-no-new
|
||||
*/
|
||||
// #docregion injector, injector-create-and-call
|
||||
injector = Injector.create({
|
||||
providers: [
|
||||
{ provide: Car, deps: [Engine, Tires] },
|
||||
{ provide: Engine, deps: [] },
|
||||
{ provide: Tires, deps: [] }
|
||||
]
|
||||
});
|
||||
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
|
||||
// #docregion injector-call
|
||||
let car = injector.get(Car);
|
||||
// #enddocregion injector-call, injector-create-and-call
|
||||
car.description = 'Injector';
|
||||
|
||||
injector = Injector.create({
|
||||
providers: [{ provide: Logger, deps: [] }]
|
||||
});
|
||||
injector = ReflectiveInjector.resolveAndCreate([Logger]);
|
||||
let logger = injector.get(Logger);
|
||||
logger.log('Injector car.drive() said: ' + car.drive());
|
||||
return car;
|
||||
|
@ -1,69 +0,0 @@
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
/* tslint:disable:quotemark */
|
||||
describe('Elements', () => {
|
||||
const messageInput = element(by.css('input'));
|
||||
const popupButtons = element.all(by.css('button'));
|
||||
|
||||
beforeEach(() => browser.get(''));
|
||||
|
||||
describe('popup component', () => {
|
||||
const popupComponentButton = popupButtons.get(0);
|
||||
const popupComponent = element(by.css('popup-component'));
|
||||
const closeButton = popupComponent.element(by.css('button'));
|
||||
|
||||
it('should be displayed on button click', () => {
|
||||
expect(popupComponent.isPresent()).toBe(false);
|
||||
|
||||
popupComponentButton.click();
|
||||
expect(popupComponent.isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should display the specified message', () => {
|
||||
messageInput.clear();
|
||||
messageInput.sendKeys('Angular rocks!');
|
||||
|
||||
popupComponentButton.click();
|
||||
expect(popupComponent.getText()).toContain('Popup: Angular rocks!');
|
||||
});
|
||||
|
||||
it('should be closed on "close" button click', () => {
|
||||
popupComponentButton.click();
|
||||
expect(popupComponent.isPresent()).toBe(true);
|
||||
|
||||
closeButton.click();
|
||||
expect(popupComponent.isPresent()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('popup element', () => {
|
||||
const popupElementButton = popupButtons.get(1);
|
||||
const popupElement = element(by.css('popup-element'));
|
||||
const closeButton = popupElement.element(by.css('button'));
|
||||
|
||||
it('should be displayed on button click', () => {
|
||||
expect(popupElement.isPresent()).toBe(false);
|
||||
|
||||
popupElementButton.click();
|
||||
expect(popupElement.isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should display the specified message', () => {
|
||||
messageInput.clear();
|
||||
messageInput.sendKeys('Angular rocks!');
|
||||
|
||||
popupElementButton.click();
|
||||
expect(popupElement.getText()).toContain('Popup: Angular rocks!');
|
||||
});
|
||||
|
||||
it('should be closed on "close" button click', () => {
|
||||
popupElementButton.click();
|
||||
expect(popupElement.isPresent()).toBe(true);
|
||||
|
||||
closeButton.click();
|
||||
expect(popupElement.isPresent()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"projectType": "elements"
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// #docregion
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { createCustomElement } from '@angular/elements';
|
||||
import { createNgElementConstructor } from '../elements-dist';
|
||||
import { PopupService } from './popup.service';
|
||||
import { PopupComponent } from './popup.component';
|
||||
|
||||
@ -7,15 +8,19 @@ import { PopupComponent } from './popup.component';
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<input #input value="Message">
|
||||
<button (click)="popup.showAsComponent(input.value)">Show as component</button>
|
||||
<button (click)="popup.showAsElement(input.value)">Show as element</button>
|
||||
`,
|
||||
<button (click)="popup.showAsComponent(input.value)">
|
||||
Show as component </button>
|
||||
<button (click)="popup.showAsElement(input.value)">
|
||||
Show as element </button>
|
||||
`
|
||||
})
|
||||
|
||||
export class AppComponent {
|
||||
constructor(injector: Injector, public popup: PopupService) {
|
||||
// Convert `PopupComponent` to a custom element.
|
||||
const PopupElement = createCustomElement(PopupComponent, {injector});
|
||||
// Register the custom element with the browser.
|
||||
customElements.define('popup-element', PopupElement);
|
||||
constructor(private injector: Injector, public popup: PopupService) {
|
||||
// on init, convert PopupComponent to a custom element
|
||||
const PopupElement =
|
||||
createNgElementConstructor(PopupComponent, {injector: this.injector});
|
||||
// register the custom element with the browser.
|
||||
customElements.define('popup-element', PopupElement);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
// #docregion
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { PopupComponent } from './popup.component';
|
||||
import { PopupService } from './popup.service';
|
||||
import { PopupComponent } from './popup.component';
|
||||
|
||||
// Include the `PopupService` provider,
|
||||
// but exclude `PopupComponent` from compilation,
|
||||
// because it will be added dynamically.
|
||||
// include the PopupService provider,
|
||||
// but exclude PopupComponent from compilation,
|
||||
// because it will be added dynamically
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, PopupComponent],
|
||||
imports: [BrowserModule, BrowserAnimationsModule],
|
||||
providers: [PopupService],
|
||||
declarations: [AppComponent, PopupComponent],
|
||||
bootstrap: [AppComponent],
|
||||
entryComponents: [PopupComponent],
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
export class AppModule {}
|
||||
|
@ -1,14 +1,14 @@
|
||||
// #docregion
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AnimationEvent } from '@angular/animations';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'my-popup',
|
||||
template: `
|
||||
<span>Popup: {{message}}</span>
|
||||
<button (click)="closed.next()">✖</button>
|
||||
`,
|
||||
template: 'Popup: {{message}}',
|
||||
host: {
|
||||
'[@state]': 'state',
|
||||
'(@state.done)': 'onAnimationDone($event)',
|
||||
},
|
||||
animations: [
|
||||
trigger('state', [
|
||||
@ -27,17 +27,13 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
||||
height: 48px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid black;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 50%;
|
||||
}
|
||||
`]
|
||||
})
|
||||
|
||||
export class PopupComponent {
|
||||
private state: 'opened' | 'closed' = 'closed';
|
||||
|
||||
@ -45,10 +41,18 @@ export class PopupComponent {
|
||||
set message(message: string) {
|
||||
this._message = message;
|
||||
this.state = 'opened';
|
||||
|
||||
setTimeout(() => this.state = 'closed', 2000);
|
||||
}
|
||||
get message(): string { return this._message; }
|
||||
_message: string;
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter();
|
||||
|
||||
onAnimationDone(e: AnimationEvent) {
|
||||
if (e.toState === 'closed') {
|
||||
this.closed.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// #docregion
|
||||
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { NgElement, WithProperties } from '@angular/elements';
|
||||
import { PopupComponent } from './popup.component';
|
||||
|
||||
import { PopupComponent } from './popup.component';
|
||||
import { NgElementConstructor } from '../elements-dist';
|
||||
|
||||
@Injectable()
|
||||
export class PopupService {
|
||||
@ -39,7 +40,7 @@ export class PopupService {
|
||||
// This uses the new custom-element method to add the popup to the DOM.
|
||||
showAsElement(message: string) {
|
||||
// Create element
|
||||
const popupEl: NgElement & WithProperties<PopupComponent> = document.createElement('popup-element') as any;
|
||||
const popupEl = document.createElement('popup-element');
|
||||
|
||||
// Listen to the close event
|
||||
popupEl.addEventListener('closed', () => document.body.removeChild(popupEl));
|
||||
|
@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base href="/">
|
||||
<title>Elements</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"description": "Angular Elements",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[1].*"
|
||||
],
|
||||
"tags":["cookbook"]
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
// #docplaster
|
||||
// #docregion app-module
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
// import the feature module here so you can add it to the imports array below
|
||||
import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
@ -16,7 +17,7 @@ import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
HttpModule,
|
||||
CustomerDashboardModule // add the feature module here
|
||||
],
|
||||
providers: [],
|
||||
|
@ -16,7 +16,6 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Template-Driven Form');
|
||||
bobTests();
|
||||
asyncValidationTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
|
||||
@ -27,7 +26,6 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Reactive Form');
|
||||
bobTests();
|
||||
asyncValidationTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
});
|
||||
@ -47,7 +45,6 @@ let page: {
|
||||
errorMessages: ElementArrayFinder,
|
||||
heroFormButtons: ElementArrayFinder,
|
||||
heroSubmitted: ElementFinder,
|
||||
alterEgoErrors: ElementFinder,
|
||||
crossValidationErrorMessage: ElementFinder,
|
||||
};
|
||||
|
||||
@ -66,7 +63,6 @@ function getPage(sectionTag: string) {
|
||||
errorMessages: section.all(by.css('div.alert')),
|
||||
heroFormButtons: buttons,
|
||||
heroSubmitted: section.element(by.css('.submitted-message')),
|
||||
alterEgoErrors: section.element(by.css('.alter-ego-errors')),
|
||||
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
|
||||
};
|
||||
}
|
||||
@ -160,16 +156,6 @@ function expectFormIsInvalid() {
|
||||
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
|
||||
}
|
||||
|
||||
function triggerAlterEgoValidation() {
|
||||
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
|
||||
element(by.css('app-root')).click()
|
||||
}
|
||||
|
||||
function waitForAlterEgoValidation() {
|
||||
// alterEgo async validation will be performed in 400ms
|
||||
browser.sleep(400);
|
||||
}
|
||||
|
||||
function bobTests() {
|
||||
const emsg = 'Name cannot be Bob.';
|
||||
|
||||
@ -191,32 +177,6 @@ function bobTests() {
|
||||
});
|
||||
}
|
||||
|
||||
function asyncValidationTests() {
|
||||
const emsg = 'Alter ego is already taken.';
|
||||
|
||||
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Eric');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsInvalid();
|
||||
expect(page.alterEgoErrors.getText()).toBe(emsg);
|
||||
});
|
||||
|
||||
it('should be ok again with different values', function () {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('John');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsValid();
|
||||
expect(page.alterEgoErrors.isPresent()).toBe(false);
|
||||
});
|
||||
}
|
||||
|
||||
function crossValidationTests() {
|
||||
const emsg = 'Name cannot match alter ego.';
|
||||
|
||||
@ -227,9 +187,6 @@ function crossValidationTests() {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Batman');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsInvalid();
|
||||
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
||||
});
|
||||
@ -241,9 +198,6 @@ function crossValidationTests() {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Superman');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsValid();
|
||||
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ import { HeroFormTemplateComponent } from './template/hero-form-template.compone
|
||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
||||
import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -21,8 +20,7 @@ import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
|
||||
HeroFormTemplateComponent,
|
||||
HeroFormReactiveComponent,
|
||||
ForbiddenValidatorDirective,
|
||||
IdentityRevealedValidatorDirective,
|
||||
UniqueAlterEgoValidatorDirective
|
||||
IdentityRevealedValidatorDirective
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -1,48 +0,0 @@
|
||||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
templateUrl: './hero-form-reactive.component.html',
|
||||
styleUrls: ['./hero-form-reactive.component.css'],
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
|
||||
|
||||
heroForm: FormGroup;
|
||||
|
||||
ngOnInit(): void {
|
||||
// #docregion async-validation
|
||||
this.heroForm = new FormGroup({
|
||||
'name': new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo, {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
}),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
// #enddocregion async-validation
|
||||
}
|
||||
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
|
||||
get alterEgo() { return this.heroForm.get('alterEgo'); }
|
||||
|
||||
// #docregion async-validation
|
||||
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
|
||||
// #enddocregion async-validation
|
||||
}
|
@ -35,13 +35,6 @@
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
|
||||
Alter ego is already taken.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
|
@ -4,7 +4,6 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
||||
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
@ -26,10 +25,7 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo, {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
}),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
||||
}
|
||||
@ -37,8 +33,4 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
|
||||
get alterEgo() { return this.heroForm.get('alterEgo'); }
|
||||
|
||||
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
import { Directive, forwardRef, Injectable } from '@angular/core';
|
||||
import {
|
||||
AsyncValidator,
|
||||
AbstractControl,
|
||||
NG_ASYNC_VALIDATORS,
|
||||
ValidationErrors
|
||||
} from '@angular/forms';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import { HeroesService } from './heroes.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
// #docregion async-validator
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UniqueAlterEgoValidator implements AsyncValidator {
|
||||
constructor(private heroesService: HeroesService) {}
|
||||
|
||||
validate(
|
||||
ctrl: AbstractControl
|
||||
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
|
||||
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
|
||||
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
|
||||
catchError(() => null)
|
||||
);
|
||||
}
|
||||
}
|
||||
// #enddocregion async-validator
|
||||
|
||||
// #docregion async-validator-directive
|
||||
@Directive({
|
||||
selector: '[appUniqueAlterEgo]',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_ASYNC_VALIDATORS,
|
||||
useExisting: forwardRef(() => UniqueAlterEgoValidator),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class UniqueAlterEgoValidatorDirective {
|
||||
constructor(private validator: UniqueAlterEgoValidator) {}
|
||||
|
||||
validate(control: AbstractControl) {
|
||||
this.validator.validate(control);
|
||||
}
|
||||
}
|
||||
// #enddocregion async-validator-directive
|
@ -1,14 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
const ALTER_EGOS = ['Eric'];
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class HeroesService {
|
||||
isAlterEgoTaken(alterEgo: string): Observable<boolean> {
|
||||
const isTaken = ALTER_EGOS.includes(alterEgo);
|
||||
|
||||
return of(isTaken).pipe(delay(400));
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
@ -35,20 +35,8 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<!-- #docregion async-validation -->
|
||||
<input id="alterEgo" class="form-control" name="alterEgo"
|
||||
#alterEgo="ngModel"
|
||||
[(ngModel)]="hero.alterEgo"
|
||||
[ngModelOptions]="{ updateOn: 'blur' }"
|
||||
appUniqueAlterEgo>
|
||||
<!-- #enddocregion async-validation -->
|
||||
|
||||
<div *ngIf="alterEgo.pending">Validating...</div>
|
||||
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
|
||||
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
|
||||
Alter ego is already taken.
|
||||
</div>
|
||||
</div>
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
@ -13,7 +13,7 @@ import { AppComponent } from './app.component';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
HttpModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
providers: [],
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
/* App Root */
|
||||
import { AppComponent } from './app.component';
|
||||
|
@ -15,7 +15,7 @@ function sequenceSubscriber(observer) {
|
||||
if (idx === arr.length - 1) {
|
||||
observer.complete();
|
||||
} else {
|
||||
doSequence(arr, ++idx);
|
||||
doSequence(arr, idx++);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
@ -95,7 +95,7 @@ function multicastSequenceSubscriber() {
|
||||
},
|
||||
complete() {
|
||||
// Notify all complete callbacks
|
||||
observers.slice(0).forEach(obs => obs.complete());
|
||||
observers.forEach(obs => obs.complete());
|
||||
}
|
||||
}, seq, 0);
|
||||
}
|
||||
@ -121,13 +121,13 @@ function doSequence(observer, arr, idx) {
|
||||
if (idx === arr.length - 1) {
|
||||
observer.complete();
|
||||
} else {
|
||||
doSequence(observer, arr, ++idx);
|
||||
doSequence(observer, arr, idx++);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Create a new Observable that will deliver the above sequence
|
||||
const multicastSequence = new Observable(multicastSequenceSubscriber());
|
||||
const multicastSequence = new Observable(multicastSequenceSubscriber);
|
||||
|
||||
// Subscribe starts the clock, and begins to emit after 1 second
|
||||
multicastSequence.subscribe({
|
||||
|
@ -1,21 +1,26 @@
|
||||
// #docregion
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
||||
import { FetchJsonPipe } from './fetch-json.pipe';
|
||||
import { FlyingHeroesComponent, FlyingHeroesImpureComponent } from './flying-heroes.component';
|
||||
import { FlyingHeroesImpurePipe, FlyingHeroesPipe } from './flying-heroes.pipe';
|
||||
import {
|
||||
FlyingHeroesComponent,
|
||||
FlyingHeroesImpureComponent
|
||||
} from './flying-heroes.component';
|
||||
import { HeroAsyncMessageComponent } from './hero-async-message.component';
|
||||
import { HeroBirthdayComponent } from './hero-birthday1.component';
|
||||
import { HeroBirthday2Component } from './hero-birthday2.component';
|
||||
import { HeroListComponent } from './hero-list.component';
|
||||
import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
|
||||
import { PowerBoosterComponent } from './power-booster.component';
|
||||
|
||||
import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
|
||||
import {
|
||||
FlyingHeroesPipe,
|
||||
FlyingHeroesImpurePipe
|
||||
} from './flying-heroes.pipe';
|
||||
import { FetchJsonPipe } from './fetch-json.pipe';
|
||||
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -38,6 +43,6 @@ import { PowerBoosterComponent } from './power-booster.component';
|
||||
FetchJsonPipe,
|
||||
ExponentialStrengthPipe
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
@ -1,14 +1,13 @@
|
||||
// #docregion
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
// #docregion pipe-metadata
|
||||
@Pipe({
|
||||
name: 'fetch',
|
||||
pure: false
|
||||
})
|
||||
// #enddocregion pipe-metadata
|
||||
export class FetchJsonPipe implements PipeTransform {
|
||||
export class FetchJsonPipe implements PipeTransform {
|
||||
private cachedData: any = null;
|
||||
private cachedUrl = '';
|
||||
|
||||
@ -18,7 +17,7 @@ export class FetchJsonPipe implements PipeTransform {
|
||||
if (url !== this.cachedUrl) {
|
||||
this.cachedData = null;
|
||||
this.cachedUrl = url;
|
||||
this.http.get(url).subscribe(result => this.cachedData = result);
|
||||
this.http.get(url).subscribe( result => this.cachedData = result );
|
||||
}
|
||||
|
||||
return this.cachedData;
|
||||
|
3
aio/content/examples/reactive-forms/debug.log
Normal file
3
aio/content/examples/reactive-forms/debug.log
Normal file
@ -0,0 +1,3 @@
|
||||
[1030/162525.401:ERROR:process_reader_win.cc(123)] NtOpenThread: {Acceso denegado} Un proceso ha solicitado acceso a un objeto, pero no se le han concedido esos derechos de acceso. (0xc0000022)
|
||||
[1030/162525.402:ERROR:exception_snapshot_win.cc(87)] thread ID 26896 not found in process
|
||||
[1030/162525.402:WARNING:crash_report_exception_handler.cc(62)] ProcessSnapshotWin::Initialize failed
|
File diff suppressed because it is too large
Load Diff
24
aio/content/examples/reactive-forms/final.stackblitz.json
Normal file
24
aio/content/examples/reactive-forms/final.stackblitz.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"description": "Angular Reactive Forms (final)",
|
||||
"files":[
|
||||
"src/styles.css",
|
||||
|
||||
"src/app/app.component.ts",
|
||||
"src/app/app.component.html",
|
||||
"src/app/app.component.css",
|
||||
"src/app/app.module.ts",
|
||||
"src/app/data-model.ts",
|
||||
"src/app/hero.service.ts",
|
||||
"src/app/hero-detail/hero-detail.component.html",
|
||||
"src/app/hero-detail/hero-detail.component.ts",
|
||||
"src/app/hero-detail/hero-detail.component.css",
|
||||
"src/app/hero-list/hero-list.component.html",
|
||||
"src/app/hero-list/hero-list.component.ts",
|
||||
"src/app/hero-list/hero-list.component.css",
|
||||
|
||||
"src/main-final.ts",
|
||||
"src/index-final.html"
|
||||
],
|
||||
"main": "src/index-final.html",
|
||||
"tags": ["reactive", "forms"]
|
||||
}
|
@ -1,10 +1,4 @@
|
||||
<!-- #docplaster -->
|
||||
<h1>Reactive Forms</h1>
|
||||
|
||||
<!-- #docregion app-name-editor-->
|
||||
<app-name-editor></app-name-editor>
|
||||
<!-- #enddocregion app-name-editor-->
|
||||
|
||||
<!-- #docregion app-profile-editor -->
|
||||
<app-profile-editor></app-profile-editor>
|
||||
<!-- #enddocregion app-profile-editor -->
|
||||
<div class="container">
|
||||
<h1>Reactive Forms</h1>
|
||||
<app-hero-detail></app-hero-detail>
|
||||
</div>
|
||||
|
@ -1,17 +1,4 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion app-name-editor -->
|
||||
<h1>Reactive Forms</h1>
|
||||
|
||||
<!-- #enddocregion app-name-editor -->
|
||||
<nav>
|
||||
<a (click)="toggleEditor('name')">Name Editor</a>
|
||||
<a (click)="toggleEditor('profile')">Profile Editor</a>
|
||||
</nav>
|
||||
|
||||
<!-- #docregion app-name-editor -->
|
||||
<app-name-editor *ngIf="showNameEditor"></app-name-editor>
|
||||
<!-- #enddocregion app-name-editor -->
|
||||
|
||||
<!-- #docregion app-profile-editor -->
|
||||
<app-profile-editor *ngIf="showProfileEditor"></app-profile-editor>
|
||||
<!-- #enddocregion app-profile-editor -->
|
||||
<div class="container">
|
||||
<h1>Reactive Forms</h1>
|
||||
<app-hero-list></app-hero-list>
|
||||
</div>
|
||||
|
@ -1,27 +0,0 @@
|
||||
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', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
it(`should have as title 'app'`, async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('app');
|
||||
}));
|
||||
it('should render title in a h1 tag', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('Welcome to reactive-forms!');
|
||||
}));
|
||||
});
|
@ -1,24 +1,9 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
export type EditorType = 'name' | 'profile';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
editor: EditorType = 'name';
|
||||
|
||||
get showNameEditor() {
|
||||
return this.editor === 'name';
|
||||
}
|
||||
|
||||
get showProfileEditor() {
|
||||
return this.editor === 'profile';
|
||||
}
|
||||
|
||||
toggleEditor(type: EditorType) {
|
||||
this.editor = type;
|
||||
}
|
||||
}
|
||||
export class AppComponent { }
|
||||
|
@ -1,34 +1,45 @@
|
||||
// #docplaster
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
// #docregion imports
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
// #docregion
|
||||
// #docregion v1
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
|
||||
|
||||
// #enddocregion imports
|
||||
import { AppComponent } from './app.component';
|
||||
import { NameEditorComponent } from './name-editor/name-editor.component';
|
||||
import { ProfileEditorComponent } from './profile-editor/profile-editor.component';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
// #enddocregion v1
|
||||
// #docregion hero-service-list
|
||||
// add JavaScript imports
|
||||
import { HeroListComponent } from './hero-list/hero-list.component';
|
||||
import { HeroService } from './hero.service';
|
||||
// #docregion v1
|
||||
|
||||
// #docregion imports
|
||||
@NgModule({
|
||||
// #enddocregion imports
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NameEditorComponent,
|
||||
ProfileEditorComponent
|
||||
HeroDetailComponent,
|
||||
// #enddocregion v1
|
||||
HeroListComponent // <--declare HeroListComponent
|
||||
// #docregion v1
|
||||
],
|
||||
// #docregion imports
|
||||
// #enddocregion hero-service-list
|
||||
imports: [
|
||||
// #enddocregion imports
|
||||
BrowserModule,
|
||||
// #docregion imports
|
||||
// other imports ...
|
||||
ReactiveFormsModule
|
||||
ReactiveFormsModule // <-- #2 add to @NgModule imports
|
||||
],
|
||||
// #enddocregion imports
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
// #docregion imports
|
||||
// #enddocregion v1
|
||||
// export for the DemoModule
|
||||
// #docregion hero-service-list
|
||||
// ...
|
||||
exports: [
|
||||
AppComponent,
|
||||
HeroDetailComponent,
|
||||
HeroListComponent // <-- export HeroListComponent
|
||||
],
|
||||
providers: [ HeroService ], // <-- provide HeroService
|
||||
// #enddocregion hero-service-list
|
||||
// #docregion v1
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
// #enddocregion imports
|
||||
// #enddocregion v1
|
||||
|
40
aio/content/examples/reactive-forms/src/app/data-model.ts
Normal file
40
aio/content/examples/reactive-forms/src/app/data-model.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// #docregion
|
||||
// #docregion model-classes
|
||||
export class Hero {
|
||||
id = 0;
|
||||
name = '';
|
||||
addresses: Address[];
|
||||
}
|
||||
|
||||
export class Address {
|
||||
street = '';
|
||||
city = '';
|
||||
state = '';
|
||||
zip = '';
|
||||
}
|
||||
// #enddocregion model-classes
|
||||
|
||||
export const heroes: Hero[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Whirlwind',
|
||||
addresses: [
|
||||
{street: '123 Main', city: 'Anywhere', state: 'CA', zip: '94801'},
|
||||
{street: '456 Maple', city: 'Somewhere', state: 'VA', zip: '23226'},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Bombastic',
|
||||
addresses: [
|
||||
{street: '789 Elm', city: 'Smallville', state: 'OH', zip: '04501'},
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Magneta',
|
||||
addresses: [ ]
|
||||
},
|
||||
];
|
||||
|
||||
export const states = ['CA', 'MD', 'OH', 'VA'];
|
@ -0,0 +1,40 @@
|
||||
<div class="container">
|
||||
<h1>Reactive Forms</h1>
|
||||
<h4><i>Pick a demo:</i>
|
||||
<select [selectedIndex]="demo - 1" (change)="selectDemo($event.target.selectedIndex)">
|
||||
<option *ngFor="let demo of demos">{{demo}}</option>
|
||||
</select>
|
||||
</h4>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="demo">
|
||||
<app-hero-list *ngIf="demo===final"></app-hero-list>
|
||||
<app-hero-detail-1 *ngIf="demo===1"></app-hero-detail-1>
|
||||
<app-hero-detail-2 *ngIf="demo===2"></app-hero-detail-2>
|
||||
<app-hero-detail-3 *ngIf="demo===3"></app-hero-detail-3>
|
||||
<app-hero-detail-4 *ngIf="demo===4"></app-hero-detail-4>
|
||||
<app-hero-detail-5 *ngIf="demo===5"></app-hero-detail-5>
|
||||
|
||||
<div *ngIf="demo >= 6 && demo !== final" >
|
||||
|
||||
<h3 *ngIf="isLoading"><i>Loading heroes ... </i></h3>
|
||||
<h3 *ngIf="!isLoading">Select a hero:</h3>
|
||||
|
||||
<nav>
|
||||
<button (click)="getHeroes()" class="btn btn-primary">Refresh</button>
|
||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
||||
</nav>
|
||||
|
||||
<div *ngIf="selectedHero">
|
||||
<hr>
|
||||
<h2>Hero Detail</h2>
|
||||
<h3>Editing: {{selectedHero.name}}</h3>
|
||||
<app-hero-detail-6 [hero]=selectedHero *ngIf="demo===6"></app-hero-detail-6>
|
||||
<app-hero-detail-7 [hero]=selectedHero *ngIf="demo===7"></app-hero-detail-7>
|
||||
<app-hero-detail-8 [hero]=selectedHero *ngIf="demo===8"></app-hero-detail-8>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,49 @@
|
||||
/* tslint:disable:member-ordering */
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { Hero } from './data-model';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './demo.component.html'
|
||||
})
|
||||
export class DemoComponent {
|
||||
|
||||
demos: string[] = [
|
||||
'Just a FormControl',
|
||||
'FormControl in a FormGroup',
|
||||
'Simple FormBuilder group',
|
||||
'Group with multiple controls',
|
||||
'Nested FormBuilder group',
|
||||
'PatchValue',
|
||||
'SetValue',
|
||||
'FormArray',
|
||||
'Final'].map(n => n + ' Demo');
|
||||
|
||||
final = this.demos.length;
|
||||
demo = this.final; // current demo
|
||||
|
||||
heroes: Observable<Hero[]>;
|
||||
isLoading = false;
|
||||
selectedHero: Hero;
|
||||
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
getHeroes() {
|
||||
this.isLoading = true;
|
||||
this.heroes = this.heroService.getHeroes().pipe(
|
||||
finalize(() => this.isLoading = false)
|
||||
);
|
||||
this.selectedHero = undefined;
|
||||
}
|
||||
|
||||
select(hero: Hero) { this.selectedHero = hero; }
|
||||
|
||||
selectDemo(demo: number) {
|
||||
this.demo = demo + 1;
|
||||
this.getHeroes();
|
||||
}
|
||||
}
|
33
aio/content/examples/reactive-forms/src/app/demo.module.ts
Normal file
33
aio/content/examples/reactive-forms/src/app/demo.module.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { DemoComponent } from './demo.component';
|
||||
import { HeroDetailComponent1 } from './hero-detail/hero-detail-1.component';
|
||||
import { HeroDetailComponent2 } from './hero-detail/hero-detail-2.component';
|
||||
import { HeroDetailComponent3 } from './hero-detail/hero-detail-3.component';
|
||||
import { HeroDetailComponent4 } from './hero-detail/hero-detail-4.component';
|
||||
import { HeroDetailComponent5 } from './hero-detail/hero-detail-5.component';
|
||||
import { HeroDetailComponent6 } from './hero-detail/hero-detail-6.component';
|
||||
import { HeroDetailComponent7 } from './hero-detail/hero-detail-7.component';
|
||||
import { HeroDetailComponent8 } from './hero-detail/hero-detail-8.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
ReactiveFormsModule,
|
||||
AppModule,
|
||||
],
|
||||
declarations: [ DemoComponent,
|
||||
HeroDetailComponent1,
|
||||
HeroDetailComponent2,
|
||||
HeroDetailComponent3,
|
||||
HeroDetailComponent4,
|
||||
HeroDetailComponent5,
|
||||
HeroDetailComponent6,
|
||||
HeroDetailComponent7,
|
||||
HeroDetailComponent8],
|
||||
bootstrap: [ DemoComponent ]
|
||||
})
|
||||
export class DemoModule { }
|
@ -0,0 +1,8 @@
|
||||
<!-- #docregion simple-control-->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>Just a FormControl</i></h3>
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" [formControl]="name">
|
||||
</label>
|
||||
<!-- #enddocregion simple-control-->
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
// #docregion import
|
||||
import { FormControl } from '@angular/forms';
|
||||
// #enddocregion import
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-1',
|
||||
templateUrl: './hero-detail-1.component.html'
|
||||
})
|
||||
// #docregion v1
|
||||
export class HeroDetailComponent1 {
|
||||
name = new FormControl();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<!-- #docregion basic-form-->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>FormControl in a FormGroup</i></h3>
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<!-- #enddocregion basic-form-->
|
||||
|
||||
<!-- #docregion form-value-json -->
|
||||
<p>Form value: {{ heroForm.value | json }}</p>
|
||||
<!-- #enddocregion form-value-json -->
|
||||
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion imports
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-2',
|
||||
templateUrl: './hero-detail-2.component.html'
|
||||
})
|
||||
// #docregion v2
|
||||
export class HeroDetailComponent2 {
|
||||
heroForm = new FormGroup ({
|
||||
name: new FormControl()
|
||||
});
|
||||
}
|
||||
// #enddocregion v2
|
@ -0,0 +1,16 @@
|
||||
<!-- #docregion basic-form-->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>A FormGroup with a single FormControl using FormBuilder</i></h3>
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<!-- #enddocregion basic-form-->
|
||||
|
||||
<!-- #docregion form-value-json -->
|
||||
<p>Form value: {{ heroForm.value | json }}</p>
|
||||
<p>Form status: {{ heroForm.status | json }}</p>
|
||||
<!-- #enddocregion form-value-json -->
|
@ -0,0 +1,27 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion imports
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-3',
|
||||
templateUrl: './hero-detail-3.component.html'
|
||||
})
|
||||
// #docregion v3
|
||||
export class HeroDetailComponent3 {
|
||||
heroForm: FormGroup; // <--- heroForm is of type FormGroup
|
||||
|
||||
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
// #docregion required
|
||||
this.heroForm = this.fb.group({
|
||||
name: ['', Validators.required ],
|
||||
});
|
||||
// #enddocregion required
|
||||
}
|
||||
}
|
||||
// #enddocregion v3
|
@ -0,0 +1,25 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion imports
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-3',
|
||||
templateUrl: './hero-detail-3.component.html'
|
||||
})
|
||||
// #docregion v3a
|
||||
export class HeroDetailComponent3 {
|
||||
heroForm: FormGroup; // <--- heroForm is of type FormGroup
|
||||
|
||||
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.heroForm = this.fb.group({
|
||||
name: '', // <--- the FormControl called "name"
|
||||
});
|
||||
}
|
||||
}
|
||||
// #enddocregion v3a
|
@ -0,0 +1,46 @@
|
||||
<!-- #docregion -->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>A FormGroup with multiple FormControls</i></h3>
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<p>Form value: {{ heroForm.value | json }}</p>
|
@ -0,0 +1,34 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion imports
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
import { states } from '../data-model';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-4',
|
||||
templateUrl: './hero-detail-4.component.html'
|
||||
})
|
||||
// #docregion v4
|
||||
export class HeroDetailComponent4 {
|
||||
heroForm: FormGroup;
|
||||
states = states;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.heroForm = this.fb.group({
|
||||
name: ['', Validators.required ],
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zip: '',
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
// #enddocregion v4
|
@ -0,0 +1,56 @@
|
||||
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
<!-- #docregion add-group-->
|
||||
<div formGroupName="address" class="well well-lg">
|
||||
<h4>Secret Lair</h4>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion add-group-->
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
||||
<h4>Extra info for the curious:</h4>
|
||||
<!-- #docregion inspect-value -->
|
||||
<p>Name value: {{ heroForm.get('name').value }}</p>
|
||||
<!-- #enddocregion inspect-value -->
|
||||
|
||||
<!-- #docregion inspect-child-control -->
|
||||
<p>Street value: {{ heroForm.get('address.street').value}}</p>
|
||||
<!-- #enddocregion inspect-child-control -->
|
@ -0,0 +1,35 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
import { Component } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
import { states } from '../data-model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-5',
|
||||
templateUrl: './hero-detail-5.component.html'
|
||||
})
|
||||
// #docregion v5
|
||||
export class HeroDetailComponent5 {
|
||||
heroForm: FormGroup;
|
||||
states = states;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.heroForm = this.fb.group({ // <-- the parent FormGroup
|
||||
name: ['', Validators.required ],
|
||||
address: this.fb.group({ // <-- the child FormGroup
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zip: ''
|
||||
}),
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
// #enddocregion v5
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!-- #docregion -->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>PatchValue to initialize a value</i></h3>
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<p>Form value: {{ heroForm.value | json }}</p>
|
@ -0,0 +1,66 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion import-input
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
// #enddocregion import-input
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
// #docregion import-hero
|
||||
import { Hero, states } from '../data-model';
|
||||
// #enddocregion import-hero
|
||||
|
||||
////////// 6 ////////////////////
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-6',
|
||||
templateUrl: './hero-detail-5.component.html'
|
||||
})
|
||||
// #docregion v6
|
||||
export class HeroDetailComponent6 implements OnChanges {
|
||||
// #docregion hero
|
||||
@Input() hero: Hero;
|
||||
// #enddocregion hero
|
||||
|
||||
heroForm: FormGroup;
|
||||
states = states;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
// #docregion hero-form-model
|
||||
this.heroForm = this.fb.group({
|
||||
name: ['', Validators.required ],
|
||||
address: this.fb.group({
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zip: ''
|
||||
}),
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
// #enddocregion hero-form-model
|
||||
}
|
||||
|
||||
// #docregion patch-value-on-changes
|
||||
ngOnChanges() { // <-- call rebuildForm in ngOnChanges
|
||||
this.rebuildForm();
|
||||
}
|
||||
// #enddocregion patch-value-on-changes
|
||||
|
||||
// #docregion patch-value-rebuildform
|
||||
rebuildForm() { // <-- wrap patchValue in rebuildForm
|
||||
this.heroForm.reset();
|
||||
// #docregion patch-value
|
||||
this.heroForm.patchValue({
|
||||
name: this.hero.name
|
||||
});
|
||||
// #enddocregion patch-value
|
||||
}
|
||||
// #enddocregion patch-value-rebuildform
|
||||
}
|
||||
|
||||
|
||||
|
||||
// #enddocregion v6
|
@ -0,0 +1,46 @@
|
||||
<!-- #docregion -->
|
||||
<h2>Hero Detail</h2>
|
||||
<h3><i>A FormGroup with multiple FormControls</i></h3>
|
||||
<form [formGroup]="heroForm">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<p>Form value: {{ heroForm.value | json }}</p>
|
@ -0,0 +1,68 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docplaster
|
||||
// #docregion imports
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
// #docregion import-address
|
||||
import { Address, Hero, states } from '../data-model';
|
||||
// #enddocregion import-address
|
||||
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-7',
|
||||
templateUrl: './hero-detail-5.component.html'
|
||||
})
|
||||
// #docregion v7
|
||||
export class HeroDetailComponent7 implements OnChanges {
|
||||
@Input() hero: Hero;
|
||||
|
||||
heroForm: FormGroup;
|
||||
states = states;
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
// #docregion address-form-group
|
||||
this.heroForm = this.fb.group({
|
||||
name: ['', Validators.required ],
|
||||
address: this.fb.group(new Address()), // <-- a FormGroup with a new address
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
// #enddocregion address-form-group
|
||||
}
|
||||
|
||||
// #docregion ngOnChanges
|
||||
ngOnChanges() {
|
||||
this.rebuildForm();
|
||||
}
|
||||
// #enddocregion ngOnChanges
|
||||
|
||||
// #docregion rebuildForm
|
||||
rebuildForm() {
|
||||
this.heroForm.reset({
|
||||
name: this.hero.name,
|
||||
// #docregion set-value-address
|
||||
address: this.hero.addresses[0] || new Address()
|
||||
// #enddocregion set-value-address
|
||||
});
|
||||
}
|
||||
// #enddocregion rebuildForm
|
||||
|
||||
/* First version of rebuildForm */
|
||||
rebuildForm1() {
|
||||
// #docregion reset
|
||||
this.heroForm.reset();
|
||||
// #enddocregion reset
|
||||
// #docregion set-value
|
||||
this.heroForm.setValue({
|
||||
name: this.hero.name,
|
||||
address: this.hero.addresses[0] || new Address()
|
||||
});
|
||||
// #enddocregion set-value
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<!-- #docplaster-->
|
||||
<h3><i>Using FormArray to add groups</i></h3>
|
||||
|
||||
<form [formGroup]="heroForm">
|
||||
<p>Form Changed: {{ heroForm.dirty }}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
<!-- #docregion form-array-->
|
||||
<!-- #docregion form-array-skeleton -->
|
||||
<!-- #docregion form-array-name -->
|
||||
<div formArrayName="secretLairs" class="well well-lg">
|
||||
<!-- #enddocregion form-array-name -->
|
||||
<div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >
|
||||
<!-- The repeated address template -->
|
||||
<!-- #enddocregion form-array-skeleton -->
|
||||
<h4>Address #{{i + 1}}</h4>
|
||||
<div style="margin-left: 1em;">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<!-- End of the repeated address template -->
|
||||
<!-- #docregion form-array-skeleton -->
|
||||
</div>
|
||||
<!-- #enddocregion form-array-skeleton -->
|
||||
<!-- #enddocregion form-array-->
|
||||
<!-- #docregion add-lair -->
|
||||
<button (click)="addLair()" type="button">Add a Secret Lair</button>
|
||||
<!-- #enddocregion add-lair -->
|
||||
<!-- #docregion form-array-->
|
||||
<!-- #docregion form-array-skeleton -->
|
||||
</div>
|
||||
<!-- #enddocregion form-array-skeleton -->
|
||||
<!-- #enddocregion form-array-->
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
@ -0,0 +1,74 @@
|
||||
/* tslint:disable:component-class-suffix */
|
||||
// #docregion imports
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
import { Address, Hero, states } from '../data-model';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail-8',
|
||||
templateUrl: './hero-detail-8.component.html'
|
||||
})
|
||||
// #docregion v8
|
||||
export class HeroDetailComponent8 implements OnChanges {
|
||||
@Input() hero: Hero;
|
||||
|
||||
heroForm: FormGroup;
|
||||
states = states;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.createForm();
|
||||
this.logNameChange();
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
createForm() {
|
||||
// #docregion secretLairs-form-array
|
||||
this.heroForm = this.fb.group({
|
||||
name: ['', Validators.required ],
|
||||
secretLairs: this.fb.array([]), // <-- secretLairs as an empty FormArray
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
// #enddocregion secretLairs-form-array
|
||||
}
|
||||
|
||||
logNameChange() {/* Coming soon */}
|
||||
|
||||
// #docregion onchanges
|
||||
ngOnChanges() {
|
||||
this.rebuildForm();
|
||||
}
|
||||
// #enddocregion onchanges
|
||||
|
||||
// #docregion rebuildform
|
||||
rebuildForm() {
|
||||
this.heroForm.reset({
|
||||
name: this.hero.name
|
||||
});
|
||||
this.setAddresses(this.hero.addresses);
|
||||
}
|
||||
// #enddocregion rebuildform
|
||||
|
||||
// #docregion get-secret-lairs
|
||||
get secretLairs(): FormArray {
|
||||
return this.heroForm.get('secretLairs') as FormArray;
|
||||
};
|
||||
// #enddocregion get-secret-lairs
|
||||
|
||||
// #docregion set-addresses
|
||||
setAddresses(addresses: Address[]) {
|
||||
const addressFGs = addresses.map(address => this.fb.group(address));
|
||||
const addressFormArray = this.fb.array(addressFGs);
|
||||
this.heroForm.setControl('secretLairs', addressFormArray);
|
||||
}
|
||||
// #enddocregion set-addresses
|
||||
|
||||
// #docregion add-lair
|
||||
addLair() {
|
||||
this.secretLairs.push(this.fb.group(new Address()));
|
||||
}
|
||||
// #enddocregion add-lair
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion -->
|
||||
<!-- #docregion buttons -->
|
||||
<form [formGroup]="heroForm" (ngSubmit)="onSubmit()">
|
||||
<div style="margin-bottom: 1em">
|
||||
<button type="submit"
|
||||
[disabled]="heroForm.pristine" class="btn btn-success">Save</button>
|
||||
<button type="button" (click)="revert()"
|
||||
[disabled]="heroForm.pristine" class="btn btn-danger">Revert</button>
|
||||
</div>
|
||||
|
||||
<!-- Hero Detail Controls -->
|
||||
<!-- #enddocregion buttons -->
|
||||
<div class="form-group">
|
||||
<label class="center-block">Name:
|
||||
<input class="form-control" formControlName="name">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div formArrayName="secretLairs" class="well well-lg">
|
||||
<div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >
|
||||
<!-- The repeated address template -->
|
||||
<h4>Address #{{i + 1}}</h4>
|
||||
<div style="margin-left: 1em;">
|
||||
<div class="form-group">
|
||||
<label class="center-block">Street:
|
||||
<input class="form-control" formControlName="street">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">City:
|
||||
<input class="form-control" formControlName="city">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">State:
|
||||
<select class="form-control" formControlName="state">
|
||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="center-block">Zip Code:
|
||||
<input class="form-control" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<!-- End of the repeated address template -->
|
||||
</div>
|
||||
<button (click)="addLair()" type="button">Add a Secret Lair</button>
|
||||
</div>
|
||||
<!-- #docregion buttons -->
|
||||
<div class="form-group radio">
|
||||
<h4>Super power:</h4>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="center-block">
|
||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
<!-- #enddocregion buttons -->
|
||||
|
||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
||||
|
||||
<!-- #docregion name-change-log -->
|
||||
<h4>Name change log</h4>
|
||||
<div *ngFor="let name of nameChangeLog">{{name}}</div>
|
||||
<!-- #enddocregion name-change-log -->
|
@ -0,0 +1,113 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
|
||||
|
||||
import { Address, Hero, states } from '../data-model';
|
||||
// #docregion import-service
|
||||
import { HeroService } from '../hero.service';
|
||||
// #enddocregion import-service
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail',
|
||||
templateUrl: './hero-detail.component.html',
|
||||
styleUrls: ['./hero-detail.component.css']
|
||||
})
|
||||
|
||||
// #docregion onchanges-implementation
|
||||
export class HeroDetailComponent implements OnChanges {
|
||||
// #enddocregion onchanges-implementation
|
||||
@Input() hero: Hero;
|
||||
|
||||
heroForm: FormGroup;
|
||||
// #docregion log-name-change
|
||||
nameChangeLog: string[] = [];
|
||||
// #enddocregion log-name-change
|
||||
states = states;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private heroService: HeroService) {
|
||||
|
||||
this.createForm();
|
||||
this.logNameChange();
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
createForm() {
|
||||
this.heroForm = this.fb.group({
|
||||
name: '',
|
||||
secretLairs: this.fb.array([]),
|
||||
power: '',
|
||||
sidekick: ''
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.rebuildForm();
|
||||
}
|
||||
|
||||
rebuildForm() {
|
||||
this.heroForm.reset({
|
||||
name: this.hero.name
|
||||
});
|
||||
this.setAddresses(this.hero.addresses);
|
||||
}
|
||||
|
||||
get secretLairs(): FormArray {
|
||||
return this.heroForm.get('secretLairs') as FormArray;
|
||||
};
|
||||
|
||||
setAddresses(addresses: Address[]) {
|
||||
const addressFGs = addresses.map(address => this.fb.group(address));
|
||||
const addressFormArray = this.fb.array(addressFGs);
|
||||
this.heroForm.setControl('secretLairs', addressFormArray);
|
||||
}
|
||||
|
||||
addLair() {
|
||||
this.secretLairs.push(this.fb.group(new Address()));
|
||||
}
|
||||
|
||||
// #docregion on-submit
|
||||
onSubmit() {
|
||||
this.hero = this.prepareSaveHero();
|
||||
this.heroService.updateHero(this.hero).subscribe(/* error handling */);
|
||||
this.rebuildForm();
|
||||
}
|
||||
// #enddocregion on-submit
|
||||
|
||||
// #docregion prepare-save-hero
|
||||
prepareSaveHero(): Hero {
|
||||
const formModel = this.heroForm.value;
|
||||
|
||||
// deep copy of form model lairs
|
||||
const secretLairsDeepCopy: Address[] = formModel.secretLairs.map(
|
||||
(address: Address) => Object.assign({}, address)
|
||||
);
|
||||
|
||||
// return new `Hero` object containing a combination of original hero value(s)
|
||||
// and deep copies of changed form model values
|
||||
const saveHero: Hero = {
|
||||
id: this.hero.id,
|
||||
name: formModel.name as string,
|
||||
// addresses: formModel.secretLairs // <-- bad!
|
||||
addresses: secretLairsDeepCopy
|
||||
};
|
||||
return saveHero;
|
||||
}
|
||||
// #enddocregion prepare-save-hero
|
||||
|
||||
// #docregion revert
|
||||
revert() { this.rebuildForm(); }
|
||||
// #enddocregion revert
|
||||
|
||||
// #docregion log-name-change
|
||||
logNameChange() {
|
||||
const nameControl = this.heroForm.get('name');
|
||||
nameControl.valueChanges.forEach(
|
||||
(value: string) => this.nameChangeLog.push(value)
|
||||
);
|
||||
}
|
||||
// #enddocregion log-name-change
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<!-- #docregion -->
|
||||
<nav>
|
||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
||||
</nav>
|
||||
|
||||
<div *ngIf="selectedHero">
|
||||
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
<!-- #docregion -->
|
||||
<h3 *ngIf="isLoading"><i>Loading heroes ... </i></h3>
|
||||
<h3 *ngIf="!isLoading">Select a hero:</h3>
|
||||
|
||||
<nav>
|
||||
<button (click)="getHeroes()" class="btn btn-primary">Refresh</button>
|
||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
||||
</nav>
|
||||
|
||||
<div *ngIf="selectedHero">
|
||||
<hr>
|
||||
<h2>Hero Detail</h2>
|
||||
<h3>Editing: {{selectedHero.name}}</h3>
|
||||
<!-- #docregion hero-binding -->
|
||||
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
|
||||
<!-- #enddocregion hero-binding -->
|
||||
</div>
|
@ -0,0 +1,32 @@
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
|
||||
import { Hero } from '../data-model';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-list',
|
||||
templateUrl: './hero-list.component.html',
|
||||
styleUrls: ['./hero-list.component.css']
|
||||
})
|
||||
export class HeroListComponent implements OnInit {
|
||||
heroes: Observable<Hero[]>;
|
||||
isLoading = false;
|
||||
selectedHero: Hero;
|
||||
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
ngOnInit() { this.getHeroes(); }
|
||||
|
||||
getHeroes() {
|
||||
this.isLoading = true;
|
||||
this.heroes = this.heroService.getHeroes()
|
||||
// TODO: error handling
|
||||
.pipe(finalize(() => this.isLoading = false));
|
||||
this.selectedHero = undefined;
|
||||
}
|
||||
|
||||
select(hero: Hero) { this.selectedHero = hero; }
|
||||
}
|
25
aio/content/examples/reactive-forms/src/app/hero.service.ts
Normal file
25
aio/content/examples/reactive-forms/src/app/hero.service.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
import { Hero, heroes } from './data-model';
|
||||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
|
||||
delayMs = 500;
|
||||
|
||||
// Fake server get; assume nothing can go wrong
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
return of(heroes).pipe(delay(this.delayMs)); // simulate latency with delay
|
||||
}
|
||||
|
||||
// Fake server update; assume nothing can go wrong
|
||||
updateHero(hero: Hero): Observable<Hero> {
|
||||
const oldHero = heroes.find(h => h.id === hero.id);
|
||||
const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero
|
||||
return of(newHero).pipe(delay(this.delayMs)); // simulate latency with delay
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
width: 6em;
|
||||
margin: .5em 0;
|
||||
color: #607D8B;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2em;
|
||||
font-size: 1em;
|
||||
padding-left: .4em;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<!-- #docregion control-binding -->
|
||||
<label>
|
||||
Name:
|
||||
<input type="text" [formControl]="name">
|
||||
</label>
|
||||
|
||||
<!-- #enddocregion control-binding -->
|
||||
|
||||
<!-- #docregion display-value -->
|
||||
|
||||
<p>
|
||||
Value: {{ name.value }}
|
||||
</p>
|
||||
<!-- #enddocregion display-value -->
|
||||
|
||||
<!-- #docregion update-value -->
|
||||
|
||||
<p>
|
||||
<button (click)="updateName()">Update Name</button>
|
||||
</p>
|
||||
<!-- #enddocregion update-value -->
|
@ -1,22 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion create-control
|
||||
import { Component } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-name-editor',
|
||||
templateUrl: './name-editor.component.html',
|
||||
styleUrls: ['./name-editor.component.css']
|
||||
})
|
||||
export class NameEditorComponent {
|
||||
name = new FormControl('');
|
||||
// #enddocregion create-control
|
||||
|
||||
// #docregion update-value
|
||||
updateName() {
|
||||
this.name.setValue('Nancy');
|
||||
}
|
||||
// #enddocregion update-value
|
||||
// #docregion create-control
|
||||
}
|
||||
// #enddocregion create-control
|
@ -1,67 +0,0 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion formgroup -->
|
||||
<form [formGroup]="profileForm">
|
||||
|
||||
<label>
|
||||
First Name:
|
||||
<input type="text" formControlName="firstName">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Last Name:
|
||||
<input type="text" formControlName="lastName">
|
||||
</label>
|
||||
|
||||
<!-- #enddocregion formgroup -->
|
||||
<!-- #docregion formgroupname -->
|
||||
<div formGroupName="address">
|
||||
<h3>Address</h3>
|
||||
|
||||
<label>
|
||||
Street:
|
||||
<input type="text" formControlName="street">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
City:
|
||||
<input type="text" formControlName="city">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
State:
|
||||
<input type="text" formControlName="state">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Zip Code:
|
||||
<input type="text" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
<!-- #enddocregion formgroupname -->
|
||||
|
||||
<!-- #docregion formarrayname -->
|
||||
<div formArrayName="aliases">
|
||||
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
|
||||
|
||||
<div *ngFor="let address of aliases.controls; let i=index">
|
||||
<!-- The repeated alias template -->
|
||||
<label>
|
||||
Alias:
|
||||
<input type="text" [formControlName]="i">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion formarrayname -->
|
||||
<!-- #docregion formgroup -->
|
||||
</form>
|
||||
<!-- #enddocregion formgroup -->
|
||||
|
||||
<p>
|
||||
Form Value: {{ profileForm.value | json }}
|
||||
</p>
|
||||
|
||||
<!-- #docregion patch-value -->
|
||||
<p>
|
||||
<button (click)="updateProfile()">Update Profile</button>
|
||||
</p>
|
||||
<!-- #enddocregion patch-value -->
|
@ -1,40 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion formgroup, nested-formgroup
|
||||
import { Component } from '@angular/core';
|
||||
// #docregion imports
|
||||
import { FormGroup, FormControl } from '@angular/forms';
|
||||
// #enddocregion imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile-editor',
|
||||
templateUrl: './profile-editor.component.html',
|
||||
styleUrls: ['./profile-editor.component.css']
|
||||
})
|
||||
export class ProfileEditorComponent {
|
||||
// #docregion formgroup-compare
|
||||
profileForm = new FormGroup({
|
||||
firstName: new FormControl(''),
|
||||
lastName: new FormControl(''),
|
||||
// #enddocregion formgroup
|
||||
address: new FormGroup({
|
||||
street: new FormControl(''),
|
||||
city: new FormControl(''),
|
||||
state: new FormControl(''),
|
||||
zip: new FormControl('')
|
||||
})
|
||||
// #docregion formgroup
|
||||
});
|
||||
// #enddocregion formgroup, nested-formgroup, formgroup-compare
|
||||
// #docregion patch-value
|
||||
updateProfile() {
|
||||
this.profileForm.patchValue({
|
||||
firstName: 'Nancy',
|
||||
address: {
|
||||
street: '123 Drew Street'
|
||||
}
|
||||
});
|
||||
}
|
||||
// #enddocregion patch-value
|
||||
// #docregion formgroup, nested-formgroup
|
||||
}
|
||||
// #enddocregion formgroup
|
@ -1,58 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion form-builder
|
||||
import { Component } from '@angular/core';
|
||||
// #docregion form-builder-imports
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
// #enddocregion form-builder-imports, form-builder
|
||||
// #docregion form-array-imports
|
||||
import { FormArray } from '@angular/forms';
|
||||
// #docregion form-builder-imports, form-builder
|
||||
// #enddocregion form-builder-imports, form-array-imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile-editor',
|
||||
templateUrl: './profile-editor.component.html',
|
||||
styleUrls: ['./profile-editor.component.css']
|
||||
})
|
||||
export class ProfileEditorComponent {
|
||||
// #docregion formgroup-compare
|
||||
profileForm = this.fb.group({
|
||||
firstName: [''],
|
||||
lastName: [''],
|
||||
address: this.fb.group({
|
||||
street: [''],
|
||||
city: [''],
|
||||
state: [''],
|
||||
zip: ['']
|
||||
}),
|
||||
// #enddocregion form-builder, formgroup-compare
|
||||
aliases: this.fb.array([
|
||||
this.fb.control('')
|
||||
])
|
||||
// #docregion form-builder, formgroup-compare
|
||||
});
|
||||
// #enddocregion form-builder, formgroup-compare
|
||||
get aliases() {
|
||||
return this.profileForm.get('aliases') as FormArray;
|
||||
}
|
||||
|
||||
// #docregion inject-form-builder, form-builder
|
||||
|
||||
constructor(private fb: FormBuilder) { }
|
||||
// #enddocregion inject-form-builder, form-builder
|
||||
|
||||
updateProfile() {
|
||||
this.profileForm.patchValue({
|
||||
firstName: 'Nancy',
|
||||
address: {
|
||||
street: '123 Drew Street'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addAlias() {
|
||||
this.aliases.push(this.fb.control(''));
|
||||
}
|
||||
// #docregion form-builder
|
||||
}
|
||||
// #enddocregion form-builder
|
@ -1,39 +0,0 @@
|
||||
/* ProfileEditorComponent's private CSS styles */
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
width: 6em;
|
||||
margin: .5em 0;
|
||||
color: #607D8B;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 2em;
|
||||
font-size: 1em;
|
||||
padding-left: .4em;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: Arial;
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #eee;
|
||||
color: #ccc;
|
||||
cursor: auto;
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion ng-submit -->
|
||||
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
|
||||
<!-- #enddocregion ng-submit -->
|
||||
<label>
|
||||
First Name:
|
||||
<!-- #docregion required-attribute -->
|
||||
<input type="text" formControlName="firstName" required>
|
||||
<!-- #enddocregion required-attribute -->
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Last Name:
|
||||
<input type="text" formControlName="lastName">
|
||||
</label>
|
||||
|
||||
<div formGroupName="address">
|
||||
<h3>Address</h3>
|
||||
|
||||
<label>
|
||||
Street:
|
||||
<input type="text" formControlName="street">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
City:
|
||||
<input type="text" formControlName="city">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
State:
|
||||
<input type="text" formControlName="state">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Zip Code:
|
||||
<input type="text" formControlName="zip">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- #docregion formarrayname -->
|
||||
<div formArrayName="aliases">
|
||||
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
|
||||
|
||||
<div *ngFor="let address of aliases.controls; let i=index">
|
||||
<!-- The repeated alias template -->
|
||||
<label>
|
||||
Alias:
|
||||
<input type="text" [formControlName]="i">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion formarrayname -->
|
||||
|
||||
<!-- #docregion submit-button -->
|
||||
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
|
||||
<!-- #enddocregion submit-button -->
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- #docregion display-value -->
|
||||
|
||||
<p>
|
||||
Form Value: {{ profileForm.value | json }}
|
||||
</p>
|
||||
<!-- #enddocregion display-value -->
|
||||
|
||||
<!-- #docregion display-status -->
|
||||
|
||||
<p>
|
||||
Form Status: {{ profileForm.status }}
|
||||
</p>
|
||||
<!-- #enddocregion display-status -->
|
||||
|
||||
<!-- #docregion patch-value -->
|
||||
<p>
|
||||
<button (click)="updateProfile()">Update Profile</button>
|
||||
</p>
|
||||
<!-- #enddocregion patch-value -->
|
@ -1,73 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion form-builder
|
||||
import { Component } from '@angular/core';
|
||||
// #docregion form-builder-imports
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
// #enddocregion form-builder-imports
|
||||
// #docregion validator-imports
|
||||
import { Validators } from '@angular/forms';
|
||||
// #enddocregion validator-imports
|
||||
// #docregion form-array-imports
|
||||
import { FormArray } from '@angular/forms';
|
||||
// #enddocregion form-array-imports
|
||||
|
||||
@Component({
|
||||
selector: 'app-profile-editor',
|
||||
templateUrl: './profile-editor.component.html',
|
||||
styleUrls: ['./profile-editor.component.css']
|
||||
})
|
||||
export class ProfileEditorComponent {
|
||||
// #docregion required-validator, aliases
|
||||
profileForm = this.fb.group({
|
||||
firstName: ['', Validators.required],
|
||||
lastName: [''],
|
||||
address: this.fb.group({
|
||||
street: [''],
|
||||
city: [''],
|
||||
state: [''],
|
||||
zip: ['']
|
||||
}),
|
||||
// #enddocregion form-builder, required-validator
|
||||
aliases: this.fb.array([
|
||||
this.fb.control('')
|
||||
])
|
||||
// #docregion form-builder, required-validator
|
||||
});
|
||||
// #enddocregion form-builder, required-validator, aliases
|
||||
// #docregion aliases-getter
|
||||
|
||||
get aliases() {
|
||||
return this.profileForm.get('aliases') as FormArray;
|
||||
}
|
||||
|
||||
// #enddocregion aliases-getter
|
||||
// #docregion inject-form-builder, form-builder
|
||||
constructor(private fb: FormBuilder) { }
|
||||
|
||||
// #enddocregion inject-form-builder
|
||||
|
||||
updateProfile() {
|
||||
this.profileForm.patchValue({
|
||||
firstName: 'Nancy',
|
||||
address: {
|
||||
street: '123 Drew Street'
|
||||
}
|
||||
});
|
||||
}
|
||||
// #enddocregion form-builder
|
||||
// #docregion add-alias
|
||||
|
||||
addAlias() {
|
||||
this.aliases.push(this.fb.control(''));
|
||||
}
|
||||
// #enddocregion add-alias
|
||||
// #docregion on-submit
|
||||
|
||||
onSubmit() {
|
||||
// TODO: Use EventEmitter with form value
|
||||
console.warn(this.profileForm.value);
|
||||
}
|
||||
// #enddocregion on-submit
|
||||
// #docregion form-builder
|
||||
}
|
||||
// #enddocregion form-builder
|
17
aio/content/examples/reactive-forms/src/index-final.html
Normal file
17
aio/content/examples/reactive-forms/src/index-final.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- #docregion -->
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Hero Form</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- #docregion bootstrap -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -2,9 +2,10 @@
|
||||
<!-- #docregion -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Angular Reactive Forms</title>
|
||||
<title>Hero Form</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,3 +1,4 @@
|
||||
// tslint:disable:no-unused-variable
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
@ -9,3 +10,4 @@ if (environment.production) {
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
@ -2,11 +2,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { AppModule } from './app/app.module'; // just the final version
|
||||
import { DemoModule } from './app/demo.module'; // demo picker
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(DemoModule);
|
||||
|
1
aio/content/examples/reactive-forms/src/styles.1.css
Normal file
1
aio/content/examples/reactive-forms/src/styles.1.css
Normal file
@ -0,0 +1 @@
|
||||
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
|
@ -3,7 +3,6 @@
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0-9].*",
|
||||
|
||||
"!src/app/app.component.1.ts",
|
||||
"!src/app/hero-list.component.1.html",
|
||||
|
@ -8,7 +8,7 @@ const nums = of(1, 2, 3, 4, 5);
|
||||
|
||||
// Create a function that accepts an Observable.
|
||||
const squareOddVals = pipe(
|
||||
filter((n: number) => n % 2 !== 0),
|
||||
filter(n => n % 2),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
|
@ -9,6 +9,6 @@ import { HeroService } from './heroes';
|
||||
<toh-heroes></toh-heroes>
|
||||
`,
|
||||
styleUrls: ['./app.component.css'],
|
||||
providers: [HeroService]
|
||||
providers: [ HeroService ]
|
||||
})
|
||||
export class AppComponent {}
|
||||
export class AppComponent { }
|
||||
|
@ -1,8 +1,9 @@
|
||||
// #docregion
|
||||
/* avoid */
|
||||
import { Component, NgModule, OnInit } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, Component, OnInit } from '@angular/core';
|
||||
|
||||
class Hero {
|
||||
id: number;
|
||||
@ -23,24 +24,24 @@ class AppComponent implements OnInit {
|
||||
heroes: Hero[] = [];
|
||||
|
||||
ngOnInit() {
|
||||
getHeroes().then(heroes => (this.heroes = heroes));
|
||||
getHeroes().then(heroes => this.heroes = heroes);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
declarations: [AppComponent],
|
||||
exports: [AppComponent],
|
||||
bootstrap: [AppComponent]
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
exports: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
||||
const HEROES: Hero[] = [
|
||||
{ id: 1, name: 'Bombasto' },
|
||||
{ id: 2, name: 'Tornado' },
|
||||
{ id: 3, name: 'Magneta' }
|
||||
{id: 1, name: 'Bombasto'},
|
||||
{id: 2, name: 'Tornado'},
|
||||
{id: 3, name: 'Magneta'},
|
||||
];
|
||||
|
||||
function getHeroes(): Promise<Hero[]> {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Hero } from './hero.model';
|
||||
|
||||
export const HEROES: Hero[] = [
|
||||
{ id: 1, name: 'Bombasto' },
|
||||
{ id: 2, name: 'Tornado' },
|
||||
{ id: 3, name: 'Magneta' }
|
||||
{id: 1, name: 'Bombasto'},
|
||||
{id: 2, name: 'Tornado'},
|
||||
{id: 3, name: 'Magneta'},
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
@ -3,8 +3,9 @@
|
||||
/* avoid */
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Http } from '@angular/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Hero } from './hero.model';
|
||||
// #enddocregion example
|
||||
|
||||
@ -15,15 +16,18 @@ export class HeroService {
|
||||
private exceptionService: ExceptionService,
|
||||
private spinnerService: SpinnerService,
|
||||
private toastService: ToastService,
|
||||
private http: HttpClient
|
||||
private http: Http
|
||||
) { }
|
||||
|
||||
getHero(id: number) {
|
||||
return this.http.get<Hero>(`api/heroes/${id}`);
|
||||
return this.http.get(`api/heroes/${id}`).pipe(
|
||||
map(response => response.json().data as Hero));
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get<Hero[]>(`api/heroes`);
|
||||
return this.http.get(`api/heroes`).pipe(
|
||||
map(response => response.json().data as Hero[]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
// #docregion
|
||||
// #docregion example
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
import { Hero } from './hero.model';
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
// #enddocregion example
|
||||
|
||||
@Injectable()
|
||||
@ -16,15 +16,18 @@ export class HeroService {
|
||||
private exceptionService: ExceptionService,
|
||||
private spinnerService: SpinnerService,
|
||||
private toastService: ToastService,
|
||||
private http: HttpClient
|
||||
private http: Http
|
||||
) { }
|
||||
|
||||
getHero(id: number) {
|
||||
return this.http.get<Hero>(`api/heroes/${id}`);
|
||||
return this.http.get(`api/heroes/${id}`).pipe(
|
||||
map(response => response.json() as Hero));
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get<Hero[]>(`api/heroes`);
|
||||
return this.http.get(`api/heroes`).pipe(
|
||||
map(response => response.json() as Hero[]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,7 @@ import { Hero, HeroService } from './shared';
|
||||
`,
|
||||
styles: [`
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
|
||||
}
|
||||
.heroes li {
|
||||
cursor: pointer;
|
||||
|
@ -1,9 +1,6 @@
|
||||
/* #docregion */
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
width: 15em;
|
||||
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
|
||||
}
|
||||
.heroes li {
|
||||
cursor: pointer;
|
||||
|
@ -1,27 +0,0 @@
|
||||
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
|
||||
import { CanvasComponent } from './canvas.component';
|
||||
describe('CanvasComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
CanvasComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
beforeEach(() => {
|
||||
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
|
||||
{
|
||||
source: 'HTMLCanvasElement.toBlob',
|
||||
callbackArgs: [{ size: 200 }]
|
||||
}
|
||||
];
|
||||
});
|
||||
it('should be able to generate blob data from canvas', fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(CanvasComponent);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.blobSize).toBeGreaterThan(0);
|
||||
}));
|
||||
});
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { Component, AfterViewInit, ViewChild } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'sample-canvas',
|
||||
template: '<canvas #sampleCanvas width="200" height="200"></canvas>'
|
||||
})
|
||||
export class CanvasComponent implements AfterViewInit {
|
||||
blobSize: number;
|
||||
@ViewChild('sampleCanvas') sampleCanvas;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngAfterViewInit() {
|
||||
const canvas = this.sampleCanvas.nativeElement;
|
||||
const context = canvas.getContext('2d');
|
||||
if (context) {
|
||||
context.clearRect(0, 0, 200, 200);
|
||||
context.fillStyle = '#FF1122';
|
||||
context.fillRect(0, 0, 200, 200);
|
||||
canvas.toBlob((blob: any) => {
|
||||
this.blobSize = blob.size;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
// #docregion added-imports
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
@ -17,7 +17,7 @@ import { HeroService } from '../hero.service';
|
||||
styleUrls: [ './hero-detail.component.css' ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
hero: Hero;
|
||||
@Input() hero: Hero;
|
||||
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
|
||||
styleUrls: [ './hero-detail.component.css' ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
hero: Hero;
|
||||
@Input() hero: Hero;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -22,7 +22,7 @@ code uses [AnimationBuilder](/api/animations/AnimationBuilder). If your code doe
|
||||
uncomment the `web-animations-js` polyfill from the `polyfills.ts` file generated by Angular CLI.
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The examples in this page are available as a <live-example></live-example>.
|
||||
|
||||
@ -183,7 +183,7 @@ transition definitions, and not in a separate `state(void)` definition. Thus, th
|
||||
are different on enter and leave: the element enters from the left
|
||||
and leaves to the right.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
These two common animations have their own aliases:
|
||||
|
||||
|
@ -4,7 +4,7 @@ The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScri
|
||||
|
||||
This guide explains how to build with the AOT compiler using different compiler options and how to write Angular metadata that AOT can compile.
|
||||
|
||||
<div class="alert is-helpful>
|
||||
<div class="l-sub-section">
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.
|
||||
|
||||
@ -39,7 +39,7 @@ For AOT compilation, append the `--aot` flags to the _build-only_ or the _build-
|
||||
ng serve --aot
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `--prod` meta-flag compiles with AOT by default.
|
||||
|
||||
@ -297,7 +297,7 @@ At the same time, the AOT **_collector_** analyzes the metadata recorded in the
|
||||
|
||||
You can think of `.metadata.json` as a diagram of the overall structure of a decorator's metadata, represented as an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/compiler-cli/src/metadata/schema.ts)
|
||||
describes the JSON format as a collection of TypeScript interfaces.
|
||||
@ -333,7 +333,7 @@ Parentheses | `(a + b)`
|
||||
If an expression uses unsupported syntax, the _collector_ writes an error node to the `.metadata.json` file. The compiler later reports the error if it needs that
|
||||
piece of metadata to generate the application code.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in `tsconfig`.
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
# Introduction to components
|
||||
|
||||
A *component* controls a patch of screen called a *view*.
|
||||
For example, individual components define and control each of the following views from the [Tutorial](tutorial):
|
||||
<img src="generated/images/guide/architecture/hero-component.png" alt="Component" class="left">
|
||||
|
||||
A _component_ controls a patch of screen called a *view*. For example, individual components define and control each of the following views from the [Tutorial](tutorial/index):
|
||||
|
||||
* The app root with the navigation links.
|
||||
* The list of heroes.
|
||||
@ -10,38 +11,38 @@ For example, individual components define and control each of the following view
|
||||
You define a component's application logic—what it does to support the view—inside a class.
|
||||
The class interacts with the view through an API of properties and methods.
|
||||
|
||||
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
|
||||
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
|
||||
The service is provided to the component through the dependency injection system.
|
||||
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property[(http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
|
||||
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
|
||||
|
||||
<hr/>
|
||||
|
||||
## Component metadata
|
||||
|
||||
<img src="generated/images/guide/architecture/metadata.png" alt="Metadata" class="left">
|
||||
|
||||
The `@Component` decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that `HeroListComponent` is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the `@Component` decorator.
|
||||
|
||||
The metadata for a component tells Angular where to get the major building blocks that it needs to create and present the component and its view. In particular, it associates a *template* with the component, either directly with inline code, or by reference. Together, the component and its template describe a *view*.
|
||||
The metadata for a component tells Angular where to get the major building blocks it needs to create and present the component and its view. In particular, it associates a _template_ with the component, either directly with inline code, or by reference. Together, the component and its template describe a _view_.
|
||||
|
||||
In addition to containing or pointing to the template, the `@Component` metadata configures, for example, how the component can be referenced in HTML and what services it requires.
|
||||
|
||||
Here's an example of basic metadata for `HeroListComponent`.
|
||||
Here's an example of basic metadata for `HeroListComponent`:
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
|
||||
|
||||
This example shows some of the most useful `@Component` configuration options:
|
||||
This example shows some of the most useful `@Component` configuration options:
|
||||
|
||||
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
|
||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's _host view_.
|
||||
|
||||
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
|
||||
<hr/>
|
||||
|
||||
## Templates and views
|
||||
|
||||
@ -49,7 +50,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
You define a component's view with its companion template. A template is a form of HTML that tells Angular how to render the component.
|
||||
|
||||
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's *host view*. The component can also define a *view hierarchy*, which contains *embedded views*, hosted by other components.
|
||||
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's _host view_. The component can also define a _view hierarchy_, which contains _embedded views_, hosted by other components.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
|
||||
@ -59,47 +60,43 @@ A view hierarchy can include views from components in the same NgModule, but it
|
||||
|
||||
## Template syntax
|
||||
|
||||
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use *data binding* to coordinate the app and DOM data, *pipes* to transform data before it is displayed, and *directives* to apply app logic to what gets displayed.
|
||||
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use _data binding_ to coordinate the app and DOM data, _pipes_ to transform data before it is displayed, and _directives_ to apply app logic to what gets displayed.
|
||||
|
||||
For example, here is a template for the Tutorial's `HeroListComponent`.
|
||||
For example, here is a template for the Tutorial's `HeroListComponent`:
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.html" title="src/app/hero-list.component.html"></code-example>
|
||||
|
||||
This template uses typical HTML elements like `<h2>` and `<p>`, and also includes Angular template-syntax elements, `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<app-hero-detail>`. The template-syntax elements tell Angular how to render the HTML to the screen, using program logic and data.
|
||||
|
||||
* The `*ngFor` directive tells Angular to iterate over a list.
|
||||
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
|
||||
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
|
||||
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
|
||||
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
|
||||
* The `*ngFor` directive tells Angular to iterate over a list.
|
||||
* The `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
|
||||
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`. The `HeroDetailComponent` (code not shown) is a child component of the `HeroListComponent` that defines the Hero-detail view. Notice how custom components like this mix seamlessly with native HTML in the same layouts.
|
||||
|
||||
### Data binding
|
||||
|
||||
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push and pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
|
||||
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
|
||||
|
||||
Angular supports *two-way data binding*, a mechanism for coordinating the parts of a template with the parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
|
||||
Angular supports *two-way data binding*, a mechanism for coordinating parts of a template with parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
|
||||
|
||||
The following diagram shows the four forms of data binding markup. Each form has a direction: to the DOM, from the DOM, or both.
|
||||
The following diagram shows the four forms of data binding markup. Each form has a direction—to the DOM, from the DOM, or in both directions.
|
||||
|
||||
<figure>
|
||||
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
|
||||
</figure>
|
||||
|
||||
This example from the `HeroListComponent` template uses three of these forms.
|
||||
This example from the `HeroListComponent` template uses three of these forms:
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding"></code-example>
|
||||
|
||||
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
|
||||
displays the component's `hero.name` property value within the `<li>` element.
|
||||
|
||||
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of
|
||||
`selectedHero` from the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
||||
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of `selectedHero` from
|
||||
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
||||
|
||||
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
|
||||
|
||||
Two-way data binding (used mainly in [template-driven forms](guide/forms))
|
||||
combines property and event binding in a single notation.
|
||||
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
|
||||
**Two-way data binding** is an important fourth form that combines property and event binding in a single notation. Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive:
|
||||
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
|
||||
@ -107,7 +104,7 @@ In two-way binding, a data property value flows to the input box from the compon
|
||||
The user's changes also flow back to the component, resetting the property to the latest value,
|
||||
as with event binding.
|
||||
|
||||
Angular processes *all* data bindings once for each JavaScript event cycle,
|
||||
Angular processes *all* data bindings once per JavaScript event cycle,
|
||||
from the root of the application component tree through all child components.
|
||||
|
||||
<figure>
|
||||
@ -122,17 +119,17 @@ Data binding plays an important role in communication between a template and its
|
||||
|
||||
### Pipes
|
||||
|
||||
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
|
||||
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
|
||||
|
||||
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
|
||||
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
|
||||
|
||||
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe).
|
||||
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe):
|
||||
|
||||
`{{interpolated_value | pipe_name}}`
|
||||
`{{interpolated_value | pipe_name}}`
|
||||
|
||||
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe.
|
||||
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe:
|
||||
|
||||
```
|
||||
```
|
||||
<!-- Default format: output 'Jun 15, 2015'-->
|
||||
<p>Today is {{today | date}}</p>
|
||||
|
||||
@ -143,38 +140,33 @@ You can chain pipes, sending the output of one pipe function to be transformed b
|
||||
<p>The time is {{today | date:'shortTime'}}</p>
|
||||
```
|
||||
|
||||
<hr/>
|
||||
|
||||
### Directives
|
||||
|
||||
<img src="generated/images/guide/architecture/directive.png" alt="Directives" class="left">
|
||||
|
||||
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive()` decorator.
|
||||
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive` decorator.
|
||||
|
||||
A component is technically a directive.
|
||||
However, components are so distinctive and central to Angular applications that Angular
|
||||
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
|
||||
template-oriented features.
|
||||
A component is technically a directive - but components are so distinctive and central to Angular applications that Angular defines the `@Component` decorator, which extends the `@Directive` decorator with template-oriented features.
|
||||
|
||||
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
|
||||
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
|
||||
|
||||
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
|
||||
There are two kinds of directives besides components: _structural_ and _attribute_ directives. Just as for components, the metadata for a directive associates the class with a `selector` that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
|
||||
|
||||
#### Structural directives
|
||||
|
||||
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
|
||||
The example template uses two built-in structural directives to add application logic to how the view is rendered.
|
||||
Structural directives alter layout by adding, removing, and replacing elements in DOM. The example template uses two built-in structural directives to add application logic to how the view is rendered:
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="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/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.
|
||||
|
||||
#### Attribute directives
|
||||
|
||||
*Attribute directives* alter the appearance or behavior of an existing element.
|
||||
Attribute directives alter the appearance or behavior of an existing element.
|
||||
In templates they look like regular HTML attributes, hence the name.
|
||||
|
||||
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
|
||||
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically an `<input>`) by setting its display value property and responding to change events.
|
||||
|
||||
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
|
||||
|
||||
@ -183,8 +175,6 @@ Angular has more pre-defined directives that either alter the layout structure
|
||||
or modify aspects of DOM elements and components
|
||||
(for example, [ngStyle](guide/template-syntax#ngStyle) and [ngClass](guide/template-syntax#ngClass)).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
You can also write your own directives. Components such as `HeroListComponent` are one kind of custom directive. You can also create custom structural and attribute directives.
|
||||
|
||||
Learn more in the [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives) guides.
|
||||
|
||||
</div>
|
||||
<!-- PENDING: link to where to learn more about other kinds! -->
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user