Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
926367150a | |||
14ff340e93 | |||
c8e750745a | |||
6b8f2da93e | |||
352219eafb | |||
e8a8770eee | |||
f536261639 | |||
2d06693560 | |||
315d95c747 | |||
5c5b62cc86 | |||
479f773bb3 | |||
629c83588d | |||
d96ba0ffd9 | |||
30700035b3 | |||
8c6d6b85db | |||
0c10b57749 | |||
fe1c0d7a0c | |||
dc053859ee | |||
4d532df52b | |||
5eb540cf34 | |||
7697650edc | |||
4517b8e618 | |||
3abc4c9c54 | |||
1d6333891c | |||
4d4e85b708 | |||
28104828d5 |
@ -33,24 +33,18 @@ var_6: &job_defaults
|
||||
docker:
|
||||
- image: *default_docker_image
|
||||
|
||||
var_7: &start-xvfb
|
||||
run:
|
||||
name: Running X virtual framebuffer
|
||||
command: Xvfb :99 -screen 0 1280x1024x24
|
||||
background: true
|
||||
|
||||
# After checkout, rebase on top of master.
|
||||
# Similar to travis behavior, but not quite the same.
|
||||
# See https://discuss.circleci.com/t/1662
|
||||
var_8: &post_checkout
|
||||
var_7: &post_checkout
|
||||
post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge"
|
||||
|
||||
var_9: &yarn_install
|
||||
var_8: &yarn_install
|
||||
run:
|
||||
name: Running Yarn install
|
||||
command: yarn install --frozen-lockfile --non-interactive
|
||||
|
||||
var_10: &setup_circleci_bazel_config
|
||||
var_9: &setup_circleci_bazel_config
|
||||
run:
|
||||
name: Setting up CircleCI bazel configuration
|
||||
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
@ -167,7 +161,6 @@ jobs:
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
# Build aio
|
||||
- run: yarn --cwd aio build --progress=false
|
||||
# Lint the code
|
||||
@ -196,7 +189,6 @@ jobs:
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar CI_STABLE_BRANCH "$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')"
|
||||
- run: yarn --cwd aio deploy-production
|
||||
@ -214,7 +206,6 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local --progress=false
|
||||
# Run PWA-score tests
|
||||
@ -255,7 +246,6 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
# Install root
|
||||
- *yarn_install
|
||||
# Install aio
|
||||
@ -276,7 +266,6 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
# Install root
|
||||
- *yarn_install
|
||||
# Install aio
|
||||
@ -315,7 +304,6 @@ jobs:
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
- run: yarn install --cwd aio --frozen-lockfile --non-interactive
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
@ -375,7 +363,6 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: dist
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
- run: ./integration/run_tests.sh
|
||||
|
||||
# This job updates the content of repos like github.com/angular/core-builds
|
||||
@ -419,7 +406,6 @@ jobs:
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
- *define_env_vars
|
||||
- *start-xvfb
|
||||
- run:
|
||||
name: Run tests against the deployed apps
|
||||
command: ./aio/scripts/test-production.sh $CI_AIO_MIN_PWA_SCORE
|
||||
|
61
.github/ISSUE_TEMPLATE.md
vendored
61
.github/ISSUE_TEMPLATE.md
vendored
@ -1,59 +1,10 @@
|
||||
<!--
|
||||
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
|
||||
-->
|
||||
Please help us process issues more efficiently by filing an
|
||||
issue using one of the following templates:
|
||||
|
||||
## I'm submitting a...
|
||||
<!-- Check one of the following options with "x" -->
|
||||
<pre><code>
|
||||
[ ] Regression (a behavior that used to work and stopped working in a new release)
|
||||
[ ] Bug report <!-- Please search GitHub for a similar issue or PR before submitting -->
|
||||
[ ] Performance issue
|
||||
[ ] Feature request
|
||||
[ ] Documentation issue or request
|
||||
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
[ ] Other... Please describe:
|
||||
</code></pre>
|
||||
https://github.com/angular/angular/issues/new/choose
|
||||
|
||||
## Current behavior
|
||||
<!-- Describe how the issue manifests. -->
|
||||
Thank you!
|
||||
|
||||
|
||||
## Expected behavior
|
||||
<!-- Describe what the desired behavior would be. -->
|
||||
|
||||
|
||||
## Minimal reproduction of the problem with instructions
|
||||
<!--
|
||||
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
https://stackblitz.com or similar (you can use this template as a starting point: https://stackblitz.com/fork/angular-gitter).
|
||||
-->
|
||||
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
<!-- Describe the motivation or the concrete use case. -->
|
||||
|
||||
|
||||
## Environment
|
||||
|
||||
<pre><code>
|
||||
Angular version: X.Y.Z
|
||||
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||
|
||||
Browser:
|
||||
- [ ] Chrome (desktop) version XX
|
||||
- [ ] Chrome (Android) version XX
|
||||
- [ ] Chrome (iOS) version XX
|
||||
- [ ] Firefox version XX
|
||||
- [ ] Safari (desktop) version XX
|
||||
- [ ] Safari (iOS) version XX
|
||||
- [ ] IE version XX
|
||||
- [ ] Edge version XX
|
||||
|
||||
For Tooling issues:
|
||||
- Node version: XX <!-- run `node --version` -->
|
||||
- Platform: <!-- Mac, Linux, Windows -->
|
||||
|
||||
Others:
|
||||
<!-- Anything else relevant? Operating system version, IDE, package manager, HTTP server, ... -->
|
||||
</code></pre>
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
63
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
Normal file
63
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
name: "\U0001F41EBug report"
|
||||
about: Report a bug in the Angular Framework
|
||||
---
|
||||
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
|
||||
|
||||
Oh hi there! 😄
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
|
||||
|
||||
|
||||
# 🐞 bug report
|
||||
|
||||
### Affected Package
|
||||
<!-- Can you pin-point one or more @angular/* packages as the source of the bug? -->
|
||||
<!-- ✍️edit: --> The issue is caused by package @angular/....
|
||||
|
||||
|
||||
### Is this a regression?
|
||||
|
||||
<!-- Did this behavior use to work in the previous version? -->
|
||||
<!-- ✍️--> Yes, the previous version in which this bug was not present was: ....
|
||||
|
||||
|
||||
### Description
|
||||
|
||||
<!-- ✍️--> A clear and concise description of the problem...
|
||||
|
||||
|
||||
## 🔬 Minimal Reproduction
|
||||
<!--
|
||||
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-issue-repro2
|
||||
-->
|
||||
<!-- ✍️--> https://stackblitz.com/...
|
||||
|
||||
<!--
|
||||
If StackBlitz is not suitable for reproduction of your issue, please create a minimal GitHub repository with the reproduction of the issue. Share the link to the repo below along with step-by-step instructions to reproduce the problem, as well as expected and actual behavior.
|
||||
-->
|
||||
|
||||
## 🔥 Exception or Error
|
||||
<pre><code>
|
||||
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
|
||||
<!-- ✍️-->
|
||||
|
||||
</code></pre>
|
||||
|
||||
|
||||
## 🌍 Your Environment
|
||||
|
||||
**Angular Version:**
|
||||
<pre><code>
|
||||
<!-- run `ng version` and paste output below -->
|
||||
<!-- ✍️-->
|
||||
|
||||
</code></pre>
|
||||
|
||||
**Anything else relevant?**
|
||||
<!-- ✍️Is this a browser specific issue? If so, please specify the browser and version. -->
|
||||
|
||||
<!-- ✍️Do any of these matter: operating system, IDE, package manager, HTTP server, ...? If so, please mention it below. -->
|
32
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
name: "\U0001F680Feature request"
|
||||
about: Suggest a feature for Angular Framework
|
||||
|
||||
---
|
||||
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
|
||||
|
||||
Oh hi there! 😄
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
|
||||
|
||||
|
||||
# 🚀 feature request
|
||||
|
||||
### Releavant Package
|
||||
<!-- Can you pin-point one or more @angular/* packages the are relevant for this feature request? -->
|
||||
<!-- ✍️edit: --> This feature request is for @angular/....
|
||||
|
||||
|
||||
### Description
|
||||
<!-- ✍️--> A clear and concise description of the problem or missing capability...
|
||||
|
||||
|
||||
### Describe the solution you'd like
|
||||
<!-- ✍️--> If you have a solution in mind, please describe it.
|
||||
|
||||
|
||||
### Describe alternatives you've considered
|
||||
<!-- ✍️--> Have you considered any alternative solutions or workarounds?
|
55
.github/ISSUE_TEMPLATE/3-docs-bug.md
vendored
Normal file
55
.github/ISSUE_TEMPLATE/3-docs-bug.md
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
name: "📚 Docs or angular.io issue report"
|
||||
about: Report an issue in Angular's documentation or angular.io application
|
||||
|
||||
---
|
||||
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
|
||||
|
||||
Oh hi there! 😄
|
||||
|
||||
To expedite issue processing please search open and closed issues before submitting a new one.
|
||||
Existing issues often contain information about workarounds, resolution, or progress updates.
|
||||
|
||||
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
|
||||
|
||||
# 📚 Docs or angular.io bug report
|
||||
|
||||
### Description
|
||||
|
||||
<!-- ✍️edit:--> A clear and concise description of the problem...
|
||||
|
||||
|
||||
## 🔬 Minimal Reproduction
|
||||
|
||||
### What's the affected URL?**
|
||||
<!-- ✍️edit:--> https://angular.io/...
|
||||
|
||||
### Reproduction Steps**
|
||||
<!-- If applicable please list the steps to take to reproduce the issue -->
|
||||
<!-- ✍️edit:-->
|
||||
|
||||
### Expected vs Actual Behavior**
|
||||
<!-- If applicable please describe the difference between the expected and actual behavior after following the repro steps. -->
|
||||
<!-- ✍️edit:-->
|
||||
|
||||
|
||||
## 📷Screenshot
|
||||
<!-- Often a screenshot can help to capture the issue better than a long description. -->
|
||||
<!-- ✍️upload a screenshot:-->
|
||||
|
||||
|
||||
## 🔥 Exception or Error
|
||||
<pre><code>
|
||||
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
|
||||
<!-- ✍️-->
|
||||
|
||||
</code></pre>
|
||||
|
||||
|
||||
## 🌍 Your Environment
|
||||
|
||||
### Browser info
|
||||
<!-- ✍️Is this a browser specific issue? If so, please specify the device, browser, and version. -->
|
||||
|
||||
### Anything else relevant?
|
||||
<!-- ✍️Please provide additional info if necessary. -->
|
11
.github/ISSUE_TEMPLATE/4-security-issue-disclosure.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/4-security-issue-disclosure.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
name: ⚠️ Security issue disclosure
|
||||
about: Report a security issue in Angular Framework, Material, or CLI
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please read https://angular.io/guide/security#report-issues on how to disclose security related issues.
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
16
.github/ISSUE_TEMPLATE/5-support-request.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/5-support-request.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
name: "❓Support request"
|
||||
about: Questions and requests for support
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please do not file questions or support requests on the GitHub issues tracker.
|
||||
|
||||
You can get your questions answered using other communication channels. Please see:
|
||||
https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
|
||||
Thank you!
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
13
.github/ISSUE_TEMPLATE/6-angular-cli.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/6-angular-cli.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
name: "\U0001F6E0️Angular CLI"
|
||||
about: Issues and feature requests for Angular CLI
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please file any Angular CLI issues at: https://github.com/angular/angular-cli/issues/new
|
||||
|
||||
For the time being, we keep Angular CLI issues in a separate repository.
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
name: "\U0001F48EAngular Material"
|
||||
about: Issues and feature requests for Angular Material
|
||||
|
||||
---
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
||||
|
||||
Please file any Angular Material issues at: https://github.com/angular/material2/issues/new
|
||||
|
||||
For the time being, we keep Angular Material issues in a separate repository.
|
||||
|
||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,3 +1,15 @@
|
||||
<a name="7.0.3"></a>
|
||||
## [7.0.3](https://github.com/angular/angular/compare/7.0.2...7.0.3) (2018-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bazel:** unknown replay compiler error in windows ([#26711](https://github.com/angular/angular/issues/26711)) ([4d532df](https://github.com/angular/angular/commit/4d532df))
|
||||
* **router:** remove type bludgeoning of context and outlet when running CanDeactivate ([#26496](https://github.com/angular/angular/issues/26496)) ([dc05385](https://github.com/angular/angular/commit/dc05385)), closes [#18253](https://github.com/angular/angular/issues/18253)
|
||||
* **upgrade:** make typings compatible with older AngularJS typings ([#26880](https://github.com/angular/angular/issues/26880)) ([315d95c](https://github.com/angular/angular/commit/315d95c)), closes [#26420](https://github.com/angular/angular/issues/26420)
|
||||
|
||||
|
||||
|
||||
<a name="7.0.2"></a>
|
||||
## [7.0.2](https://github.com/angular/angular/compare/7.0.1...7.0.2) (2018-10-31)
|
||||
|
||||
|
@ -33,7 +33,8 @@ cd my-first-project
|
||||
ng serve
|
||||
</code-example>
|
||||
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
In your browser, open http://localhost:4200/ to see the new app run.
|
||||
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
||||
|
||||
## Workspaces and project files
|
||||
|
||||
@ -51,12 +52,13 @@ Commands such as [add](cli/add) and [generate](cli/generate), which create or op
|
||||
|
||||
* See more about the [Workspace file structure](guide/file-structure).
|
||||
|
||||
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
||||
### Workspace and project configuration
|
||||
|
||||
A single workspace configuration file, `angular.json`, is created at the top level of the workspace.
|
||||
This is where you can set workspace-wide defaults, and specify configurations to use when the CLI builds a project for different targets.
|
||||
This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets.
|
||||
|
||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands can use either camelCase or dash-case.
|
||||
|
||||
* See the [complete schema](https://github.com/angular/angular-cli/wiki/angular-workspace) for `angular.json`.
|
||||
<!-- * Learn more about *configuration options for Angular(links to new guide or topics TBD)*. -->
|
||||
|
@ -107,7 +107,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Firefox</td>
|
||||
<td> Set the <code>dom.webcomponents.enabled</code> and <code>dom.webcomponents.customelements.enabled</code> preferences to true. Planned to be enabled by default in version 63.</td>
|
||||
<td>Supported natively as of version 63. In older versions: Set the <code>dom.webcomponents.enabled</code> and <code>dom.webcomponents.customelements.enabled</code> preferences to true.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edge</td>
|
||||
|
@ -20,16 +20,15 @@ The top level of the workspace contains a number of workspace-wide configuration
|
||||
| :--------------------- | :------------------------------------------|
|
||||
| `.editorconfig` | Configuration for code editors. See [EditorConfig](https://editorconfig.org/). |
|
||||
| `.gitignore` | Specifies intentionally untracked files that [Git](https://git-scm.com/) should ignore. |
|
||||
| `angular.json` | CLI configuration for all projects in the workspace, including configuration options for build, serve, and test tools that the CLI uses, such as [Karma](https://karma-runner.github.io/) and [Protractor](http://www.protractortest.org/). |
|
||||
| `angular.json` | CLI configuration defaults for all projects in the workspace, including configuration options for build, serve, and test tools that the CLI uses, such as [TSLint](https://palantir.github.io/tslint/), [Karma](https://karma-runner.github.io/), and [Protractor](http://www.protractortest.org/). For details, see [Angular Workspace Configuration](guide/workspace-config). |
|
||||
| `node_modules` | Provides [npm packages](guide/npm-packages) to the entire workspace. |
|
||||
| `package.json` | Lists package dependencies. See [npm documentation](https://docs.npmjs.com/files/package.json) for the specific format and contents of this file.|
|
||||
| `tsconfig.app.json` | Default [TypeScript](https://www.typescriptlang.org/) configuration for apps in the workspace. |
|
||||
| `tsconfig.spec.json` | Default TypeScript configuration for e2e test apps in the workspace. |
|
||||
| `tslint.json` | Default [TSLint](https://palantir.github.io/tslint/) configuration for apps in the workspace. |
|
||||
| `README.md` | Introductory documentation. |
|
||||
| `package.json` | Configures [npm package dependencies](guide/npm-packages) that are available to all projects in the workspace. See [npm documentation](https://docs.npmjs.com/files/package.json) for the specific format and contents of this file. |
|
||||
| `package-lock.json` | Provides version information for all packages installed into `node_modules` by the npm client. See [npm documentation](https://docs.npmjs.com/files/package-lock.json) for details. If you use the yarn client, this file will be [yarn.lock](https://yarnpkg.com/lang/en/docs/yarn-lock/) instead. |
|
||||
| `tsconfig.json` | Default [TypeScript](https://www.typescriptlang.org/) configuration for apps in the workspace, including TypeScript and Angular template compiler options. See [TypeScript Configuration](guide/typescript-configuration). |
|
||||
| `tslint.json` | Default [TSLint](https://palantir.github.io/tslint/) configuration for apps in the workspace. |
|
||||
| `README.md` | Introductory documentation. |
|
||||
|
||||
All projects within a workspace share this configuration context.
|
||||
All projects within a workspace share a [CLI configuration context](guide/workspace-config).
|
||||
Project-specific [TypeScript](https://www.typescriptlang.org/) configuration files inherit from the workspace-wide `tsconfig.*.json`, and app-specific [TSLint](https://palantir.github.io/tslint/) configuration files inherit from the workspace-wide `tslint.json`.
|
||||
|
||||
### Default app project files
|
||||
@ -40,9 +39,9 @@ This initial app is the *default app* for CLI commands (unless you change the de
|
||||
A newly generated app contains the source files for a root module, with a root component and template.
|
||||
When the workspace file structure is in place, you can use the `ng generate` command on the command line to add functionality and data to the initial app.
|
||||
|
||||
<div class="alert is-helpful>
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Besides using the CLI on the command line, You can also use an interactive development environment like [Angular Console](https://angular.console.com), or manipulate files directly in the app's source folder and configuration files.
|
||||
Besides using the CLI on the command line, you can also use an interactive development environment like [Angular Console](https://angular.console.com), or manipulate files directly in the app's source folder and configuration files.
|
||||
|
||||
</div>
|
||||
|
||||
@ -104,18 +103,6 @@ my-app/
|
||||
|
||||
Inside the `src/` folder, the `app/` folder contains your app's logic and data. Angular components, templates, and styles go here. An `assets/` subfolder contains images and anything else your app needs. Files at the top level of `src/` support testing and running your app.
|
||||
|
||||
<code-example language="none" linenums="false">
|
||||
src/
|
||||
app/
|
||||
app.component.css
|
||||
app.component.html
|
||||
app.component.spec.ts
|
||||
app.component.ts
|
||||
app.module.ts
|
||||
assets/...
|
||||
...
|
||||
</code-example>
|
||||
|
||||
| APP SOURCE FILES | PURPOSE |
|
||||
| :-------------------------- | :------------------------------------------|
|
||||
| `app/app.component.ts` | Defines the logic for the app's root component, named `AppComponent`. The view associated with this root component becomes the root of the [view hierarchy](guide/glossary#view-hierarchy) as you add components and services to your app. |
|
||||
|
@ -120,3 +120,20 @@ Try this <live-example></live-example>.
|
||||
<live-example embedded name="testy" stackblitz="super-stackblitz"></live-example>
|
||||
|
||||
<p>More text follows ...</p>
|
||||
|
||||
<p>Getting Started Widgets</p>
|
||||
|
||||
<p>Interpolation</p>
|
||||
<aio-gs-interpolation></aio-gs-interpolation>
|
||||
|
||||
<p>Property Binding</p>
|
||||
<aio-gs-property-binding></aio-gs-property-binding>
|
||||
|
||||
<p>Event Binding</p>
|
||||
<aio-gs-event-binding></aio-gs-event-binding>
|
||||
|
||||
<p>NgIf</p>
|
||||
<aio-gs-ng-if></aio-gs-ng-if>
|
||||
|
||||
<p>NgFor</p>
|
||||
<aio-gs-ng-for></aio-gs-ng-for>
|
||||
|
@ -18,7 +18,7 @@
|
||||
"build-for": "yarn ~~build --configuration",
|
||||
"prebuild-local": "yarn setup-local",
|
||||
"build-local": "yarn ~~build --configuration=stable",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js 4faa81e25",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js b50950b97",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn update-webdriver",
|
||||
@ -104,7 +104,7 @@
|
||||
"@types/jasminewd2": "^2.0.4",
|
||||
"@types/node": "~6.0.60",
|
||||
"archiver": "^1.3.0",
|
||||
"canonical-path": "^0.0.2",
|
||||
"canonical-path": "1.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "~4.2.1",
|
||||
|
@ -2,8 +2,8 @@
|
||||
"aio": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 3173,
|
||||
"main": 494475,
|
||||
"runtime": 3881,
|
||||
"main": 499953,
|
||||
"polyfills": 53926,
|
||||
"prettify": 14917
|
||||
}
|
||||
|
@ -48,6 +48,26 @@ export const ELEMENT_MODULE_PATHS_AS_ROUTES = [
|
||||
selector: 'live-example',
|
||||
loadChildren: './live-example/live-example.module#LiveExampleModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-gs-interpolation',
|
||||
loadChildren: './getting-started/interpolation/interpolation.module#InterpolationModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-gs-property-binding',
|
||||
loadChildren: './getting-started/property-binding/property-binding.module#PropertyBindingModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-gs-event-binding',
|
||||
loadChildren: './getting-started/event-binding/event-binding.module#EventBindingModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-gs-ng-if',
|
||||
loadChildren: './getting-started/ng-if/ng-if.module#NgIfModule'
|
||||
},
|
||||
{
|
||||
selector: 'aio-gs-ng-for',
|
||||
loadChildren: './getting-started/ng-for/ng-for.module#NgForModule'
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,39 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContainerComponent } from './container.component';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template">Template</ng-container>
|
||||
<ng-container class="data">Data</ng-container>
|
||||
<ng-container class="result">Result</ng-container>
|
||||
</aio-gs-container>
|
||||
`
|
||||
})
|
||||
export class TestComponent {}
|
||||
|
||||
describe('Getting Started Container Component', () => {
|
||||
let fixture: ComponentFixture<ContainerComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ContainerComponent, TestComponent ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should project the content into the appropriate areas', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const pre = compiled.querySelector('pre');
|
||||
const code = compiled.querySelector('code');
|
||||
const tabledata = compiled.querySelectorAll('td');
|
||||
|
||||
expect(pre.textContent).toContain('Template');
|
||||
expect(code.textContent).toContain('Data');
|
||||
expect(tabledata[2].textContent).toContain('Result');
|
||||
});
|
||||
});
|
@ -0,0 +1,80 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-container',
|
||||
template: `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Template</th>
|
||||
<th>Data</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<pre><ng-content select=".template"></ng-content></pre>
|
||||
</td>
|
||||
<td>
|
||||
<code><ng-content select=".data"></ng-content></code>
|
||||
</td>
|
||||
<td><ng-content select=".result"></ng-content></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
/* Force table to not be like tables anymore */
|
||||
table, thead, tbody, th, td, tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Hide table headers (but not display: none;, for accessibility) */
|
||||
thead tr {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
tr { border: 1px solid #ccc; }
|
||||
|
||||
td {
|
||||
/* Behave like a "row" */
|
||||
border: none;
|
||||
border-bottom: 1px solid #eee;
|
||||
position: relative;
|
||||
padding-top: 10%;
|
||||
}
|
||||
|
||||
td:before {
|
||||
/* Now like a table header */
|
||||
position: absolute;
|
||||
/* Top/left values mimic padding */
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* Label the data */
|
||||
td:nth-of-type(1):before { content: "Template"; }
|
||||
td:nth-of-type(2):before { content: "Data"; }
|
||||
td:nth-of-type(3):before { content: "Result"; }
|
||||
}
|
||||
`
|
||||
]
|
||||
})
|
||||
export class ContainerComponent { }
|
@ -0,0 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ContainerComponent } from './container.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule ],
|
||||
declarations: [ ContainerComponent ],
|
||||
exports: [ ContainerComponent ]
|
||||
})
|
||||
export class ContainerModule { }
|
@ -0,0 +1,46 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { EventBindingComponent } from './event-binding.component';
|
||||
import { createCustomEvent } from '../../../../testing/dom-utils';
|
||||
|
||||
describe('Getting Started Event Binding Component', () => {
|
||||
let component: EventBindingComponent;
|
||||
let fixture: ComponentFixture<EventBindingComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ ContainerModule ],
|
||||
declarations: [ EventBindingComponent ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(EventBindingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
spyOn(window, 'alert');
|
||||
});
|
||||
|
||||
it('should update the name property on input change', () => {
|
||||
const text = 'Hello Angular';
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const input: HTMLInputElement = compiled.querySelector('input');
|
||||
|
||||
|
||||
input.value = text;
|
||||
input.dispatchEvent(createCustomEvent(document, 'input', ''));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.name).toBe(text);
|
||||
});
|
||||
|
||||
it('should display an alert when the button is clicked', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const button: HTMLButtonElement = compiled.querySelector('button');
|
||||
|
||||
button.click();
|
||||
|
||||
expect(window.alert).toHaveBeenCalledWith('Hello, Angular!');
|
||||
});
|
||||
});
|
@ -0,0 +1,30 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-event-binding',
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template"><button (click)="greet(name)">
|
||||
Greet
|
||||
</button></ng-container>
|
||||
|
||||
<ng-container class="data">
|
||||
name = '<input #input (input)="name = input.value" [value]="name">';
|
||||
</ng-container>
|
||||
|
||||
<ng-container class="result">
|
||||
<button (click)="greet(name)">
|
||||
Greet
|
||||
</button>
|
||||
</ng-container>
|
||||
</aio-gs-container>
|
||||
`,
|
||||
preserveWhitespaces: true
|
||||
})
|
||||
export class EventBindingComponent {
|
||||
name = 'Angular';
|
||||
|
||||
greet(name: string) {
|
||||
window.alert(`Hello, ${name}!`);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { WithCustomElementComponent } from '../../element-registry';
|
||||
import { EventBindingComponent } from './event-binding.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, ContainerModule ],
|
||||
declarations: [ EventBindingComponent ],
|
||||
exports: [ EventBindingComponent ],
|
||||
entryComponents: [ EventBindingComponent ]
|
||||
})
|
||||
export class EventBindingModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = EventBindingComponent;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InterpolationComponent } from './interpolation.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { createCustomEvent } from '../../../../testing/dom-utils';
|
||||
|
||||
describe('Getting Started Interpolation Component', () => {
|
||||
let component: InterpolationComponent;
|
||||
let fixture: ComponentFixture<InterpolationComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ ContainerModule ],
|
||||
declarations: [ InterpolationComponent ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(InterpolationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should update the siteName property on input change', () => {
|
||||
const text = 'Hello Angular';
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const input: HTMLInputElement = compiled.querySelector('input');
|
||||
|
||||
input.value = text;
|
||||
input.dispatchEvent(createCustomEvent(document, 'input', ''));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.siteName).toBe(text);
|
||||
});
|
||||
|
||||
it('should display the siteName', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const header: HTMLHeadingElement = compiled.querySelector('h1');
|
||||
|
||||
expect(header.textContent).toContain('My Store');
|
||||
});
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-interpolation',
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template"><h1>Welcome to {{'{'+'{'}}siteName{{'}'+'}'}}<h1></ng-container>
|
||||
|
||||
<ng-container class="data">
|
||||
siteName = '<input #input (input)="siteName = input.value" [value]="siteName">';
|
||||
</ng-container>
|
||||
|
||||
<ng-container class="result"><h1>Welcome to {{ siteName }}</h1></ng-container>
|
||||
</aio-gs-container>
|
||||
`
|
||||
})
|
||||
export class InterpolationComponent {
|
||||
siteName = 'My Store';
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { WithCustomElementComponent } from '../../element-registry';
|
||||
import { InterpolationComponent } from './interpolation.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, ContainerModule ],
|
||||
declarations: [ InterpolationComponent ],
|
||||
exports: [ InterpolationComponent ],
|
||||
entryComponents: [ InterpolationComponent ]
|
||||
})
|
||||
export class InterpolationModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = InterpolationComponent;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NgForComponent } from './ng-for.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
describe('Getting Started NgFor Component', () => {
|
||||
let component: NgForComponent;
|
||||
let fixture: ComponentFixture<NgForComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ ContainerModule ],
|
||||
declarations: [ NgForComponent ],
|
||||
providers: [ ProductService ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(NgForComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the products', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const spans = compiled.querySelectorAll('span');
|
||||
|
||||
expect(spans[0]!.textContent).toContain('Shoes');
|
||||
expect(spans[1]!.textContent).toContain('Phones');
|
||||
});
|
||||
|
||||
it('should display an error message if provided products JSON is invalid', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.productsData$.next('bad');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
component.parseError$.subscribe(error => {
|
||||
expect(error).toBeTruthy();
|
||||
})
|
||||
});
|
||||
});
|
@ -0,0 +1,46 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-ng-for',
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template"><span *ngFor="let product of products">
|
||||
{{'{'+'{'}}product{{'}'+'}'}}
|
||||
</span></ng-container>
|
||||
|
||||
<ng-container class="data">
|
||||
products = <input #input (input)="productsData$.next(input.value)" [value]="productsData$ | async">;
|
||||
<div *ngIf="parseError$ | async" class="material-icons" matTooltip="The provided JSON is invalid">error_outline</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container class="result">
|
||||
<span *ngFor="let product of products$ | async">{{product}}</span>
|
||||
</ng-container>
|
||||
</aio-gs-container>
|
||||
`,
|
||||
styles: [`
|
||||
span::after {
|
||||
content: ' ';
|
||||
}
|
||||
`],
|
||||
preserveWhitespaces: true
|
||||
})
|
||||
export class NgForComponent implements OnInit, OnDestroy {
|
||||
productsData$ = this.productService.productsData$;
|
||||
products$ = this.productService.products$;
|
||||
parseError$ = this.productService.parseError$;
|
||||
productsSub: Subscription;
|
||||
|
||||
constructor(private productService: ProductService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.productsSub = this.productService.init().subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.productsSub.unsubscribe();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { WithCustomElementComponent } from '../../element-registry';
|
||||
import { NgForComponent } from './ng-for.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, ContainerModule, MatTooltipModule ],
|
||||
declarations: [ NgForComponent ],
|
||||
exports: [ NgForComponent ],
|
||||
entryComponents: [ NgForComponent ],
|
||||
providers: [ ProductService ]
|
||||
})
|
||||
export class NgForModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = NgForComponent;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NgIfComponent } from './ng-if.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
describe('Getting Started NgIf Component', () => {
|
||||
let component: NgIfComponent;
|
||||
let fixture: ComponentFixture<NgIfComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ ContainerModule ],
|
||||
declarations: [ NgIfComponent ],
|
||||
providers: [ ProductService ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(NgIfComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display the message if products are listed', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const paragraph = compiled.querySelector('p');
|
||||
|
||||
expect(paragraph.textContent).toContain('available');
|
||||
});
|
||||
|
||||
it('should not display the message if products list is empty', () => {
|
||||
component.products$.next([]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const paragraph = compiled.querySelector('p');
|
||||
|
||||
expect(paragraph).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should display an error message if provided products JSON is invalid', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.productsData$.next('bad');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
component.parseError$.subscribe(error => {
|
||||
expect(error).toBeTruthy();
|
||||
})
|
||||
});
|
||||
});
|
@ -0,0 +1,43 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-ng-if',
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template"><p *ngIf="products.length > 0">
|
||||
We still have products available.
|
||||
</p></ng-container>
|
||||
|
||||
<ng-container class="data">
|
||||
products = <input #input (input)="productsData$.next(input.value)" [value]="productsData$ | async">;
|
||||
<div *ngIf="parseError$ | async" class="material-icons" matTooltip="The provided JSON is invalid">error_outline</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container class="result">
|
||||
<p *ngIf="(products$ | async)?.length > 0">
|
||||
We still have products available.
|
||||
</p>
|
||||
</ng-container>
|
||||
</aio-gs-container>
|
||||
`,
|
||||
preserveWhitespaces: true
|
||||
})
|
||||
export class NgIfComponent implements OnInit, OnDestroy {
|
||||
productsData$ = this.productService.productsData$;
|
||||
products$ = this.productService.products$;
|
||||
parseError$ = this.productService.parseError$;
|
||||
productsSub: Subscription;
|
||||
|
||||
constructor(private productService: ProductService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.productsSub = this.productService.init().subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.productsSub.unsubscribe();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { WithCustomElementComponent } from '../../element-registry';
|
||||
import { NgIfComponent } from './ng-if.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { ProductService } from '../product.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, ContainerModule, MatTooltipModule ],
|
||||
declarations: [ NgIfComponent ],
|
||||
exports: [ NgIfComponent ],
|
||||
entryComponents: [ NgIfComponent ],
|
||||
providers: [ ProductService ]
|
||||
})
|
||||
export class NgIfModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = NgIfComponent;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { tap, debounceTime } from 'rxjs/operators';
|
||||
|
||||
export const PRODUCTS = ['Shoes', 'Phones'];
|
||||
|
||||
@Injectable()
|
||||
export class ProductService {
|
||||
productsData$ = new BehaviorSubject(JSON.stringify(PRODUCTS));
|
||||
products$ = new BehaviorSubject<string[]>(PRODUCTS);
|
||||
parseError$ = new Subject<boolean>();
|
||||
|
||||
init() {
|
||||
return this.productsData$.pipe(
|
||||
debounceTime(250),
|
||||
tap(data => {
|
||||
let parsed;
|
||||
|
||||
try {
|
||||
parsed = JSON.parse(data);
|
||||
} catch (e) {
|
||||
parsed = null;
|
||||
}
|
||||
|
||||
if (parsed && Array.isArray(parsed)) {
|
||||
this.products$.next(parsed);
|
||||
this.parseError$.next(false);
|
||||
} else {
|
||||
this.parseError$.next(true);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PropertyBindingComponent } from './property-binding.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
import { createCustomEvent } from '../../../../testing/dom-utils';
|
||||
|
||||
describe('Getting Started Property Binding Component', () => {
|
||||
let component: PropertyBindingComponent;
|
||||
let fixture: ComponentFixture<PropertyBindingComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ ContainerModule ],
|
||||
declarations: [ PropertyBindingComponent ]
|
||||
});
|
||||
|
||||
fixture = TestBed.createComponent(PropertyBindingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should update the image title on input change', () => {
|
||||
const text = 'Hello Angular';
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const input: HTMLInputElement = compiled.querySelector('input');
|
||||
|
||||
input.value = text;
|
||||
input.dispatchEvent(createCustomEvent(document, 'input', ''));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.imageTitle).toBe(text);
|
||||
});
|
||||
|
||||
it('should display the image title', () => {
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
const image: HTMLImageElement = compiled.querySelector('img');
|
||||
|
||||
expect(image.getAttribute('title')).toContain('Angular Logo');
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'aio-gs-property-binding',
|
||||
template: `
|
||||
<aio-gs-container>
|
||||
<ng-container class="template"><img ... [title]="imageTitle"></ng-container>
|
||||
|
||||
<ng-container class="data">
|
||||
imageTitle = '<input #input (input)="imageTitle = input.value" [value]="imageTitle">';
|
||||
</ng-container>
|
||||
|
||||
<ng-container class="result">
|
||||
<img src="/assets/images/logos/angular/angular.svg" width="37" height="40" [title]="imageTitle">
|
||||
</ng-container>
|
||||
</aio-gs-container>
|
||||
`
|
||||
})
|
||||
export class PropertyBindingComponent {
|
||||
imageTitle = 'Angular Logo';
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { NgModule, Type } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { WithCustomElementComponent } from '../../element-registry';
|
||||
import { PropertyBindingComponent } from './property-binding.component';
|
||||
import { ContainerModule } from '../container/container.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [ CommonModule, ContainerModule ],
|
||||
declarations: [ PropertyBindingComponent ],
|
||||
exports: [ PropertyBindingComponent ],
|
||||
entryComponents: [ PropertyBindingComponent ]
|
||||
})
|
||||
export class PropertyBindingModule implements WithCustomElementComponent {
|
||||
customElementComponent: Type<any> = PropertyBindingComponent;
|
||||
}
|
@ -123,7 +123,7 @@ td {
|
||||
padding: 8px 30px;
|
||||
letter-spacing: 0.30px;
|
||||
|
||||
p {
|
||||
p:first-child, p:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
.api-body {
|
||||
|
||||
max-width: 1200px;
|
||||
|
||||
table {
|
||||
margin: 12px 0 24px;
|
||||
|
||||
th {
|
||||
text-transform: none;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $lightgray;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.item-table {
|
||||
td {
|
||||
padding: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.list-table {
|
||||
td {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.short-description {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
LAYOUT STYLES
|
||||
============================== */
|
||||
|
||||
@import 'api-pages';
|
||||
@import 'content-layout';
|
||||
@import 'doc-viewer';
|
||||
@import 'footer';
|
||||
|
@ -1,13 +1,111 @@
|
||||
.github-links {
|
||||
float: right;
|
||||
.material-icons {
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
font-size: 20px;
|
||||
&:hover {
|
||||
background-color: $mist;
|
||||
.api-body {
|
||||
max-width: 1200px;
|
||||
|
||||
table {
|
||||
margin: 12px 0 24px;
|
||||
|
||||
th {
|
||||
text-transform: none;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $lightgray;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// This is overriding a style here:
|
||||
// https://github.com/angular/angular/blob/95993e1/aio/src/styles/2-modules/_table.scss#L58-L62
|
||||
tbody > tr > td tr td:first-child {
|
||||
@media screen and (max-width: 480px) {
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.item-table {
|
||||
td {
|
||||
padding: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.list-table {
|
||||
td {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.short-description {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.parameters-table {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
box-shadow: none;
|
||||
|
||||
tr {
|
||||
@media screen and (max-width: 480px) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
font-weight: 600;
|
||||
padding-left: 16px;
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 8px 8px 0;
|
||||
border: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.class-overview {
|
||||
position: relative;
|
||||
|
||||
code-example {
|
||||
clear: left;
|
||||
}
|
||||
}
|
||||
|
||||
.short-description {
|
||||
margin: 6px 0 0 10px;
|
||||
}
|
||||
|
||||
.properties-table {
|
||||
font-size: 14px;
|
||||
|
||||
thead th {
|
||||
&:nth-child(1) {
|
||||
width: 20%;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.api-header {
|
||||
@ -18,6 +116,22 @@
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
|
||||
.github-links {
|
||||
float: right;
|
||||
.material-icons {
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
font-size: 20px;
|
||||
&:hover {
|
||||
background-color: $mist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-body {
|
||||
@ -65,10 +179,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.api-heading {
|
||||
padding: 5px 0;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.parameters-table {
|
||||
@ -80,21 +194,71 @@
|
||||
}
|
||||
|
||||
details.overloads {
|
||||
margin-left: -8px;
|
||||
box-shadow: none;
|
||||
|
||||
.icon-action-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-contents {
|
||||
padding: 0;
|
||||
border: 1px solid $lightgray;
|
||||
border-radius: 2px;
|
||||
box-shadow: none;
|
||||
|
||||
> *:not(hr) {
|
||||
margin: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
summary {
|
||||
height: inherit;
|
||||
padding: 8px 12px;
|
||||
padding: 0;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.show-all {
|
||||
display: initial;
|
||||
}
|
||||
.collapse-all {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[open] > summary {
|
||||
.show-all {
|
||||
display: none;
|
||||
}
|
||||
.collapse-all {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
details.overload {
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.from-constructor, .read-only-property, .write-only-property {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
font-style: italic;
|
||||
color: $blue;
|
||||
background-color: $lightgray;
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.ngmodule-list {
|
||||
@ -120,10 +284,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.deprecated-api-item {
|
||||
|
@ -25,14 +25,7 @@ summary {
|
||||
display: none; // Remove the built in details marker in webkit
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '\E5CE'; // See https://material.io/icons/#ic_expand_less
|
||||
font-family: 'Material Icons';
|
||||
font-size: 24px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@include rotate(0deg); // We will rotate 180 degrees when details is open
|
||||
float: right;
|
||||
}
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
details {
|
||||
@ -42,7 +35,11 @@ details {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
&[open] > summary::before {
|
||||
@include rotate(180deg); // Rotate the icon
|
||||
// Rotate the icon
|
||||
summary i.material-icons.expand {
|
||||
@include rotate(0deg);
|
||||
}
|
||||
&[open] > summary i.material-icons.expand {
|
||||
@include rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,3 @@ hr {
|
||||
background: $lightgray;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.hr-margin {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding: 0;
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
============================== */
|
||||
|
||||
@import 'alert';
|
||||
@import 'api-pages';
|
||||
@import 'api-list';
|
||||
@import 'api-pages';
|
||||
@import 'buttons';
|
||||
@import 'callout';
|
||||
@import 'card';
|
||||
|
16
aio/src/testing/dom-utils.ts
Normal file
16
aio/src/testing/dom-utils.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Create a `CustomEvent` (even on browsers where `CustomEvent` is not a constructor).
|
||||
*/
|
||||
export function createCustomEvent(doc: Document, name: string, detail: any): CustomEvent {
|
||||
const bubbles = false;
|
||||
const cancelable = false;
|
||||
|
||||
// On IE9-11, `CustomEvent` is not a constructor.
|
||||
if (typeof CustomEvent !== 'function') {
|
||||
const event = doc.createEvent('CustomEvent');
|
||||
event.initCustomEvent(name, bubbles, cancelable, detail);
|
||||
return event;
|
||||
}
|
||||
|
||||
return new CustomEvent(name, {bubbles, cancelable, detail});
|
||||
}
|
@ -57,7 +57,7 @@
|
||||
"@types/jasminewd2": "^2.0.4",
|
||||
"@types/jquery": "^3.3.4",
|
||||
"@types/node": "~8.9.4",
|
||||
"canonical-path": "0.0.2",
|
||||
"canonical-path": "1.0.0",
|
||||
"concurrently": "^3.0.0",
|
||||
"http-server": "^0.9.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
|
@ -1450,6 +1450,11 @@ canonical-path@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-0.0.2.tgz#e31eb937a8c93ee2a01df1839794721902874574"
|
||||
|
||||
canonical-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d"
|
||||
integrity sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
|
@ -2,19 +2,21 @@
|
||||
{% set comma = joiner(',') %}
|
||||
{% set breadcrumbDelimiter = joiner('>') %}
|
||||
<article>
|
||||
{$ github.githubLinks(doc, versionInfo) $}
|
||||
<div class="breadcrumb">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{%- for crumb in doc.breadCrumbs %}{$ comma() $}
|
||||
{ "@type": "ListItem", "position": {$ loop.index $}, "item": { "@id": "https://angular.io/{$ crumb.path $}", "name": "{$ crumb.text $}" } }{% endfor %}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
|
||||
<div class="breadcrumb-container">
|
||||
<div class="breadcrumb">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
"itemListElement": [
|
||||
{%- for crumb in doc.breadCrumbs %}{$ comma() $}
|
||||
{ "@type": "ListItem", "position": {$ loop.index $}, "item": { "@id": "https://angular.io/{$ crumb.path $}", "name": "{$ crumb.text $}" } }{% endfor %}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
|
||||
</div>
|
||||
{$ github.githubLinks(doc, versionInfo) $}
|
||||
</div>
|
||||
{% block header %}
|
||||
<header class="api-header">
|
||||
|
@ -40,40 +40,41 @@
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro renderOverloadInfo(overload, cssClass, method) -%}
|
||||
<div class="overload-info">
|
||||
{% if overload.shortDescription and (overload.shortDescription != method.shortDescription) %}
|
||||
<div class="short-description">
|
||||
{$ overload.shortDescription | marked $}
|
||||
</div>{% endif %}
|
||||
|
||||
{% if overload.shortDescription and (overload.shortDescription != method.shortDescription) %}
|
||||
<div class="short-description">
|
||||
{$ overload.shortDescription | marked $}
|
||||
</div>{% endif %}
|
||||
<code-example language="ts" hideCopy="true" linenums="false" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
|
||||
|
||||
<code-example language="ts" hideCopy="true" linenums="false" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
|
||||
{% if overload.deprecated !== undefined %}
|
||||
<div class="deprecated">
|
||||
{$ ('**Deprecated** ' + overload.deprecated) | marked $}
|
||||
</div>{% endif %}
|
||||
|
||||
{% if overload.deprecated !== undefined %}
|
||||
<div class="deprecated">
|
||||
{$ ('**Deprecated** ' + overload.deprecated) | marked $}
|
||||
</div>{% endif %}
|
||||
<h6 class="no-anchor">Parameters</h6>
|
||||
{$ params.renderParameters(overload.parameterDocs, cssClass + '-parameters', cssClass + '-parameter', true) $}
|
||||
|
||||
<h4 class="no-anchor">Parameters</h4>
|
||||
{$ params.renderParameters(overload.parameterDocs, cssClass + '-parameters', cssClass + '-parameter') $}
|
||||
|
||||
{% if overload.type or overload.returns.type %}
|
||||
<h4 class="no-anchor">Returns</h4>
|
||||
{% marked %}`{$ (overload.type or overload.returns.type) $}`{% if overload.returns %}: {$ overload.returns.description $}{% endif %}{% endmarked %}
|
||||
{% endif %}
|
||||
{% if overload.type or overload.returns.type %}
|
||||
<h6 class="no-anchor">Returns</h6>
|
||||
{% marked %}`{$ (overload.type or overload.returns.type) $}`{% if overload.returns %}: {$ overload.returns.description $}{% endif %}{% endmarked %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if overload.throws.length %}
|
||||
<h4 class="no-anchor">Throws</h4>
|
||||
{% for error in overload.throws %}
|
||||
{% marked %}`{$ (error.typeList or 'Error') $}` {$ error.description $}{% endmarked %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if overload.throws.length %}
|
||||
<h6 class="no-anchor">Throws</h6>
|
||||
{% for error in overload.throws %}
|
||||
{% marked %}`{$ (error.typeList or 'Error') $}` {$ error.description $}{% endmarked %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if overload.description and (overload.description != method.description) -%}
|
||||
<div class="description">
|
||||
{$ overload.description | marked $}
|
||||
{% if overload.description and (overload.description != method.description) -%}
|
||||
<div class="description">
|
||||
{$ overload.description | marked $}
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro renderMethodDetail(versionInfo, method, cssClass) -%}
|
||||
@ -81,13 +82,13 @@
|
||||
<table class="is-full-width method-table {$ cssClass $}">
|
||||
{% if method.name !== 'constructor' %}<thead><tr><th>
|
||||
<div class="with-github-links">
|
||||
<h3>
|
||||
{% if method.isCallMember %}<i>call signature</i>
|
||||
{% elseif method.isNewMember %}<i>construct signature</i>
|
||||
{% else %}{$ method.name $}()
|
||||
{% endif %}
|
||||
</h3>
|
||||
{$ github.githubLinks(method, versionInfo) $}
|
||||
<h3>
|
||||
{% if method.isCallMember %}<i>call signature</i>
|
||||
{% elseif method.isNewMember %}<i>construct signature</i>
|
||||
{% else %}{$ method.name $}()
|
||||
{% endif %}
|
||||
</h3>
|
||||
{$ github.githubLinks(method, versionInfo) $}
|
||||
</div>
|
||||
</th></tr></thead>{% endif %}
|
||||
<tbody>
|
||||
@ -121,16 +122,26 @@
|
||||
<tr>
|
||||
<td>
|
||||
<details class="overloads">
|
||||
<summary><h4 class="no-anchor">{$ method.overloads.length $} overloads...</h4></summary>
|
||||
<summary>
|
||||
<div class="icon-action-header">
|
||||
<h4 class="no-anchor">{$ method.overloads.length $} overloads...</h4>
|
||||
<a>
|
||||
<span class="show-all">Show All</span>
|
||||
<span class="collapse-all">Hide All</span>
|
||||
<i class="material-icons expand">expand_more</i>
|
||||
</a>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="detail-contents">
|
||||
{% if method.isAbstract %}
|
||||
{$ renderOverloadInfo(method, cssClass + '-overload', method) $}
|
||||
<hr class="hr-margin fullwidth">
|
||||
{% endif %}
|
||||
{% for overload in method.overloads %}
|
||||
{$ renderOverloadInfo(overload, cssClass + '-overload', method) $}
|
||||
{% if not loop.last %}<hr class="hr-margin fullwidth">{% endif %}
|
||||
{% endfor %}
|
||||
{% if method.isAbstract %}
|
||||
{$ renderOverloadInfo(method, cssClass + '-overload', method) $}
|
||||
<hr class="hr-margin fullwidth">
|
||||
{% endif %}
|
||||
{% for overload in method.overloads %}
|
||||
<h5 class="no-anchor">Overload #{$ loop.index $}</h5>
|
||||
{$ renderOverloadInfo(overload, cssClass + '-overload', method) $}
|
||||
{% if not loop.last %}<hr class="hr-margin">{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</details>
|
||||
</td>
|
||||
@ -140,7 +151,7 @@
|
||||
{% if method.description -%}
|
||||
<tr>
|
||||
<td class="description">
|
||||
{$ method.description | marked({ h3: 'h4' }) $}
|
||||
{$ method.description | marked({ h3: 'h5' }) $}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endif %}
|
||||
@ -148,7 +159,8 @@
|
||||
{% if method.usageNotes -%}
|
||||
<tr>
|
||||
<td class="usage-notes">
|
||||
{$ method.usageNotes | marked({ h3: 'h4' }) $}
|
||||
<h4 id="{$ method.anchor $}-usage-notes">Usage Notes</h4>
|
||||
{$ method.usageNotes | marked({ h3: 'h5' }) $}
|
||||
</td>
|
||||
</tr>
|
||||
{%- endif %}
|
||||
|
@ -17,12 +17,14 @@
|
||||
<tbody>
|
||||
{% for parameter in parameters %}
|
||||
<tr class="{$ parameterClass $}">
|
||||
<td class="param-name"><a id="{$ parameter.anchor $}"></a>{$ parameter.name $}</td>
|
||||
{% if showType %}<td class="param-type"><code>{$ parameter.type $}</code></td>{% endif %}
|
||||
<td class="param-name">
|
||||
<a id="{$ parameter.anchor $}"></a>
|
||||
<code>{$ parameter.name $}</code>
|
||||
</td>
|
||||
{% if showType %}<td class="param-type"><code>{% if r/^\{/.test(parameter.type) and r/\}$/.test(parameter.type) and parameter.type.length > 20 %}object{% else %}{$ parameter.type $}{% endif %}</code></td>{% endif %}
|
||||
<td class="param-description">
|
||||
{% marked %}
|
||||
{% if (parameter.shortDescription | trim) or (parameter.description | trim) %}{$ parameter.shortDescription + '\n\n' + parameter.description $}
|
||||
{% elseif not showType and parameter.type %}<p>Type: <code>{$ parameter.type $}</code>.</p>
|
||||
{% endif %}
|
||||
|
||||
{% if parameter.isOptional or parameter.defaultValue !== undefined %}Optional. Default is `{$ parameter.defaultValue === undefined and 'undefined' or parameter.defaultValue $}`.{% endif %}
|
||||
@ -34,4 +36,4 @@
|
||||
{%- else -%}
|
||||
<p>There are no parameters.</p>
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
{%- endmacro -%}
|
||||
|
1834
aio/yarn.lock
1834
aio/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -10,10 +10,11 @@ To test Angular CLI applications, we use the integration test `cli-hello-world`.
|
||||
When a significant change is released in the CLI, the application should be updated with `ng update`:
|
||||
|
||||
```bash
|
||||
$ cd integration
|
||||
$ ng update
|
||||
# ng build
|
||||
# ng test
|
||||
$ cd integration/cli-hello-world
|
||||
$ yarn install
|
||||
$ yarn ng update @angular/cli @angular-devkit/build-angular
|
||||
# yarn build
|
||||
# yarn test
|
||||
# typescript version
|
||||
```
|
||||
|
||||
@ -36,7 +37,7 @@ The API for each test is:
|
||||
|
||||
This means that the test should be started by test script, like
|
||||
```
|
||||
'scripts' { 'test': 'runProgramA && assertResultIsGood' }
|
||||
"scripts": {"test": "runProgramA && assertResultIsGood"}
|
||||
```
|
||||
|
||||
Note that the `package.json` file uses a special `file://../../dist` scheme
|
||||
|
@ -2,9 +2,9 @@
|
||||
"cli-hello-world": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1110,
|
||||
"main": 151765,
|
||||
"polyfills": 59462
|
||||
"runtime": 1497,
|
||||
"main": 181839,
|
||||
"polyfills": 59608
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -12,32 +12,8 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"bundle": "TODO(i): temporarily increase the payload size limit from 105779 - this is due to a closure issue related to ESM reexports that still needs to be investigated",
|
||||
"bundle": 178101
|
||||
}
|
||||
}
|
||||
},
|
||||
"hello_world__render3__closure": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"bundle": 8153
|
||||
}
|
||||
}
|
||||
},
|
||||
"hello_world__render3__rollup": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"bundle": 10129
|
||||
}
|
||||
}
|
||||
},
|
||||
"hello_world__render3__cli": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"inline": 1447,
|
||||
"main": 40513,
|
||||
"polyfills": 60105
|
||||
"bundle": 177585
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,13 @@
|
||||
},
|
||||
"production": {
|
||||
"browserTarget": "cli-hello-world:build:production"
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
},
|
||||
"ci-production": {
|
||||
"browserTarget": "cli-hello-world:build:production",
|
||||
"progress": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -141,6 +148,12 @@
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "cli-hello-world:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "cli-hello-world:serve:ci"
|
||||
},
|
||||
"ci-production": {
|
||||
"devServerTarget": "cli-hello-world:serve:ci-production"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4,12 +4,12 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "ng build --prod --progress=false",
|
||||
"e2e": "ng e2e",
|
||||
"e2e": "ng e2e --webdriver-update=false",
|
||||
"lint": "ng lint",
|
||||
"ng": "ng",
|
||||
"postinstall": "webdriver-manager update --gecko=false --standalone=false $CHROMEDRIVER_VERSION_ARG",
|
||||
"start": "ng serve",
|
||||
"test": "ng test --progress=false && ng e2e --webdriver-update=false && ng e2e --prod --webdriver-update=false"
|
||||
"test": "ng test --progress=false --watch=false && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@ -27,8 +27,8 @@
|
||||
"zone.js": "file:../../node_modules/zone.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.5.0",
|
||||
"@angular/cli": "7.0.0-beta.4",
|
||||
"@angular-devkit/build-angular": "~0.10.3",
|
||||
"@angular/cli": "7.0.3",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||
"@types/jasmine": "~2.8.3",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,20 +1,22 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { browser, element, ExpectedConditions as EC, by } from 'protractor';
|
||||
|
||||
browser.waitForAngularEnabled(false);
|
||||
describe('Element E2E Tests', function () {
|
||||
describe('Hello World Elements', () => {
|
||||
it('should display: Hello world!', function () {
|
||||
browser.get('hello-world.html');
|
||||
const helloWorldEl = element(by.css('hello-world-el'));
|
||||
const helloWorldEl = element(by.css('hello-world-el'));
|
||||
|
||||
beforeEach(() => browser.get('hello-world.html'));
|
||||
|
||||
it('should display "Hello World!"', function () {
|
||||
expect(helloWorldEl.getText()).toEqual('Hello World!');
|
||||
});
|
||||
|
||||
it('should display: Hello Foo! via name attribute', function () {
|
||||
browser.get('hello-world.html');
|
||||
const helloWorldEl = element(by.css('hello-world-el'));
|
||||
it('should display "Hello Foo!" via name attribute', function () {
|
||||
const input = element(by.css('input[type=text]'));
|
||||
['f', 'o', 'o'].forEach((key) => input.sendKeys(key));
|
||||
expect(helloWorldEl.getText()).toEqual('Hello foo!');
|
||||
input.sendKeys('Foo');
|
||||
|
||||
// Make tests less flaky on CI by waiting up to 5s for the element text to be updated.
|
||||
browser.wait(EC.textToBePresentInElement(helloWorldEl, 'Hello Foo!'), 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -19,5 +19,3 @@ export class AppModule {
|
||||
}
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
export {HelloWorldComponent};
|
||||
|
@ -3,11 +3,8 @@ import {AppModuleNgFactory} from './app.ngfactory';
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory, {ngZone: 'noop'});
|
||||
|
||||
const input = document.querySelector('input');
|
||||
const helloWorld = document.querySelector('hello-world-el');
|
||||
const input = document.querySelector('input[type=text]');
|
||||
if(input && helloWorld){
|
||||
input.addEventListener('input', e => {
|
||||
const newText = (e.target as any).value;
|
||||
helloWorld.setAttribute('name', newText);
|
||||
});
|
||||
input.addEventListener('input', () => helloWorld.setAttribute('name', input.value));
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ if $CI; then
|
||||
# We don't install this by default because it contains some broken Bazel setup
|
||||
# and also it's a very big dependency that we never use except when publishing
|
||||
# payload sizes on CI.
|
||||
yarn add --silent -D firebase-tools@3.12.0
|
||||
yarn add --silent -D firebase-tools@5.1.1
|
||||
source ${basedir}/scripts/ci/payload-size.sh
|
||||
|
||||
# NB: we don't run build-packages-dist.sh because we expect that it was done
|
||||
@ -47,20 +47,21 @@ for testDir in $(ls | grep -v node_modules) ; do
|
||||
|
||||
yarn install --cache-folder ../$cache
|
||||
yarn test || exit 1
|
||||
|
||||
# Track payload size for cli-hello-world and hello_world__closure and the render3 tests
|
||||
if [[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__closure ]] || [[ $testDir == hello_world__render3__closure ]] || [[ $testDir == hello_world__render3__rollup ]] || [[ $testDir == hello_world__render3__cli ]]; then
|
||||
if [[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__render3__cli ]]; then
|
||||
if $CI && ([[ $testDir == cli-hello-world ]] || [[ $testDir == hello_world__closure ]]); then
|
||||
if [[ $testDir == cli-hello-world ]]; then
|
||||
yarn build
|
||||
fi
|
||||
#if $CI; then
|
||||
# trackPayloadSize "$testDir" "dist/*.js" true false "${basedir}/integration/_payload-limits.json"
|
||||
#fi
|
||||
|
||||
trackPayloadSize "$testDir" "dist/*.js" true false "${basedir}/integration/_payload-limits.json"
|
||||
fi
|
||||
|
||||
# remove the temporary node modules directory to keep the source folder clean.
|
||||
rm -rf node_modules
|
||||
)
|
||||
done
|
||||
|
||||
#if $CI; then
|
||||
# trackPayloadSize "umd" "../dist/packages-dist/*/bundles/*.umd.min.js" false false
|
||||
#fi
|
||||
if $CI; then
|
||||
trackPayloadSize "umd" "../dist/packages-dist/*/bundles/*.umd.min.js" false false
|
||||
fi
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.0.2",
|
||||
"version": "7.0.3",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -57,7 +57,7 @@
|
||||
"angular-mocks-1.5": "npm:angular-mocks@1.5",
|
||||
"angular-mocks-1.6": "npm:angular-mocks@1.6",
|
||||
"base64-js": "1.2.1",
|
||||
"canonical-path": "0.0.2",
|
||||
"canonical-path": "1.0.0",
|
||||
"chokidar": "1.7.0",
|
||||
"convert-source-map": "^1.5.1",
|
||||
"dependency-graph": "^0.7.2",
|
||||
|
@ -81,9 +81,11 @@ def _esm5_outputs_aspect(target, ctx):
|
||||
)
|
||||
|
||||
replay_compiler = target.typescript.replay_params.compiler.path.split("/")[-1]
|
||||
if replay_compiler == "tsc_wrapped":
|
||||
|
||||
# in windows replay_compiler path end with '.exe'
|
||||
if replay_compiler.startswith("tsc_wrapped"):
|
||||
compiler = ctx.executable._tsc_wrapped
|
||||
elif replay_compiler == "ngc-wrapped":
|
||||
elif replay_compiler.startswith("ngc-wrapped"):
|
||||
compiler = ctx.executable._ngc_wrapped
|
||||
else:
|
||||
fail("Unknown replay compiler", target.typescript.replay_params.compiler.path)
|
||||
|
@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0",
|
||||
"canonical-path": "0.0.2",
|
||||
"canonical-path": "1.0.0",
|
||||
"chokidar": "^1.4.2",
|
||||
"convert-source-map": "^1.5.1",
|
||||
"dependency-graph": "^0.7.2",
|
||||
|
@ -22,6 +22,7 @@ ts_library(
|
||||
"@ngdeps//@types/shelljs",
|
||||
"@ngdeps//@types/source-map",
|
||||
"@ngdeps//@types/yargs",
|
||||
"@ngdeps//canonical-path",
|
||||
"@ngdeps//dependency-graph",
|
||||
"@ngdeps//magic-string",
|
||||
"@ngdeps//source-map",
|
||||
|
@ -1,14 +0,0 @@
|
||||
declare module 'canonical-path' {
|
||||
export function normalize(p: string): string;
|
||||
export function join(...paths: any[]): string;
|
||||
export function resolve(...pathSegments: any[]): string;
|
||||
export function isAbsolute(p: string): boolean;
|
||||
export function relative(from: string, to: string): string;
|
||||
export function dirname(p: string): string;
|
||||
export function basename(p: string, ext?: string): string;
|
||||
export function extname(p: string): string;
|
||||
export var sep: string;
|
||||
export var delimiter: string;
|
||||
export function parse(p: string): ParsedPath;
|
||||
export function format(pP: ParsedPath): string;
|
||||
}
|
@ -15,6 +15,7 @@ ts_library(
|
||||
"//packages/compiler-cli/src/ngtsc/transform",
|
||||
"@ngdeps//@types/convert-source-map",
|
||||
"@ngdeps//@types/mock-fs",
|
||||
"@ngdeps//canonical-path",
|
||||
"@ngdeps//magic-string",
|
||||
"@ngdeps//typescript",
|
||||
],
|
||||
|
@ -113,7 +113,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
let cachedOptions: ParsedConfiguration|undefined; // CompilerOptions cached from last compilation
|
||||
let timerHandleForRecompilation: any; // Handle for 0.25s wait timer to trigger recompilation
|
||||
|
||||
const ingoreFilesForWatch = new Set<string>();
|
||||
const ignoreFilesForWatch = new Set<string>();
|
||||
const fileCache = new Map<string, CacheEntry>();
|
||||
|
||||
const firstCompileResult = doCompilation();
|
||||
@ -162,7 +162,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
cachedCompilerHost.writeFile = function(
|
||||
fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles: ReadonlyArray<ts.SourceFile> = []) {
|
||||
ingoreFilesForWatch.add(path.normalize(fileName));
|
||||
ignoreFilesForWatch.add(path.normalize(fileName));
|
||||
return originalWriteFileCallback(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
};
|
||||
const originalFileExists = cachedCompilerHost.fileExists;
|
||||
@ -191,7 +191,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
return ce.content !;
|
||||
};
|
||||
}
|
||||
ingoreFilesForWatch.clear();
|
||||
ignoreFilesForWatch.clear();
|
||||
const oldProgram = cachedProgram;
|
||||
// We clear out the `cachedProgram` here as a
|
||||
// program can only be used as `oldProgram` 1x
|
||||
@ -253,7 +253,7 @@ export function performWatchCompilation(host: PerformWatchHost):
|
||||
fileCache.delete(path.normalize(fileName));
|
||||
}
|
||||
|
||||
if (!ingoreFilesForWatch.has(path.normalize(fileName))) {
|
||||
if (!ignoreFilesForWatch.has(path.normalize(fileName))) {
|
||||
// Ignore the file if the file is one that was written by the compiler.
|
||||
startTimerForRecompilation();
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ export class Token {
|
||||
|
||||
isString(): boolean { return this.type == TokenType.String; }
|
||||
|
||||
isOperator(operater: string): boolean {
|
||||
return this.type == TokenType.Operator && this.strValue == operater;
|
||||
isOperator(operator: string): boolean {
|
||||
return this.type == TokenType.Operator && this.strValue == operator;
|
||||
}
|
||||
|
||||
isIdentifier(): boolean { return this.type == TokenType.Identifier; }
|
||||
|
@ -30,38 +30,17 @@ export const formControlBinding: any = {
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
* Syncs a standalone `FormControl` instance to a form control element.
|
||||
*
|
||||
* This directive ensures that any values written to the `FormControl`
|
||||
* instance programmatically will be written to the DOM element (model -> view). Conversely,
|
||||
* any values written to the DOM element through user input will be reflected in the
|
||||
* `FormControl` instance (view -> model).
|
||||
* * Syncs a standalone `FormControl` instance to a form control element.
|
||||
*
|
||||
* @see [Reactive Forms Guide](guide/reactive-forms)
|
||||
* @see `FormControl`
|
||||
* @see `AbstractControl`
|
||||
*
|
||||
* @usageNotes
|
||||
* Use this directive if you'd like to create and manage a `FormControl` instance directly.
|
||||
* Simply create a `FormControl`, save it to your component class, and pass it into the
|
||||
* `FormControlDirective`.
|
||||
*
|
||||
* This directive is designed to be used as a standalone control. Unlike `FormControlName`,
|
||||
* it does not require that your `FormControl` instance be part of any parent
|
||||
* `FormGroup`, and it won't be registered to any `FormGroupDirective` that
|
||||
* exists above it.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* `FormControl` instance. See a full list of available properties in
|
||||
* `AbstractControl`.
|
||||
*
|
||||
* **Set the value**: You can pass in an initial value when instantiating the `FormControl`,
|
||||
* or you can set it programmatically later using {@link AbstractControl#setValue setValue} or
|
||||
* {@link AbstractControl#patchValue patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
* ### Registering a single form control
|
||||
*
|
||||
* The following examples shows how to register a standalone control and set its value.
|
||||
*
|
||||
* {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'}
|
||||
*
|
||||
@ -138,11 +117,23 @@ export const formControlBinding: any = {
|
||||
@Directive({selector: '[formControl]', providers: [formControlBinding], exportAs: 'ngForm'})
|
||||
|
||||
export class FormControlDirective extends NgControl implements OnChanges {
|
||||
/**
|
||||
* @description
|
||||
* Internal reference to the view model value.
|
||||
*/
|
||||
viewModel: any;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the `FormControl` instance bound to the directive.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('formControl') form !: FormControl;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Triggers a warning that this input should not be used with reactive forms.
|
||||
*/
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||
|
||||
@ -155,6 +146,7 @@ export class FormControlDirective extends NgControl implements OnChanges {
|
||||
@Output('ngModelChange') update = new EventEmitter();
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Static property used to track whether any ngModel warnings have been sent across
|
||||
* all instances of FormControlDirective. Used to support warning config of "once".
|
||||
*
|
||||
@ -163,8 +155,9 @@ export class FormControlDirective extends NgControl implements OnChanges {
|
||||
static _ngModelWarningSentOnce = false;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Instance property used to track whether an ngModel warning has been sent out for this
|
||||
* particular FormControlDirective instance. Used to support warning config of "always".
|
||||
* particular `FormControlDirective` instance. Used to support warning config of "always".
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@ -181,6 +174,13 @@ export class FormControlDirective extends NgControl implements OnChanges {
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called when the directive's inputs change. For internal use
|
||||
* only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this._isControlChanged(changes)) {
|
||||
setUpControl(this.form, this);
|
||||
@ -197,16 +197,41 @@ export class FormControlDirective extends NgControl implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns an array that represents the path from the top-level form to this control.
|
||||
* Each index is the string name of the control on that level.
|
||||
*/
|
||||
get path(): string[] { return []; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Synchronous validator function composed of all the synchronous validators
|
||||
* registered with this directive.
|
||||
*/
|
||||
get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Async validator function composed of all the async validators registered with this
|
||||
* directive.
|
||||
*/
|
||||
get asyncValidator(): AsyncValidatorFn|null {
|
||||
return composeAsyncValidators(this._rawAsyncValidators);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The `FormControl` bound to this directive.
|
||||
*/
|
||||
get control(): FormControl { return this.form; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Sets the new value for the view model and emits an `ngModelChange` event.
|
||||
*
|
||||
* @param newValue The new value for the view model.
|
||||
*/
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
|
@ -29,42 +29,19 @@ export const controlNameBinding: any = {
|
||||
|
||||
/**
|
||||
* @description
|
||||
*
|
||||
* Syncs a `FormControl` in an existing `FormGroup` to a form control
|
||||
* element by name.
|
||||
*
|
||||
* This directive ensures that any values written to the `FormControl`
|
||||
* instance programmatically will be written to the DOM element (model -> view). Conversely,
|
||||
* any values written to the DOM element through user input will be reflected in the
|
||||
* `FormControl` instance (view -> model).
|
||||
*
|
||||
* @see [Reactive Forms Guide](guide/reactive-forms)
|
||||
* @see `FormControl`
|
||||
* @see `AbstractControl`
|
||||
*
|
||||
* @usageNotes
|
||||
* This directive is designed to be used with a parent `FormGroupDirective` (selector:
|
||||
* `[formGroup]`).
|
||||
*
|
||||
* ### Register `FormControl` within a group
|
||||
*
|
||||
* It accepts the string name of the `FormControl` instance you want to
|
||||
* link, and will look for a `FormControl` registered with that name in the
|
||||
* closest `FormGroup` or `FormArray` above it.
|
||||
*
|
||||
* **Access the control**: You can access the `FormControl` associated with
|
||||
* this directive by using the {@link AbstractControl#get get} method.
|
||||
* Ex: `this.form.get('first');`
|
||||
*
|
||||
* **Get value**: the `value` property is always synced and available on the `FormControl`.
|
||||
* See a full list of available properties in `AbstractControl`.
|
||||
*
|
||||
* **Set value**: You can set an initial value for the control when instantiating the
|
||||
* `FormControl`, or you can set it programmatically later using
|
||||
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the control, you can
|
||||
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* In this example, we create form controls for first name and last name.
|
||||
* The following example shows how to register multiple form controls within a form group
|
||||
* and set their value.
|
||||
*
|
||||
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
|
||||
*
|
||||
@ -150,14 +127,32 @@ export const controlNameBinding: any = {
|
||||
@Directive({selector: '[formControlName]', providers: [controlNameBinding]})
|
||||
export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
private _added = false;
|
||||
/** @internal */
|
||||
/**
|
||||
* @description
|
||||
* Internal reference to the view model value.
|
||||
* @internal
|
||||
*/
|
||||
viewModel: any;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the `FormControl` instance bound to the directive.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly control !: FormControl;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the name of the `FormControl` bound to the directive. The name corresponds
|
||||
* to a key in the parent `FormGroup` or `FormArray`.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('formControlName') name !: string;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Triggers a warning that this input should not be used with reactive forms.
|
||||
*/
|
||||
@Input('disabled')
|
||||
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
|
||||
|
||||
@ -170,6 +165,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
@Output('ngModelChange') update = new EventEmitter();
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Static property used to track whether any ngModel warnings have been sent across
|
||||
* all instances of FormControlName. Used to support warning config of "once".
|
||||
*
|
||||
@ -178,6 +174,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
static _ngModelWarningSentOnce = false;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Instance property used to track whether an ngModel warning has been sent out for this
|
||||
* particular FormControlName instance. Used to support warning config of "always".
|
||||
*
|
||||
@ -200,6 +197,12 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
this.valueAccessor = selectValueAccessor(this, valueAccessors);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called when the directive's inputs change. For internal use only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this._added) this._setUpControl();
|
||||
if (isPropertyUpdated(changes, this.viewModel)) {
|
||||
@ -209,23 +212,54 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Lifecycle method called before the directive's instance is destroyed. For internal use only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeControl(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Sets the new value for the view model and emits an `ngModelChange` event.
|
||||
*
|
||||
* @param newValue The new value for the view model.
|
||||
*/
|
||||
viewToModelUpdate(newValue: any): void {
|
||||
this.viewModel = newValue;
|
||||
this.update.emit(newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns an array that represents the path from the top-level form to this control.
|
||||
* Each index is the string name of the control on that level.
|
||||
*/
|
||||
get path(): string[] { return controlPath(this.name, this._parent !); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The top-level directive for this group if present, otherwise null.
|
||||
*/
|
||||
get formDirective(): any { return this._parent ? this._parent.formDirective : null; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Synchronous validator function composed of all the synchronous validators
|
||||
* registered with this directive.
|
||||
*/
|
||||
get validator(): ValidatorFn|null { return composeValidators(this._rawValidators); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Async validator function composed of all the async validators registered with this
|
||||
* directive.
|
||||
*/
|
||||
get asyncValidator(): AsyncValidatorFn {
|
||||
return composeAsyncValidators(this._rawAsyncValidators) !;
|
||||
}
|
||||
|
@ -31,25 +31,14 @@ export const formDirectiveProvider: any = {
|
||||
* `FormGroup` instance to match any child `FormControl`, `FormGroup`,
|
||||
* and `FormArray` instances to child `FormControlName`, `FormGroupName`,
|
||||
* and `FormArrayName` directives.
|
||||
*
|
||||
* @see [Reactive Forms Guide](guide/reactive-forms)
|
||||
* @see `AbstractControl`
|
||||
*
|
||||
* @usageNotes
|
||||
* **Set value**: You can set the form's initial value when instantiating the
|
||||
* `FormGroup`, or you can set it programmatically later using the `FormGroup`'s
|
||||
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}
|
||||
* methods.
|
||||
* ### Register Form Group
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the form, you can subscribe
|
||||
* to the `FormGroup`'s {@link AbstractControl#valueChanges valueChanges} event. You can also
|
||||
* listen to its {@link AbstractControl#statusChanges statusChanges} event to be notified when the
|
||||
* validation status is re-calculated.
|
||||
*
|
||||
* Furthermore, you can listen to the directive's `ngSubmit` event to be notified when the user has
|
||||
* triggered a form submission. The `ngSubmit` event will be emitted with the original form
|
||||
* submission event.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* In this example, we create form controls for first name and last name.
|
||||
* The following example registers a `FormGroup` with first name and last name controls,
|
||||
* and listens for the *ngSubmit* event when the button is clicked.
|
||||
*
|
||||
* {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'}
|
||||
*
|
||||
@ -64,13 +53,31 @@ export const formDirectiveProvider: any = {
|
||||
})
|
||||
export class FormGroupDirective extends ControlContainer implements Form,
|
||||
OnChanges {
|
||||
/**
|
||||
* @description
|
||||
* Reports whether the form submission has been triggered.
|
||||
*/
|
||||
public readonly submitted: boolean = false;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _oldForm !: FormGroup;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the list of added `FormControlName` instances
|
||||
*/
|
||||
directives: FormControlName[] = [];
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the `FormGroup` bound to this directive.
|
||||
*/
|
||||
@Input('formGroup') form: FormGroup = null !;
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Emits an event when the form submission has been triggered.
|
||||
*/
|
||||
@Output() ngSubmit = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
@ -79,6 +86,12 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called when the directive's inputs change. For internal use only.
|
||||
*
|
||||
* @param changes A object of key/value pairs for the set of changed inputs.
|
||||
*/
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this._checkFormPresent();
|
||||
if (changes.hasOwnProperty('form')) {
|
||||
@ -88,12 +101,32 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns this directive's instance.
|
||||
*/
|
||||
get formDirective(): Form { return this; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns the `FormGroup` bound to this directive.
|
||||
*/
|
||||
get control(): FormGroup { return this.form; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns an array representing the path to this group. Because this directive
|
||||
* always lives at the top level of a form, it always an empty array.
|
||||
*/
|
||||
get path(): string[] { return []; }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Method that sets up the control directive in this group, re-calculates its value
|
||||
* and validity, and adds the instance to the internal list of directives.
|
||||
*
|
||||
* @param dir The `FormControlName` directive instance.
|
||||
*/
|
||||
addControl(dir: FormControlName): FormControl {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpControl(ctrl, dir);
|
||||
@ -102,35 +135,92 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves the `FormControl` instance from the provided `FormControlName` directive
|
||||
*
|
||||
* @param dir The `FormControlName` directive instance.
|
||||
*/
|
||||
getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Removes the `FormControlName` instance from the internal list of directives
|
||||
*
|
||||
* @param dir The `FormControlName` directive instance.
|
||||
*/
|
||||
removeControl(dir: FormControlName): void { removeDir<FormControlName>(this.directives, dir); }
|
||||
|
||||
/**
|
||||
* Adds a new `FormGroupName` directive instance to the form.
|
||||
*
|
||||
* @param dir The `FormGroupName` directive instance.
|
||||
*/
|
||||
addFormGroup(dir: FormGroupName): void {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpFormContainer(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op method to remove the form group.
|
||||
*
|
||||
* @param dir The `FormGroupName` directive instance.
|
||||
*/
|
||||
removeFormGroup(dir: FormGroupName): void {}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves the `FormGroup` for a provided `FormGroupName` directive instance
|
||||
*
|
||||
* @param dir The `FormGroupName` directive instance.
|
||||
*/
|
||||
getFormGroup(dir: FormGroupName): FormGroup { return <FormGroup>this.form.get(dir.path); }
|
||||
|
||||
/**
|
||||
* Adds a new `FormArrayName` directive instance to the form.
|
||||
*
|
||||
* @param dir The `FormArrayName` directive instance.
|
||||
*/
|
||||
addFormArray(dir: FormArrayName): void {
|
||||
const ctrl: any = this.form.get(dir.path);
|
||||
setUpFormContainer(ctrl, dir);
|
||||
ctrl.updateValueAndValidity({emitEvent: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op method to remove the form array.
|
||||
*
|
||||
* @param dir The `FormArrayName` directive instance.
|
||||
*/
|
||||
removeFormArray(dir: FormArrayName): void {}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves the `FormArray` for a provided `FormArrayName` directive instance.
|
||||
*
|
||||
* @param dir The `FormArrayName` directive instance.
|
||||
*/
|
||||
getFormArray(dir: FormArrayName): FormArray { return <FormArray>this.form.get(dir.path); }
|
||||
|
||||
/**
|
||||
* Sets the new value for the provided `FormControlName` directive.
|
||||
*
|
||||
* @param dir The `FormControlName` directive instance.
|
||||
* @param value The new value for the directive's control.
|
||||
*/
|
||||
updateModel(dir: FormControlName, value: any): void {
|
||||
const ctrl = <FormControl>this.form.get(dir.path);
|
||||
ctrl.setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Method called with the "submit" event is triggered on the form.
|
||||
* Triggers the `ngSubmit` emitter to emit the "submit" event as its payload.
|
||||
*
|
||||
* @param $event The "submit" event object
|
||||
*/
|
||||
onSubmit($event: Event): boolean {
|
||||
(this as{submitted: boolean}).submitted = true;
|
||||
syncPendingControls(this.form, this.directives);
|
||||
@ -138,8 +228,18 @@ export class FormGroupDirective extends ControlContainer implements Form,
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Method called when the "reset" event is triggered on the form.
|
||||
*/
|
||||
onReset(): void { this.resetForm(); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Resets the form to an initial value and resets its submitted status.
|
||||
*
|
||||
* @param value The new value for the form.
|
||||
*/
|
||||
resetForm(value: any = undefined): void {
|
||||
this.form.reset(value);
|
||||
(this as{submitted: boolean}).submitted = false;
|
||||
|
@ -28,37 +28,42 @@ export const formGroupNameProvider: any = {
|
||||
*
|
||||
* Syncs a nested `FormGroup` to a DOM element.
|
||||
*
|
||||
* This directive can only be used with a parent `FormGroupDirective` (selector:
|
||||
* `[formGroup]`).
|
||||
* This directive can only be used with a parent `FormGroupDirective`.
|
||||
*
|
||||
* It accepts the string name of the nested `FormGroup` you want to link, and
|
||||
* will look for a `FormGroup` registered with that name in the parent
|
||||
* It accepts the string name of the nested `FormGroup` to link, and
|
||||
* looks for a `FormGroup` registered with that name in the parent
|
||||
* `FormGroup` instance you passed into `FormGroupDirective`.
|
||||
*
|
||||
* Nested form groups can come in handy when you want to validate a sub-group of a
|
||||
* form separately from the rest or when you'd like to group the values of certain
|
||||
* Use nested form groups to validate a sub-group of a
|
||||
* form separately from the rest or to group the values of certain
|
||||
* controls into their own nested object.
|
||||
*
|
||||
* @see [Reactive Forms Guide](guide/reactive-forms)
|
||||
*
|
||||
* @usageNotes
|
||||
* **Access the group**: You can access the associated `FormGroup` using the
|
||||
* {@link AbstractControl#get get} method. Ex: `this.form.get('name')`.
|
||||
*
|
||||
* ### Access the group by name
|
||||
*
|
||||
* The following example uses the {@link AbstractControl#get get} method to access the
|
||||
* associated `FormGroup`
|
||||
*
|
||||
* You can also access individual controls within the group using dot syntax.
|
||||
* Ex: `this.form.get('name.first')`
|
||||
* ```ts
|
||||
* this.form.get('name');
|
||||
* ```
|
||||
*
|
||||
* ### Access individual controls in the group
|
||||
*
|
||||
* The following example uses the {@link AbstractControl#get get} method to access
|
||||
* individual controls within the group using dot syntax.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* `FormGroup`. See a full list of available properties in `AbstractControl`.
|
||||
* ```ts
|
||||
* this.form.get('name.first');
|
||||
* ```
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the `FormGroup`, or you can set it programmatically later using
|
||||
* {@link AbstractControl#setValue setValue} or {@link AbstractControl#patchValue patchValue}.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the group, you can
|
||||
* subscribe to the {@link AbstractControl#valueChanges valueChanges} event. You can also listen to
|
||||
* {@link AbstractControl#statusChanges statusChanges} to be notified when the validation status is
|
||||
* re-calculated.
|
||||
*
|
||||
* ### Example
|
||||
* ### Register a nested `FormGroup`.
|
||||
*
|
||||
* The following example registers a nested *name* `FormGroup` within an existing `FormGroup`,
|
||||
* and provides methods to retrieve the nested `FormGroup` and individual controls.
|
||||
*
|
||||
* {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'}
|
||||
*
|
||||
@ -67,6 +72,11 @@ export const formGroupNameProvider: any = {
|
||||
*/
|
||||
@Directive({selector: '[formGroupName]', providers: [formGroupNameProvider]})
|
||||
export class FormGroupName extends AbstractFormGroupDirective implements OnInit, OnDestroy {
|
||||
/**
|
||||
* @description
|
||||
* Tracks the name of the `FormGroup` bound to the directive. The name corresponds
|
||||
* to a key in the parent `FormGroup` or `FormArray`.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('formGroupName') name !: string;
|
||||
|
||||
@ -104,32 +114,11 @@ export const formArrayNameProvider: any = {
|
||||
* It accepts the string name of the nested `FormArray` you want to link, and
|
||||
* will look for a `FormArray` registered with that name in the parent
|
||||
* `FormGroup` instance you passed into `FormGroupDirective`.
|
||||
*
|
||||
* Nested form arrays can come in handy when you have a group of form controls but
|
||||
* you're not sure how many there will be. Form arrays allow you to create new
|
||||
* form controls dynamically.
|
||||
*
|
||||
* @see [Reactive Forms Guide](guide/reactive-forms)
|
||||
* @see `AbstractControl`
|
||||
*
|
||||
* @usageNotes
|
||||
* **Access the array**: You can access the associated `FormArray` using the
|
||||
* {@link AbstractControl#get get} method on the parent `FormGroup`.
|
||||
* Ex: `this.form.get('cities')`.
|
||||
*
|
||||
* **Get the value**: the `value` property is always synced and available on the
|
||||
* `FormArray`. See a full list of available properties in `AbstractControl`.
|
||||
*
|
||||
* **Set the value**: You can set an initial value for each child control when instantiating
|
||||
* the `FormArray`, or you can set the value programmatically later using the
|
||||
* `FormArray`'s {@link AbstractControl#setValue setValue} or
|
||||
* {@link AbstractControl#patchValue patchValue} methods.
|
||||
*
|
||||
* **Listen to value**: If you want to listen to changes in the value of the array, you can
|
||||
* subscribe to the `FormArray`'s {@link AbstractControl#valueChanges valueChanges} event.
|
||||
* You can also listen to its {@link AbstractControl#statusChanges statusChanges} event to be
|
||||
* notified when the validation status is re-calculated.
|
||||
*
|
||||
* **Add new controls**: You can add new controls to the `FormArray` dynamically by calling
|
||||
* its {@link FormArray#push push} method.
|
||||
* Ex: `this.form.get('cities').push(new FormControl());`
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
@ -149,6 +138,11 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
|
||||
/** @internal */
|
||||
_asyncValidators: any[];
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Tracks the name of the `FormArray` bound to the directive. The name corresponds
|
||||
* to a key in the parent `FormGroup` or `FormArray`.
|
||||
*/
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('formArrayName') name !: string;
|
||||
|
||||
@ -162,27 +156,59 @@ export class FormArrayName extends ControlContainer implements OnInit, OnDestroy
|
||||
this._asyncValidators = asyncValidators;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called when the directive's inputs are initialized. For internal use only.
|
||||
*
|
||||
* @throws If the directive does not have a valid parent.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this._checkParentType();
|
||||
this.formDirective !.addFormArray(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A lifecycle method called before the directive's instance is destroyed. For internal use only.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
if (this.formDirective) {
|
||||
this.formDirective.removeFormArray(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The `FormArray` bound to this directive.
|
||||
*/
|
||||
get control(): FormArray { return this.formDirective !.getFormArray(this); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* The top-level directive for this group if present, otherwise null.
|
||||
*/
|
||||
get formDirective(): FormGroupDirective|null {
|
||||
return this._parent ? <FormGroupDirective>this._parent.formDirective : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Returns an array that represents the path from the top-level form to this control.
|
||||
* Each index is the string name of the control on that level.
|
||||
*/
|
||||
get path(): string[] { return controlPath(this.name, this._parent); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Synchronous validator function composed of all the synchronous validators registered with this
|
||||
* directive.
|
||||
*/
|
||||
get validator(): ValidatorFn|null { return composeValidators(this._validators); }
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Async validator function composed of all the async validators registered with this directive.
|
||||
*/
|
||||
get asyncValidator(): AsyncValidatorFn|null {
|
||||
return composeAsyncValidators(this._asyncValidators);
|
||||
}
|
||||
|
@ -121,8 +121,8 @@ function getRouteGuards(
|
||||
}
|
||||
|
||||
if (shouldRun) {
|
||||
const outlet = context !.outlet !;
|
||||
checks.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
|
||||
const component = context && context.outlet && context.outlet.component || null;
|
||||
checks.canDeactivateChecks.push(new CanDeactivate(component, curr));
|
||||
}
|
||||
} else {
|
||||
if (curr) {
|
||||
|
@ -10,7 +10,12 @@ export type Ng1Token = string;
|
||||
|
||||
export type Ng1Expression = string | Function;
|
||||
|
||||
export interface IAnnotatedFunction extends Function { $inject?: ReadonlyArray<Ng1Token>; }
|
||||
export interface IAnnotatedFunction extends Function {
|
||||
// Older versions of `@types/angular` typings extend the global `Function` interface with
|
||||
// `$inject?: string[]`, which is not compatible with `$inject?: ReadonlyArray<string>` (used in
|
||||
// latest versions).
|
||||
$inject?: Function extends{$inject?: string[]}? Ng1Token[]: ReadonlyArray<Ng1Token>;
|
||||
}
|
||||
|
||||
export type IInjectable = (Ng1Token | Function)[] | IAnnotatedFunction;
|
||||
|
||||
|
@ -24,8 +24,8 @@ if (msgFile) {
|
||||
isValid = checkMsg(firstLine);
|
||||
|
||||
if (!isValid) {
|
||||
console.error('\nCheck CONTRIBUTING.md at the root of the repo for more information.')
|
||||
console.error('\nCheck CONTRIBUTING.md at the root of the repo for more information.');
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(isValid ? 0 : 1);
|
||||
process.exit(isValid ? 0 : 1);
|
||||
|
@ -1274,10 +1274,10 @@ camelcase@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||
integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
|
||||
|
||||
canonical-path@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-0.0.2.tgz#e31eb937a8c93ee2a01df1839794721902874574"
|
||||
integrity sha1-4x65N6jJPuKgHfGDl5RyGQKHRXQ=
|
||||
canonical-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d"
|
||||
integrity sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==
|
||||
|
||||
caseless@~0.11.0:
|
||||
version "0.11.0"
|
||||
|
Reference in New Issue
Block a user