Compare commits

...

81 Commits

Author SHA1 Message Date
8a09015211 docs: add changelog for 4.2.4 2017-06-21 17:01:14 -07:00
f4b7bf7e38 release: cut the 4.2.4 release 2017-06-21 16:57:31 -07:00
d20313698d revert: feat(core): update zone.js to 0.8.12
This reverts commit 25234227ec.

Mistakenly cherry picked a feature commit which does not belong on a
patch branch.
2017-06-21 16:52:14 -07:00
74954f1a8d docs(aio): ward's changes 2017-06-21 16:28:19 -07:00
368aed087c docs(aio): change to overall prose 2017-06-21 16:28:19 -07:00
975341a77e docs(aio): create author style guide 2017-06-21 16:28:19 -07:00
c112232fa3 fix(compiler): avoid emitting self importing factories
Fixes: #17389
2017-06-21 16:20:05 -07:00
36348325de feat(aio): display “Searching ..." while building search index
closes #15923
2017-06-21 14:31:59 -07:00
fdbe62f112 fix(aio): fix patch script on windows
Running the patch script on Windows (with `patch` available) yields an invalid syntax warning, and does not apply patches.

```
kamik@T460p MINGW64 /d/work/angular/aio (master)
$ yarn postinstall
yarn postinstall v0.24.6
$ node tools/cli-patches/patch.js && uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map
The syntax of the command is incorrect.
Done in 1.52s.
```
2017-06-21 13:51:47 -07:00
af9ba91e5c build(aio): ensure all doc tests are run
It is not possible to run all the docs tests directly via the  jasmine CLI.
Instead we now have a small script that will run jasmine via its library.
2017-06-21 13:51:27 -07:00
b840ec3684 feat(aio): select contributor group with URL “about?group=gde”
closes #17656
also adds test for ContributorListComponent.
2017-06-21 13:51:09 -07:00
b38552f36e fix(aio): restore component-styles/exclude hidden children 2017-06-21 11:33:33 -07:00
572885dcc8 docs(aio): fix numbered list in testing.md
This list renders OK in the github UI but not at https://angular.io/guide/testing#setup
2017-06-20 16:43:46 -07:00
33906489ad fix(tsc-wrapped): skip collecting metadata for default functions
Fixes: #17518
2017-06-20 14:23:04 -07:00
59299de374 fix(compiler-cli): find lazy routes in nested module import arrays
Fixes: #17531
2017-06-20 14:21:35 -07:00
f99793700f docs(aio): update resource listing 2017-06-20 12:58:20 -07:00
77860a0023 fix: argument destructuring sometimes breaks strictNullChecks
Destructuring of the form:

function foo({a, b}: {a?, b?} = {})

breaks strictNullChecks, due to the TypeScript bug https://github.com/microsoft/typescript/issues/10078.
This change eliminates usage of destructuring in function argument lists in cases where it would leak
into the public API .d.ts.
2017-06-20 12:56:21 -07:00
93d834a0b2 refactor(core): remove toString() method from DefaultKeyValueDiffer
toString() from DefaultKeyValueDiffer is only used in tests and should not
be part of the production code. toString() methods from differs add
~ 0.3KB (min+gzip) to the production bundle size.
2017-06-20 12:55:35 -07:00
63a5f33e99 fix(language-service): infer any ngForOf of type any
Fixes: #17611
2017-06-20 12:05:10 -07:00
20eb5cfd59 fix(language-service): rollup tslib into the language service package
Fixes: #17614
2017-06-20 11:50:42 -07:00
4ab7353a9f fix(forms): roll back breaking change with min/max directives
With 4.2, we introduced the min and max validator directives. This was actually a breaking change because their selectors could include custom value accessors using the min/max properties for their own purposes.

For now, we are rolling back the change by removing the exports. At the least, we should wait to add them until a major version. In the meantime, we will have further discussion about what the best solution is going forward for all validator directives.

Closes #17491.

----

PR #17551 tried to roll this back, but did not remove the dead code. This failed internal tests that were checking that all declared directives were used.
This PR rolls back the original PR and commit the same as #17551 while also removing the dead code.
2017-06-20 09:08:25 -07:00
eb23460e09 revert: fix(forms): temp roll back breaking change with min/max directives
This reverts commit 232bd9395d.
2017-06-20 09:08:25 -07:00
2e8efdaffa fix(aio): leave results panel open when opening search result on new page
Fixes #17580
2017-06-19 15:12:57 -07:00
3cfb13f746 build(aio): upgrade jasmine to v2.6.4
This version fixes the DISCONNECTED errors (described in #17543) and removes the
need to the workaround (8af203c).
The relevant jasmine commit is jasmine/jasmine@c60d66994.
2017-06-19 15:12:38 -07:00
7de1ae2be8 fix(router): update the version placeholder so that it gets replaced during the build
Fixes #17403
2017-06-19 15:11:22 -07:00
341b812a10 docs(TRIAGE_AND_LABELS): update labels to reflect the current state 2017-06-19 14:54:15 -07:00
09512c7770 docs(RELEASE_SCHEDULE): fix version numbers for August/September releases 2017-06-19 14:46:49 -07:00
1ec0a53fec docs(RELEASE_SCHEDULE): update the release schedule w/ recent regression patch releases 2017-06-19 13:20:53 -07:00
3607a48a08 docs: remove aio from changelog 2017-06-19 11:28:17 -07:00
62957fa515 fix(aio): switch from innerText to textContent to support older browsers
`innerText` is not supported in Firefox prior to v45. In most cases (at least
the ones we are interested in), `innerText` and `textContent` work equally well,
but `textContent` is more performant (as it doesn't require a reflow).

From [MDN][1] on the differences of `innerText` vs `textContent`:

> - [...]
> - `innerText` is aware of style and will not return the text of hidden
>   elements, whereas `textContent` will.
> - As `innerText` is aware of CSS styling, it will trigger a reflow, whereas
>   `textContent` will not.
> - [...]

[1]: https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText

Fixes #17585
2017-06-19 10:32:58 -07:00
ab81c1c068 build(aio): update @angular/service-worker to 1.0.0-beta.16
This version includes a fix for potential cache corruption and invalid redirect behavior in rare cases.
2017-06-16 13:34:00 -07:00
25234227ec feat(core): update zone.js to 0.8.12 2017-06-16 12:13:31 -07:00
71a8ef5a15 fix(aio): improve no-javascript screen 2017-06-16 12:05:03 -07:00
4dd6deca16 fix(aio): remove unused icon reference and file
These icons are not there (and never were afaict).

Fixes #17561
2017-06-16 12:04:15 -07:00
f8db05ef7d fix(aio): correctly redirect /docs/ts/latest and /styleguide
Previously, we had redirect rules for Firebase for `/docs/ts/latest` and
`/styleguide`, but once the ServiceWorker was activated, it would take over
routing and rewrite these requests to `/index.html`.
This commit fixes it by excluding them from ServiceWorker routing.

Fixes #17542
2017-06-16 12:03:24 -07:00
623b9c6e58 test(compiler): fix typo "mamespace" 2017-06-16 11:17:49 -07:00
8a547eeee0 docs: add changelog for 4.2.3 2017-06-16 09:41:51 -07:00
4211432fc8 release: cut the 4.2.3 release 2017-06-16 09:38:29 -07:00
b8c39cdf71 fix(forms): temp roll back breaking change with min/max directives
With 4.2, we introduced the min and max validator directives. This was actually a breaking change because
their selectors could include custom value accessors using the min/max properties for their own purposes.

For now, we are rolling back the change by removing the exports.

Closes #17491.
2017-06-16 09:32:19 -07:00
9c7a84de51 docs(aio): re-add biography entry for devversion
With SHA 2c3e948e61 the biography of Paul Gschwendtner has been accidentally removed.

This re-adds the biography entry (picture still present) as requested on Slack.
2017-06-16 07:56:30 +01:00
dbc6a4cb12 build(aio): do not fail if check-env for the main angular project fails
Fixes #17434
2017-06-16 07:53:26 +01:00
784410e3c8 build: fix link to DEVELOPER.md in check-environment.js 2017-06-16 07:53:25 +01:00
90a5a1ef43 build(aio): remove dependency on build artifacts from parent folder 2017-06-16 07:53:25 +01:00
301f99cd6c build: remove redundant line
The same value is set a few lines below.
2017-06-16 07:53:25 +01:00
64e63b9422 fix(aio): add missing redirect rule for /styleguide
Fixes #17542
2017-06-16 07:52:29 +01:00
b192dd5761 fix(animations): remove duplicate license header 2017-06-15 14:51:40 -07:00
96aa3bb135 docs(aio): update about page 2017-06-15 22:15:59 +01:00
8abc1df2c1 feat(aio): add iphone pwa features 2017-06-15 22:15:59 +01:00
f5eb528a5c docs(aio): incorporate Ward's comments 2017-06-15 22:15:58 +01:00
5cf06a9f3f docs(aio): add short section on built-in validators, copy edits 2017-06-15 22:15:58 +01:00
ab90f63575 fix(aio): do not log messages in production
In dev mode, all messages passed to `Logger` will be logged.
In production mode, only warnings and errors will be logged.

Fixes #17453
2017-06-15 22:15:57 +01:00
150d271f79 refactor(aio): remove unused Logger dependencies 2017-06-15 22:15:57 +01:00
a686eb2c9e build(aio): extra redirect rule 2017-06-15 22:15:57 +01:00
bfa788935a docs(aio): http guide shows how to import toPromise operator from rxjs
Solves #17454
2017-06-15 22:15:56 +01:00
64fa100a71 fix(aio): specify large image for PWA splash-screen 2017-06-14 09:45:37 -07:00
5fae987bfa build(aio): upgrade lighthouse to v2.1 2017-06-14 09:45:37 -07:00
9c4cda1c7d ci(aio): fail the build if the PWA score is too low
Previously, there was an issue with testing the PWA score on staging and failing
the build was temporarily disabled. It works now, so we need to enable failing
the build is the score drops below some threshold.
2017-06-14 09:45:37 -07:00
d9cbe56b63 test(aio): add async beforeEach to prevent Chrome disconnects
Related to 3d5f520ff0 from #17405
2017-06-14 09:45:37 -07:00
86df7108b0 fix(aio): always cover the whole footer with its background
Fixes #17465
2017-06-14 09:45:37 -07:00
e7a4f92be7 fix(aio): fix trackBy demo in template-syntax article 2017-06-14 09:45:36 -07:00
76af452d29 test(platform-server): fix and re-enable integration tests 2017-06-14 09:45:36 -07:00
11dfb685f4 ci: update github templates (#17466) 2017-06-13 15:27:35 -07:00
d363aa0aa4 fix(aio): make the footer links clickable on all browsers
The footer background (implemented via `footer:after`) had a higher `z-index`
than other footer elements and was obscuring the footer links on certain
browsers (Firefox, Edge, IE), which made them unclickable.
This commit lowers the index of `footer:after`, so that links are clickable on
these browsers.

Fixes #17460
2017-06-13 15:27:35 -07:00
209d74c342 ci: disable platform-server integration test as it is currently broken 2017-06-13 15:27:35 -07:00
fec8f6febe refactor(compiler): remove duplicate code 2017-06-13 15:27:35 -07:00
ee9daaf4c8 ci: use npm_install for bazel
using yarn_install polluted our node_modules cache because it disregards the npm_shrinkwrap.json
2017-06-13 15:27:35 -07:00
4164369db4 docs(aio): update typescript for examples/webpack to same as cli 2017-06-13 11:35:17 -07:00
747b6a61b8 build(aio): add staging environment
You can now specify what environment you are building
by add it to the `yarn build` command. For example:

```
yarn build -- --env=stage
```

Moreover the `deploy-to-firebase.sh` script will automatically apply the
appropriate environment.
2017-06-13 11:35:17 -07:00
9ed836c939 docs(aio): i18n guide - updates for v4 2017-06-13 11:35:17 -07:00
e4c82443f9 build(aio): increase docs integration test timeouts
The API docs tests have very variable run times, depending
upon the build environment.
This change doubles their test timeout values to prevent
false-negative failures.
2017-06-13 11:35:17 -07:00
a2f232166b fix(aio): fix scrolling to elements near the bottom of the page
Previously, we always assumed that elements would be scrolled to the top of the
page, when calling `element.scrollIntoView()`. This is not true for elements
that cannot be scrolled to the top, e.g. when the viewport height is larger than
the height of the content after the element (common for small sections near the
end of the page).
In such cases, we would unnecessarily scroll up to account for the static
toolbar, which was unnecessary (since the element was not behind the toolbar
anyway) and caused ScrollSpy to fail to identify the scrolled-to section as
active.

This commit fixes it by ensuring that we do not scroll more than necessary in
order to align the top of the element with the bottom of the toolbar.

Fixes #17452
2017-06-13 11:35:17 -07:00
668f9ede65 fix(aio): show search results when search box gets focus
Due to a previous commit, the search was only triggered
if the query changed, and not when the search box regained
focus.
2017-06-13 11:35:17 -07:00
b784829512 fix(aio): use locally hosted lunr library
The library is downloaded from npm but then
copied into the assets folder (and ignored by git)
as part of the postinstall step.
2017-06-13 11:35:17 -07:00
ad4fee7053 fix(aio): make search results better
* update to latest version of lunr search
* add trailing wildcard to search terms to increase matches
* fix unwanted error when escape was pressed

Closes #17417
2017-06-13 11:35:17 -07:00
2d31e17251 fix(aio): fix buttons in "Home" and "Features"
Using `<a>` inside a `<button>` is not syntactically valid HTML and breaks on
some browsers (e.g. Firefox). Furthermore, clicking the button doesn't do
anything unless you click on the link (e.g. clicking on the padding around the
link does nothing), which is inconvenient and confusing.

Fixes #17448
2017-06-13 11:35:17 -07:00
39cff565ee docs: clarify when non-null-assertion-operator is needed in template-syntax 2017-06-13 11:35:17 -07:00
203c5ba1b3 fix(aio): ensure that API filter page can display 3 columns in wide view
Fixes #17251
2017-06-13 11:35:17 -07:00
eda7bb5c3e fix(aio): tidy up layout of api filter page
* Remove the "info-banner" styling from the filters.
* Fix alignment of the search box on a narrow screen (closes #17395)
* Remove unnecessary whitespace before section headers
2017-06-13 11:35:17 -07:00
1480a30050 docs(aio): rename Upgrade docs to cheatsheet 2017-06-13 11:35:16 -07:00
d6087f75e2 fix(aio): remove outline from search input on focus
Closes #17396
2017-06-13 11:35:16 -07:00
dc084a5bdf build(aio): make deploy-to-firebase.sh executable 2017-06-12 16:13:46 -07:00
242 changed files with 3500 additions and 1778 deletions

View File

@ -3,7 +3,7 @@ jobs:
build: build:
working_directory: ~/ng working_directory: ~/ng
docker: docker:
- image: alexeagle/ngcontainer - image: angular/ngcontainer
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache:

View File

@ -1,39 +1,57 @@
<!-- <!--
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
ISSUES MISSING IMPORTANT INFORMATION MIGHT BE CLOSED WITHOUT INVESTIGATION.
--> -->
**I'm submitting a ...** (check one with "x") ## I'm submitting a ...
``` <!-- Check one of the following options with "x" -->
[ ] bug report => search github for a similar issue or PR before submitting <pre><code>
[ ] feature request [ ] Regression (behavior that used to work and stopped working in a new release)
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question [ ] Bug report <!-- Please search github for a similar issue or PR before submitting -->
``` [ ] 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
</code></pre>
**Current behavior** ## Current behavior
<!-- Describe how the bug manifests. --> <!-- Describe how the issue manifests. -->
**Expected behavior**
<!-- Describe what the behavior would be without the bug. -->
**Minimal reproduction of the problem with instructions** ## Expected behavior
<!-- Describe what the desired behavior would be. -->
## Minimal reproduction of the problem with instructions
<!-- <!--
If the current behavior is a bug or you can illustrate your feature request better with an example, For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
--> -->
**What is the motivation / use case for changing the behavior?** ## What is the motivation / use case for changing the behavior?
<!-- Describe the motivation or the concrete use case --> <!-- Describe the motivation or the concrete use case. -->
**Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... -->
* **Angular version:** 2.0.X ## Please tell us about your environment
<pre><code>
Angular version: X.Y.Z
<!-- Check whether this is still an issue in the most recent Angular version --> <!-- Check whether this is still an issue in the most recent Angular version -->
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] Browser:
<!-- All browsers where this could be reproduced --> - [ ] 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
* **Language:** [all | TypeScript X.X | ES6/7 | ES5] For Tooling issues:
- Node version: XX <!-- use `node --version` -->
- Platform: <!-- Mac, Linux, Windows -->
* **Node (for AoT issues):** `node --version` = Others:
<!-- Anything else relevant? Operating system version, IDE, package manager, HTTP server, ... -->
</code></pre>

View File

@ -1,10 +1,15 @@
**Please check if the PR fulfills these requirements** ## PR Checklist
Does please check if your PR fulfills the following requirements:
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit - [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
- [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features)
**What kind of change does this PR introduce?** (check one with "x") ## PR Type
What kind of change does this PR introduce?
<!-- Please check the one that applies to this PR using "x". -->
``` ```
[ ] Bugfix [ ] Bugfix
[ ] Feature [ ] Feature
@ -12,25 +17,27 @@
[ ] Refactoring (no functional changes, no api changes) [ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes [ ] Build related changes
[ ] CI related changes [ ] CI related changes
[ ] Documentation content changes
[ ] angular.io application / infrastructure changes
[ ] Other... Please describe: [ ] Other... Please describe:
``` ```
**What is the current behavior?** (You can also link to an open issue here) ## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
Issue Number: N/A
## What is the new behavior?
**What is the new behavior?**
## Does this PR introduce a breaking change?
**Does this PR introduce a breaking change?** (check one with "x")
``` ```
[ ] Yes [ ] Yes
[ ] No [ ] No
``` ```
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ... <!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
**Other information**: ## Other information

View File

@ -1,3 +1,34 @@
<a name="4.2.4"></a>
## [4.2.4](https://github.com/angular/angular/compare/4.2.3...4.2.4) (2017-06-21)
### Bug Fixes
* **compiler:** avoid emitting self importing factories ([c112232](https://github.com/angular/angular/commit/c112232))
* **compiler-cli:** find lazy routes in nested module import arrays ([59299de](https://github.com/angular/angular/commit/59299de))
* **core**: argument destructuring sometimes breaks strictNullChecks ([77860a0](https://github.com/angular/angular/commit/77860a0))
* **forms:** roll back breaking change with min/max directives ([4ab7353](https://github.com/angular/angular/commit/4ab7353)), closes [#17491](https://github.com/angular/angular/issues/17491)
* **language-service:** infer `any` `ngForOf` of type `any` ([63a5f33](https://github.com/angular/angular/commit/63a5f33))
* **language-service:** rollup `tslib` into the language service package ([20eb5cf](https://github.com/angular/angular/commit/20eb5cf))
* **router:** update the version placeholder so that it gets replaced during the build ([7de1ae2](https://github.com/angular/angular/commit/7de1ae2)), closes [#17403](https://github.com/angular/angular/issues/17403)
* **tsc-wrapped:** skip collecting metadata for default functions ([3390648](https://github.com/angular/angular/commit/3390648))
<a name="4.2.3"></a>
## [4.2.3](https://github.com/angular/angular/compare/4.2.1...4.2.3) (2017-06-16)
### Bug Fixes
* **animations:** compute removal node height correctly ([185075d](https://github.com/angular/angular/commit/185075d))
* **animations:** do not treat a `0` animation state as `void` ([451257a](https://github.com/angular/angular/commit/451257a))
* **animations:** properly collect :enter nodes in a partially updated collection ([6ca4692](https://github.com/angular/angular/commit/6ca4692)), closes [#17440](https://github.com/angular/angular/issues/17440)
* **animations:** remove duplicate license header ([b192dd5](https://github.com/angular/angular/commit/b192dd5))
* **forms:** temp roll back breaking change with min/max directives ([b8c39cd](https://github.com/angular/angular/commit/b8c39cd)), closes [#17491](https://github.com/angular/angular/issues/17491)
<a name="4.2.2"></a> <a name="4.2.2"></a>
## [4.2.2](https://github.com/angular/angular/compare/4.2.1...4.2.2) (2017-06-12) ## [4.2.2](https://github.com/angular/angular/compare/4.2.1...4.2.2) (2017-06-12)

View File

@ -3,10 +3,10 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository( git_repository(
name = "io_bazel_rules_typescript", name = "io_bazel_rules_typescript",
remote = "https://github.com/bazelbuild/rules_typescript.git", remote = "https://github.com/bazelbuild/rules_typescript.git",
tag = "0.0.3", commit = "804c5da",
) )
load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories", "yarn_install") load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories", "npm_install")
node_repositories() node_repositories()
yarn_install(package_json = "//:package.json") npm_install(package_json = "//:package.json")

View File

@ -31,6 +31,7 @@
"environmentSource": "environments/environment.ts", "environmentSource": "environments/environment.ts",
"environments": { "environments": {
"dev": "environments/environment.ts", "dev": "environments/environment.ts",
"stage": "environments/environment.stage.ts",
"prod": "environments/environment.prod.ts" "prod": "environments/environment.prod.ts"
} }
} }

3
aio/.gitignore vendored
View File

@ -43,3 +43,6 @@ protractor-results*.txt
# System Files # System Files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# copied dependencies
src/assets/js/lunr*

View File

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

View File

@ -0,0 +1,7 @@
{
"scripts": {
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
"test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
"lint": "tslint ./src/**/*.ts -t verbose"
}
}

View File

@ -0,0 +1,10 @@
{
"description": "Authors style guide",
"basePath": "src/",
"files": [
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2,3].*"
],
"tags": ["author", "style guide"]
}

View File

@ -0,0 +1,9 @@
{
"description": "Second authors style guide plunker (non-executing)",
"basePath": "src/",
"files": [
"index.2.html"
],
"main": "index.2.html",
"tags": ["author", "style guide"]
}

View File

@ -0,0 +1,51 @@
/* #docregion heroes */
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
/* #enddocregion heroes */
.heroes li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
.selected {
background-color: #CFD8DC !important;
color: white;
}

View File

@ -0,0 +1,21 @@
<!-- #docplaster -->
<!-- #docregion -->
<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name}} details!</h2>
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
<label>name: </label>
<!-- #docregion selected-hero -->
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
<!-- #enddocregion selected-hero -->
</div>
</div>

View File

@ -0,0 +1,23 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { Hero, HEROES } from './hero';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
// #docregion class, class-skeleton
export class AppComponent {
// #enddocregion class-skeleton
title = 'Authors Style Guide Sample';
heroes = HEROES;
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
// #docregion class-skeleton
}
// #enddocregion class, class-skeleton

View File

@ -0,0 +1,15 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
// #docregion class
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
// #enddocregion class

View File

@ -0,0 +1,11 @@
export class Hero {
id: number;
name: string;
}
export const HEROES: Hero[] = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' }
];

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Second Authors Style Guide</title>
</head>
<body>
<h1>Second Authors Style Guide</h1>
<p>Placeholder. Does nothing at all.</p>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- #docregion -->
<html>
<head>
<title>Docs Style Guide</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- #docregion styles -->
<link rel="stylesheet" href="styles.css">
<!-- #enddocregion styles -->
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>

View File

@ -0,0 +1,4 @@
// #docregion
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -30,4 +30,8 @@ describe('i18n E2E Tests', () => {
expect(element.all(by.css('span')).get(1).getText()).toBe('El heroe es mujer'); expect(element.all(by.css('span')).get(1).getText()).toBe('El heroe es mujer');
}); });
it('should display the nested expression', function() {
expect(element.all(by.css('span')).get(2).getText()).toBe('Aquí tenemos: 3 mujeres');
});
}); });

View File

@ -2,8 +2,10 @@
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template"> <file source-language="en" datatype="plaintext" original="ng2.template">
<body> <body>
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html"> <trans-unit id="introductionHeader" datatype="html">
<source>Hello i18n!</source> <source>
Hello i18n!
</source>
<target/> <target/>
<note priority="1" from="description">An introduction header for this sample</note> <note priority="1" from="description">An introduction header for this sample</note>
<note priority="1" from="meaning">User welcome</note> <note priority="1" from="meaning">User welcome</note>
@ -24,12 +26,6 @@ I don&apos;t output any element either
<source>Angular logo</source> <source>Angular logo</source>
<target/> <target/>
</trans-unit> </trans-unit>
<trans-unit id="2579611bfcccd75bcd41fac90150d27d6ebb30b8" datatype="html">
<source>
<x id="START_TAG_SPAN" ctype="x-span"/><x id="ICU"/><x id="CLOSE_TAG_SPAN" ctype="x-span"/>
</source>
<target/>
</trans-unit>
<trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html"> <trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html">
<source/> <source/>
<target/> <target/>
@ -42,6 +38,14 @@ I don&apos;t output any element either
<source/> <source/>
<target/> <target/>
</trans-unit> </trans-unit>
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
<source>Here we have: <x id="ICU"/></source>
<target/>
</trans-unit>
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
<source/>
<target/>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -10,6 +10,23 @@
<h1 i18n="An introduction header for this sample">Hello i18n!</h1> <h1 i18n="An introduction header for this sample">Hello i18n!</h1>
<!--#enddocregion i18n-attribute-desc--> <!--#enddocregion i18n-attribute-desc-->
<!--#docregion i18n-attribute-meaning-->
<h1 i18n="site header|An introduction header for this sample">Hello i18n!</h1>
<!--#enddocregion i18n-attribute-meaning-->
<!--#docregion i18n-attribute-id-->
<h1 i18n="An introduction header for this sample@@introductionHeader">Hello i18n!</h1>
<!--#enddocregion i18n-attribute-id-->
<!--#docregion i18n-attribute-meaning-and-id-->
<h1 i18n="site header|An introduction header for this sample@@introductionHeader">Hello i18n!</h1>
<!--#enddocregion i18n-attribute-meaning-and-id-->
<!--#docregion i18n-attribute-solo-id-->
<h1 i18n="@@introductionHeader">Hello i18n!</h1>
<!--#enddocregion i18n-attribute-solo-id-->
<!--#docregion i18n-title--> <!--#docregion i18n-title-->
<img [src]="logo" title="Angular logo"> <img [src]="logo" title="Angular logo">
<!--#enddocregion i18n-title--> <!--#enddocregion i18n-title-->
Contact GitHub API Training Shop Blog About

View File

@ -1,6 +1,8 @@
<!--#docregion--> <!--#docregion-->
<!--#docregion i18n-attribute-meaning--> <!--#docregion i18n-attribute-meaning-->
<h1 i18n="User welcome|An introduction header for this sample">Hello i18n!</h1> <h1 i18n="User welcome|An introduction header for this sample@@introductionHeader">
Hello i18n!
</h1>
<!--#enddocregion i18n-attribute-meaning--> <!--#enddocregion i18n-attribute-meaning-->
<!--#docregion i18n-ng-container--> <!--#docregion i18n-ng-container-->
@ -31,4 +33,11 @@ I don't output any element either
<!--#docregion i18n-select--> <!--#docregion i18n-select-->
<span i18n>The hero is {gender, select, m {male} f {female}}</span> <span i18n>The hero is {gender, select, m {male} f {female}}</span>
<!--#enddocregion i18n-select--> <!--#enddocregion i18n-select-->
<br> <br><br>
<!--#docregion i18n-nested-->
<span i18n>Here we have: {count, plural,
=0 {no one}
=1 {one {gender, select, male {man} female {woman}}}
other {{{heroes.length}} {gender, select, male {men} female {women}}}
}</span>
<!--#enddocregion i18n-nested-->

View File

@ -10,6 +10,8 @@ export class AppComponent {
gender = 'f'; gender = 'f';
fly = true; fly = true;
logo = 'https://angular.io/resources/images/logos/angular/angular.png'; logo = 'https://angular.io/resources/images/logos/angular/angular.png';
count = 3;
heroes: string[] = ['Magneta', 'Celeritas', 'Dynama'];
inc(i: number) { inc(i: number) {
this.wolves = Math.min(5, Math.max(0, this.wolves + i)); this.wolves = Math.min(5, Math.max(0, this.wolves + i));
} }

View File

@ -1,5 +1,7 @@
// #docregion // #docplaster
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID } from '@angular/core'; // #docregion without-missing-translation
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy } from '@angular/core';
import { CompilerConfig } from '@angular/compiler';
export function getTranslationProviders(): Promise<Object[]> { export function getTranslationProviders(): Promise<Object[]> {
@ -17,13 +19,18 @@ export function getTranslationProviders(): Promise<Object[]> {
// Ex: 'locale/messages.es.xlf` // Ex: 'locale/messages.es.xlf`
const translationFile = `./locale/messages.${locale}.xlf`; const translationFile = `./locale/messages.${locale}.xlf`;
// #docregion missing-translation
return getTranslationsWithSystemJs(translationFile) return getTranslationsWithSystemJs(translationFile)
.then( (translations: string ) => [ .then( (translations: string ) => [
{ provide: TRANSLATIONS, useValue: translations }, { provide: TRANSLATIONS, useValue: translations },
{ provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }, { provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
{ provide: LOCALE_ID, useValue: locale } { provide: LOCALE_ID, useValue: locale },
// #enddocregion without-missing-translation
{ provide: CompilerConfig, useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error }) }
// #docregion without-missing-translation
]) ])
.catch(() => noProviders); // ignore if file not found .catch(() => noProviders); // ignore if file not found
// #enddocregion missing-translation
} }
declare var System: any; declare var System: any;
@ -31,3 +38,4 @@ declare var System: any;
function getTranslationsWithSystemJs(file: string) { function getTranslationsWithSystemJs(file: string) {
return System.import(file + '!text'); // relies on text plugin return System.import(file + '!text'); // relies on text plugin
} }
// #enddocregion without-missing-translation

View File

@ -2,7 +2,7 @@
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template"> <file source-language="en" datatype="plaintext" original="ng2.template">
<body> <body>
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html"> <trans-unit id="introductionHeader" datatype="html">
<source>Hello i18n!</source> <source>Hello i18n!</source>
<target>¡Hola i18n!</target> <target>¡Hola i18n!</target>
<note priority="1" from="description">An introduction header for this sample</note> <note priority="1" from="description">An introduction header for this sample</note>
@ -36,6 +36,20 @@ I don&apos;t output any element either
<source/> <source/>
<target>{gender, select, m {hombre} f {mujer}}</target> <target>{gender, select, m {hombre} f {mujer}}</target>
</trans-unit> </trans-unit>
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
<source>Here we have: <x id="ICU"/></source>
<target>Aquí tenemos: <x id="ICU"/></target>
</trans-unit>
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
<source/>
<target>
{count, plural,
=0 { nadie }
=1 {{gender, select, m {un hombre} f {una mujer}}}
other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
}
</target>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -5,7 +5,9 @@
<file source-language="en" datatype="plaintext" original="ng2.template"> <file source-language="en" datatype="plaintext" original="ng2.template">
<body> <body>
<!-- #docregion translated-hello --> <!-- #docregion translated-hello -->
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html"> <!-- #docregion custom-id -->
<trans-unit id="introductionHeader" datatype="html">
<!-- #enddocregion custom-id -->
<source>Hello i18n!</source> <source>Hello i18n!</source>
<target>¡Hola i18n!</target> <target>¡Hola i18n!</target>
<note priority="1" from="description">An introduction header for this sample</note> <note priority="1" from="description">An introduction header for this sample</note>
@ -13,7 +15,9 @@
</trans-unit> </trans-unit>
<!-- #enddocregion translated-hello --> <!-- #enddocregion translated-hello -->
<!-- #docregion translated-other-nodes --> <!-- #docregion translated-other-nodes -->
<!-- #docregion generated-id -->
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html"> <trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
<!-- #enddocregion generated-id -->
<source>I don&apos;t output any element</source> <source>I don&apos;t output any element</source>
<target>No genero ningún elemento</target> <target>No genero ningún elemento</target>
</trans-unit> </trans-unit>
@ -48,6 +52,34 @@
</trans-unit> </trans-unit>
<!-- #enddocregion translate-select-2 --> <!-- #enddocregion translate-select-2 -->
<!-- #enddocregion translated-select --> <!-- #enddocregion translated-select -->
<trans-unit id="db04527df562d12c8607eab2b5723ef6e2066ba0" datatype="html">
<source>Here we have: <x id="ICU"/></source>
<target/>
</trans-unit>
<trans-unit id="000058be4e6f08b685d1d0a70f9da68067df7379" datatype="html">
<source/>
<target/>
</trans-unit>
<!-- #docregion translate-nested -->
<!-- #docregion translate-nested-1 -->
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
<source>Here we have: <x id="ICU"/></source>
<target>Aquí tenemos: <x id="ICU"/></target>
</trans-unit>
<!-- #enddocregion translate-nested-1 -->
<!-- #docregion translate-nested-2 -->
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
<source/>
<target>
{count, plural,
=0 { nadie }
=1 {{gender, select, m {un hombre} f {una mujer}}}
other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
}
</target>
</trans-unit>
<!-- #enddocregion translate-nested-2 -->
<!-- #enddocregion translate-nested -->
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@ -27,7 +27,7 @@ describe('AppComponent', function () {
it('should have expected <h1> text', () => { it('should have expected <h1> text', () => {
fixture.detectChanges(); fixture.detectChanges();
const h1 = de.nativeElement; const h1 = de.nativeElement;
expect(h1.innerText).toMatch(/angular/i, expect(h1.textContent).toMatch(/angular/i,
'<h1> should say something about "Angular"'); '<h1> should say something about "Angular"');
}); });
}); });

View File

@ -36,6 +36,7 @@
<a href="#inputs-and-outputs">Inputs and outputs</a><br> <a href="#inputs-and-outputs">Inputs and outputs</a><br>
<a href="#pipes">Pipes</a><br> <a href="#pipes">Pipes</a><br>
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br> <a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
<a href="#non-null-assertion-operator">Non-null assertion operator <i>!.</i></a><br>
<a href="#enums">Enums</a><br> <a href="#enums">Enums</a><br>
<!-- Interpolation and expressions --> <!-- Interpolation and expressions -->
@ -803,6 +804,12 @@ The null hero's name is {{nullHero && nullHero.name}}
<!-- #enddocregion safe-6 --> <!-- #enddocregion safe-6 -->
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- non-null assertion operator -->
<hr><h2 id="non-null-assertion-operator">Non-null assertion operator <i>!.</i></h2>
<div> <div>
<!-- #docregion non-null-assertion-1 --> <!-- #docregion non-null-assertion-1 -->
<!--No hero, no text --> <!--No hero, no text -->

View File

@ -111,7 +111,7 @@ export class AppComponent implements AfterViewInit, OnInit {
} }
onSave(event: KeyboardEvent) { onSave(event: KeyboardEvent) {
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerText : ''; let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
this.alert('Saved.' + evtMsg); this.alert('Saved.' + evtMsg);
if (event) { event.stopPropagation(); } if (event) { event.stopPropagation(); }
} }
@ -127,6 +127,7 @@ export class AppComponent implements AfterViewInit, OnInit {
resetHeroes() { resetHeroes() {
this.heroes = Hero.heroes.map(hero => hero.clone()); this.heroes = Hero.heroes.map(hero => hero.clone());
this.currentHero = this.heroes[0]; this.currentHero = this.heroes[0];
this.hero = this.currentHero;
this.heroesWithTrackByCountReset = 0; this.heroesWithTrackByCountReset = 0;
} }
@ -172,8 +173,8 @@ function trackChanges(views: QueryList<ElementRef>, changed: () => void) {
let oldRefs = views.toArray(); let oldRefs = views.toArray();
views.changes.subscribe((changes: QueryList<ElementRef>) => { views.changes.subscribe((changes: QueryList<ElementRef>) => {
const changedRefs = changes.toArray(); const changedRefs = changes.toArray();
// Is every changed ElemRef the same as old and in the same position // Check if every changed Element is the same as old and in the same position
const isSame = oldRefs.every((v, i) => v === changedRefs[i]); const isSame = oldRefs.every((v, i) => v.nativeElement === changedRefs[i].nativeElement);
if (!isSame) { if (!isSame) {
oldRefs = changedRefs; oldRefs = changedRefs;
// wait a tick because called after views are constructed // wait a tick because called after views are constructed

View File

@ -41,7 +41,7 @@
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"rimraf": "^2.5.2", "rimraf": "^2.5.2",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"typescript": "~2.0.10", "typescript": "~2.3.1",
"webpack": "2.2.1", "webpack": "2.2.1",
"webpack-dev-server": "2.4.1", "webpack-dev-server": "2.4.1",
"webpack-merge": "^3.0.0" "webpack-merge": "^3.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,21 @@
# Form Validation # Form Validation
{@a top}
Improve overall data quality by validating user input for accuracy and completeness. Improve overall data quality by validating user input for accuracy and completeness.
This cookbook shows how to validate user input in the UI and display useful validation messages This page shows how to validate user input in the UI and display useful validation messages
using first the template-driven forms and then the reactive forms approach. using first the Template Driven Forms and then the Reactive Forms approach.
<div class="l-sub-section"> <div class="l-sub-section">
Read more about these choices in the [Forms](guide/forms) Read more about these choices in the [Forms](guide/forms)
and the [Reactive Forms](guide/reactive-forms) guides. and the [Reactive Forms](guide/reactive-forms) guides.
</div> </div>
{@a toc}
{@a live-example} {@a live-example}
@ -34,17 +28,29 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a template1} ## Built-in validators
Angular forms include a number of built-in validator functions, which are functions
that help you check common user input in forms. In addition to the built-in
validators covered here of `minlength`, `maxlength`,
and `required`, there are others such as `min`, `max`, `email` and `pattern`
for Template Driven as well as Reactive Forms.
For a full list of built-in validators,
see the [Validators](api/forms/Validators) API reference.
## Simple template-driven forms
In the template-driven approach, you arrange
## Simple Template Driven Forms
In the Template Driven approach, you arrange
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template. [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
You add Angular form directives (mostly directives beginning `ng...`) to help You add Angular form directives (mostly directives beginning `ng...`) to help
Angular construct a corresponding internal control model that implements form functionality. Angular construct a corresponding internal control model that implements form functionality.
In template-drive forms, the control model is _implicit_ in the template. In Template Driven forms, the control model is _implicit_ in the template.
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
to the elements. Angular interprets those as well, adding validator functions to the control model. to the elements. Angular interprets those as well, adding validator functions to the control model.
@ -76,7 +82,8 @@ This gives you a reference to the Angular `NgModel` directive
associated with this control that you can use _in the template_ associated with this control that you can use _in the template_
to check for control states such as `valid` and `dirty`. to check for control states such as `valid` and `dirty`.
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and * The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
but only if there are `name` errors and
the control is either `dirty` or `touched`. the control is either `dirty` or `touched`.
* Each nested `<div>` can present a custom message for one of the possible validation errors. * Each nested `<div>` can present a custom message for one of the possible validation errors.
@ -112,9 +119,9 @@ as well as other code to support the view.
Use this template-driven validation technique when working with static forms with simple, standard validation rules. Use this Template Driven validation technique when working with static forms with simple, standard validation rules.
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach: Here are the complete files for the first version of `HeroFormTemplateCompononent` in the Template Driven approach:
<code-tabs> <code-tabs>
@ -132,10 +139,8 @@ Here are the complete files for the first version of `HeroFormTemplateCompononen
{@a template2}
## Template Driven Forms with validation messages in code
## Template-driven forms with validation messages in code
While the layout is straightforward, While the layout is straightforward,
there are obvious shortcomings with the way it's handling validation messages: there are obvious shortcomings with the way it's handling validation messages:
@ -152,7 +157,7 @@ In this example, you can move the logic and the messages into the component with
the template and component. the template and component.
Here's the hero name again, excerpted from the revised template Here's the hero name again, excerpted from the revised template
(Template 2), next to the original version: (template 2), next to the original version:
<code-tabs> <code-tabs>
@ -174,14 +179,14 @@ The `<input>` element HTML is almost the same. There are noteworthy differences:
* There's a new attribute, `forbiddenName`, that is actually a custom validation directive. * There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)). It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)).
See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information See the [custom validation](guide/form-validation#custom-validation) section later in this page for more information
on custom validation directives. on custom validation directives.
* The `#name` template variable is gone because the app no longer refers to the Angular control for this element. * The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
* Binding to the new `formErrors.name` property is sufficent to display all name validation error messages. * Binding to the new `formErrors.name` property is sufficient to display all name validation error messages.
{@a component-class} {@a component-class}
@ -219,7 +224,7 @@ the name of that variable as a string (`'heroForm'` in this case).
* The `heroForm` object changes several times during the life of the component, most notably when you add a new hero. * The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes. Periodically inspecting it reveals these changes.
* Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks#afterview) * Angular calls the `ngAfterViewChecked()` [lifecycle hook method](guide/lifecycle-hooks#afterview)
when anything changes in the view. when anything changes in the view.
That's the right time to see if there's a new `heroForm` object. That's the right time to see if there's a new `heroForm` object.
@ -246,7 +251,7 @@ For each field, the `onValueChanged` handler does the following:
* If such a control exists _and_ it's been changed ("dirty") * If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors. _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
Next, the component needs some error messages of course&mdash;a set for each validated property with Next, the component needs some error messages&mdash;a set for each validated property with
one message per validation rule: one message per validation rule:
<code-example path="form-validation/src/app/template/hero-form-template2.component.ts" region="messages" title="template/hero-form-template2.component.ts (messages)" linenums="false"> <code-example path="form-validation/src/app/template/hero-form-template2.component.ts" region="messages" title="template/hero-form-template2.component.ts (messages)" linenums="false">
@ -278,8 +283,6 @@ Each field has approximately the same number of lines no matter its number of va
The component also grows proportionally, at the rate of one line per validated field The component also grows proportionally, at the rate of one line per validated field
and one line per validation message. and one line per validation message.
Both trends are manageable.
Now that the messages are in code, you have more flexibility and can compose messages more efficiently. Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server. You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code. In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
@ -288,14 +291,14 @@ In short, there are more opportunities to improve message handling now that text
{@a formmodule} {@a formmodule}
### _FormModule_ and template-driven forms ### _FormModule_ and Template Driven forms
Angular has two different forms modules&mdash;`FormsModule` and Angular has two different forms modules&mdash;`FormsModule` and
`ReactiveFormsModule`&mdash;that correspond with the `ReactiveFormsModule`&mdash;that correspond with the
two approaches to form development. Both modules come two approaches to form development. Both modules come
from the same `@angular/forms` library package. from the same `@angular/forms` library package.
You've been reviewing the "Template-driven" approach which requires the `FormsModule`. You've been reviewing the Template Driven approach which requires the `FormsModule`.
Here's how you imported it in the `HeroFormTemplateModule`. Here's how you imported it in the `HeroFormTemplateModule`.
@ -323,9 +326,9 @@ They're not germane to the validation story. Look at the [live example](guide/fo
{@a reactive} {@a reactive}
## Reactive forms with validation in code ## Reactive Forms with validation in code
In the template-driven approach, you markup the template with form elements, validation attributes, In the Template Driven approach, you mark up the template with form elements, validation attributes,
and `ng...` directives from the Angular `FormsModule`. and `ng...` directives from the Angular `FormsModule`.
At runtime, Angular interprets the template and derives its _form control model_. At runtime, Angular interprets the template and derives its _form control model_.
@ -334,23 +337,21 @@ You create the form control model in code. You write the template with form elem
and `form...` directives from the Angular `ReactiveFormsModule`. and `form...` directives from the Angular `ReactiveFormsModule`.
At runtime, Angular binds the template elements to your control model based on your instructions. At runtime, Angular binds the template elements to your control model based on your instructions.
This approach requires a bit more effort. *You have to write the control model and manage it*.
This allows you to do the following: This allows you to do the following:
* Add, change, and remove validation functions on the fly. * Add, change, and remove validation functions on the fly.
* Manipulate the control model dynamically from within the component. * Manipulate the control model dynamically from within the component.
* [Test](guide/form-validation#testing) validation and control logic with isolated unit tests. * [Test](guide/form-validation#testing) validation and control logic with isolated unit tests.
The following cookbook sample re-writes the hero form in _reactive forms_ style. The following sample re-writes the hero form in Reactive Forms style.
{@a reactive-forms-module} {@a reactive-forms-module}
### Switch to the _ReactiveFormsModule_ ### Switch to the _ReactiveFormsModule_
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`. The Reactive Forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
The application module for the reactive forms feature in this sample looks like this: The application module for the Reactive Forms feature in this sample looks like this:
<code-example path="form-validation/src/app/reactive/hero-form-reactive.module.ts" title="src/app/reactive/hero-form-reactive.module.ts" linenums="false"> <code-example path="form-validation/src/app/reactive/hero-form-reactive.module.ts" title="src/app/reactive/hero-form-reactive.module.ts" linenums="false">
@ -358,7 +359,7 @@ The application module for the reactive forms feature in this sample looks like
The reactive forms feature module and component are in the `src/app/reactive` folder. The Reactive Forms feature module and component are in the `src/app/reactive` folder.
Focus on the `HeroFormReactiveComponent` there, starting with its template. Focus on the `HeroFormReactiveComponent` there, starting with its template.
@ -378,8 +379,8 @@ The `heroForm` is the control model that the component class builds and maintain
Next, modify the template HTML elements to match the _reactive forms_ style. Next, modify the template HTML elements to match the Reactive Forms style.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version: Here is the "name" portion of the template again, revised for Reactive Forms and compared with the Template Driven version:
<code-tabs> <code-tabs>
@ -405,15 +406,13 @@ but rather for css styling and accessibility.
<div class="l-sub-section"> <div class="l-sub-section">
Currently, Reactive Forms doesn't add the `required` or `aria-required`
HTML validation attribute to the DOM element
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element when the control has the `required` validator function.
(and perhaps the `aria-required` attribute) when the control has the `required` validator function.
Until then, apply the `required` attribute _and_ add the `Validator.required` function Until then, apply the `required` attribute _and_ add the `Validator.required` function
to the control model, as you'll see below. to the control model, as you'll see below.
</div> </div>
@ -426,15 +425,6 @@ The reactive approach does not use data binding to move data into and out of the
That's all in code. That's all in code.
<div class="l-sub-section">
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
</div>
{@a reactive-component-class} {@a reactive-component-class}
@ -447,7 +437,7 @@ Angular no longer derives the control model from the template so you can no long
You can create the Angular form control model explicitly with You can create the Angular form control model explicitly with
the help of the `FormBuilder` class. the help of the `FormBuilder` class.
Here's the section of code devoted to that process, paired with the template-driven code it replaces: Here's the section of code devoted to that process, paired with the Template Driven code it replaces:
<code-tabs> <code-tabs>
@ -507,18 +497,17 @@ discussed in a separate [section below](guide/form-validation#custom-validation)
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms#formbuilder) section of Reactive Forms guide. Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms#formbuilder) section of Reactive Forms guide.
</div> </div>
{@a committing-changes} {@a committing-changes}
#### Committing hero value changes #### Committing hero value changes
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties. In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
Reactive forms do not use data binding to update data model properties. A Reactive Forms component should not use data binding to
automatically update data model properties.
The developer decides _when and how_ to update the data model from control values. The developer decides _when and how_ to update the data model from control values.
This sample updates the model twice: This sample updates the model twice:
@ -533,18 +522,6 @@ The `onSubmit()` method simply replaces the `hero` object with the combined valu
</code-example> </code-example>
<div class="l-sub-section">
This example is lucky in that the `heroForm.value` properties _just happen_ to
correspond _exactly_ to the hero data object properties.
</div>
The `addHero()` method discards pending changes and creates a brand new `hero` model object. The `addHero()` method discards pending changes and creates a brand new `hero` model object.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" title="form-validation/src/app/reactive/hero-form-reactive.component.ts" linenums="false"> <code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" title="form-validation/src/app/reactive/hero-form-reactive.component.ts" linenums="false">
@ -556,7 +533,7 @@ The `addHero()` method discards pending changes and creates a brand new `hero` m
Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one. Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one.
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model. The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
Here's the complete reactive component file, compared to the two template-driven component files. Here's the complete reactive component file, compared to the two Template Driven component files.
<code-tabs> <code-tabs>
@ -581,7 +558,7 @@ Here's the complete reactive component file, compared to the two template-driven
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves, Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves,
and to compare all of the files in this cookbook sample. and to compare all of the files in this sample.
</div> </div>
@ -594,7 +571,7 @@ and to compare all of the files in this cookbook sample.
## Custom validation ## Custom validation
This cookbook sample has a custom `forbiddenNameValidator()` function that's applied to both the This cookbook sample has a custom `forbiddenNameValidator()` function that's applied to both the
template-driven and the reactive form controls. It's in the `src/app/shared` folder Template Driven and the reactive form controls. It's in the `src/app/shared` folder
and declared in the `SharedModule`. and declared in the `SharedModule`.
Here's the `forbiddenNameValidator()` function: Here's the `forbiddenNameValidator()` function:
@ -623,7 +600,7 @@ and whose value is an arbitrary dictionary of values that you could insert into
### Custom validation directive ### Custom validation directive
In the reactive forms component, the `'name'` control's validator function list In the Reactive Forms component, the `'name'` control's validator function list
has a `forbiddenNameValidator` at the bottom. has a `forbiddenNameValidator` at the bottom.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" title="reactive/hero-form-reactive.component.ts (name validators)" linenums="false"> <code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" title="reactive/hero-form-reactive.component.ts (name validators)" linenums="false">
@ -632,7 +609,7 @@ has a `forbiddenNameValidator` at the bottom.
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`) In the Template Driven example, the `<input>` has the selector (`forbiddenName`)
of a custom _attribute directive_, which rejects "bob". of a custom _attribute directive_, which rejects "bob".
<code-example path="form-validation/src/app/template/hero-form-template2.component.html" region="name-input" title="template/hero-form-template2.component.html (name input)" linenums="false"> <code-example path="form-validation/src/app/template/hero-form-template2.component.html" region="name-input" title="template/hero-form-template2.component.html (name input)" linenums="false">
@ -703,7 +680,7 @@ see [Attribute Directives](guide/attribute-directives).
## Testing Considerations ## Testing Considerations
You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_. You can write _isolated unit tests_ of validation and control logic in Reactive Forms.
_Isolated unit tests_ probe the component class directly, independent of its _Isolated unit tests_ probe the component class directly, independent of its
interactions with its template, the DOM, other dependencies, or Angular itself. interactions with its template, the DOM, other dependencies, or Angular itself.
@ -711,8 +688,8 @@ interactions with its template, the DOM, other dependencies, or Angular itself.
Such tests have minimal setup, are quick to write, and easy to maintain. Such tests have minimal setup, are quick to write, and easy to maintain.
They do not require the `Angular TestBed` or asynchronous testing practices. They do not require the `Angular TestBed` or asynchronous testing practices.
That's not possible with _template-driven_ forms. That's not possible with Template Driven forms.
The template-driven approach relies on Angular to produce the control model and The Template Driven approach relies on Angular to produce the control model and
to derive validation rules from the HTML validation attributes. to derive validation rules from the HTML validation attributes.
You must use the `Angular TestBed` to create component test instances, You must use the `Angular TestBed` to create component test instances,
write asynchronous tests, and interact with the DOM. write asynchronous tests, and interact with the DOM.

View File

@ -407,6 +407,9 @@ like the Observable-based version.
</div> </div>
First, make sure to import the `toPromise` operator of the RxJS library.
<code-example path="http/src/app/toh/hero.service.promise.ts" region="rxjs-imports" title="src/app/toh/hero.service.promise.ts (import rxjs)" linenums="false"></code-example>
Here is a comparison of the `HeroService` using Promises versus Observables, Here is a comparison of the `HeroService` using Promises versus Observables,
highlighting just the parts that are different. highlighting just the parts that are different.

View File

@ -1,19 +1,12 @@
# Internationalization (i18n) # Internationalization (i18n)
{@a top}
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages. Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
Try this <live-example name="i18n" title="i18n Example in Spanish">live example</live-example> Try this <live-example name="i18n" title="i18n Example in Spanish">live example</live-example>
of a JIT-compiled app, translated into Spanish. of a JIT-compiled app, translated into Spanish.
{@a angular-i18n} {@a angular-i18n}
## Angular and _i18n_ template translation ## Angular and _i18n_ template translation
Application internationalization is a challenging, many-faceted effort that Application internationalization is a challenging, many-faceted effort that
@ -23,20 +16,13 @@ Angular's _i18n_ internationalization facilities can help.
This page describes the _i18n_ tools available to assist translation of component template text This page describes the _i18n_ tools available to assist translation of component template text
into multiple languages. into multiple languages.
<div class="l-sub-section"> <div class="l-sub-section">
Practitioners of _internationalization_ refer to a translatable text as a "_message_". Practitioners of _internationalization_ refer to a translatable text as a "_message_".
This page uses the words "_text_" and "_message_" interchangably and in the combination, "_text message_". This page uses the words "_text_" and "_message_" interchangeably and in the combination, "_text message_".
</div> </div>
The _i18n_ template translation process has four phases: The _i18n_ template translation process has four phases:
1. Mark static text messages in your component templates for translation. 1. Mark static text messages in your component templates for translation.
@ -52,11 +38,8 @@ in the target language.
You need to build and deploy a separate version of the application for each supported language. You need to build and deploy a separate version of the application for each supported language.
{@a i18n-attribute} {@a i18n-attribute}
## Mark text with the _i18n_ attribute ## Mark text with the _i18n_ attribute
The Angular `i18n` attribute is a marker for translatable content. The Angular `i18n` attribute is a marker for translatable content.
@ -65,73 +48,112 @@ Place it on every element tag whose fixed text should be translated.
<div class="alert is-helpful"> <div class="alert is-helpful">
`i18n` is not an Angular _directive_. `i18n` is not an Angular _directive_.
It's a custom _attribute_, recognized by Angular tools and compilers. It's a custom _attribute_, recognized by Angular tools and compilers.
After translation, the compiler removes it. After translation, the compiler removes it.
</div> </div>
In the accompanying sample, an `<h1>` tag displays a simple English language greeting In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish: that you translate into Spanish:
<code-example path="i18n/src/app/app.component.1.html" region="greeting" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.1.html" region="greeting" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
Add the `i18n` attribute to the tag to mark it for translation. Add the `i18n` attribute to the tag to mark it for translation.
<code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
{@a help-translator} {@a help-translator}
### Help the translator with a _description_ and _meaning_
### Help the translator with a _description_ and _intent_
In order to translate it accurately, the translator may In order to translate it accurately, the translator may
need a description of the message. need a description of the message.
Assign a description to the i18n attribute: Assign a description to the i18n attribute:
<code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute-desc" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute-desc" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
In order to deliver a correct translation, the translator may need to In order to deliver a correct translation, the translator may need to
know your _intent_&mdash;the true _meaning_ of the text know the _meaning_ or _intent_ of the text within _this particular_ application context.
within _this particular_ application context.
In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`):
You add context by beginning the string with the _meaning_ and
separating it from the _description_ with the `|` character (`<meaning>|<description>`):
<code-example path="i18n/src/app/app.component.html" region="i18n-attribute-meaning" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute-meaning" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
While all appearances of a message with the _same_ meaning have the _same_ translation, While all appearances of a message with the _same_ meaning have the _same_ translation,
a message with *a variety of possible meanings* could have different translations. a message with *a variety of possible meanings* could have different translations.
The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file
to facilitiate contextually-specific translations. to facilitate contextually-specific translations.
{@a custom-id}
### Set a custom _id_ to improve search and maintenance
The angular _i18n_ extractor tool generates a file with a _translation unit_ entry for each `i18n` attribute in a template. By default, it assigns each translation unit a unique _id_ such as this one:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="generated-id" linenums="false">
</code-example>
This _id_ is obscure and difficult for humans to read or remember.
Worse, when you change the translatable text, perhaps to fix a typo,
the extractor tool generates a new _id_ for that translation.
You will lose the translation unless you update it with the new _id_.
That [complicates maintenance](#maintenance).
Consider specifying your own, meaningful _id_ in the `i18n` attribute, **prefixed with `@@`**.
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-solo-id' title='app/app.component.html' linenums="false">
</code-example>
Now the extractor tool and compiler will generate a translation unit with _your custom id_ and never change it.
<code-example path="i18n/src/locale/messages.es.xlf.html" region="custom-id" linenums="false">
</code-example>
Here is the `i18n` attribute with a _definition_, followed by the custom `id`:
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-id' title='app/app.component.html' linenums="false">
</code-example>
Here is a _meaning_ and a _description_ and the _id_ at the end:
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-meaning-and-id' title='app/app.component.html' linenums="false">
</code-example>
<div class="l-sub-section">
Be sure to define _unique_ custom ids. If you use the same id for 2 _different_ blocks of text, only the first one will be extracted,
and its translation used in both blocks of text.
For example:
```html
<p i18n="@@myId">Hello</p>
<p i18n="@@myId">Good bye</p>
```
with the translation:
```xml
<trans-unit id="myId" datatype="html">
<source>Hello</source>
<target state="new">Hola</target>
</trans-unit>
```
Both `<p>` elements will contain the text `Hola`.
</div>
{@a no-element} {@a no-element}
### Translate text without creating an element ### Translate text without creating an element
Suppose there is a stretch of text that you'd like to translate. Suppose there is a stretch of text that you'd like to translate.
@ -140,58 +162,37 @@ you don't want to create a new DOM element merely to facilitate translation.
Here are two techniques to try. Here are two techniques to try.
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered: (1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never rendered:
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
(2) Wrap the text in a pair of HTML comments: (2) Wrap the text in a pair of HTML comments:
<code-example path="i18n/src/app/app.component.html" region="i18n-with-comment" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-with-comment" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
{@a translate-attributes} {@a translate-attributes}
## Add _i18n_ translation attributes
## Add _i18n-..._ translation attributes
You've added an image to your template. You care about accessibility too so you add a `title` attribute: You've added an image to your template. You care about accessibility too so you add a `title` attribute:
<code-example path="i18n/src/app/app.component.1.html" region="i18n-title" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.1.html" region="i18n-title" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
The `title` attribute needs to be translated. The `title` attribute needs to be translated.
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
name of the attribute to translate. name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write: To translate the `title` on the `img` tag from the previous example, write:
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax. You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
{@a cardinality} {@a cardinality}
## Handle singular and plural ## Handle singular and plural
Different languages have different pluralization rules. Different languages have different pluralization rules.
@ -202,13 +203,9 @@ Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves: Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-plural" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
* The first parameter is the key. It is bound to the component property (`wolves`) * The first parameter is the key. It is bound to the component property (`wolves`)
that determines the number of wolves. that determines the number of wolves.
* The second parameter identifies this as a `plural` translation type. * The second parameter identifies this as a `plural` translation type.
@ -217,43 +214,41 @@ categories and their matching values.
Pluralization categories include: Pluralization categories include:
* =0 * =0 (or any other number)
* =1 * zero
* =5 * one
* two
* few * few
* many
* other * other
Put the default _English_ translation in braces (`{}`) next to the pluralization category. Put the default _English_ translation in braces (`{}`) next to the pluralization category.
* When you're talking about one wolf, you could write `=1 {one wolf}`. * When you're talking about one wolf, you could write `=1 {one wolf}`.
* For zero wolves, you could write `=0 {no wolves}`. * For zero wolves, you could write `=0 {no wolves}`.
* For two wolves, you could write `=2 {two wolves}`. * For two wolves, you could write `=2 {two wolves}`.
You could keep this up for three, four, and every other number of wolves. You could keep this up for three, four, and every other number of wolves.
Or you could specify the **`other`** category as a catch-all for any unmatched cardinality Or you could specify the **`other`** category as a catch-all for any unmatched cardinality
and write something like: `other {a wolf pack}`. and write something like: `other {a wolf pack}`.
<div class="l-sub-section"> <div class="l-sub-section">
This syntax conforms to the This syntax conforms to the
<a href="http://userguide.icu-project.org/formatparse/messages" title="ICU Message Format">ICU Message Format</a> <a href="http://userguide.icu-project.org/formatparse/messages" title="ICU Message Format">ICU Message Format</a>
that derives from the that derives from the
<a href="http://cldr.unicode.org/" title="CLDR">Common Locale Data Repository (CLDR)</a>, <a href="http://cldr.unicode.org/" title="CLDR">Common Locale Data Repository (CLDR)</a>,
which specifies the which specifies the
<a href="http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules" title="Pluralization Rules">pluralization rules</a>. <a href="http://cldr.unicode.org/index/cldr-spec/plural-rules" title="Pluralization Rules">pluralization rules</a>.
</div> </div>
{@a select} {@a select}
## Select among alternative texts ## Select among alternative texts
The application displays different text depending upon whether the hero is male or female. The application displays different text depending upon whether the hero is male or female.
These text alternatives require translation too. These text alternatives require translation too.
@ -266,17 +261,18 @@ The following format message in the component template binds to the component's
property, which outputs either an "m" or an "f". property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation: The message maps those values to the appropriate translation:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
## Nesting pluralization and selection expressions
You can also nest different ICU expressions together. For example:
<code-example path="i18n/src/app/app.component.html" region="i18n-nested" title="src/app/app.component.html">
</code-example>
{@a ng-xi18n} {@a ng-xi18n}
## Create a translation source file with the _ng-xi18n_ tool ## Create a translation source file with the _ng-xi18n_ tool
Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts
@ -285,63 +281,46 @@ into a translation source file in an industry standard format.
This is an Angular CLI tool in the `@angular/compiler-cli` npm package. This is an Angular CLI tool in the `@angular/compiler-cli` npm package.
If you haven't already installed the CLI and its `platform-server` peer dependency, do so now: If you haven't already installed the CLI and its `platform-server` peer dependency, do so now:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
npm install @angular/compiler-cli @angular/platform-server --save npm install @angular/compiler-cli @angular/platform-server --save
</code-example> </code-example>
Open a terminal window at the root of the application project and enter the `ng-xi18n` command: Open a terminal window at the root of the application project and enter the `ng-xi18n` command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n ./node_modules/.bin/ng-xi18n
</code-example> </code-example>
<div class="l-sub-section"> <div class="l-sub-section">
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"` Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
</div> </div>
By default, the tool generates a translation file named **`messages.xlf`** in the By default, the tool generates a translation file named **`messages.xlf`** in the
<a href="https://en.wikipedia.org/wiki/XLIFF">XML Localisation Interchange File Format (XLIFF, version 1.2)</a>. <a href="https://en.wikipedia.org/wiki/XLIFF">XML Localization Interchange File Format (XLIFF, version 1.2)</a>.
{@a other-formats} {@a other-formats}
### Other translation formats ### Other translation formats
You can generate a file named **`messages.xmb`** in the Angular i18n tooling supports XLIFF 1.2 and XLIFF 2 as well as the
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" >XML Message Bundle (XMB)</a> format <a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" >XML Message Bundle (XMB)</a>.
by adding the `--i18nFormat=xmb` flag.
You can specify your choice of format _explicitly_ with the `--i18nFormat` flag as illustrated in these example commands
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n --i18nFormat=xmb ./node_modules/.bin/ng-xi18n --i18nFormat=xlf --outFile=messages.xlf
./node_modules/.bin/ng-xi18n --i18nFormat=xlf2 --outFile=messages.xliff2.xlf
./node_modules/.bin/ng-xi18n --i18nFormat=xmb --outFile=messages.xmb
</code-example> </code-example>
The sample in _this_ guide sticks with the default _XLIFF 1.2_ format.
This sample sticks with the _XLIFF_ format.
{@a ng-xi18n-options} {@a ng-xi18n-options}
### Other options ### Other options
You may have to specify additional options. You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration For example, if the `tsconfig.json` TypeScript configuration
file is located somewhere other than in the root folder, file is located somewhere other than in the root folder,
@ -350,15 +329,10 @@ you must identify the path to it with the `-p` option:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json ./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json ./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
</code-example> </code-example>
{@a npm-i18n-script} {@a npm-i18n-script}
### Add an _npm_ script for convenience ### Add an _npm_ script for convenience
Consider adding a convenience shortcut to the `scripts` section of the `package.json` Consider adding a convenience shortcut to the `scripts` section of the `package.json`
@ -371,8 +345,6 @@ to make the command easier to remember and run:
} }
</code-example> </code-example>
Now you can issue command variations such as these: Now you can issue command variations such as these:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
@ -381,28 +353,21 @@ Now you can issue command variations such as these:
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
</code-example> </code-example>
Note the `--` flag before the options. Note the `--` flag before the options.
It tells _npm_ to pass every flag thereafter to `ng-xi18n`. It tells _npm_ to pass every flag thereafter to `ng-xi18n`.
{@a translate} {@a translate}
## Translate text messages ## Translate text messages
The `ng-xi18n` command generates a translation source file The `ng-xi18n` command generates a translation source file
in the project root folder named `messages.xlf`. in the project root folder named `messages.xlf`.
The next step is to translate the English language template The next step is to translate the English language template
text into the specific language translation text into the specific language translation
files. The cookbook sample creates a Spanish translation file. files. The guide sample creates a Spanish translation file.
{@a localization-folder} {@a localization-folder}
### Create a localization folder ### Create a localization folder
You will probably translate into more than one other language so it's a good idea You will probably translate into more than one other language so it's a good idea
@ -413,16 +378,12 @@ such as internationalization files, there.
<div class="l-sub-section"> <div class="l-sub-section">
Localization and internationalization are Localization and internationalization are
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization">different but closely related terms</a>. <a href="https://en.wikipedia.org/wiki/Internationalization_and_localization">different but closely related terms</a>.
</div> </div>
This guide follows that suggestion. It has a `locale` folder under `src/`.
This cookbook follows that suggestion. It has a `locale` folder under the `src/`.
Assets within the folder carry a filename extension that matches a language-culture code from a Assets within the folder carry a filename extension that matches a language-culture code from a
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx">well-known codeset</a>. <a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx">well-known codeset</a>.
@ -432,7 +393,6 @@ Do the same for each target language.
{@a translate-text-nodes} {@a translate-text-nodes}
### Translate text nodes ### Translate text nodes
In the real world, you send the `messages.es.xlf` file to a Spanish translator who fills in the translations In the real world, you send the `messages.es.xlf` file to a Spanish translator who fills in the translations
using one of the using one of the
@ -441,50 +401,41 @@ using one of the
This sample file is easy to translate without a special editor or knowledge of Spanish. This sample file is easy to translate without a special editor or knowledge of Spanish.
Open `messages.es.xlf` and find the first `<trans-unit>` section: Open `messages.es.xlf` and find the first `<trans-unit>` section:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute. This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
<div class="l-sub-section">
Note that the translation unit `id=introductionHeader` is derived from the _custom_ `id`](#custom-id "Set a custom id") that you set earlier, but **without the `@@` prefix** required in the source HTML.
</div>
Using the _source_, _description_, and _meaning_ elements to guide your translation, Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting: replace the `<target/>` tag with the Spanish greeting:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;, after translation)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;, after translation)" linenums="false">
</code-example> </code-example>
<div class="alert is-important">
Note that the tool generates the `id`. **Don't touch it.**
Its value depends on the content of the message and its assigned meaning.
Change either factor and the `id` changes as well.
See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
</div>
Translate the other text nodes the same way: Translate the other text nodes the same way:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
<div class="alert is-important">
**The tool generated the `id`s for _these_ translation units. Don't touch them.**
Each `id` depends upon the content of the message and its assigned meaning.
Change either factor and the `id` changes as well.
See the **[translation file maintenance discussion](#maintenance)**.
This is why you should **[specify custom ids](#custom-id "Set a custom id")** and avoid tool generated ids.
</div>
{@a translate-plural-select} {@a translate-plural-select}
## Translate _plural_ and _select_ ## Translate _plural_ and _select_
Translating _plural_ and _select_ messages is a little tricky. Translating _plural_ and _select_ messages is a little tricky.
@ -496,121 +447,92 @@ However, the `XMB` format does support the ICU rules.
You'll just have to look for them in relation to other translation units that you recognize from elsewhere in the source template. You'll just have to look for them in relation to other translation units that you recognize from elsewhere in the source template.
In this example, you know the translation unit for the `select` must be just below the translation unit for the logo. In this example, you know the translation unit for the `select` must be just below the translation unit for the logo.
{@a translate-plural} {@a translate-plural}
### Translate _plural_ ### Translate _plural_
To translate a `plural`, translate its ICU format match values: To translate a `plural`, translate its ICU format match values:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-plural" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-plural" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
{@a translate-select} {@a translate-select}
### Translate _select_ ### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template: The `select` behaves a little differently. Here again is the ICU format message in the component template:
<code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false"> <code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
The extraction tool broke that into _two_ translation units. The extraction tool broke that into _two_ translation units.
The first unit contains the text that was _outside_ the `select`. The first unit contains the text that was _outside_ the `select`.
In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `select` message. In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `select` message.
Translate the text and leave the placeholder where it is. Translate the text and leave the placeholder where it is.
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-1" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-1" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
The second translation unit, immediately below the first one, contains the `select` message. Translate that. The second translation unit, immediately below the first one, contains the `select` message. Translate that.
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-2" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-2" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
Here they are together, after translation: Here they are together, after translation:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-select" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false"> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-select" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example> </code-example>
### Translate a nested expression
A nested expression is not different from the previous ones. As in the previous example, we have _two_ translation units.
<div class='l-main-content'> The first one contains the text outside the nested expression:
</div> <code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested-1" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The second unit contains the complete nested expression:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested-2" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
And both together:
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The entire template translation is complete. It's The entire template translation is complete. It's
time to incorporate that translation into the application. time to incorporate that translation into the application.
<a id='app-pre-translation'></a>
<div id='app-pre-translation'>
</div>
### The app before translation ### The app before translation
When the previous steps finish, the sample app _and_ its translation file are as follows: When the previous steps finish, the sample app _and_ its translation file are as follows:
<code-tabs> <code-tabs>
<code-pane title="src/app/app.component.html" path="i18n/src/app/app.component.html"> <code-pane title="src/app/app.component.html" path="i18n/src/app/app.component.html">
</code-pane> </code-pane>
<code-pane title="src/app/app.component.ts" path="i18n/src/app/app.component.ts"> <code-pane title="src/app/app.component.ts" path="i18n/src/app/app.component.ts">
</code-pane> </code-pane>
<code-pane title="src/app/app.module.ts" path="i18n/src/app/app.module.ts"> <code-pane title="src/app/app.module.ts" path="i18n/src/app/app.module.ts">
</code-pane> </code-pane>
<code-pane title="src/main.ts" path="i18n/src/main.1.ts"> <code-pane title="src/main.ts" path="i18n/src/main.1.ts">
</code-pane> </code-pane>
<code-pane title="src/locale/messages.es.xlf" path="i18n/src/locale/messages.es.xlf.html"> <code-pane title="src/locale/messages.es.xlf" path="i18n/src/locale/messages.es.xlf.html">
</code-pane> </code-pane>
</code-tabs> </code-tabs>
{@a merge} {@a merge}
## Merge the completed translation file into the app ## Merge the completed translation file into the app
To merge the translated text into component templates, To merge the translated text into component templates,
compile the application with the completed translation file. compile the application with the completed translation file.
The process is the same whether the file is in `.xlf` format or The process is the same whether the file is in `.xlf` format or
in another format that Angular understands, such as `.xlif` or `.xtb`. in another format that Angular understands, such as `.xtb`.
You provide the Angular compiler with three new pieces of information: You provide the Angular compiler with three new pieces of information:
@ -625,11 +547,8 @@ the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
* With [JIT](guide/i18n#jit), you provide the information at bootstrap time. * With [JIT](guide/i18n#jit), you provide the information at bootstrap time.
* With [AOT](guide/i18n#aot), you pass the information as `ngc` options. * With [AOT](guide/i18n#aot), you pass the information as `ngc` options.
{@a jit} {@a jit}
### Merge with the JIT compiler ### Merge with the JIT compiler
The JIT compiler compiles the application in the browser as the application loads. The JIT compiler compiles the application in the browser as the application loads.
@ -643,20 +562,22 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows: Open `index.html` and revise the launch script as follows:
<code-example path="i18n/src/index.html" region="i18n" title="index.html (launch script)" linenums="false"> <code-example path="i18n/src/index.html" region="i18n" title="index.html (launch script)" linenums="false">
</code-example> </code-example>
In this sample, the user's language is hard-coded as a global `document.locale` variable
In this sample, the user's language is hardcoded as a global `document.locale` variable
in the `index.html`. in the `index.html`.
{@a text-plugin} {@a text-plugin}
### SystemJS text plugin ### SystemJS text plugin
<div class="alert is-important">
This plugin only applies to an application using SystemJS. If you are using the Angular CLI, please refer to their
[docs](https://github.com/angular/angular-cli/wiki/xi18n).
</div>
Notice the SystemJS mapping of `text` to a `systemjs-text-plugin.js`. Notice the SystemJS mapping of `text` to a `systemjs-text-plugin.js`.
With the help of a text plugin, SystemJS can read any file as raw text and With the help of a text plugin, SystemJS can read any file as raw text and
return the contents as a string. return the contents as a string.
@ -666,33 +587,26 @@ SystemJS doesn't ship with a raw text plugin but it's easy to add.
Create the following `systemjs-text-plugin.js` in the `src/` folder: Create the following `systemjs-text-plugin.js` in the `src/` folder:
<code-example path="i18n/src/systemjs-text-plugin.js" title="src/systemjs-text-plugin.js" linenums="false"> <code-example path="i18n/src/systemjs-text-plugin.js" title="src/systemjs-text-plugin.js" linenums="false">
</code-example> </code-example>
{@a create-translation-providers} {@a create-translation-providers}
### Create translation providers ### Create translation providers
Three providers tell the JIT compiler how to translate the template texts for a particular language Three providers tell the JIT compiler how to translate the template texts for a particular language
while compiling the application: while compiling the application:
* `TRANSLATIONS` is a string containing the content of the translation file. * `TRANSLATIONS` is a string containing the content of the translation file.
* `TRANSLATIONS_FORMAT` is the format of the file: `xlf`, `xlif`, or `xtb`. * `TRANSLATIONS_FORMAT` is the format of the file: `xlf`, `xlf2`, or `xtb`.
* `LOCALE_ID` is the locale of the target language. * `LOCALE_ID` is the locale of the target language.
The `getTranslationProviders()` function in the following `src/app/i18n-providers.ts` The `getTranslationProviders()` function in the following `src/app/i18n-providers.ts`
creates those providers based on the user's _locale_ creates those providers based on the user's _locale_
and the corresponding translation file: and the corresponding translation file:
<code-example path="i18n/src/app/i18n-providers.ts" title="src/app/i18n-providers.ts"> <code-example path="i18n/src/app/i18n-providers.ts" region="without-missing-translation" title="src/app/i18n-providers.ts">
</code-example> </code-example>
1. It gets the locale from the global `document.locale` variable that was set in `index.html`. 1. It gets the locale from the global `document.locale` variable that was set in `index.html`.
1. If there is no locale or the language is U.S. English (`en-US`), there is no need to translate. 1. If there is no locale or the language is U.S. English (`en-US`), there is no need to translate.
@ -709,10 +623,15 @@ Notice that it appends `!text` to the filename, telling SystemJS to use the [tex
1. Finally, `getTranslationProviders()` returns the entire effort as a promise. 1. Finally, `getTranslationProviders()` returns the entire effort as a promise.
<div class="alert is-important">
The `LOCALE_ID` has to be a valid locale id as explained in [here](http://userguide.icu-project.org/locale).
</div>
{@a bootstrap-the-app} {@a bootstrap-the-app}
### Bootstrap with translation providers
### Bootstrap the app with translation providers
The Angular `bootstrapModule()` method has a second _options_ parameter The Angular `bootstrapModule()` method has a second _options_ parameter
that can influence the behavior of the compiler. that can influence the behavior of the compiler.
@ -722,22 +641,16 @@ and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows: Open the `src/main.ts` and modify the bootstrap code as follows:
<code-example path="i18n/src/main.ts" title="src/main.ts" linenums="false"> <code-example path="i18n/src/main.ts" title="src/main.ts" linenums="false">
</code-example> </code-example>
Notice that it waits for the `getTranslationProviders()` promise to resolve before Notice that it waits for the `getTranslationProviders()` promise to resolve before
bootstrapping the app. bootstrapping the app.
The app is now _internationalized_ for English and Spanish and there is a clear path for adding The app is now _internationalized_ for English and Spanish and there is a clear path for adding
more languages. more languages.
{@a aot} {@a aot}
### _Internationalization_ with the AOT compiler ### _Internationalization_ with the AOT compiler
The JIT compiler translates the application into the target language The JIT compiler translates the application into the target language
@ -752,7 +665,7 @@ language. Then in the host web page, in this case `index.html`,
you determine which language the user needs you determine which language the user needs
and serve the appropriate application package. and serve the appropriate application package.
This cookbook doesn't cover how to build multiple application packages and This guide doesn't cover how to build multiple application packages and
serve them according to the user's language preference. serve them according to the user's language preference.
It does explain the few steps necessary to tell the AOT compiler to apply a translations file. It does explain the few steps necessary to tell the AOT compiler to apply a translations file.
@ -760,7 +673,7 @@ Internationalization with the AOT compiler requires
some setup specifically for AOT compilation. some setup specifically for AOT compilation.
Start with the application project as shown Start with the application project as shown
[just before merging the translation file](guide/i18n#app-pre-translation) [just before merging the translation file](guide/i18n#app-pre-translation)
and refer to the [AOT cookbook](guide/aot-compiler) to make it _AOT-ready_. and refer to the [AOT guide](guide/aot-compiler) to make it _AOT-ready_.
Next, issue an `ngc` compile command for each supported language, including English. Next, issue an `ngc` compile command for each supported language, including English.
The result is a separate version of the application for each language. The result is a separate version of the application for each language.
@ -775,45 +688,65 @@ For this sample, the Spanish language command would be:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf ./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
</code-example> </code-example>
<div class="l-sub-section"> <div class="l-sub-section">
Windows users may have to quote the command: Windows users may have to quote the command:
<code-example language="sh" class="code-shell"> <code-example language="sh" class="code-shell">
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf "./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
</code-example> </code-example>
</div> </div>
### Report missing translations
If you forgot to provide a translation, the build will succeed with a warning that you might easily overlook.
You can configure the Angular compiler for different "missing translation" behaviors:
* Error
* Warning (default)
* Ignore
To change the behavior in JIT, you can use the following configuration:
<code-example language="typescript">
{ provide: CompilerConfig, useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error }) }
</code-example>
A good place to use it is the translation providers:
<code-example path="i18n/src/app/i18n-providers.ts" region="missing-translation" title="src/app/i18n-providers.ts"></code-example>
To change the behavior in AOT, add the `--missingTranslation` flag to the compilation command:
<code-example language="sh" class="code-shell">
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf --missingTranslation=error
</code-example>
{@a maintenance} {@a maintenance}
## File maintenance and _id_ changes
## Translation file maintenance and _id_ changes
As the application evolves, you will change the _i18n_ markup As the application evolves, you will change the _i18n_ markup
and re-run the `ng-xi18n` extraction tool many times. and re-run the `ng-xi18n` extraction tool many times.
The _new_ markup that you add is not a problem; The _new_ markup that you add is not a problem.
but _most_ changes to existing markup trigger But the `id` _can be a serious problem!_
generation of new `id`s for the affected translation units.
If the `id` is generated by the tool, _most_ changes to _existing_ markup
cause the tool to generate a _new_ `id` for the affected translation unit.
After an `id` changes, the translation files are no longer in sync. After an `id` changes, the translation files are no longer in sync.
**All translated versions of the application will fail** during re-compilation. Because of that, you get some warning messages during re-compilation.
The error messages identify the old `id`s that are no longer valid but The warning messages identify that some translations are missing, but they don't tell you which
they don't tell you what the new `id`s should be. old `ids` are no longer valid.
**Commit all translation message files to source control**, If you use a [custom id](#custom-id "Set a custom id"),
the tooling preserves the custom `id` as you make changes to the corresponding translation unit. **Use custom _ids_ unless you have a very good reason to do otherwise.**
Whether you use generated or custom `ids`, **always commit all translation message files to source control**,
especially the English source `messages.xlf`. especially the English source `messages.xlf`.
The difference between the old and the new `messages.xlf` file The difference between the old and the new `messages.xlf` file
help you find and update `id` changes across your translation files. will help you find and update `ids` and other changes across your translation files.

View File

@ -12,6 +12,13 @@ component class instance (the *component*) and its user-facing template.
You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM). You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM).
In Angular, the component plays the part of the controller/viewmodel, and the template represents the view. In Angular, the component plays the part of the controller/viewmodel, and the template represents the view.
This page is a comprehensive technical reference to the Angular template language.
It explains basic principles of the template language and describes most of the syntax that you'll encounter elsewhere in the documentation.
Many code snippets illustrate the points and concepts, all of them available
in the <live-example title="Template Syntax Live Code"></live-example>.
{@a html} {@a html}
## HTML in templates ## HTML in templates
@ -1927,6 +1934,7 @@ The display is blank, but the app keeps rolling without errors.
</code-example> </code-example>
It works perfectly with long property paths such as `a?.b?.c?.d`. It works perfectly with long property paths such as `a?.b?.c?.d`.
<a href="#top-of-page">back to top</a> <a href="#top-of-page">back to top</a>
<hr/> <hr/>
@ -1935,22 +1943,36 @@ It works perfectly with long property paths such as `a?.b?.c?.d`.
### The non-null assertion operator ( <span class="syntax">!</span> ) ### The non-null assertion operator ( <span class="syntax">!</span> )
The Angular **non-null assertion operator (`!`)** is a post-fix operator that asserts that the preceeding property path As of Typescript 2.0, you can enforce [strict null checking](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html "Strict null checking in TypeScript") with the `--strictNullChecks` flag. TypeScript then ensures that no variable is _unintentionally_ null or undefined.
will never be null or undefined.
Unlike the [_safe navigation operator_](guide/template-syntax#safe-navigation-operator "Safe naviation operator (?.)") In this mode, typed variables disallow null and undefined by default. The type checker throws an error if you leave a variable unassigned or try to assign null or undefined to a variable whose type disallows null and undefined.
the **non-null assertion operator** does not guard against a null or undefined; rather, it informs the TypeScript type
checker that there is something it might be unaware of that ensures that this property path is defined. This prevents
TypeScript from reporting that the path is possibly null or undefined when strict null checking is enabled.
For example, if you use [*ngIf](guide/template-syntax#ngIf) to check if `hero` is defined, you can assert the uses of The type checker also throws an error if it can't determine whether a variable will be null or undefined at runtime.
`hero` are defined in the body of the template. You may know that can't happen but the type checker doesn't know.
You tell the type checker that it can't happen by applying the post-fix
[_non-null assertion operator (!)_]((http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator "Non-null assertion operator").
The _Angular_ **non-null assertion operator (`!`)** serves the same purpose in an Angular template.
For example, after you use [*ngIf](guide/template-syntax#ngIf) to check that `hero` is defined, you can assert that
`hero` properties are also defined.
<code-example path="template-syntax/src/app/app.component.html" region="non-null-assertion-1" title="src/app/app.component.html" linenums="false"> <code-example path="template-syntax/src/app/app.component.html" region="non-null-assertion-1" title="src/app/app.component.html" linenums="false">
</code-example> </code-example>
The Angular **non-null assertion operator (`!`)** is like TypeScript's _non-null assertion operator (!)_ When the Angular compiler turns your template into TypeScript code,
introduced in [TypeScript 2.0](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html). it prevents TypeScript from reporting that `hero.name` might be null or undefined.
Unlike the [_safe navigation operator_](guide/template-syntax#safe-navigation-operator "Safe naviation operator (?.)"),
the **non-null assertion operator** does not guard against null or undefined.
Rather it tells the TypeScript type checker to suspend strict null checks for a specific property expression.
You'll need this template operator when you turn on strict null checks. It's optional otherwise.
<a href="#top-of-page">back to top</a>
<hr/>
## Summary ## Summary
You've completed this survey of template syntax. You've completed this survey of template syntax.

View File

@ -160,6 +160,7 @@ This guide describes specific choices that are known to work well.
### Setup ### Setup
There are two fast paths to getting started with unit testing. There are two fast paths to getting started with unit testing.
1. Start a new project following the instructions in [Setup](guide/setup "Setup"). 1. Start a new project following the instructions in [Setup](guide/setup "Setup").
1. Start a new project with the 1. Start a new project with the

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
aio/content/images/bios/deborah.jpg Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
aio/content/images/bios/jeffwhelpley.jpg Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
aio/content/images/bios/josue.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
aio/content/images/bios/lukas.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
aio/content/images/bios/minko.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 11 KiB

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