Compare commits

...

92 Commits
2.4.2 ... 2.4.6

Author SHA1 Message Date
343ee8a3a2 docs(changelog): add changelog for 2.4.6 2017-02-02 20:04:17 -08:00
223b5eb367 chore(release): cut the 2.4.6 release 2017-02-02 19:18:08 -08:00
7e639aac15 fix: ngModel should use rxjs/symbol/observable to detect observable (#14236)
PR closes #14236
2017-02-02 19:18:08 -08:00
83dafd3054 ci: increase git fetch depth to 150 2017-02-02 19:18:08 -08:00
e641636624 fix(common): DatePipe parses input string if it's not a valid date in browser (#13895)
Closes #12334
Closes #13874

PR Close #13895
2017-02-02 19:18:08 -08:00
c409860a9f refactor(common): remove isDate from facade (#13895) 2017-02-02 19:18:07 -08:00
0101aa31d6 ci: fix .pullapprove.yaml'sfile conditions (#14214)
According to [the docs](http://docs.pullapprove.com/groups/conditions/), the correct keywords are `include`/`exclude`, without the trailing `s`.
2017-02-02 19:18:07 -08:00
a5b4af0fdd fix(language-service): do not crash when Angular cannot be located (#14123)
Fixes #14122

PR Close #14123
2017-02-02 19:18:07 -08:00
d9420311ca docs(forms): fix FormArray description (#14094)
Closes #14075

PR Close #14094
2017-02-02 18:50:40 -08:00
774e1db87c fix(forms): Verify functions passed into async validators returns Observable or Promise (#14053) 2017-02-02 18:50:40 -08:00
109f0d16ef fix(common): introduce isObservable method (#14067)
Closes #8848

PR Close #14067
2017-02-02 18:50:40 -08:00
71567d1eee fix(common): add PopStateEvent interface (#13400)
Closes #13378

PR Close #13400
2017-02-02 18:50:40 -08:00
bb71acc172 build: fix red travis: fetch more github history (#14193) 2017-02-02 16:02:55 -08:00
e98d6f0912 ci: fix compiler-cli paths (#14177) 2017-02-02 16:02:55 -08:00
1dbebb184f ci: fix pullapprove groups and conditions (#14167)
- restrict root to be just root
- add fallback users to all groups
- fix indentation
- change order of users so that primary reviewers are first, follow by alpha-sorted secondaries, followed by fallback reviewers
2017-02-02 16:02:55 -08:00
8882b86b54 fix(common) add interface PipeTransform to Async pipe (#14049)
PR Close #14049
2017-02-02 16:02:54 -08:00
0965636735 fix(router): fix CanActivateChild guard provided in a lazy loaded module (#13989)
Closes #12275

PR Close #13989
2017-02-02 16:02:54 -08:00
4d2901d480 fix(router): fix navigation from the root component ngOnInit hook (#13932)
Closes #13795

PR Close #13932
2017-02-02 16:02:54 -08:00
a047124e1a fix(router): fix CanActivate redirect to the root on initial load (#13929)
Closes #13530

PR Close #13929
2017-02-02 16:02:54 -08:00
09e2d20e22 fix(forms): select shows blank line when nothing is selected in IE/Edge (#13903)
Closes #10010

PR Close #13903
2017-02-02 16:02:54 -08:00
e3bdf82c0d docs(developer): add description of npm-run to run locally installed npm scripts (#13765)
PR Close #13765
2017-02-02 16:02:54 -08:00
0614289608 fix(platform-browser): remove style nodes on destroy (#13744)
Closes #11746

PR Close #13744
2017-02-02 16:02:53 -08:00
7c344a4e49 refactor(platform-browser): polishing (#13744) 2017-02-02 16:02:53 -08:00
250dbc4bc8 fix(core): add bootstrapped modules into platform modules list (#13740)
Closes #12015

PR Close #13740
2017-02-02 16:02:53 -08:00
70bbdf55da fix(testing): async/fakeAsync/inject/withModule helpers should pass through context to callback functions (#13718)
Make sure that context (`this`) that is passed to functions generated by test helpers is passed through to the callback functions. Enables usage of Jasmine's variable sharing system to prevent accidental memory leaks during test runs.
2017-02-02 16:02:53 -08:00
41b8d95fa7 fix(core): ViewContainerRef.indexOf doesn't throw error when empty (#13220)
PR Close #13220
2017-02-02 16:02:53 -08:00
1eece5046d fix(http): remove dots from jsonp callback name (#13219)
PR Close #13219
2017-02-02 16:02:52 -08:00
1ef3eeecbd docs: update COMITTER.md with info about pullapprove.com 2017-02-02 16:02:52 -08:00
94500e0fad ci: configure pullapprove to cover the whole repository 2017-02-02 16:02:52 -08:00
gc
dd53606f69 docs(public_api): change description (#13583)
* doc(public_api): change description

Benchpress has been moved to angular/angular in modules/@angular/benchpress

* docs(public_api): change description

Here means 'other projects',like angular-cli, Angular Material. And as we know, benchpress project has been moved to angular/angular in modules/@angular/benchpress. It should not be 'other projects'.
2017-02-02 16:02:52 -08:00
6c8b5dda87 style(docs): update copyright years (#13736) 2017-02-02 16:02:52 -08:00
458ccc1aff refactor(core): simplify ReflectiveInjector by removing code for Dart implementation (#14126)
ReflectiveInjector previously used two strategies for resolving dependencies. These
were to support the Dart implementation, but are no longer needed. A result of this
PR is there is no longer a 20 dependency limit and the generated code is smaller.

PR Close #14126
2017-02-02 16:01:04 -08:00
07cfd8c432 docs: remove obsolete bundles/overview.md file (#14132) 2017-02-02 16:01:03 -08:00
23bd0fbfc1 docs(http): vanilla links do not need link tags (#14097) 2017-02-02 16:32:23 -06:00
3d1e536143 docs(router): remove invalid jsdoc tags (#14097)
The `@selector` tags are not valid.
Dgeni should be able to extract this information
from the directive annotation metadata.
2017-02-02 16:31:49 -06:00
c827097610 ci: add pullapprove config for angular.io 2017-02-02 16:31:49 -06:00
8d4aa82c04 fix(i18n): parse ICU messages while normalizing templates (#14153)
Fixes:
- Inject the i18n specific HtmlParser into the directive normalizer,
- Parse ICU messages while normalizing templates,
- Normalize (visit) the content of ICU messages.

🎄🎁🎅
2017-01-31 21:00:32 -08:00
14e97516cb refactor(abstract): Use abstract keyword where possible to decrease file size. (#14112)
PR Close: #14112
2017-01-27 12:52:12 -08:00
bc47a8cc74 refactor(compiler): add ability to get the context around a ParseLocation (#14113) 2017-01-27 12:55:54 -06:00
32cc6759ef fix(common): DatePipe doesn't throw for NaN (#14117)
Fixes #14103

PR Close #14117
2017-01-27 12:55:28 -06:00
d5f1419afe refactor(size): Use abstract keyword where possible to decrease file size. (#14112) 2017-01-27 12:55:20 -06:00
117fa79c7c fix(upgrade): detect async downgrade component changes (#14039)
This commit effectively reverts 7e0f02f but for `upgrade/static`
as it was an invalid fix for #6385, that created a more significant
bug, which was that changes were not always being detected.

Angular 1 digests should be run inside the ngZone to ensure
that async changes are detected.

We don't know how to fix #6385 without breaking change detection
at this stage. That issue is triggered by async operations, such as
`setTimeout`, being triggered inside scope watcher functions.

One could argue that watcher functions should be pure and not do
work such as triggering async operations. It is possible that the
original use case could be supported by moving the debounce
logic into the watch listener function, which is only called if the
watched value actually changes.

See #13812

PR Close #14039
2017-01-27 12:53:48 -06:00
777ba46837 refactor(compiler): improve error messages in aot compiler (#14017)
Previously aot compiler prints stack traces when it fails to resolve.
New behavior: aot compiler outputs the error message.
Example: https://gist.github.com/bowenni/a7fe81d916e8cd4a06b0e133436f40fb

PR Close #14017
2017-01-27 12:53:43 -06:00
f3d55068a8 fix(compiler): allow empty translations for attributes (#14085)
fixes #13897
2017-01-27 12:53:13 -06:00
7ed39ebaaf docs(changelog): add changelog for 2.4.5 2017-01-25 13:48:29 -08:00
091f0a5aaa chore(release): cut the 2.4.5 release 2017-01-25 13:48:21 -08:00
315606e02c style(compiler): run format 2017-01-25 13:21:04 -08:00
5ea373d184 docs(core): add docs for AnimationStyles and AnimationKeyframe (#14107) 2017-01-25 11:51:02 -08:00
6e36bb7b20 docs(compiler): add comment to warn about regexp changes (#14106)
ref #14082
2017-01-25 11:50:55 -08:00
3b2fb23805 fix(upgrade/static): ensure upgraded injector is initialized early enough (#14065)
This change ensures that the upgraded AngularJS injector is initialized
before the application run blocks are executed.

Closes #13811
2017-01-25 11:49:59 -08:00
bd2eecb4de fix(compiler): fix regexp to support firefox 31 (#14082)
fixes #14029
closes #13900
2017-01-25 11:44:09 -08:00
3d351a4f5f fixup: remove message.id check from this branch 2017-01-25 11:43:16 -08:00
5492fada21 fix(compiler): [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n
There are restrictions on the character set that can be used for xmb and xtb
placeholder names.

However because changing the placeholder names would change the message IDs it
is not possible to add those restrictions to the names used internally. Then we
have to map internal name to public names when generating an xmb file and back
when translating using an xtb file.

Note for implementors of `Serializer`:
- When writing a file, the implementor should take care of converting the
internal names to public names while visiting the message nodes - this is
required because the original nodes are needed to compute the message ID.
- When reading a file, the implementor does not need to take care of the mapping
back to internal names as this is handled in the `I18nToHtmlVisitor` used by the
`TranslationBundle`.

fixes b/34339636
2017-01-25 10:35:03 -08:00
fd4f9acbcf fix(core): export animation classes required for Renderer impl (#14002)
Closes #14001
2017-01-25 10:32:16 -08:00
48528a86e1 docs(common): fix a typo on the DatePipe API docs (#14060) 2017-01-25 10:32:08 -08:00
80364def27 ci: bump node and npm versions in circle.yaml to match travis 2017-01-25 10:31:50 -08:00
1803beb4d5 Fixed documentation reference to canActivate in canDeactivate (#14018)
Simple update to code sample which references canActivate: ['canDeactivateTeam'].
2017-01-25 10:31:42 -08:00
3bcba8a570 chore(docs): add missing comments (#14003)
This is a load-bearing change to avoid duplicate licenses in closure-compiled bundles.
See https://github.com/angular/tsickle/issues/332
2017-01-25 10:30:46 -08:00
84542d8ae7 docs(changelog): add changelog for 2.4.4 2017-01-18 18:35:54 -06:00
17cb3ec565 chore(release): cut the 2.4.4 release 2017-01-18 18:32:57 -06:00
015878afe6 fix(http): don't create a blob out of ArrayBuffer when type is application/octet-stream (#13992)
Closes #13973
2017-01-18 18:28:37 -06:00
2af58622c1 fix(router): enable loadChildren with function in aot (#13909)
Closes #11075
2017-01-18 18:28:02 -06:00
7ffd10541d refactor(core): remove an unused import in application_ref (#13901) 2017-01-18 18:27:52 -06:00
481b099d82 docs(CHANGELOG): added reference to closed issue in CHANGELOG for informational purposes (#13985) 2017-01-18 18:27:25 -06:00
49c4b0fa92 fix(router): routerLinkActive should not throw when not initialized (#13273)
Fixes #13270

PR Close #13273
2017-01-18 18:27:14 -06:00
b8b6b1d27a refactor(router): clean up RouterLinkActive (#13273)
PR Close #13273
2017-01-18 18:27:03 -06:00
892b5ba950 chore(tsc-wrapped): update tsickle to latest (#13471) 2017-01-18 18:26:37 -06:00
bd15110c7d feat(security): allow calc and gradient functions. (#13943)
PR Close #13943

Also includes support for # color notation in function arguments (common
in gradient functions).
2017-01-18 18:25:45 -06:00
2250082fd7 fix(upgrade): detect async downgrade component changes (#13812)
This commit effectively reverts 7e0f02f96e
as it was an invalid fix for #6385, that created a more significant
bug, which was that changes were not always being detected.

Angular 1 digests should be run inside the ngZone to ensure
that async changes are detected.

We don't know how to fix #6385 without breaking change detection
at this stage. That issue is triggered by async operations, such as
`setTimeout`, being triggered inside scope watcher functions.

One could argue that watcher functions should be pure and not do
work such as triggering async operations. It is possible that the
original use case could be supported by moving the debounce
logic into the watch listener function, which is only called if the
watched value actually changes.

Closes #10660, #12318, #12034

PR Close #13812
2017-01-18 18:21:29 -06:00
87316c52db test(upgrade): reorganise test layout (#13812) 2017-01-18 18:21:24 -06:00
606b76d9bb chore(compiler-cli): Move calculateEmitPath into CompilerHost (#13904)
This is so that it can be overriden in an environment specific CompilerHost(like within Google) to customize the output paths.

PR Close #13904
2017-01-18 18:21:09 -06:00
3d0b1b8184 fix(common): support numeric value as discrete cases for NgPlural (#13876)
PR Close #13876
2017-01-18 18:20:56 -06:00
261fd16780 fix(animations): fix internal jscompiler issue and AOT quoting (#13798)
CL #143630929
PR Close #13798
2017-01-18 18:20:47 -06:00
104cc42f6d docs(http): Spelling Fix #13867 2017-01-18 18:20:30 -06:00
a7d28044c5 docs(changelog): add changelog for 2.4.3 2017-01-11 13:38:23 -08:00
055bea2969 chore(release): cut v2.4.3 2017-01-11 13:38:16 -08:00
dad0d21b89 chore(owners): configure pullapprove.com 2017-01-11 11:35:31 -08:00
313683f6f3 fix(compiler-cli): avoid handling functions in loadChildren as lazy load routes paths
The change avoids the compiler CLI internal API from mismatching the following case as lazy loading

```
import { NonLazyLoadedModule } from './non-lazy-loaded/non-lazy-loaded.module';

export function getNonLazyLoadedModule() { return NonLazyLoadedModule; }

export const routes = [
{ path: '/some-path', loadChildren: getNonLazyLoadedModule }
];
```

The output of the check is later passed to `RouteDef.fromString()`, so, it makes sense to be only a string.

Fixes angular/angular-cli#3204
2017-01-11 11:35:23 -08:00
338be6d6a5 refactor(common): remove some facade usages 2017-01-11 11:34:03 -08:00
4b56f79328 refactor(test): <template>/<ng-container>/*-directives
- remove outer `<div>` in tests,
- use `<ng-container>` instead of `<template>` where possible,
- use *... instead of template (tag or attr) where possible.

Fixes #13816
2017-01-11 11:33:30 -08:00
d7f2a3c71b fix(i18n): translate attributes inside elements marked for translation 2017-01-10 17:15:42 -08:00
1c929ae244 docs(NgPlural): fix API docs
Fixes #13786
2017-01-10 16:51:52 -08:00
83d0ff6d13 refactor(Compiler): misc cleanup 2017-01-10 16:50:20 -08:00
d43e5dd44d chore(benchmarks): change var to let 2017-01-09 16:08:33 -08:00
61ba223c1a fix(router): throw an error when navigate to null/undefined path
Closes #10560
Fixes #13384
2017-01-09 18:55:31 -05:00
6164eb25f3 fix(compiler-cli): add support for more than 2 levels of nested lazy routes
This change adds Compiler CLI support for any level of nesting for lazy routes.

For example `{app-root}/lazy-loaded-module-1/lazy-loaded-module-2/lazy-loaded-module-3`

Where `lazy-loaded-module-3` is lazy loaded from `lazy-loaded-module-2`,
and `lazy-loaded-module-2` is lazy loaded from module `lazy-loaded-module-1`,
and `lazy-loaded-module-1` is lazy loaded from `AppModule`

Fixes angular/angular-cli#3663
2017-01-09 18:06:26 -05:00
5e9d3dba3a fix(compiler): avoid evaluating arguments to unknown decorators
Fixes #13605
2017-01-09 18:06:11 -05:00
16922655ca fix(Router): fix checking for object intersection 2017-01-09 18:06:03 -05:00
7dc12b93fe fix(Compiler): fix template binding parsing (*directive="-...")
fixes #13800
2017-01-09 16:50:14 -05:00
1c82b58185 fix(router): RouterLink mirrors input target as attribute
Closes #13837
2017-01-09 16:50:06 -05:00
d6c414c08f fix: correctly show error when karma fails to load 2017-01-09 16:49:01 -05:00
d25d1730c7 chore(tsc-wrapped): bump version number to 0.5.1 2017-01-06 12:46:07 -08:00
159 changed files with 7871 additions and 4366 deletions

233
.pullapprove.yml Normal file
View File

@ -0,0 +1,233 @@
# Configuration for pullapprove.com
#
# Approval access and primary role is determined by info in the project ownership spreadsheet:
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
#
# === GitHub username to Full name map ===
#
# alexeagle - Alex Eagle
# alxhub - Alex Rickabaugh
# chuckjaz - Chuck Jazdzewski
# gkalpak - George Kalpakas
# IgorMinar - Igor Minar
# kara - Kara Erickson
# matsko - Matias Niemelä
# mhevery - Misko Hevery
# petebacondarwin - Pete Bacon Darwin
# pkozlowski-opensource - Pawel Kozlowski
# robwormald - Rob Wormald
# tbosch - Tobias Bosch
# vicb - Victor Berchet
# vikerman - Vikram Subramanian
version: 2
group_defaults:
required: 1
reset_on_reopened:
enabled: true
approve_by_comment:
enabled: false
groups:
root:
conditions:
files:
include:
- "*"
exclude:
- "angular.io/*"
- "integration/*"
- "modules/*"
- "tools/*"
users:
- IgorMinar
- mhevery
build-and-ci:
conditions:
files:
include:
- "*.yml"
- "*.json"
- "*.lock"
- "tools/*"
exclude:
- "tools/@angular/tsc-wrapped/*"
users:
- IgorMinar #primary
- mhevery
integration:
conditions:
files:
- "integration/*"
users:
- alexeagle
- mhevery
- tbosch
- vicb
- IgorMinar #fallback
core:
conditions:
files:
- "modules/@angular/core/*"
users:
- tbosch #primary
- mhevery
- vicb
- IgorMinar #fallback
compiler/animations:
conditions:
files:
- "modules/@angular/compiler/src/animation/*"
users:
- matsko #primary
- tbosch
- IgorMinar #fallback
- mhevery #fallback
compiler/i18n:
conditions:
files:
- "modules/@angular/compiler/src/i18n/*"
users:
- vicb #primary
- tbosch
- IgorMinar #fallback
- mhevery #fallback
compiler:
conditions:
files:
- "modules/@angular/compiler/*"
users:
- tbosch #primary
- mhevery
- IgorMinar #fallback
compiler-cli:
conditions:
files:
- "tools/@angular/tsc-wrapped/*"
- "modules/@angular/compiler-cli/*"
users:
- alexeagle
- chuckjaz
- tbosch
- IgorMinar #fallback
- mhevery #fallback
common:
conditions:
files:
- "modules/@angular/common/*"
users:
- pkozlowski-opensource #primary
- vicb
- IgorMinar #fallback
- mhevery #fallback
forms:
conditions:
files:
- "modules/@angular/forms/*"
users:
- kara #primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
http:
conditions:
files:
- "modules/@angular/http/*"
users:
- vikerman #primary
- alxhub
- IgorMinar #fallback
- mhevery #fallback
language-service:
conditions:
files:
- "modules/@angular/language-service/*"
users:
- chuckjaz #primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
router:
conditions:
files:
- "modules/@angular/router/*"
users:
- vicb #primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
upgrade:
conditions:
files:
- "modules/@angular/upgrade/*"
users:
- petebacondarwin #primary
- gkalpak
- IgorMinar #fallback
- mhevery #fallback
platform-browser:
conditions:
files:
- "modules/@angular/platform-browser/*"
users:
- tbosch #primary
- vicb
- IgorMinar #fallback
- mhevery #fallback
platform-server:
conditions:
files:
- "modules/@angular/platform-server/*"
users:
- vikerman #primary
- alxhub
- IgorMinar #fallback
- mhevery #fallback
platform-webworker:
conditions:
files:
- "modules/@angular/platform-webworker/*"
users:
- vicb #primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
benchpress:
conditions:
files:
- "modules/@angular/benchpress/*"
users:
- tbosch #primary
# needs secondary
- IgorMinar #fallback
- mhevery #fallback
angular.io:
conditions:
files:
- "angular.io/*"
users:
- IgorMinar
- robwormald
- mhevery #fallback

View File

@ -2,6 +2,9 @@ language: node_js
sudo: false
node_js:
- '6.6.0'
git:
# Increased from default (50) to ensure last release tag is in this range
depth: 150
addons:
# firefox: "38.0"

View File

@ -1,3 +1,77 @@
<a name="2.4.6"></a>
## [2.4.6](https://github.com/angular/angular/compare/2.4.5...2.4.6) (2017-02-03)
### Bug Fixes
* **common:** add PopStateEvent interface ([#13400](https://github.com/angular/angular/issues/13400)) ([71567d1](https://github.com/angular/angular/commit/71567d1)), closes [#13378](https://github.com/angular/angular/issues/13378)
* **common:** DatePipe does't throw for NaN ([#14117](https://github.com/angular/angular/issues/14117)) ([32cc675](https://github.com/angular/angular/commit/32cc675)), closes [#14103](https://github.com/angular/angular/issues/14103)
* **common:** DatePipe parses input string if it's not a valid date in browser ([#13895](https://github.com/angular/angular/issues/13895)) ([e641636](https://github.com/angular/angular/commit/e641636)), closes [#12334](https://github.com/angular/angular/issues/12334) [#13874](https://github.com/angular/angular/issues/13874)
* **common:** introduce isObservable method ([#14067](https://github.com/angular/angular/issues/14067)) ([109f0d1](https://github.com/angular/angular/commit/109f0d1)), closes [#8848](https://github.com/angular/angular/issues/8848)
* **compiler:** allow empty translations for attributes ([#14085](https://github.com/angular/angular/issues/14085)) ([f3d5506](https://github.com/angular/angular/commit/f3d5506)), closes [#13897](https://github.com/angular/angular/issues/13897)
* **core:** add bootstrapped modules into platform modules list ([#13740](https://github.com/angular/angular/issues/13740)) ([250dbc4](https://github.com/angular/angular/commit/250dbc4)), closes [#12015](https://github.com/angular/angular/issues/12015)
* **core:** ViewContainerRef.indexOf should not throw error when empty ([#13220](https://github.com/angular/angular/issues/13220)) ([41b8d95](https://github.com/angular/angular/commit/41b8d95))
* **forms:** show a blank line when nothing is selected in IE or Edge ([#13903](https://github.com/angular/angular/issues/13903)) ([09e2d20](https://github.com/angular/angular/commit/09e2d20)), closes [#10010](https://github.com/angular/angular/issues/10010)
* **forms:** verify functions passed into async validators returns Observable or Promise ([#14053](https://github.com/angular/angular/issues/14053)) ([774e1db](https://github.com/angular/angular/commit/774e1db))
* ngModel should use rxjs/symbol/observable to detect observable ([#14236](https://github.com/angular/angular/issues/14236)) ([7e639aa](https://github.com/angular/angular/commit/7e639aa))
* **http:** remove dots from jsonp callback name ([#13219](https://github.com/angular/angular/issues/13219)) ([1eece50](https://github.com/angular/angular/commit/1eece50))
* **i18n:** parse ICU messages while normalizing templates ([#14153](https://github.com/angular/angular/issues/14153)) ([8d4aa82](https://github.com/angular/angular/commit/8d4aa82))
* **language-service:** do not crash when Angular cannot be located ([#14123](https://github.com/angular/angular/issues/14123)) ([a5b4af0](https://github.com/angular/angular/commit/a5b4af0)), closes [#14122](https://github.com/angular/angular/issues/14122)
* **platform-browser:** remove style nodes on destroy ([#13744](https://github.com/angular/angular/issues/13744)) ([0614289](https://github.com/angular/angular/commit/0614289)), closes [#11746](https://github.com/angular/angular/issues/11746)
* **router:** fix CanActivate redirect to the root on initial load ([#13929](https://github.com/angular/angular/issues/13929)) ([a047124](https://github.com/angular/angular/commit/a047124)), closes [#13530](https://github.com/angular/angular/issues/13530)
* **router:** should find guard provided in a lazy loaded module ([#13989](https://github.com/angular/angular/issues/13989)) ([0965636](https://github.com/angular/angular/commit/0965636)), closes [#12275](https://github.com/angular/angular/issues/12275)
* **router:** should allow navigation from root component in ngOnInit hook ([#13932](https://github.com/angular/angular/issues/13932)) ([4d2901d](https://github.com/angular/angular/commit/4d2901d)), closes [#13795](https://github.com/angular/angular/issues/13795)
* **testing:** async/fakeAsync/inject/withModule helpers should pass through context to callback functions ([#13718](https://github.com/angular/angular/issues/13718)) ([70bbdf5](https://github.com/angular/angular/commit/70bbdf5))
* **upgrade:** detect async downgrade component changes ([#14039](https://github.com/angular/angular/issues/14039)) ([117fa79](https://github.com/angular/angular/commit/117fa79)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385)
<a name="2.4.5"></a>
## [2.4.5](https://github.com/angular/angular/compare/2.4.4...2.4.5) (2017-01-25)
### Bug Fixes
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([5492fad](https://github.com/angular/angular/commit/5492fad))
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([bd2eecb](https://github.com/angular/angular/commit/bd2eecb)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([fd4f9ac](https://github.com/angular/angular/commit/fd4f9ac)), closes [#14001](https://github.com/angular/angular/issues/14001)
* **upgrade:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([3b2fb23](https://github.com/angular/angular/commit/3b2fb23)), closes [#13811](https://github.com/angular/angular/issues/13811)
<a name="2.4.4"></a>
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
### Bug Fixes
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
<a name="2.4.3"></a>
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
### Bug Fixes
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
<a name="2.4.2"></a>
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
@ -9,7 +83,7 @@
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
* **compiler:** dont throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645)
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))

View File

@ -6,29 +6,16 @@ for details about how we maintain a linear commit history, and the rules for com
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
Someone with committer access will do the rest.
## The `PR: merge` label and `presubmit-*` branches
# Change approvals
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
Angular committers and also prevent breakages on master.
Change approvals in our monorepo are managed via [pullapprove.com](https://about.pullapprove.com/) and are configured via the `.pullapprove.yaml` file.
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
so the caretaker reviews the changes manually.
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
A robot running as [mary-poppins](https://github.com/mary-poppins)
is notified that the label was added by an authorized person,
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
# Merging
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
Once a change has all the approvals either the last approver or the PR author (if PR author has the project collaborator status) should mark the PR with "PR: merge" label.
This signals to the caretaker that the PR should be merged.
When a Travis build succeeds for a presubmit branch named following the convention,
Travis will re-base the commits, merge to master, and close the PR automatically.
# Who is the Caretaker?
Finally, after merge `mary-poppins` removes the presubmit branch.
## Administration
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
Edit the contents of the [CoreTeamMember Table](
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
See [this explanation](https://twitter.com/IgorMinar/status/799365744806854656).

View File

@ -71,7 +71,13 @@ particular `gulp` and `protractor` commands. If you prefer, you can drop this pa
Since global installs can become stale, and required versions can vary by project, we avoid their
use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
*Option 2*: globally installing the package `npm-run` by running `npm install -g npm-run`
(you might need to prefix this command with `sudo`). You will then be able to run locally installed
package scripts by invoking: e.g., `npm-run gulp build`
(see [npm-run project page](https://github.com/timoxley/npm-run) for more details).
*Option 3*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Installing Bower Modules

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2014-2016 Google, Inc. http://angular.io
Copyright (c) 2014-2017 Google, Inc. http://angular.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,10 +1,10 @@
machine:
node:
version: 5.4.1
version: 6.6.0
dependencies:
pre:
- npm install -g npm@3.6.0
- npm install -g npm@3.5.3
test:
override:

View File

@ -19,7 +19,7 @@ One intentional omission from this list is `@angular/compiler`, which is current
Additionally only the command line usage (not direct use of APIs) of `@angular/compiler-cli` is covered.
Other projects developed by the Angular team like angular-cli, Angular Material, benchpress, will be covered by these or similar guarantees in the future as they mature.
Other projects developed by the Angular team like angular-cli, Angular Material, will be covered by these or similar guarantees in the future as they mature.
Within the supported packages, we provide guarantees for:
@ -37,4 +37,4 @@ We explicitly don't consider the following to be our public API surface:
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)
Our peer dependencies (e.g. typescript, zone.js, or rxjs) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.
Our peer dependencies (e.g. TypeScript, Zone.js, or RxJS) are not considered part of our API surface, but they are included in our SemVer policies. We might update the required version of any of these dependencies in minor releases if the update doesn't cause breaking changes for Angular applications. Peer dependency updates that result in non-trivial breaking changes must be deferred to major Angular releases.

View File

@ -9,7 +9,7 @@
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {isListLikeIterable} from '../facade/collection';
import {isPresent, stringify} from '../facade/lang';
import {stringify} from '../facade/lang';
/**
* @ngModule CommonModule
@ -134,7 +134,7 @@ export class NgClass implements DoCheck {
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
} else {
Object.keys(rawClassVal).forEach(klass => {
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
if (rawClassVal[klass] != null) this._toggleClass(klass, !isCleanup);
});
}
}

View File

@ -21,10 +21,9 @@ import {SwitchView} from './ng_switch';
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* </some-element>
* ```
*
@ -90,8 +89,8 @@ export class NgPlural {
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <ng-container *ngPluralCase="'=0'">...</ng-container>
* <ng-container *ngPluralCase="'other'">...</ng-container>
* <template ngPluralCase="=0">...</template>
* <template ngPluralCase="other">...</template>
* </some-element>
*```
*
@ -104,6 +103,7 @@ export class NgPluralCase {
constructor(
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
ngPlural.addCase(value, new SwitchView(viewContainer, template));
const isANumber: boolean = !isNaN(Number(value));
ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
}
}

View File

@ -10,6 +10,12 @@ import {EventEmitter, Injectable} from '@angular/core';
import {LocationStrategy} from './location_strategy';
/** @experimental */
export interface PopStateEvent {
pop?: boolean;
type?: string;
url?: string;
}
/**
* @whatItDoes `Location` is a service that applications can use to interact with a browser's URL.
@ -122,7 +128,7 @@ export class Location {
* Subscribe to the platform's `popState` events.
*/
subscribe(
onNext: (value: any) => void, onThrow: (exception: any) => void = null,
onNext: (value: PopStateEvent) => void, onThrow: (exception: any) => void = null,
onReturn: () => void = null): Object {
return this._subject.subscribe({next: onNext, error: onThrow, complete: onReturn});
}

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
import {ChangeDetectorRef, OnDestroy, Pipe, PipeTransform, WrappedValue} from '@angular/core';
import {EventEmitter, Observable} from '../facade/async';
import {isPromise} from '../private_import_core';
import {isObservable, isPromise} from '../private_import_core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
interface SubscriptionStrategy {
@ -66,7 +66,7 @@ const _observableStrategy = new ObservableStrategy();
* @stable
*/
@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy {
export class AsyncPipe implements OnDestroy, PipeTransform {
private _latestValue: Object = null;
private _latestReturnedValue: Object = null;
@ -116,7 +116,7 @@ export class AsyncPipe implements OnDestroy {
return _promiseStrategy;
}
if ((<any>obj).subscribe) {
if (isObservable(obj)) {
return _observableStrategy;
}
@ -131,7 +131,7 @@ export class AsyncPipe implements OnDestroy {
this._obj = null;
}
private _updateLatestValue(async: any, value: Object) {
private _updateLatestValue(async: any, value: Object): void {
if (async === this._obj) {
this._latestValue = value;
this._ref.markForCheck();

View File

@ -7,13 +7,13 @@
*/
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {NumberWrapper, isDate} from '../facade/lang';
import {NumberWrapper} from '../facade/lang';
import {DateFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
const ISO8601_DATE_REGEX =
/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
// 1 2 3 4 5 6 7 8 9 10 11
/**
* @ngModule CommonModule
@ -24,7 +24,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
* Where:
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
* (https://www.w3.org/TR/NOTE-datetime).
* - `format` indicates which date/time components to include. The format can be predifined as
* - `format` indicates which date/time components to include. The format can be predefined as
* shown below or custom as shown in the table.
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
@ -103,7 +103,7 @@ export class DatePipe implements PipeTransform {
transform(value: any, pattern: string = 'mediumDate'): string {
let date: Date;
if (isBlank(value)) return null;
if (isBlank(value) || value !== value) return null;
if (typeof value === 'string') {
value = value.trim();
@ -130,8 +130,13 @@ export class DatePipe implements PipeTransform {
}
if (!isDate(date)) {
let match: RegExpMatchArray;
if ((typeof value === 'string') && (match = value.match(ISO8601_DATE_REGEX))) {
date = isoStringToDate(match);
} else {
throw new InvalidPipeArgumentError(DatePipe, value);
}
}
return DateFormatter.format(date, this._locale, DatePipe._ALIASES[pattern] || pattern);
}
@ -140,3 +145,31 @@ export class DatePipe implements PipeTransform {
function isBlank(obj: any): boolean {
return obj == null || obj === '';
}
function isDate(obj: any): obj is Date {
return obj instanceof Date && !isNaN(obj.valueOf());
}
function isoStringToDate(match: RegExpMatchArray): Date {
const date = new Date(0);
let tzHour = 0;
let tzMin = 0;
const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
const timeSetter = match[8] ? date.setUTCHours : date.setHours;
if (match[9]) {
tzHour = toInt(match[9] + match[10]);
tzMin = toInt(match[9] + match[11]);
}
dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
const h = toInt(match[4] || '0') - tzHour;
const m = toInt(match[5] || '0') - tzMin;
const s = toInt(match[6] || '0');
const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
timeSetter.call(date, h, m, s, ms);
return date;
}
function toInt(str: string): number {
return parseInt(str, 10);
}

View File

@ -7,7 +7,6 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -35,7 +34,7 @@ export class I18nPluralPipe implements PipeTransform {
constructor(private _localization: NgLocalization) {}
transform(value: number, pluralMap: {[count: string]: string}): string {
if (isBlank(value)) return '';
if (value == null) return '';
if (typeof pluralMap !== 'object' || pluralMap === null) {
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);

View File

@ -8,7 +8,7 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
import {NumberWrapper} from '../facade/lang';
import {NumberFormatStyle, NumberFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -18,7 +18,7 @@ const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
function formatNumber(
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
if (isBlank(value)) return null;
if (value == null) return null;
// Convert strings to numbers
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
@ -41,13 +41,13 @@ function formatNumber(
if (parts === null) {
throw new Error(`${digits} is not a valid digit info for number pipes`);
}
if (isPresent(parts[1])) { // min integer digits
if (parts[1] != null) { // min integer digits
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
}
if (isPresent(parts[3])) { // min fraction digits
if (parts[3] != null) { // min fraction digits
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
}
if (isPresent(parts[5])) { // max fraction digits
if (parts[5] != null) { // max fraction digits
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
}
}

View File

@ -7,7 +7,6 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -58,7 +57,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@Pipe({name: 'slice', pure: false})
export class SlicePipe implements PipeTransform {
transform(value: any, start: number, end?: number): any {
if (isBlank(value)) return value;
if (value == null) return value;
if (!this.supports(value)) {
throw new InvalidPipeArgumentError(SlicePipe, value);

View File

@ -9,3 +9,4 @@
import {__core_private__ as r} from '@angular/core';
export const isPromise: typeof r.isPromise = r.isPromise;
export const isObservable: typeof r.isObservable = r.isObservable;

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Component, ContentChild, TemplateRef} from '@angular/core';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -29,10 +29,7 @@ export function main() {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
TestComponent,
ComponentUsingTestComponent,
],
declarations: [TestComponent],
imports: [CommonModule],
});
});
@ -77,7 +74,7 @@ export function main() {
}));
it('should iterate over an array of objects', async(() => {
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
const template = '<ul><li *ngFor="let item of items">{{item["name"]}};</li></ul>';
fixture = createTestComponent(template);
// INIT
@ -95,7 +92,7 @@ export function main() {
}));
it('should gracefully handle nulls', async(() => {
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
const template = '<ul><li *ngFor="let item of null">{{item}};</li></ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('');
@ -140,12 +137,8 @@ export function main() {
}));
it('should repeat over nested arrays', async(() => {
const template = '<div>' +
'<div template="ngFor let item of items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div>|' +
'</div>' +
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>|' +
'</div>';
fixture = createTestComponent(template);
@ -157,10 +150,9 @@ export function main() {
}));
it('should repeat over nested arrays with no intermediate element', async(() => {
const template = '<div><template ngFor let-item [ngForOf]="items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>';
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>' +
'</div>';
fixture = createTestComponent(template);
getComponent().items = [['a', 'b'], ['c']];
@ -170,10 +162,11 @@ export function main() {
detectChangesAndExpectText('e-1;f-2;g-2;');
}));
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
it('should repeat over nested ngIf that are the last node in the ngFor template', async(() => {
const template = `<div *ngFor="let item of items; let i=index">` +
`<div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div>` +
`</div>`;
fixture = createTestComponent(template);
@ -189,8 +182,7 @@ export function main() {
}));
it('should display indices correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
@ -202,7 +194,7 @@ export function main() {
it('should display first item correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
'<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -214,7 +206,7 @@ export function main() {
it('should display last item correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
'<span *ngFor="let item of items; let isLast=last">{{isLast.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -226,7 +218,7 @@ export function main() {
it('should display even items correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
'<span *ngFor="let item of items; let isEven=even">{{isEven.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -238,7 +230,7 @@ export function main() {
it('should display odd items correctly', async(() => {
const template =
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
'<span *ngFor="let item of items; let isOdd=odd">{{isOdd.toString()}}</span>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3];
@ -249,55 +241,38 @@ export function main() {
}));
it('should allow to use a custom template', async(() => {
const tcTemplate =
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>';
TestBed.overrideComponent(TestComponent, {set: {template: tcTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
it('should use a default template if a custom one is null', async(() => {
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
`<ul><ng-container *ngFor="let item of items; template: null; let i=index">{{i}}: {{item}};</ng-container></ul>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
it('should use a custom template when both default and a custom one are present', async(() => {
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
const template =
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
fixture.detectChanges();
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
detectChangesAndExpectText('0: a;1: b;2: c;');
}));
describe('track by', () => {
it('should console.warn if trackBy is not a function', async(() => {
// TODO(vicb): expect a warning message when we have a proper log service
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value"></template>`;
const template = `<p *ngFor="let item of items; trackBy: value"></p>`;
fixture = createTestComponent(template);
fixture.componentInstance.value = 0;
fixture.detectChanges();
@ -305,8 +280,7 @@ export function main() {
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
// TODO(vicb): expect no warning message when we have a proper log service
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="value">{{ item }}</template>`;
const template = `<p *ngFor="let item of items; trackBy: value">{{ item }}</p>`;
fixture = createTestComponent(template);
fixture.componentInstance.items = ['a', 'b', 'c'];
fixture.componentInstance.value = null;
@ -317,7 +291,7 @@ export function main() {
it('should set the context to the component instance', async(() => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
`<p *ngFor="let item of items; trackBy: trackByContext.bind(this)"></p>`;
fixture = createTestComponent(template);
thisArg = null;
@ -327,9 +301,7 @@ export function main() {
it('should not replace tracked items', async(() => {
const template =
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
<p>{{items[i]}}</p>
</template>`;
`<p *ngFor="let item of items; trackBy: trackById; let i=index">{{items[i]}}</p>`;
fixture = createTestComponent(template);
const buildItemList = () => {
@ -345,7 +317,7 @@ export function main() {
it('should update implicit local variable on view', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}];
@ -357,7 +329,7 @@ export function main() {
it('should move items around and keep them updated ', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
@ -368,8 +340,7 @@ export function main() {
}));
it('should handle added and removed items properly when tracking by index', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
const template = `<div *ngFor="let item of items; trackBy: trackByIndex">{{item}}</div>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c', 'd'];
@ -389,7 +360,6 @@ class Foo {
@Component({selector: 'test-cmp', template: ''})
class TestComponent {
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
value: any;
items: any[] = [1, 2];
trackById(index: number, item: any): string { return item['id']; }
@ -397,12 +367,7 @@ class TestComponent {
trackByContext(): void { thisArg = this; }
}
@Component({selector: 'outer-cmp', template: ''})
class ComponentUsingTestComponent {
items: any = [1, 2];
}
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
const TEMPLATE = '<div><span *ngFor="let item of items">{{item.toString()}};</span></div>';
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
return TestBed.overrideComponent(TestComponent, {set: {template: template}})

View File

@ -9,6 +9,7 @@
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -28,131 +29,114 @@ export function main() {
});
it('should work in a template attribute', async(() => {
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
const template = '<span *ngIf="booleanCondition">hello</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
}));
it('should work in a template element', async(() => {
const template =
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
it('should work on a template element', async(() => {
const template = '<template [ngIf]="booleanCondition">hello2</template>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello2');
}));
it('should toggle node when condition changes', async(() => {
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
const template = '<span *ngIf="booleanCondition">hello</span>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should handle nested if correctly', async(() => {
const template =
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
'<div *ngIf="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></div>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().nestedBooleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().nestedBooleanCondition = true;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should update several nodes with if', async(() => {
const template = '<div>' +
'<span template="ngIf numberCondition + 1 >= 2">helloNumber</span>' +
'<span template="ngIf stringCondition == \'foo\'">helloString</span>' +
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
'</div>';
const template = '<span *ngIf="numberCondition + 1 >= 2">helloNumber</span>' +
'<span *ngIf="stringCondition == \'foo\'">helloString</span>' +
'<span *ngIf="functionCondition(stringCondition, numberCondition)">helloFunction</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
expect(getDOM().getText(fixture.nativeElement))
.toEqual('helloNumberhelloStringhelloFunction');
getComponent().numberCondition = 0;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloString');
getComponent().numberCondition = 1;
getComponent().stringCondition = 'bar';
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloNumber');
}));
it('should not add the element twice if the condition goes from true to true (JS)',
async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
it('should not add the element twice if the condition goes from truthy to truthy', async(() => {
const template = '<span *ngIf="numberCondition">hello</span>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
let els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
getDOM().addClass(els[0].nativeElement, 'marker');
expect(fixture.nativeElement).toHaveText('hello');
getComponent().numberCondition = 2;
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
expect(fixture.nativeElement).toHaveText('hello');
}));
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
getComponent().numberCondition = 2;
fixture.detectChanges();
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
.toBe(true);
}));
});
}

View File

@ -12,7 +12,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('switch', () => {
describe('ngPlural', () => {
let fixture: ComponentFixture<any>;
function getComponent(): TestComponent { return fixture.componentInstance; }
@ -33,10 +33,25 @@ export function main() {
});
it('should display the template according to the exact value', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul>';
fixture = createTestComponent(template);
getComponent().switchValue = 0;
detectChangesAndExpectText('you have no messages.');
getComponent().switchValue = 1;
detectChangesAndExpectText('you have one message.');
}));
it('should display the template according to the exact numeric value', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="1"><li>you have one message.</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
@ -51,10 +66,9 @@ export function main() {
// https://github.com/angular/angular/issues/9868
// https://github.com/angular/angular/issues/9882
it('should not throw when ngPluralCase contains expressions', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -64,11 +78,10 @@ export function main() {
it('should be applicable to <ng-container> elements', async(() => {
const template = '<div>' +
'<ng-container [ngPlural]="switchValue">' +
const template = '<ng-container [ngPlural]="switchValue">' +
'<template ngPluralCase="=0">you have no messages.</template>' +
'<template ngPluralCase="=1">you have one message.</template>' +
'</ng-container></div>';
'</ng-container>';
fixture = createTestComponent(template);
@ -80,11 +93,10 @@ export function main() {
}));
it('should display the template according to the category', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -96,11 +108,10 @@ export function main() {
}));
it('should default to other when no matches are found', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="other"><li>default message.</li></template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);
@ -109,11 +120,10 @@ export function main() {
}));
it('should prioritize value matches over category matches', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="=2">you have two messages.</template>' +
'</ul></div>';
'</ul>';
fixture = createTestComponent(template);

View File

@ -29,22 +29,19 @@ export function main() {
it('should add styles specified in an object literal', async(() => {
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
fixture = createTestComponent(template);
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
}));
it('should add and change styles specified in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
let expr: {[k: string]: string};
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
expr = getComponent().expr;
let expr = getComponent().expr;
expr['max-width'] = '30%';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});

View File

@ -33,11 +33,10 @@ export function main() {
describe('switch value changes', () => {
it('should switch amongst when values', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a</li></template>' +
'<template ngSwitchCase="b"><li>when b</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchCase="\'b\'">when b</li>' +
'</ul>';
fixture = createTestComponent(template);
@ -51,11 +50,10 @@ export function main() {
});
it('should switch amongst when values with fallback to default', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<li template="ngSwitchCase \'a\'">when a</li>' +
'<li template="ngSwitchDefault">when default</li>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchDefault>when default</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default');
@ -71,15 +69,14 @@ export function main() {
});
it('should support multiple whens with the same value', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -94,12 +91,11 @@ export function main() {
describe('when values changes', () => {
it('should switch amongst when values', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
'<template ngSwitchDefault><li>when default;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="when1">when 1;</li>' +
'<li *ngSwitchCase="when2">when 2;</li>' +
'<li *ngSwitchDefault>when default;</li>' +
'</ul>';
fixture = createTestComponent(template);
getComponent().when1 = 'a';
@ -148,11 +144,10 @@ export function main() {
});
it('should create the default case if there is no other case', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -160,15 +155,14 @@ export function main() {
});
it('should allow defaults before cases', () => {
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'</ul></div>';
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'</ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');

View File

@ -34,29 +34,22 @@ export function main() {
});
});
it('should do nothing if templateRef is null', async(() => {
const template = `<template [ngTemplateOutlet]="null"></template>`;
it('should do nothing if templateRef is `null`', async(() => {
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
}));
it('should insert content specified by TemplateRef', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
const template = `<template #tpl>foo</template>` +
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should clear content if TemplateRef becomes null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
it('should clear content if TemplateRef becomes `null`', async(() => {
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
@ -70,7 +63,8 @@ export function main() {
it('should swap content if TemplateRef changes', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
@ -83,70 +77,47 @@ export function main() {
detectChangesAndExpectText('bar');
}));
it('should display template if context is null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
it('should display template if context is `null`', async(() => {
const template = `<template #tpl>foo</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="null"></ng-container>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should reflect initial context and changes', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('bar');
fixture.componentInstance.context.foo = 'alter-bar';
detectChangesAndExpectText('alter-bar');
}));
it('should reflect user defined $implicit property in the context', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
it('should reflect user defined `$implicit` property in the context', async(() => {
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
detectChangesAndExpectText('bar');
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
detectChangesAndExpectText('bra');
}));
it('should reflect context re-binding', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
`<ng-container [ngTemplateOutlet]="tpl" [ngOutletContext]="context"></ng-container>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {shawshank: 'brooks'};
detectChangesAndExpectText('brooks');
fixture.componentInstance.context = {shawshank: 'was here'};
detectChangesAndExpectText('was here');
}));
});
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;

View File

@ -53,11 +53,13 @@ export function main() {
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
it('should return null for NaN', () => expect(pipe.transform(Number.NaN)).toEqual(null));
it('should support ISO string without time',
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
it('should not support other objects',
() => { expect(() => pipe.transform({})).toThrowError(); });
() => expect(() => pipe.transform({})).toThrowError(/Invalid argument/));
});
describe('transform', () => {
@ -188,8 +190,14 @@ export function main() {
});
it('should format invalid in IE ISO date',
() => expect(pipe.transform('2017-01-11T09:25:14.014-0500')).toEqual('Jan 11, 2017'));
it('should format invalid in Safari ISO date',
() => expect(pipe.transform('2017-01-20T19:00:00+0000')).toEqual('Jan 20, 2017'));
it('should remove bidi control characters',
() => { expect(pipe.transform(date, 'MM/dd/yyyy').length).toEqual(10); });
() => expect(pipe.transform(date, 'MM/dd/yyyy').length).toEqual(10));
});
});
}

View File

@ -0,0 +1,23 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'lazy-feature-comp', template: 'lazy feature, nested!'})
export class LazyFeatureNestedComponent {
}
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureNestedComponent, pathMatch: 'full'},
])],
declarations: [LazyFeatureNestedComponent]
})
export class LazyFeatureNestedModule {
}

View File

@ -16,7 +16,10 @@ export class LazyFeatureComponent {
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
{path: 'feature', loadChildren: './feature.module#FeatureModule'}
{path: 'feature', loadChildren: './feature.module#FeatureModule'}, {
path: 'nested-feature',
loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'
}
])],
declarations: [LazyFeatureComponent]
})

View File

@ -191,6 +191,8 @@ function lazyRoutesTest() {
'./lazy.module#LazyModule': 'lazy.module.ts',
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
'./feature.module#FeatureModule': 'feature/feature.module.ts',
'./lazy-feature-nested.module#LazyFeatureNestedModule': 'feature/lazy-feature-nested.module.ts',
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
'./default.module': 'feature2/default.module.ts',
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'

View File

@ -9,7 +9,7 @@
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"@angular/tsc-wrapped": "0.5.0",
"@angular/tsc-wrapped": "0.5.2",
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0"
},

View File

@ -36,29 +36,6 @@ export class CodeGenerator {
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
private ngCompilerHost: CompilerHost) {}
// Write codegen in a directory structure matching the sources.
private calculateEmitPath(filePath: string): string {
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
codegen(): Promise<any> {
return this.compiler
.compileAll(this.program.getSourceFiles().map(
@ -66,7 +43,7 @@ export class CodeGenerator {
.then(generatedModules => {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
PREAMBLE + generatedModule.source;
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);

View File

@ -261,6 +261,29 @@ export class CompilerHost implements AotCompilerHost {
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
return !excludeRegex.test(filePath);
}
calculateEmitPath(filePath: string): string {
// Write codegen in a directory structure matching the sources.
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
}
export class CompilerHostContextAdapter {

View File

@ -15,8 +15,6 @@
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
import {NgModule} from '@angular/core';
// We cannot depend directly to @angular/router.
type Route = any;
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
@ -63,29 +61,37 @@ export function listLazyRoutesOfModule(
const className = entryRouteDef.className;
// List loadChildren of this single module.
const staticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
const appStaticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
const lazyRoutes: LazyRoute[] =
_extractLazyRoutesFromStaticModule(staticSymbol, reflector, host, ROUTES);
const routes: LazyRouteMap = {};
_extractLazyRoutesFromStaticModule(appStaticSymbol, reflector, host, ROUTES);
lazyRoutes.forEach((lazyRoute: LazyRoute) => {
const allLazyRoutes = lazyRoutes.reduce(
function includeLazyRouteAndSubRoutes(allRoutes: LazyRouteMap, lazyRoute: LazyRoute):
LazyRouteMap {
const route: string = lazyRoute.routeDef.toString();
_assertRoute(routes, lazyRoute);
routes[route] = lazyRoute;
_assertRoute(allRoutes, lazyRoute);
allRoutes[route] = lazyRoute;
// StaticReflector does not support discovering annotations like `NgModule` on default
// exports
// Which means: if a default export NgModule was lazy-loaded, we can discover it, but,
// we cannot parse its routes to see if they have loadChildren or not.
if (!lazyRoute.routeDef.className) {
return allRoutes;
}
const lazyModuleSymbol = reflector.findDeclaration(
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
const subRoutes = _extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
// Populate the map using the routes we just found.
subRoutes.forEach(subRoute => {
_assertRoute(routes, subRoute);
routes[subRoute.routeDef.toString()] = subRoute;
});
});
const subRoutes =
_extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
return routes;
return subRoutes.reduce(includeLazyRouteAndSubRoutes, allRoutes);
},
{});
return allLazyRoutes;
}
@ -192,7 +198,7 @@ function _collectRoutes(
*/
function _collectLoadChildren(routes: Route[]): string[] {
return routes.reduce((m, r) => {
if (r.loadChildren) {
if (r.loadChildren && typeof r.loadChildren === 'string') {
return m.concat(r.loadChildren);
} else if (Array.isArray(r)) {
return m.concat(_collectLoadChildren(r));

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';

View File

@ -9,9 +9,10 @@
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {ReflectorReader} from '../private_import_core';
import {SyntaxError} from '../util';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_IMPORT_LOCATIONS = {
coreDecorators: '@angular/core/src/metadata',
@ -310,6 +311,7 @@ export class StaticReflector implements ReflectorReader {
if (value && (depth != 0 || value.__symbolic != 'error')) {
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
args = args.map(arg => simplifyInContext(context, arg, depth + 1));
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
@ -499,15 +501,15 @@ export class StaticReflector implements ReflectorReader {
return context;
}
const argExpressions: any[] = expression['arguments'] || [];
const args =
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
let converter = self.conversionMap.get(staticSymbol);
if (converter) {
const args =
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
return converter(context, args);
} else {
// Determine if the function is one we can simplify.
const targetFunction = resolveReferenceValue(staticSymbol);
return simplifyCall(staticSymbol, targetFunction, args);
return simplifyCall(staticSymbol, targetFunction, argExpressions);
}
}
break;
@ -537,7 +539,7 @@ export class StaticReflector implements ReflectorReader {
if (e.fileName) {
throw positionalError(message, e.fileName, e.line, e.column);
}
throw new Error(message);
throw new SyntaxError(message);
}
}
@ -651,10 +653,6 @@ class PopulatedScope extends BindingScope {
}
}
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
return a === b;
}
function shouldIgnore(value: any): boolean {
return value && value.__symbolic == 'ignore';
}

View File

@ -273,7 +273,12 @@ export class StaticSymbolResolver {
getSymbolByModule(module: string, symbolName: string, containingFile?: string): StaticSymbol {
const filePath = this.resolveModule(module, containingFile);
if (!filePath) {
throw new Error(`Could not resolve module ${module} relative to ${containingFile}`);
this.reportError(
new Error(`Could not resolve module ${module}${containingFile ? ` relative to $ {
containingFile
} `: ''}`),
null);
return this.getStaticSymbol(`ERROR:${module}`, symbolName);
}
return this.getStaticSymbol(filePath, symbolName);
}

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';

View File

@ -5,16 +5,16 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
import {CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {GeneratedFile} from './generated_file';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummarySerializerHost {
/**
* Returns the output file path of a source file.

View File

@ -15,10 +15,6 @@ import {LifecycleHooks, reflector} from './private_import_core';
import {CssSelector} from './selector';
import {splitAtColon} from './util';
function unimplemented(): any {
throw new Error('unimplemented');
}
// group 0: "[prop] or (event) or @trigger"
// group 1: "prop" from "[prop]"
// group 2: "event" from "(event)"

View File

@ -5,7 +5,6 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';

View File

@ -8,7 +8,7 @@
import * as cdAst from '../expression_parser/ast';
import {isBlank, isPresent} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';
@ -338,7 +338,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) {
if (varExpr) {
result = varExpr.callFn(args);
}
}
@ -374,7 +374,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (isPresent(varExpr)) {
if (varExpr) {
throw new Error('Cannot assign to a reference or variable!');
}
}

View File

@ -11,10 +11,6 @@ import {ViewEncapsulation, isDevMode} from '@angular/core';
import {CompileIdentifierMetadata} from './compile_metadata';
import {Identifiers, createIdentifier} from './identifiers';
function unimplemented(): any {
throw new Error('unimplemented');
}
export class CompilerConfig {
public renderTypes: RenderTypes;
public defaultEncapsulation: ViewEncapsulation;
@ -52,12 +48,12 @@ export class CompilerConfig {
* to help tree shaking.
*/
export abstract class RenderTypes {
get renderer(): CompileIdentifierMetadata { return unimplemented(); }
get renderText(): CompileIdentifierMetadata { return unimplemented(); }
get renderElement(): CompileIdentifierMetadata { return unimplemented(); }
get renderComment(): CompileIdentifierMetadata { return unimplemented(); }
get renderNode(): CompileIdentifierMetadata { return unimplemented(); }
get renderEvent(): CompileIdentifierMetadata { return unimplemented(); }
abstract get renderer(): CompileIdentifierMetadata;
abstract get renderText(): CompileIdentifierMetadata;
abstract get renderElement(): CompileIdentifierMetadata;
abstract get renderComment(): CompileIdentifierMetadata;
abstract get renderNode(): CompileIdentifierMetadata;
abstract get renderEvent(): CompileIdentifierMetadata;
}
export class DefaultRenderTypes implements RenderTypes {

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, ViewEncapsulation} from '@angular/core';
import {ViewEncapsulation} from '@angular/core';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
import {CompilerConfig} from './config';
import {isBlank, isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
@ -102,11 +102,12 @@ export class DirectiveNormalizer {
templateAbsUrl: string): CompileTemplateMetadata {
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
const rootNodesAndErrors = this._htmlParser.parse(
template, stringify(prenomData.componentType), false, interpolationConfig);
template, stringify(prenomData.componentType), true, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n');
throw new SyntaxError(`Template parse errors:\n${errorString}`);
}
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
styles: prenomData.styles,
styleUrls: prenomData.styleUrls,
@ -228,9 +229,13 @@ class TemplatePreparseVisitor implements html.Visitor {
return null;
}
visitExpansion(ast: html.Expansion, context: any): any { html.visitAll(this, ast.cases); }
visitExpansionCase(ast: html.ExpansionCase, context: any): any {
html.visitAll(this, ast.expression);
}
visitComment(ast: html.Comment, context: any): any { return null; }
visitAttribute(ast: html.Attribute, context: any): any { return null; }
visitText(ast: html.Text, context: any): any { return null; }
visitExpansion(ast: html.Expansion, context: any): any { return null; }
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
}

View File

@ -14,8 +14,6 @@ import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util';
/*
* Resolve a `Type` for {@link Directive}.
*

View File

@ -128,7 +128,6 @@ class DirectiveWrapperBuilder implements ClassBuilder {
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
];
const fields: o.ClassField[] = [
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),

View File

@ -7,7 +7,7 @@
*/
import * as chars from '../chars';
import {NumberWrapper, isPresent} from '../facade/lang';
import {NumberWrapper} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
export enum TokenType {
@ -241,7 +241,7 @@ class _Scanner {
this.advance();
str += two;
}
if (isPresent(threeCode) && this.peek == threeCode) {
if (threeCode != null && this.peek == threeCode) {
this.advance();
str += three;
}

View File

@ -52,7 +52,7 @@ export class Extractor {
extract(rootFiles: string[]): Promise<MessageBundle> {
const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
const {ngModuleByPipeOrDirective, files, ngModules} =
const {files, ngModules} =
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
return Promise
.all(ngModules.map(

View File

@ -53,20 +53,22 @@ enum _VisitorMode {
* @internal
*/
class _Visitor implements html.Visitor {
private _depth: number;
// <el i18n>...</el>
private _inI18nNode: boolean;
private _depth: number;
private _inImplicitNode: boolean;
// <!--i18n-->...<!--/i18n-->
private _inI18nBlock: boolean;
private _blockMeaningAndDesc: string;
private _blockChildren: html.Node[];
private _blockStartDepth: number;
private _inI18nBlock: boolean;
// {<icu message>}
private _inIcu: boolean;
// set to void 0 when not in a section
private _msgCountAtSectionStart: number;
private _errors: I18nError[];
private _mode: _VisitorMode;
@ -208,50 +210,31 @@ class _Visitor implements html.Visitor {
this._depth++;
const wasInI18nNode = this._inI18nNode;
const wasInImplicitNode = this._inImplicitNode;
let childNodes: html.Node[];
let childNodes: html.Node[] = [];
let translatedChildNodes: html.Node[];
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
// message
// Extract:
// - top level nodes with the (implicit) "i18n" attribute if not already in a section
// - ICU messages
const i18nAttr = _getI18nAttr(el);
const i18nMeta = i18nAttr ? i18nAttr.value : '';
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
!this._isInTranslatableSection;
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
this._inImplicitNode = this._inImplicitNode || isImplicit;
this._inImplicitNode = wasInImplicitNode || isImplicit;
if (!this._isInTranslatableSection && !this._inIcu) {
if (i18nAttr) {
// explicit translation
if (i18nAttr || isTopLevelImplicit) {
this._inI18nNode = true;
const message = this._addMessage(el.children, i18nAttr.value);
childNodes = this._translateMessage(el, message);
} else if (isTopLevelImplicit) {
// implicit translation
this._inI18nNode = true;
const message = this._addMessage(el.children);
childNodes = this._translateMessage(el, message);
const message = this._addMessage(el.children, i18nMeta);
translatedChildNodes = this._translateMessage(el, message);
}
if (this._mode == _VisitorMode.Extract) {
const isTranslatable = i18nAttr || isTopLevelImplicit;
if (isTranslatable) {
this._openTranslatableSection(el);
}
if (isTranslatable) this._openTranslatableSection(el);
html.visitAll(this, el.children);
if (isTranslatable) {
this._closeTranslatableSection(el, el.children);
}
}
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isTopLevelImplicit) {
childNodes = [];
el.children.forEach(child => {
const visited = child.visit(this, context);
if (visited && !this._isInTranslatableSection) {
// Do not add the children from translatable sections (= i18n blocks here)
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
childNodes = childNodes.concat(visited);
}
});
if (isTranslatable) this._closeTranslatableSection(el, el.children);
}
} else {
if (i18nAttr || isTopLevelImplicit) {
@ -263,20 +246,19 @@ class _Visitor implements html.Visitor {
// Descend into child nodes for extraction
html.visitAll(this, el.children);
}
}
if (this._mode == _VisitorMode.Merge) {
// Translate attributes in ICU messages
childNodes = [];
el.children.forEach(child => {
if (this._mode === _VisitorMode.Merge) {
const visitNodes = translatedChildNodes || el.children;
visitNodes.forEach(child => {
const visited = child.visit(this, context);
if (visited && !this._isInTranslatableSection) {
// Do not add the children from translatable sections (= i18n blocks here)
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
// They will be added later in this loop when the block closes (i.e. on `<!-- /i18n -->`)
childNodes = childNodes.concat(visited);
}
});
}
}
this._visitAttributesOf(el);
@ -285,7 +267,6 @@ class _Visitor implements html.Visitor {
this._inImplicitNode = wasInImplicitNode;
if (this._mode === _VisitorMode.Merge) {
// There are no childNodes in translatable sections - those nodes will be replace anyway
const translatedAttrs = this._translateAttributes(el);
return new html.Element(
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
@ -344,6 +325,7 @@ class _Visitor implements html.Visitor {
}
// Translates the given message given the `TranslationBundle`
// This is used for translating elements / blocks - see `_translateAttributes` for attributes
// no-op when called in extraction mode (returns [])
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
if (message && this._mode === _VisitorMode.Merge) {
@ -385,7 +367,9 @@ class _Visitor implements html.Visitor {
const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
const nodes = this._translations.get(message);
if (nodes) {
if (nodes[0] instanceof html.Text) {
if (nodes.length == 0) {
translatedAttributes.push(new html.Attribute(attr.name, '', attr.sourceSpan));
} else if (nodes[0] instanceof html.Text) {
const value = (nodes[0] as html.Text).value;
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
} else {
@ -420,7 +404,7 @@ class _Visitor implements html.Visitor {
}
/**
* Marks the start of a section, see `_endSection`
* Marks the start of a section, see `_closeTranslatableSection`
*/
private _openTranslatableSection(node: html.Node): void {
if (this._isInTranslatableSection) {
@ -432,7 +416,7 @@ class _Visitor implements html.Visitor {
/**
* A translatable section could be:
* - a translatable element,
* - the content of translatable element,
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
*/
private get _isInTranslatableSection(): boolean {

View File

@ -111,8 +111,14 @@ export class PlaceholderRegistry {
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
private _generateUniqueName(base: string): string {
const next = this._placeHolderNameCounts[base];
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
return next ? `${base}_${next}` : base;
const seen = this._placeHolderNameCounts.hasOwnProperty(base);
if (!seen) {
this._placeHolderNameCounts[base] = 1;
return base;
}
const id = this._placeHolderNameCounts[base];
this._placeHolderNameCounts[base] = id + 1;
return `${base}_${id}`;
}
}

View File

@ -8,10 +8,26 @@
import * as i18n from '../i18n_ast';
export interface Serializer {
write(messages: i18n.Message[]): string;
export abstract class Serializer {
abstract write(messages: i18n.Message[]): string;
load(content: string, url: string): {[msgId: string]: i18n.Node[]};
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
digest(message: i18n.Message): string;
abstract digest(message: i18n.Message): string;
// Creates a name mapper, see `PlaceholderMapper`
// Returning `null` means that no name mapping is used.
createNameMapper(message: i18n.Message): PlaceholderMapper { return null; }
}
/**
* A `PlaceholderMapper` converts placeholder names from internal to serialized representation and
* back.
*
* It should be used for serialization format that put constraints on the placeholder names.
*/
export interface PlaceholderMapper {
toPublicName(internalName: string): string;
toInternalName(publicName: string): string;
}

View File

@ -27,7 +27,7 @@ const _UNIT_TAG = 'trans-unit';
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
export class Xliff implements Serializer {
export class Xliff extends Serializer {
write(messages: i18n.Message[]): string {
const visitor = new _WriteVisitor();
const visited: {[id: string]: boolean} = {};

View File

@ -9,7 +9,7 @@
import {decimalDigest} from '../digest';
import * as i18n from '../i18n_ast';
import {Serializer} from './serializer';
import {PlaceholderMapper, Serializer} from './serializer';
import * as xml from './xml_helper';
const _MESSAGES_TAG = 'messagebundle';
@ -37,7 +37,7 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
<!ELEMENT ex (#PCDATA)>`;
export class Xmb implements Serializer {
export class Xmb extends Serializer {
write(messages: i18n.Message[]): string {
const exampleVisitor = new ExampleVisitor();
const visitor = new _Visitor();
@ -51,6 +51,8 @@ export class Xmb implements Serializer {
if (visited[id]) return;
visited[id] = true;
const mapper = this.createNameMapper(message);
const attrs: {[k: string]: string} = {id};
if (message.description) {
@ -62,7 +64,8 @@ export class Xmb implements Serializer {
}
rootNode.children.push(
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
new xml.CR(2),
new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes, {mapper})));
});
rootNode.children.push(new xml.CR());
@ -82,22 +85,29 @@ export class Xmb implements Serializer {
}
digest(message: i18n.Message): string { return digest(message); }
createNameMapper(message: i18n.Message): PlaceholderMapper {
return new XmbPlaceholderMapper(message);
}
}
class _Visitor implements i18n.Visitor {
visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; }
visitText(text: i18n.Text, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
return [new xml.Text(text.value)];
}
visitContainer(container: i18n.Container, context?: any): xml.Node[] {
visitContainer(container: i18n.Container, ctx: any): xml.Node[] {
const nodes: xml.Node[] = [];
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this)));
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this, ctx)));
return nodes;
}
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
visitIcu(icu: i18n.Icu, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
Object.keys(icu.cases).forEach((c: string) => {
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this, ctx), new xml.Text(`} `));
});
nodes.push(new xml.Text(`}`));
@ -105,30 +115,34 @@ class _Visitor implements i18n.Visitor {
return nodes;
}
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
const startEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`<${ph.tag}>`)]);
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.startName}, [startEx]);
let name = ctx.mapper.toPublicName(ph.startName);
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [startEx]);
if (ph.isVoid) {
// void tags have no children nor closing tags
return [startTagPh];
}
const closeEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`</${ph.tag}>`)]);
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.closeName}, [closeEx]);
name = ctx.mapper.toPublicName(ph.closeName);
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [closeEx]);
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
return [startTagPh, ...this.serialize(ph.children, ctx), closeTagPh];
}
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
visitPlaceholder(ph: i18n.Placeholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
const name = ctx.mapper.toPublicName(ph.name);
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
}
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
const name = ctx.mapper.toPublicName(ph.name);
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
}
serialize(nodes: i18n.Node[]): xml.Node[] {
return [].concat(...nodes.map(node => node.visit(this)));
serialize(nodes: i18n.Node[], ctx: {mapper: PlaceholderMapper}): xml.Node[] {
return [].concat(...nodes.map(node => node.visit(this, ctx)));
}
}
@ -158,3 +172,69 @@ class ExampleVisitor implements xml.IVisitor {
visitDeclaration(decl: xml.Declaration): void {}
visitDoctype(doctype: xml.Doctype): void {}
}
/**
* XMB/XTB placeholders can only contain A-Z, 0-9 and _
*
* Because such restrictions do not exist on placeholder names generated locally, the
* `PlaceholderMapper` is used to convert internal names to XMB names when the XMB file is
* serialized and back from XTB to internal names when an XTB is loaded.
*/
export class XmbPlaceholderMapper implements PlaceholderMapper, i18n.Visitor {
private internalToXmb: {[k: string]: string} = {};
private xmbToNextId: {[k: string]: number} = {};
private xmbToInternal: {[k: string]: string} = {};
// create a mapping from the message
constructor(message: i18n.Message) { message.nodes.forEach(node => node.visit(this)); }
toPublicName(internalName: string): string {
return this.internalToXmb.hasOwnProperty(internalName) ? this.internalToXmb[internalName] :
null;
}
toInternalName(publicName: string): string {
return this.xmbToInternal.hasOwnProperty(publicName) ? this.xmbToInternal[publicName] : null;
}
visitText(text: i18n.Text, ctx?: any): any { return null; }
visitContainer(container: i18n.Container, ctx?: any): any {
container.children.forEach(child => child.visit(this));
}
visitIcu(icu: i18n.Icu, ctx?: any): any {
Object.keys(icu.cases).forEach(k => { icu.cases[k].visit(this); });
}
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx?: any): any {
this.addPlaceholder(ph.startName);
ph.children.forEach(child => child.visit(this));
this.addPlaceholder(ph.closeName);
}
visitPlaceholder(ph: i18n.Placeholder, ctx?: any): any { this.addPlaceholder(ph.name); }
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx?: any): any { this.addPlaceholder(ph.name); }
// XMB placeholders could only contains A-Z, 0-9 and _
private addPlaceholder(internalName: string): void {
if (!internalName || this.internalToXmb.hasOwnProperty(internalName)) {
return;
}
let xmbName = internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
if (this.xmbToInternal.hasOwnProperty(xmbName)) {
// Create a new XMB when it has already been used
const nextId = this.xmbToNextId[xmbName];
this.xmbToNextId[xmbName] = nextId + 1;
xmbName = `${xmbName}_${nextId}`;
} else {
this.xmbToNextId[xmbName] = 1;
}
this.internalToXmb[internalName] = xmbName;
this.xmbToInternal[xmbName] = internalName;
}
}

View File

@ -11,14 +11,14 @@ import {XmlParser} from '../../ml_parser/xml_parser';
import * as i18n from '../i18n_ast';
import {I18nError} from '../parse_util';
import {Serializer} from './serializer';
import {digest} from './xmb';
import {PlaceholderMapper, Serializer} from './serializer';
import {XmbPlaceholderMapper, digest} from './xmb';
const _TRANSLATIONS_TAG = 'translationbundle';
const _TRANSLATION_TAG = 'translation';
const _PLACEHOLDER_TAG = 'ph';
export class Xtb implements Serializer {
export class Xtb extends Serializer {
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
@ -43,6 +43,10 @@ export class Xtb implements Serializer {
}
digest(message: i18n.Message): string { return digest(message); }
createNameMapper(message: i18n.Message): PlaceholderMapper {
return new XmbPlaceholderMapper(message);
}
}
// Extract messages as xml nodes from the xtb file

View File

@ -11,7 +11,7 @@ import {HtmlParser} from '../ml_parser/html_parser';
import * as i18n from './i18n_ast';
import {I18nError} from './parse_util';
import {Serializer} from './serializers/serializer';
import {PlaceholderMapper, Serializer} from './serializers/serializer';
/**
* A container for translated messages
@ -21,16 +21,20 @@ export class TranslationBundle {
constructor(
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
public digest: (m: i18n.Message) => string) {
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest);
public digest: (m: i18n.Message) => string,
public mapperFactory?: (m: i18n.Message) => PlaceholderMapper) {
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest, mapperFactory);
}
// Creates a `TranslationBundle` by parsing the given `content` with the `serializer`.
static load(content: string, url: string, serializer: Serializer): TranslationBundle {
const i18nNodesByMsgId = serializer.load(content, url);
const digestFn = (m: i18n.Message) => serializer.digest(m);
return new TranslationBundle(i18nNodesByMsgId, digestFn);
const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m);
return new TranslationBundle(i18nNodesByMsgId, digestFn, mapperFactory);
}
// Returns the translation as HTML nodes from the given source message.
get(srcMsg: i18n.Message): html.Node[] {
const html = this._i18nToHtml.convert(srcMsg);
@ -46,15 +50,17 @@ export class TranslationBundle {
class I18nToHtmlVisitor implements i18n.Visitor {
private _srcMsg: i18n.Message;
private _srcMsgStack: i18n.Message[] = [];
private _contextStack: {msg: i18n.Message, mapper: (name: string) => string}[] = [];
private _errors: I18nError[] = [];
private _mapper: (name: string) => string;
constructor(
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
private _digest: (m: i18n.Message) => string) {}
private _digest: (m: i18n.Message) => string,
private _mapperFactory: (m: i18n.Message) => PlaceholderMapper) {}
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
this._srcMsgStack.length = 0;
this._contextStack.length = 0;
this._errors.length = 0;
// i18n to text
const text = this._convertToText(srcMsg);
@ -88,7 +94,7 @@ class I18nToHtmlVisitor implements i18n.Visitor {
}
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
const phName = ph.name;
const phName = this._mapper(ph.name);
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
return this._srcMsg.placeholders[phName];
}
@ -105,14 +111,26 @@ class I18nToHtmlVisitor implements i18n.Visitor {
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
/**
* Convert a source message to a translated text string:
* - text nodes are replaced with their translation,
* - placeholders are replaced with their content,
* - ICU nodes are converted to ICU expressions.
*/
private _convertToText(srcMsg: i18n.Message): string {
const digest = this._digest(srcMsg);
const mapper = this._mapperFactory ? this._mapperFactory(srcMsg) : null;
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
this._srcMsgStack.push(this._srcMsg);
this._contextStack.push({msg: this._srcMsg, mapper: this._mapper});
this._srcMsg = srcMsg;
this._mapper = (name: string) => mapper ? mapper.toInternalName(name) : name;
const nodes = this._i18nNodesByMsgId[digest];
const text = nodes.map(node => node.visit(this)).join('');
this._srcMsg = this._srcMsgStack.pop();
const context = this._contextStack.pop();
this._srcMsg = context.msg;
this._mapper = context.mapper;
return text;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol';
import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, OpaqueToken, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config';
@ -40,6 +40,8 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
`No ResourceLoader implementation has been provided. Can't read the url "${url}"`);}
};
const baseHtmlParser = new OpaqueToken('HtmlParser');
/**
* A set of providers that provide `JitCompiler` and its dependencies to use for
* template compilation.
@ -52,17 +54,24 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
Console,
Lexer,
Parser,
HtmlParser,
{
provide: baseHtmlParser,
useClass: HtmlParser,
},
{
provide: i18n.I18NHtmlParser,
useFactory: (parser: HtmlParser, translations: string, format: string) =>
new i18n.I18NHtmlParser(parser, translations, format),
deps: [
HtmlParser,
baseHtmlParser,
[new Optional(), new Inject(TRANSLATIONS)],
[new Optional(), new Inject(TRANSLATIONS_FORMAT)],
]
},
{
provide: HtmlParser,
useExisting: i18n.I18NHtmlParser,
},
TemplateParser,
DirectiveNormalizer,
CompileMetadataResolver,

View File

@ -283,7 +283,7 @@ class _TreeBuilder {
const tagDef = this.getTagDefinition(el.name);
const {parent, container} = this._getParentElementSkippingContainers();
if (isPresent(parent) && tagDef.requireExtraParent(parent.name)) {
if (parent && tagDef.requireExtraParent(parent.name)) {
const newParent = new html.Element(
tagDef.parentToAdd, [], [], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
this._insertBeforeContainer(parent, container, newParent);

View File

@ -9,7 +9,7 @@
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
import {createDiTokenExpression} from './compiler_util/identifier_util';
import {isPresent} from './facade/lang';
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifier, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast';

View File

@ -9,7 +9,7 @@
import {NgModule, Type} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang';
import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
@ -30,7 +30,7 @@ export class NgModuleResolver {
const ngModuleMeta: NgModule =
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
if (isPresent(ngModuleMeta)) {
if (ngModuleMeta) {
return ngModuleMeta;
} else {
if (throwIfNotFound) {

View File

@ -7,7 +7,6 @@
*/
import {CompileIdentifierMetadata} from '../compile_metadata';
import {ValueTransformer, visitValue} from '../util';
import * as o from './output_ast';

View File

@ -48,6 +48,51 @@ export class ParseLocation {
}
return new ParseLocation(this.file, offset, line, col);
}
// Return the source around the location
// Up to `maxChars` or `maxLines` on each side of the location
getContext(maxChars: number, maxLines: number): {before: string, after: string} {
const content = this.file.content;
let startOffset = this.offset;
if (isPresent(startOffset)) {
if (startOffset > content.length - 1) {
startOffset = content.length - 1;
}
let endOffset = startOffset;
let ctxChars = 0;
let ctxLines = 0;
while (ctxChars < maxChars && startOffset > 0) {
startOffset--;
ctxChars++;
if (content[startOffset] == '\n') {
if (++ctxLines == maxLines) {
break;
}
}
}
ctxChars = 0;
ctxLines = 0;
while (ctxChars < maxChars && endOffset < content.length - 1) {
endOffset++;
ctxChars++;
if (content[endOffset] == '\n') {
if (++ctxLines == maxLines) {
break;
}
}
}
return {
before: content.substring(startOffset, this.offset),
after: content.substring(this.offset, endOffset + 1),
};
}
return null;
}
}
export class ParseSourceFile {
@ -74,47 +119,9 @@ export class ParseError {
public level: ParseErrorLevel = ParseErrorLevel.FATAL) {}
toString(): string {
const source = this.span.start.file.content;
let ctxStart = this.span.start.offset;
let contextStr = '';
let details = '';
if (isPresent(ctxStart)) {
if (ctxStart > source.length - 1) {
ctxStart = source.length - 1;
}
let ctxEnd = ctxStart;
let ctxLen = 0;
let ctxLines = 0;
while (ctxLen < 100 && ctxStart > 0) {
ctxStart--;
ctxLen++;
if (source[ctxStart] == '\n') {
if (++ctxLines == 3) {
break;
}
}
}
ctxLen = 0;
ctxLines = 0;
while (ctxLen < 100 && ctxEnd < source.length - 1) {
ctxEnd++;
ctxLen++;
if (source[ctxEnd] == '\n') {
if (++ctxLines == 3) {
break;
}
}
}
const context = source.substring(ctxStart, this.span.start.offset) + '[ERROR ->]' +
source.substring(this.span.start.offset, ctxEnd + 1);
contextStr = ` ("${context}")`;
}
if (this.span.details) {
details = `, ${this.span.details}`;
}
const ctx = this.span.start.getContext(100, 3);
const contextStr = ctx ? ` ("${ctx.before}[ERROR ->]${ctx.after}")` : '';
const details = this.span.details ? `, ${this.span.details}` : '';
return `${this.msg}${contextStr}: ${this.span.start}${details}`;
}
}

View File

@ -9,7 +9,7 @@
import {Pipe, Type, resolveForwardRef} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang';
import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
@ -38,9 +38,9 @@ export class PipeResolver {
*/
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
const metas = this._reflector.annotations(resolveForwardRef(type));
if (isPresent(metas)) {
if (metas) {
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
if (isPresent(annotation)) {
if (annotation) {
return annotation;
}
}

View File

@ -9,7 +9,7 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {Identifiers, resolveIdentifier} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
@ -114,7 +114,7 @@ export class ProviderElementContext {
let queries: CompileQueryMetadata[];
while (currentEl !== null) {
queries = currentEl._contentQueries.get(tokenReference(token));
if (isPresent(queries)) {
if (queries) {
result.push(...queries.filter((query) => query.descendants || distance <= 1));
}
if (currentEl._directiveAsts.length > 0) {
@ -123,7 +123,7 @@ export class ProviderElementContext {
currentEl = currentEl._parent;
}
queries = this.viewContext.viewQueries.get(tokenReference(token));
if (isPresent(queries)) {
if (queries) {
result.push(...queries);
}
return result;
@ -143,7 +143,7 @@ export class ProviderElementContext {
return null;
}
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (isPresent(transformedProviderAst)) {
if (transformedProviderAst) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
@ -165,11 +165,11 @@ export class ProviderElementContext {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
} else if (provider.useFactory) {
const deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
} else if (isPresent(provider.useClass)) {
} else if (provider.useClass) {
const deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
@ -235,7 +235,7 @@ export class ProviderElementContext {
}
} else {
// check parent elements
while (!result && isPresent(currElement._parent)) {
while (!result && currElement._parent) {
const prevElement = currElement;
currElement = currElement._parent;
if (prevElement._isViewRoot) {
@ -301,7 +301,7 @@ export class NgModuleProviderAnalyzer {
return null;
}
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (isPresent(transformedProviderAst)) {
if (transformedProviderAst) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
@ -324,11 +324,11 @@ export class NgModuleProviderAnalyzer {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (isPresent(provider.useFactory)) {
} else if (provider.useFactory) {
const deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
} else if (isPresent(provider.useClass)) {
} else if (provider.useClass) {
const deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
@ -454,7 +454,7 @@ function _resolveProviders(
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
const viewQueries = new Map<any, CompileQueryMetadata[]>();
if (isPresent(component.viewQueries)) {
if (component.viewQueries) {
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
}
return viewQueries;
@ -464,7 +464,7 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
Map<any, CompileQueryMetadata[]> {
const contentQueries = new Map<any, CompileQueryMetadata[]>();
directives.forEach(directive => {
if (isPresent(directive.queries)) {
if (directive.queries) {
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
}
});

View File

@ -12,7 +12,8 @@ const _SELECTOR_REGEXP = new RegExp(
'(\\:not\\()|' + //":not("
'([-\\w]+)|' + // "tag"
'(?:\\.([-\\w]+))|' + // ".class"
'(?:\\[([.-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
'(?:\\[([-.\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
'(\\))|' + // ")"
'(\\s*,\\s*)', // ","
'g');

View File

@ -11,7 +11,6 @@ import {SecurityContext} from '@angular/core';
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
import {ASTWithSource, BindingPipe, EmptyExpr, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser';
import {isPresent} from '../facade/lang';
import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {mergeNsAndName} from '../ml_parser/tags';
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
@ -111,14 +110,14 @@ export class BindingParser {
}
parseInlineTemplateBinding(
name: string, prefixToken: string, value: string, sourceSpan: ParseSourceSpan,
prefixToken: string, value: string, sourceSpan: ParseSourceSpan,
targetMatchableAttrs: string[][], targetProps: BoundProperty[], targetVars: VariableAst[]) {
const bindings = this._parseTemplateBindings(prefixToken, value, sourceSpan);
for (let i = 0; i < bindings.length; i++) {
const binding = bindings[i];
if (binding.keyIsVar) {
targetVars.push(new VariableAst(binding.key, binding.name, sourceSpan));
} else if (isPresent(binding.expression)) {
} else if (binding.expression) {
this._parsePropertyAst(
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
} else {
@ -136,7 +135,7 @@ export class BindingParser {
const bindingsResult = this._exprParser.parseTemplateBindings(prefixToken, value, sourceInfo);
this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
bindingsResult.templateBindings.forEach((binding) => {
if (isPresent(binding.expression)) {
if (binding.expression) {
this._checkPipes(binding.expression, sourceSpan);
}
});
@ -193,7 +192,7 @@ export class BindingParser {
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
targetProps: BoundProperty[]): boolean {
const expr = this.parseInterpolation(value, sourceSpan);
if (isPresent(expr)) {
if (expr) {
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
return true;
}
@ -374,7 +373,7 @@ export class BindingParser {
}
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
if (isPresent(ast)) {
if (ast) {
const collector = new PipeCollector();
ast.visit(collector);
collector.pipes.forEach((ast, pipeName) => {

View File

@ -276,7 +276,7 @@ class TemplateParseVisitor implements html.Visitor {
templateBindingsSource = attr.value;
} else if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
templateBindingsSource = attr.value;
prefixToken = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
prefixToken = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length) + ':';
}
const hasTemplateBinding = isPresent(templateBindingsSource);
@ -288,7 +288,7 @@ class TemplateParseVisitor implements html.Visitor {
}
hasInlineTemplates = true;
this._bindingParser.parseInlineTemplateBinding(
attr.name, prefixToken, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
prefixToken, templateBindingsSource, attr.sourceSpan, templateMatchableAttrs,
templateElementOrDirectiveProps, templateElementVars);
}
@ -309,9 +309,11 @@ class TemplateParseVisitor implements html.Visitor {
const elementProps: BoundElementPropertyAst[] =
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
const providerContext = new ProviderElementContext(
this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs,
references, element.sourceSpan);
const children = html.visitAll(
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
ElementContext.create(

View File

@ -19,7 +19,7 @@ import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../templa
import {CompileMethod} from './compile_method';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {CompileView, CompileViewRootNode} from './compile_view';
import {InjectMethodVars, ViewProperties} from './constants';
import {InjectMethodVars} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
import {getPropertyInView, injectFromViewParentInjector} from './util';
@ -195,7 +195,7 @@ export class CompileElement extends CompileNode {
const propName =
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
const instance = createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
propName, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager, this);
if (isDirectiveWrapper) {
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
@ -289,7 +289,7 @@ export class CompileElement extends CompileNode {
CompileQuery {
const propName =
`_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
const queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
const queryList = createQueryList(propName, this.view);
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
addQueryToTokenMap(this._queries, query);
return query;
@ -369,8 +369,8 @@ function createInjectInternalCondition(
}
function createProviderProperty(
propName: string, provider: ProviderAst, providerValueExpressions: o.Expression[],
isMulti: boolean, isEager: boolean, compileElement: CompileElement): o.Expression {
propName: string, providerValueExpressions: o.Expression[], isMulti: boolean, isEager: boolean,
compileElement: CompileElement): o.Expression {
const view = compileElement.view;
let resolvedProviderValueExpr: o.Expression;
let type: o.Type;

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {isPresent} from '../facade/lang';
import * as o from '../output/output_ast';
import {TemplateAst} from '../template_parser/template_ast';
@ -34,7 +33,7 @@ export class CompileMethod {
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
this._newState.sourceAst !== this._currState.sourceAst) {
const expr = this._updateDebugContext(this._newState);
if (isPresent(expr)) {
if (expr) {
this._bodyStatements.push(expr.toStmt());
}
}
@ -43,13 +42,12 @@ export class CompileMethod {
private _updateDebugContext(newState: _DebugState): o.Expression {
this._currState = this._newState = newState;
if (this._debugEnabled) {
const sourceLocation =
isPresent(newState.sourceAst) ? newState.sourceAst.sourceSpan.start : null;
const sourceLocation = newState.sourceAst ? newState.sourceAst.sourceSpan.start : null;
return o.THIS_EXPR.callMethod('debug', [
o.literal(newState.nodeIndex),
isPresent(sourceLocation) ? o.literal(sourceLocation.line) : o.NULL_EXPR,
isPresent(sourceLocation) ? o.literal(sourceLocation.col) : o.NULL_EXPR
sourceLocation ? o.literal(sourceLocation.line) : o.NULL_EXPR,
sourceLocation ? o.literal(sourceLocation.col) : o.NULL_EXPR
]);
} else {
return null;

View File

@ -8,7 +8,6 @@
import {CompileQueryMetadata, tokenReference} from '../compile_metadata';
import {ListWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
@ -33,7 +32,7 @@ export class CompileQuery {
addValue(value: o.Expression, view: CompileView) {
let currentView = view;
const elPath: CompileElement[] = [];
while (isPresent(currentView) && currentView !== this.view) {
while (currentView && currentView !== this.view) {
const parentEl = currentView.declarationElement;
elPath.unshift(parentEl);
currentView = parentEl.view;
@ -67,7 +66,7 @@ export class CompileQuery {
generateStatements(targetStaticMethod: CompileMethod, targetDynamicMethod: CompileMethod) {
const values = createQueryValues(this._values);
const updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
if (isPresent(this.ownerDirectiveExpression)) {
if (this.ownerDirectiveExpression) {
const valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
updateStmts.push(
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
@ -110,9 +109,7 @@ function mapNestedViews(
]);
}
export function createQueryList(
query: CompileQueryMetadata, directiveInstance: o.Expression, propertyName: string,
compileView: CompileView): o.Expression {
export function createQueryList(propertyName: string, compileView: CompileView): o.Expression {
compileView.fields.push(new o.ClassField(
propertyName, o.importType(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])));
const expr = o.THIS_EXPR.prop(propertyName);

View File

@ -12,7 +12,6 @@ import {EventHandlerVars, NameResolver} from '../compiler_util/expression_conver
import {createPureProxy} from '../compiler_util/identifier_util';
import {CompilerConfig} from '../config';
import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core';
@ -119,7 +118,7 @@ export class CompileView implements NameResolver {
const directiveInstance = o.THIS_EXPR.prop('context');
this.component.viewQueries.forEach((queryMeta, queryIndex) => {
const propName = `_viewQuery_${tokenName(queryMeta.selectors[0])}_${queryIndex}`;
const queryList = createQueryList(queryMeta, directiveInstance, propName, this);
const queryList = createQueryList(propName, this);
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
addQueryToTokenMap(viewQueries, query);
});

View File

@ -11,7 +11,7 @@ import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {createEnumExpression} from '../compiler_util/identifier_util';
import {Identifiers} from '../identifiers';
import * as o from '../output/output_ast';
import {ChangeDetectorStatus, ViewType} from '../private_import_core';
import {ViewType} from '../private_import_core';
export class ViewTypeEnum {
static fromValue(value: ViewType): o.Expression {
@ -25,12 +25,6 @@ export class ViewEncapsulationEnum {
}
}
export class ChangeDetectionStrategyEnum {
static fromValue(value: ChangeDetectionStrategy): o.Expression {
return createEnumExpression(Identifiers.ChangeDetectionStrategy, value);
}
}
export class ChangeDetectorStatusEnum {
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);

View File

@ -15,7 +15,6 @@ import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
import {CompileMethod} from './compile_method';
import {ViewProperties} from './constants';
import {getHandleEventMethodName} from './util';
export function bindOutputs(

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileQueryMetadata, CompileTokenMetadata, tokenReference} from '../compile_metadata';
import {CompileTokenMetadata, tokenReference} from '../compile_metadata';
import * as o from '../output/output_ast';
import {CompileElement} from './compile_element';
@ -17,16 +17,18 @@ import {CompileQuery} from './compile_query';
// as we create embedded views before the <template> elements themselves.
export function bindQueryValues(ce: CompileElement) {
const queriesWithReads: _QueryWithRead[] = [];
ce.getProviderTokens().forEach((token) => {
const queriesForProvider = ce.getQueriesFor(token);
queriesWithReads.push(...queriesForProvider.map(query => new _QueryWithRead(query, token)));
});
Object.keys(ce.referenceTokens).forEach(varName => {
const token = ce.referenceTokens[varName];
const varToken = {value: varName};
queriesWithReads.push(
...ce.getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
});
queriesWithReads.forEach((queryWithRead) => {
let value: o.Expression;
if (queryWithRead.read.identifier) {

View File

@ -9,8 +9,6 @@
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, CompileTokenMetadata, identifierName} from '../compile_metadata';
import {createDiTokenExpression} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core';
@ -23,7 +21,7 @@ export function getPropertyInView(
} else {
let viewProp: o.Expression = o.THIS_EXPR;
let currView: CompileView = callingView;
while (currView !== definedView && isPresent(currView.declarationElement.view)) {
while (currView !== definedView && currView.declarationElement.view) {
currView = currView.declarationElement.view;
viewProp = viewProp.prop('parentView');
}

View File

@ -15,7 +15,6 @@ import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
import {createClassStmt} from '../output/class_builder';
import * as o from '../output/output_ast';
import {ParseSourceSpan} from '../parse_util';
import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
@ -405,8 +404,9 @@ function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statemen
o.literal(view.component.template.ngContentSelectors.length),
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
view.styles,
o.literalMap(view.animations.map(
(entry): [string, o.Expression] => [entry.name, entry.fnExp])),
o.literalMap(
view.animations.map((entry): [string, o.Expression] => [entry.name, entry.fnExp]),
null, true),
]))
.toDeclStmt(o.importType(createIdentifier(Identifiers.RenderComponentType))));
}
@ -695,7 +695,6 @@ function generateCreateEmbeddedViewsMethod(view: CompileView): o.ClassMethod {
view.nodes.forEach((node) => {
if (node instanceof CompileElement) {
if (node.embeddedView) {
const parentNodeIndex = node.isRootElement() ? null : node.parent.nodeIndex;
stmts.push(new o.IfStmt(
nodeIndexVar.equals(o.literal(node.nodeIndex)),
[new o.ReturnStatement(node.embeddedView.classExpr.instantiate([

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost} from '@angular/compiler';
import {StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, SyntaxError} from '@angular/compiler';
import {HostListener, Inject, animate, group, keyframes, sequence, state, style, transition, trigger} from '@angular/core';
import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
@ -344,6 +344,20 @@ describe('StaticReflector', () => {
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts'));
});
it('should throw a SyntaxError without stack trace when the required resource cannot be resolved',
() => {
expect(
() => simplify(
reflector.getStaticSymbol('/tmp/src/function-reference.ts', 'AppModule'), ({
__symbolic: 'error',
message:
'Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts'
})))
.toThrowError(
SyntaxError,
'Error encountered resolving symbol values statically. Could not resolve ./does-not-exist.component relative to /tmp/src/function-reference.ts, resolving symbol AppModule in /tmp/src/function-reference.ts');
});
it('should record data about the error in the exception', () => {
let threw = false;
try {
@ -449,6 +463,40 @@ describe('StaticReflector', () => {
expect(annotations[0].providers[0].useValue.members[0]).toEqual('staticMethod');
});
// #13605
it('should not throw on unknown decorators', () => {
const data = Object.create(DEFAULT_TEST_DATA);
const file = '/tmp/src/app.component.ts';
data[file] = `
import { Component } from '@angular/core';
export const enum TypeEnum {
type
}
export function MyValidationDecorator(p1: any, p2: any): any {
return null;
}
export function ValidationFunction(a1: any): any {
return null;
}
@Component({
selector: 'my-app',
template: "<h1>Hello {{name}}</h1>",
})
export class AppComponent {
name = 'Angular';
@MyValidationDecorator( TypeEnum.type, ValidationFunction({option: 'value'}))
myClassProp: number;
}`;
init(data);
const appComponent = reflector.getStaticSymbol(file, 'AppComponent');
expect(() => reflector.propMetadata(appComponent)).not.toThrow();
});
describe('inheritance', () => {
class ClassDecorator {
constructor(public value: any) {}

View File

@ -342,6 +342,24 @@ export function main() {
const HTML = `<div>before<p i18n="m|d">foo</p><!-- comment --></div>`;
expect(fakeTranslate(HTML)).toEqual('<div>before<p>**foo**</p></div>');
});
it('should merge empty messages', () => {
const HTML = `<div i18n>some element</div>`;
const htmlNodes: html.Node[] = parseHtml(HTML);
const messages: i18n.Message[] =
extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages;
expect(messages.length).toEqual(1);
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
i18nMsgMap[digest(messages[0])] = [];
const translations = new TranslationBundle(i18nMsgMap, digest);
const output =
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
expect(output.errors).toEqual([]);
expect(serializeHtmlNodes(output.rootNodes).join('')).toEqual(`<div></div>`);
});
});
describe('blocks', () => {
@ -381,6 +399,25 @@ export function main() {
const HTML = `<p i18n-title="m|d" title=""></p>`;
expect(fakeTranslate(HTML)).toEqual('<p title=""></p>');
});
it('should merge empty attributes', () => {
const HTML = `<div i18n-title title="some attribute">some element</div>`;
const htmlNodes: html.Node[] = parseHtml(HTML);
const messages: i18n.Message[] =
extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages;
expect(messages.length).toEqual(1);
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
i18nMsgMap[digest(messages[0])] = [];
const translations = new TranslationBundle(i18nMsgMap, digest);
const output =
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
expect(output.errors).toEqual([]);
expect(serializeHtmlNodes(output.rootNodes).join(''))
.toEqual(`<div title="">some element</div>`);
});
});
});
}
@ -412,12 +449,11 @@ function fakeTranslate(
const translations = new TranslationBundle(i18nMsgMap, digest);
const translatedNodes =
mergeTranslations(
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs)
.rootNodes;
const output = mergeTranslations(
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
expect(output.errors).toEqual([]);
return serializeHtmlNodes(translatedNodes).join('');
return serializeHtmlNodes(output.rootNodes).join('');
}
function extract(

View File

@ -141,12 +141,22 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
<!-- /i18n -->
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
<!-- make sure that ICU messages are not treated as text nodes -->
<div i18n="desc">{
response.getItemsList().length,
plural,
=0 {Found no results}
=1 {Found one result}
other {Found {{response.getItemsList().length}} results}
}</div>
`
})
class I18nComponent {
count: number;
sex: string;
sexB: string;
response: any = {getItemsList: (): any[] => []};
}
class FrLocalization extends NgLocalization {
@ -182,6 +192,12 @@ const XTB = `
<ph name="START_TAG_DIV_1"/><ph name="ICU"/><ph name="CLOSE_TAG_DIV"></ph>
</translation>
<translation id="1491627405349178954">ca <ph name="START_BOLD_TEXT"/>devrait<ph name="CLOSE_BOLD_TEXT"/> marcher</translation>
<translation id="i18n16">avec un ID explicite</translation>
<translation id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph
name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>beaucoup<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</translation>
<translation id="4085484936881858615">{VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {<ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> réponse} }</translation>
<translation id="4035252431381981115">FOO<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>BAR<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></translation>
<translation id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></translation>
</translationbundle>`;
// unused, for reference only
@ -197,18 +213,76 @@ const XMB = `
<msg id="8670732454866344690">on translatable node</msg>
<msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="1746565782635215">
<ph name="ICU"/>
<ph name="ICU"><ex>ICU</ex></ph>
</msg>
<msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
<msg id="4851788426695310455"><ph name="INTERPOLATION"/></msg>
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"/></msg>
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"/></msg>
<msg id="4851788426695310455"><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
<msg id="7685649297917455806">in a translatable section</msg>
<msg id="2387287228265107305">
<ph name="START_HEADING_LEVEL1"><ex>&lt;h1&gt;</ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex>&lt;/h1&gt;</ex></ph>
<ph name="START_TAG_DIV"><ex>&lt;div&gt;</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
<ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
<ph name="START_TAG_DIV_1"><ex>&lt;div&gt;</ex></ph><ph name="ICU"><ex>ICU</ex></ph><ph name="CLOSE_TAG_DIV"><ex>&lt;/div&gt;</ex></ph>
</msg>
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph> work</msg>
</messagebundle>
<msg id="i18n16">with an explicit ID</msg>
<msg id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex>&lt;b&gt;</ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex>&lt;/b&gt;</ex></ph>} }</msg>
<msg id="4085484936881858615" desc="desc">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
<msg id="4035252431381981115">foo<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>bar<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></msg>
<msg id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>;
</messagebundle>`;
const HTML = `
<div>
<h1 i18n>i18n attribute on tags</h1>
<div id="i18n-1"><p i18n>nested</p></div>
<div id="i18n-2"><p i18n="different meaning|">nested</p></div>
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
<div>
<p id="i18n-4" i18n-title title="on not translatable node"></p>
<p id="i18n-5" i18n i18n-title title="on translatable node"></p>
<p id="i18n-6" i18n-title title></p>
</div>
<!-- no ph below because the ICU node is the only child of the div, i.e. no text nodes -->
<div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<div i18n id="i18n-8">
{sex, select, m {male} f {female}}
</div>
<div i18n id="i18n-8b">
{sexB, select, m {male} f {female}}
</div>
<div i18n id="i18n-9">{{ "count = " + count }}</div>
<div i18n id="i18n-10">sex = {{ sex }}</div>
<div i18n id="i18n-11">{{ "custom name" //i18n(ph="CUSTOM_NAME") }}</div>
</div>
<!-- i18n -->
<h1 id="i18n-12" >Markers in html comments</h1>
<div id="i18n-13" i18n-title title="in a translatable section"></div>
<div id="i18n-14">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<!-- /i18n -->
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
<!-- make sure that ICU messages are not treated as text nodes -->
<div i18n="desc">{
response.getItemsList().length,
plural,
=0 {Found no results}
=1 {Found one result}
other {Found {{response.getItemsList().length}} results}
}</div>
<div i18n id="i18n-18">foo<a i18n-title title="in a translatable section">bar</a></div>
<div i18n>{{ 'test' //i18n(ph="map name") }}</div>
`;

View File

@ -42,7 +42,7 @@ export function main(): void {
});
}
class _TestSerializer implements Serializer {
class _TestSerializer extends Serializer {
write(messages: i18n.Message[]): string {
return messages.map(msg => `${serializeNodes(msg.nodes)} (${msg.meaning}|${msg.description})`)
.join('//');

View File

@ -72,6 +72,11 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
<target><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/><x id="TAG_IMG" ctype="image"/><x id="LINE_BREAK" ctype="lb"/></target>
<note priority="1" from="description">ph names</note>
</trans-unit>
<trans-unit id="empty target" datatype="html">
<source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
<target/>
<note priority="1" from="description">ph names</note>
</trans-unit>
</body>
</file>
</xliff>
@ -88,6 +93,7 @@ export function main(): void {
function loadAsMap(xliff: string): {[id: string]: string} {
const i18nNodesByMsgId = serializer.load(xliff, 'url');
const msgMap: {[id: string]: string} = {};
Object.keys(i18nNodesByMsgId)
.forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join(''));
@ -109,6 +115,7 @@ export function main(): void {
'db3e0a6a5a96481f60aec61d98c3eecddef5ac23': 'oof',
'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d':
'<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>',
'empty target': '',
});
});

View File

@ -1323,17 +1323,30 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
});
it('should work with *... and use the attribute name as property binding name', () => {
expect(humanizeTplAst(parse('<div *ngIf="test">', [ngIf]))).toEqual([
[EmbeddedTemplateAst], [DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'test'], [ElementAst, 'div']
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'test'],
[ElementAst, 'div'],
]);
// https://github.com/angular/angular/issues/13800
expect(humanizeTplAst(parse('<div *ngIf="-1">', [ngIf]))).toEqual([
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', '0 - 1'],
[ElementAst, 'div'],
]);
});
it('should work with *... and empty value', () => {
expect(humanizeTplAst(parse('<div *ngIf>', [ngIf]))).toEqual([
[EmbeddedTemplateAst], [DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'null'], [ElementAst, 'div']
[EmbeddedTemplateAst],
[DirectiveAst, ngIf],
[BoundDirectivePropertyAst, 'ngIf', 'null'],
[ElementAst, 'div'],
]);
});
});

View File

@ -108,9 +108,12 @@ export const platformCoreDynamicTesting: (extraProviders?: any[]) => PlatformRef
provide: COMPILER_OPTIONS,
useValue: {
providers: [
MockPipeResolver, {provide: PipeResolver, useExisting: MockPipeResolver},
MockDirectiveResolver, {provide: DirectiveResolver, useExisting: MockDirectiveResolver},
MockNgModuleResolver, {provide: NgModuleResolver, useExisting: MockNgModuleResolver}
MockPipeResolver,
{provide: PipeResolver, useExisting: MockPipeResolver},
MockDirectiveResolver,
{provide: DirectiveResolver, useExisting: MockDirectiveResolver},
MockNgModuleResolver,
{provide: NgModuleResolver, useExisting: MockNgModuleResolver},
]
},
multi: true

View File

@ -8,6 +8,16 @@
import {AnimationStyles} from './animation_styles';
/**
* `AnimationKeyframe` consists of a series of styles (contained within {@link AnimationStyles
* `AnimationStyles`})
* and an offset value indicating when those styles are applied within the `duration/delay/easing`
* timings.
* `AnimationKeyframe` is mostly an internal class which is designed to be used alongside {@link
* Renderer#animate-anchor `Renderer.animate`}.
*
* @experimental Animation support is experimental
*/
export class AnimationKeyframe {
constructor(public offset: number, public styles: AnimationStyles) {}
}

View File

@ -6,6 +6,20 @@
* found in the LICENSE file at https://angular.io/license
*/
// having an import prevents dgeni from truncating out
// the class description in the docs. DO NOT REMOVE.
import {isPresent} from '../facade/lang';
/**
* `AnimationStyles` consists of a collection of key/value maps containing CSS-based style data
* that can either be used as initial styling data or apart of a series of keyframes within an
* animation.
* This class is mostly internal, and it is designed to be used alongside
* {@link AnimationKeyframe `AnimationKeyframe`} and {@link Renderer#animate-anchor
* `Renderer.animate`}.
*
* @experimental Animation support is experimental
*/
export class AnimationStyles {
constructor(public styles: {[key: string]: string | number}[]) {}
}

View File

@ -8,13 +8,11 @@
import {ErrorHandler} from '../src/error_handler';
import {ListWrapper} from '../src/facade/collection';
import {unimplemented} from '../src/facade/errors';
import {stringify} from '../src/facade/lang';
import {isPromise} from '../src/util/lang';
import {ApplicationInitStatus} from './application_init';
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
import {ChangeDetectorRef} from './change_detection/change_detector_ref';
import {Console} from './console';
import {Injectable, Injector, OpaqueToken, Optional, Provider, ReflectiveInjector} from './di';
import {CompilerFactory, CompilerOptions} from './linker/compiler';
@ -186,9 +184,7 @@ export abstract class PlatformRef {
*
* @experimental APIs related to application bootstrap are currently under review.
*/
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
throw unimplemented();
}
abstract bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>>;
/**
* Creates an instance of an `@NgModule` for a given platform using the given runtime compiler.
@ -205,10 +201,9 @@ export abstract class PlatformRef {
* ```
* @stable
*/
bootstrapModule<M>(moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = []):
Promise<NgModuleRef<M>> {
throw unimplemented();
}
abstract bootstrapModule<M>(
moduleType: Type<M>,
compilerOptions?: CompilerOptions|CompilerOptions[]): Promise<NgModuleRef<M>>;
/**
* Register a listener to be called when the platform is disposed.
@ -219,14 +214,14 @@ export abstract class PlatformRef {
* Retrieve the platform {@link Injector}, which is the parent injector for
* every Angular application on the page and provides singleton providers.
*/
get injector(): Injector { throw unimplemented(); };
abstract get injector(): Injector;
/**
* Destroy the Angular platform and all Angular applications on the page.
*/
abstract destroy(): void;
get destroyed(): boolean { throw unimplemented(); }
abstract get destroyed(): boolean;
}
function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () => any): any {
@ -331,7 +326,7 @@ export class PlatformRef_ extends PlatformRef {
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
}
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>) {
private _moduleDoBootstrap(moduleRef: NgModuleInjector<any>): void {
const appRef = moduleRef.injector.get(ApplicationRef);
if (moduleRef.bootstrapFactories.length > 0) {
moduleRef.bootstrapFactories.forEach((compFactory) => appRef.bootstrap(compFactory));
@ -342,6 +337,7 @@ export class PlatformRef_ extends PlatformRef {
`The module ${stringify(moduleRef.instance.constructor)} was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` +
`Please define one of these.`);
}
this._modules.push(moduleRef);
}
}
@ -383,29 +379,29 @@ export abstract class ApplicationRef {
* Get a list of component types registered to this application.
* This list is populated even before the component is created.
*/
get componentTypes(): Type<any>[] { return <Type<any>[]>unimplemented(); };
abstract get componentTypes(): Type<any>[];
/**
* Get a list of components registered to this application.
*/
get components(): ComponentRef<any>[] { return <ComponentRef<any>[]>unimplemented(); };
abstract get components(): ComponentRef<any>[];
/**
* Attaches a view so that it will be dirty checked.
* The view will be automatically detached when it is destroyed.
* This will throw if the view is already attached to a ViewContainer.
*/
attachView(view: ViewRef): void { unimplemented(); }
abstract attachView(view: ViewRef): void;
/**
* Detaches a view from dirty checking again.
*/
detachView(view: ViewRef): void { unimplemented(); }
abstract detachView(view: ViewRef): void;
/**
* Returns the number of attached views.
*/
get viewCount() { return unimplemented(); }
abstract get viewCount(): number;
}
@Injectable()

View File

@ -35,4 +35,6 @@ export * from './core_private_export';
export * from './animation/metadata';
export {AnimationTransitionEvent} from './animation/animation_transition_event';
export {AnimationPlayer} from './animation/animation_player';
export {AnimationStyles} from './animation/animation_styles';
export {AnimationKeyframe} from './animation/animation_keyframe';
export {Sanitizer, SecurityContext} from './security';

View File

@ -41,7 +41,7 @@ import * as reflector_reader from './reflection/reflector_reader';
import * as reflection_types from './reflection/types';
import * as api from './render/api';
import * as decorators from './util/decorators';
import {isPromise} from './util/lang';
import {isObservable, isPromise} from './util/lang';
export const __core_private__: {
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
@ -108,6 +108,7 @@ export const __core_private__: {
_ComponentStillLoadingError?: ComponentStillLoadingError,
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise,
isObservable: typeof isObservable,
AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils,
} = {
@ -159,5 +160,6 @@ export const __core_private__: {
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
ComponentStillLoadingError: ComponentStillLoadingError,
isPromise: isPromise,
isObservable: isObservable,
AnimationTransition: AnimationTransition
};

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {unimplemented} from '../facade/errors';
import {stringify} from '../facade/lang';
const _THROW_IF_NOT_FOUND = new Object();
@ -52,5 +51,5 @@ export abstract class Injector {
* Injector.THROW_IF_NOT_FOUND is given
* - Returns the `notFoundValue` otherwise
*/
get(token: any, notFoundValue?: any): any { return unimplemented(); }
abstract get(token: any, notFoundValue?: any): any;
}

View File

@ -6,9 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {unimplemented} from '../facade/errors';
import {Type} from '../type';
import {Injector, THROW_IF_NOT_FOUND} from './injector';
import {Self, SkipSelf} from './metadata';
import {Provider} from './provider';
@ -17,308 +14,8 @@ import {ReflectiveKey} from './reflective_key';
import {ReflectiveDependency, ResolvedReflectiveFactory, ResolvedReflectiveProvider, resolveReflectiveProviders} from './reflective_provider';
// Threshold for the dynamic version
const _MAX_CONSTRUCTION_COUNTER = 10;
const UNDEFINED = new Object();
export interface ReflectiveProtoInjectorStrategy {
getProviderAtIndex(index: number): ResolvedReflectiveProvider;
createInjectorStrategy(inj: ReflectiveInjector_): ReflectiveInjectorStrategy;
}
export class ReflectiveProtoInjectorInlineStrategy implements ReflectiveProtoInjectorStrategy {
provider0: ResolvedReflectiveProvider = null;
provider1: ResolvedReflectiveProvider = null;
provider2: ResolvedReflectiveProvider = null;
provider3: ResolvedReflectiveProvider = null;
provider4: ResolvedReflectiveProvider = null;
provider5: ResolvedReflectiveProvider = null;
provider6: ResolvedReflectiveProvider = null;
provider7: ResolvedReflectiveProvider = null;
provider8: ResolvedReflectiveProvider = null;
provider9: ResolvedReflectiveProvider = null;
keyId0: number = null;
keyId1: number = null;
keyId2: number = null;
keyId3: number = null;
keyId4: number = null;
keyId5: number = null;
keyId6: number = null;
keyId7: number = null;
keyId8: number = null;
keyId9: number = null;
constructor(protoEI: ReflectiveProtoInjector, providers: ResolvedReflectiveProvider[]) {
const length = providers.length;
if (length > 0) {
this.provider0 = providers[0];
this.keyId0 = providers[0].key.id;
}
if (length > 1) {
this.provider1 = providers[1];
this.keyId1 = providers[1].key.id;
}
if (length > 2) {
this.provider2 = providers[2];
this.keyId2 = providers[2].key.id;
}
if (length > 3) {
this.provider3 = providers[3];
this.keyId3 = providers[3].key.id;
}
if (length > 4) {
this.provider4 = providers[4];
this.keyId4 = providers[4].key.id;
}
if (length > 5) {
this.provider5 = providers[5];
this.keyId5 = providers[5].key.id;
}
if (length > 6) {
this.provider6 = providers[6];
this.keyId6 = providers[6].key.id;
}
if (length > 7) {
this.provider7 = providers[7];
this.keyId7 = providers[7].key.id;
}
if (length > 8) {
this.provider8 = providers[8];
this.keyId8 = providers[8].key.id;
}
if (length > 9) {
this.provider9 = providers[9];
this.keyId9 = providers[9].key.id;
}
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index == 0) return this.provider0;
if (index == 1) return this.provider1;
if (index == 2) return this.provider2;
if (index == 3) return this.provider3;
if (index == 4) return this.provider4;
if (index == 5) return this.provider5;
if (index == 6) return this.provider6;
if (index == 7) return this.provider7;
if (index == 8) return this.provider8;
if (index == 9) return this.provider9;
throw new OutOfBoundsError(index);
}
createInjectorStrategy(injector: ReflectiveInjector_): ReflectiveInjectorStrategy {
return new ReflectiveInjectorInlineStrategy(injector, this);
}
}
export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoInjectorStrategy {
keyIds: number[];
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
const len = providers.length;
this.keyIds = new Array(len);
for (let i = 0; i < len; i++) {
this.keyIds[i] = providers[i].key.id;
}
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this.providers.length) {
throw new OutOfBoundsError(index);
}
return this.providers[index];
}
createInjectorStrategy(ei: ReflectiveInjector_): ReflectiveInjectorStrategy {
return new ReflectiveInjectorDynamicStrategy(this, ei);
}
}
export class ReflectiveProtoInjector {
static fromResolvedProviders(providers: ResolvedReflectiveProvider[]): ReflectiveProtoInjector {
return new ReflectiveProtoInjector(providers);
}
/** @internal */
_strategy: ReflectiveProtoInjectorStrategy;
numberOfProviders: number;
constructor(providers: ResolvedReflectiveProvider[]) {
this.numberOfProviders = providers.length;
this._strategy = providers.length > _MAX_CONSTRUCTION_COUNTER ?
new ReflectiveProtoInjectorDynamicStrategy(this, providers) :
new ReflectiveProtoInjectorInlineStrategy(this, providers);
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
return this._strategy.getProviderAtIndex(index);
}
}
export interface ReflectiveInjectorStrategy {
getObjByKeyId(keyId: number): any;
getObjAtIndex(index: number): any;
getMaxNumberOfObjects(): number;
resetConstructionCounter(): void;
instantiateProvider(provider: ResolvedReflectiveProvider): any;
}
export class ReflectiveInjectorInlineStrategy implements ReflectiveInjectorStrategy {
obj0: any = UNDEFINED;
obj1: any = UNDEFINED;
obj2: any = UNDEFINED;
obj3: any = UNDEFINED;
obj4: any = UNDEFINED;
obj5: any = UNDEFINED;
obj6: any = UNDEFINED;
obj7: any = UNDEFINED;
obj8: any = UNDEFINED;
obj9: any = UNDEFINED;
constructor(
public injector: ReflectiveInjector_,
public protoStrategy: ReflectiveProtoInjectorInlineStrategy) {}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedReflectiveProvider): any {
return this.injector._new(provider);
}
getObjByKeyId(keyId: number): any {
const p = this.protoStrategy;
const inj = this.injector;
if (p.keyId0 === keyId) {
if (this.obj0 === UNDEFINED) {
this.obj0 = inj._new(p.provider0);
}
return this.obj0;
}
if (p.keyId1 === keyId) {
if (this.obj1 === UNDEFINED) {
this.obj1 = inj._new(p.provider1);
}
return this.obj1;
}
if (p.keyId2 === keyId) {
if (this.obj2 === UNDEFINED) {
this.obj2 = inj._new(p.provider2);
}
return this.obj2;
}
if (p.keyId3 === keyId) {
if (this.obj3 === UNDEFINED) {
this.obj3 = inj._new(p.provider3);
}
return this.obj3;
}
if (p.keyId4 === keyId) {
if (this.obj4 === UNDEFINED) {
this.obj4 = inj._new(p.provider4);
}
return this.obj4;
}
if (p.keyId5 === keyId) {
if (this.obj5 === UNDEFINED) {
this.obj5 = inj._new(p.provider5);
}
return this.obj5;
}
if (p.keyId6 === keyId) {
if (this.obj6 === UNDEFINED) {
this.obj6 = inj._new(p.provider6);
}
return this.obj6;
}
if (p.keyId7 === keyId) {
if (this.obj7 === UNDEFINED) {
this.obj7 = inj._new(p.provider7);
}
return this.obj7;
}
if (p.keyId8 === keyId) {
if (this.obj8 === UNDEFINED) {
this.obj8 = inj._new(p.provider8);
}
return this.obj8;
}
if (p.keyId9 === keyId) {
if (this.obj9 === UNDEFINED) {
this.obj9 = inj._new(p.provider9);
}
return this.obj9;
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index == 0) return this.obj0;
if (index == 1) return this.obj1;
if (index == 2) return this.obj2;
if (index == 3) return this.obj3;
if (index == 4) return this.obj4;
if (index == 5) return this.obj5;
if (index == 6) return this.obj6;
if (index == 7) return this.obj7;
if (index == 8) return this.obj8;
if (index == 9) return this.obj9;
throw new OutOfBoundsError(index);
}
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
}
export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStrategy {
objs: any[];
constructor(
public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
public injector: ReflectiveInjector_) {
this.objs = new Array(protoStrategy.providers.length).fill(UNDEFINED);
}
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
instantiateProvider(provider: ResolvedReflectiveProvider): any {
return this.injector._new(provider);
}
getObjByKeyId(keyId: number): any {
const p = this.protoStrategy;
for (let i = 0; i < p.keyIds.length; i++) {
if (p.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
this.objs[i] = this.injector._new(p.providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
getObjAtIndex(index: number): any {
if (index < 0 || index >= this.objs.length) {
throw new OutOfBoundsError(index);
}
return this.objs[index];
}
getMaxNumberOfObjects(): number { return this.objs.length; }
}
/**
* A ReflectiveDependency injection container used for instantiating objects and resolving
* dependencies.
@ -448,8 +145,7 @@ export abstract class ReflectiveInjector implements Injector {
*/
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null):
ReflectiveInjector {
return new ReflectiveInjector_(
ReflectiveProtoInjector.fromResolvedProviders(providers), parent);
return new ReflectiveInjector_(providers, parent);
}
@ -467,7 +163,7 @@ export abstract class ReflectiveInjector implements Injector {
* expect(child.parent).toBe(parent);
* ```
*/
get parent(): Injector { return unimplemented(); }
abstract get parent(): Injector;
/**
* Resolves an array of providers and creates a child injector from those providers.
@ -496,7 +192,7 @@ export abstract class ReflectiveInjector implements Injector {
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
*/
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector { return unimplemented(); }
abstract resolveAndCreateChild(providers: Provider[]): ReflectiveInjector;
/**
* Creates a child injector from previously resolved providers.
@ -523,9 +219,7 @@ export abstract class ReflectiveInjector implements Injector {
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
* ```
*/
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
return unimplemented();
}
abstract createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector;
/**
* Resolves a provider and instantiates an object in the context of the injector.
@ -551,7 +245,7 @@ export abstract class ReflectiveInjector implements Injector {
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
* ```
*/
resolveAndInstantiate(provider: Provider): any { return unimplemented(); }
abstract resolveAndInstantiate(provider: Provider): any;
/**
* Instantiates an object using a resolved provider in the context of the injector.
@ -577,51 +271,52 @@ export abstract class ReflectiveInjector implements Injector {
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
* ```
*/
instantiateResolved(provider: ResolvedReflectiveProvider): any { return unimplemented(); }
abstract instantiateResolved(provider: ResolvedReflectiveProvider): any;
abstract get(token: any, notFoundValue?: any): any;
}
export class ReflectiveInjector_ implements ReflectiveInjector {
private _strategy: ReflectiveInjectorStrategy;
/** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
public _providers: ResolvedReflectiveProvider[];
/** @internal */
public _parent: Injector;
keyIds: number[];
objs: any[];
/**
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null) {
this._proto = _proto;
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) {
this._providers = _providers;
this._parent = _parent;
this._strategy = _proto._strategy.createInjectorStrategy(this);
const len = _providers.length;
this.keyIds = new Array(len);
this.objs = new Array(len);
for (let i = 0; i < len; i++) {
this.keyIds[i] = _providers[i].key.id;
this.objs[i] = UNDEFINED;
}
}
get(token: any, notFoundValue: any = THROW_IF_NOT_FOUND): any {
return this._getByKey(ReflectiveKey.get(token), null, null, notFoundValue);
return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
}
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
get parent(): Injector { return this._parent; }
/**
* @internal
* Internal. Do not use.
* We return `any` not to export the InjectorStrategy type.
*/
get internalStrategy(): any { return this._strategy; }
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return this.createChildFromResolved(ResolvedReflectiveProviders);
}
createChildFromResolved(providers: ResolvedReflectiveProvider[]): ReflectiveInjector {
const proto = new ReflectiveProtoInjector(providers);
const inj = new ReflectiveInjector_(proto);
const inj = new ReflectiveInjector_(providers);
inj._parent = this;
return inj;
}
@ -634,14 +329,23 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
return this._instantiateProvider(provider);
}
getProviderAtIndex(index: number): ResolvedReflectiveProvider {
if (index < 0 || index >= this._providers.length) {
throw new OutOfBoundsError(index);
}
return this._providers[index];
}
/** @internal */
_new(provider: ResolvedReflectiveProvider): any {
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
throw new CyclicDependencyError(this, provider.key);
}
return this._instantiateProvider(provider);
}
private _getMaxNumberOfObjects(): number { return this.objs.length; }
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
if (provider.multiProvider) {
const res = new Array(provider.resolvedFactories.length);
@ -658,50 +362,11 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
provider: ResolvedReflectiveProvider,
ResolvedReflectiveFactory: ResolvedReflectiveFactory): any {
const factory = ResolvedReflectiveFactory.factory;
const deps = ResolvedReflectiveFactory.dependencies;
const length = deps.length;
let d0: any;
let d1: any;
let d2: any;
let d3: any;
let d4: any;
let d5: any;
let d6: any;
let d7: any;
let d8: any;
let d9: any;
let d10: any;
let d11: any;
let d12: any;
let d13: any;
let d14: any;
let d15: any;
let d16: any;
let d17: any;
let d18: any;
let d19: any;
let deps: any[];
try {
d0 = length > 0 ? this._getByReflectiveDependency(provider, deps[0]) : null;
d1 = length > 1 ? this._getByReflectiveDependency(provider, deps[1]) : null;
d2 = length > 2 ? this._getByReflectiveDependency(provider, deps[2]) : null;
d3 = length > 3 ? this._getByReflectiveDependency(provider, deps[3]) : null;
d4 = length > 4 ? this._getByReflectiveDependency(provider, deps[4]) : null;
d5 = length > 5 ? this._getByReflectiveDependency(provider, deps[5]) : null;
d6 = length > 6 ? this._getByReflectiveDependency(provider, deps[6]) : null;
d7 = length > 7 ? this._getByReflectiveDependency(provider, deps[7]) : null;
d8 = length > 8 ? this._getByReflectiveDependency(provider, deps[8]) : null;
d9 = length > 9 ? this._getByReflectiveDependency(provider, deps[9]) : null;
d10 = length > 10 ? this._getByReflectiveDependency(provider, deps[10]) : null;
d11 = length > 11 ? this._getByReflectiveDependency(provider, deps[11]) : null;
d12 = length > 12 ? this._getByReflectiveDependency(provider, deps[12]) : null;
d13 = length > 13 ? this._getByReflectiveDependency(provider, deps[13]) : null;
d14 = length > 14 ? this._getByReflectiveDependency(provider, deps[14]) : null;
d15 = length > 15 ? this._getByReflectiveDependency(provider, deps[15]) : null;
d16 = length > 16 ? this._getByReflectiveDependency(provider, deps[16]) : null;
d17 = length > 17 ? this._getByReflectiveDependency(provider, deps[17]) : null;
d18 = length > 18 ? this._getByReflectiveDependency(provider, deps[18]) : null;
d19 = length > 19 ? this._getByReflectiveDependency(provider, deps[19]) : null;
deps =
ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
} catch (e) {
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
e.addKey(this, provider.key);
@ -711,106 +376,45 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
let obj: any;
try {
switch (length) {
case 0:
obj = factory();
break;
case 1:
obj = factory(d0);
break;
case 2:
obj = factory(d0, d1);
break;
case 3:
obj = factory(d0, d1, d2);
break;
case 4:
obj = factory(d0, d1, d2, d3);
break;
case 5:
obj = factory(d0, d1, d2, d3, d4);
break;
case 6:
obj = factory(d0, d1, d2, d3, d4, d5);
break;
case 7:
obj = factory(d0, d1, d2, d3, d4, d5, d6);
break;
case 8:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
break;
case 9:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
break;
case 10:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
break;
case 11:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
break;
case 12:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
break;
case 13:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
break;
case 14:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
break;
case 15:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
break;
case 16:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
break;
case 17:
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
break;
case 18:
obj = factory(
d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17);
break;
case 19:
obj = factory(
d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18);
break;
case 20:
obj = factory(
d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18,
d19);
break;
default:
throw new Error(
`Cannot instantiate '${provider.key.displayName}' because it has more than 20 dependencies`);
}
obj = factory(...deps);
} catch (e) {
throw new InstantiationError(this, e, e.stack, provider.key);
}
return obj;
}
private _getByReflectiveDependency(
provider: ResolvedReflectiveProvider, dep: ReflectiveDependency): any {
return this._getByKey(
dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
dep.optional ? null : THROW_IF_NOT_FOUND);
private _getByReflectiveDependency(dep: ReflectiveDependency): any {
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
}
private _getByKey(
key: ReflectiveKey, lowerBoundVisibility: Object, upperBoundVisibility: Object,
notFoundValue: any): any {
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any {
if (key === INJECTOR_KEY) {
return this;
}
if (upperBoundVisibility instanceof Self) {
if (visibility instanceof Self) {
return this._getByKeySelf(key, notFoundValue);
} else {
return this._getByKeyDefault(key, notFoundValue, lowerBoundVisibility);
return this._getByKeyDefault(key, notFoundValue, visibility);
}
}
private _getObjByKeyId(keyId: number): any {
for (let i = 0; i < this.keyIds.length; i++) {
if (this.keyIds[i] === keyId) {
if (this.objs[i] === UNDEFINED) {
this.objs[i] = this._new(this._providers[i]);
}
return this.objs[i];
}
}
return UNDEFINED;
}
/** @internal */
_throwOrNull(key: ReflectiveKey, notFoundValue: any): any {
if (notFoundValue !== THROW_IF_NOT_FOUND) {
@ -822,15 +426,15 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
/** @internal */
_getByKeySelf(key: ReflectiveKey, notFoundValue: any): any {
const obj = this._strategy.getObjByKeyId(key.id);
const obj = this._getObjByKeyId(key.id);
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
}
/** @internal */
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, lowerBoundVisibility: Object): any {
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any {
let inj: Injector;
if (lowerBoundVisibility instanceof SkipSelf) {
if (visibility instanceof SkipSelf) {
inj = this._parent;
} else {
inj = this;
@ -838,7 +442,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
while (inj instanceof ReflectiveInjector_) {
const inj_ = <ReflectiveInjector_>inj;
const obj = inj_._strategy.getObjByKeyId(key.id);
const obj = inj_._getObjByKeyId(key.id);
if (obj !== UNDEFINED) return obj;
inj = inj_._parent;
}
@ -862,9 +466,9 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
const INJECTOR_KEY = ReflectiveKey.get(Injector);
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
const res: any[] = new Array(injector._proto.numberOfProviders);
for (let i = 0; i < injector._proto.numberOfProviders; ++i) {
res[i] = fn(injector._proto.getProviderAtIndex(i));
const res: any[] = new Array(injector._providers.length);
for (let i = 0; i < injector._providers.length; ++i) {
res[i] = fn(injector.getProviderAtIndex(i));
}
return res;
}

View File

@ -25,11 +25,10 @@ interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider,
*/
export class ReflectiveDependency {
constructor(
public key: ReflectiveKey, public optional: boolean, public lowerBoundVisibility: any,
public upperBoundVisibility: any, public properties: any[]) {}
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {}
static fromKey(key: ReflectiveKey): ReflectiveDependency {
return new ReflectiveDependency(key, false, null, null, []);
return new ReflectiveDependency(key, false, null);
}
}
@ -219,20 +218,18 @@ function _dependenciesFor(typeOrFunc: any): ReflectiveDependency[] {
function _extractToken(
typeOrFunc: any, metadata: any[] | any, params: any[][]): ReflectiveDependency {
const depProps: any[] = [];
let token: any = null;
let optional = false;
if (!Array.isArray(metadata)) {
if (metadata instanceof Inject) {
return _createDependency(metadata.token, optional, null, null, depProps);
return _createDependency(metadata.token, optional, null);
} else {
return _createDependency(metadata, optional, null, null, depProps);
return _createDependency(metadata, optional, null);
}
}
let lowerBoundVisibility: any = null;
let upperBoundVisibility: any = null;
let visibility: Self|SkipSelf = null;
for (let i = 0; i < metadata.length; ++i) {
const paramMetadata = metadata[i];
@ -246,29 +243,21 @@ function _extractToken(
} else if (paramMetadata instanceof Optional) {
optional = true;
} else if (paramMetadata instanceof Self) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof Host) {
upperBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof SkipSelf) {
lowerBoundVisibility = paramMetadata;
} else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
visibility = paramMetadata;
}
}
token = resolveForwardRef(token);
if (token != null) {
return _createDependency(token, optional, lowerBoundVisibility, upperBoundVisibility, depProps);
return _createDependency(token, optional, visibility);
} else {
throw new NoAnnotationError(typeOrFunc, params);
}
}
function _createDependency(
token: any, optional: boolean, lowerBoundVisibility: any, upperBoundVisibility: any,
depProps: any[]): ReflectiveDependency {
return new ReflectiveDependency(
ReflectiveKey.get(token), optional, lowerBoundVisibility, upperBoundVisibility, depProps);
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency {
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
}

View File

@ -8,12 +8,10 @@
import {ChangeDetectorRef} from '../change_detection/change_detection';
import {Injector} from '../di/injector';
import {unimplemented} from '../facade/errors';
import {Type} from '../type';
import {ElementRef} from './element_ref';
import {AppView} from './view';
import {ViewContainer} from './view_container';
import {ViewRef} from './view_ref';
import {ViewUtils} from './view_utils';
@ -31,32 +29,32 @@ export abstract class ComponentRef<C> {
/**
* Location of the Host Element of this Component Instance.
*/
get location(): ElementRef { return unimplemented(); }
abstract get location(): ElementRef;
/**
* The injector on which the component instance exists.
*/
get injector(): Injector { return unimplemented(); }
abstract get injector(): Injector;
/**
* The instance of the Component.
*/
get instance(): C { return unimplemented(); };
abstract get instance(): C;
/**
* The {@link ViewRef} of the Host View of this Component instance.
*/
get hostView(): ViewRef { return unimplemented(); };
abstract get hostView(): ViewRef;
/**
* The {@link ChangeDetectorRef} of the Component instance.
*/
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
abstract get changeDetectorRef(): ChangeDetectorRef;
/**
* The component type.
*/
get componentType(): Type<any> { return unimplemented(); }
abstract get componentType(): Type<any>;
/**
* Destroys the component instance and all of the data structures associated with it.
@ -86,11 +84,6 @@ export class ComponentRef_<C> extends ComponentRef<C> {
onDestroy(callback: Function): void { this.hostView.onDestroy(callback); }
}
/**
* @experimental
*/
const EMPTY_CONTEXT = new Object();
/**
* @stable
*/

View File

@ -7,7 +7,6 @@
*/
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {unimplemented} from '../facade/errors';
import {stringify} from '../facade/lang';
import {Type} from '../type';
import {ComponentFactory} from './component_factory';
@ -27,18 +26,18 @@ export abstract class NgModuleRef<T> {
/**
* The injector that contains all of the providers of the NgModule.
*/
get injector(): Injector { return unimplemented(); }
abstract get injector(): Injector;
/**
* The ComponentFactoryResolver to get hold of the ComponentFactories
* declared in the `entryComponents` property of the module.
*/
get componentFactoryResolver(): ComponentFactoryResolver { return unimplemented(); }
abstract get componentFactoryResolver(): ComponentFactoryResolver;
/**
* The NgModule instance.
*/
get instance(): T { return unimplemented(); }
abstract get instance(): T;
/**
* Destroys the module instance and all of the data structures associated with it.

View File

@ -8,7 +8,6 @@
import {ElementRef} from './element_ref';
import {AppView} from './view';
import {ViewContainer} from './view_container';
import {EmbeddedViewRef} from './view_ref';
@ -38,7 +37,7 @@ export abstract class TemplateRef<C> {
*
*/
// TODO(i): rename to anchor or location
get elementRef(): ElementRef { return null; }
abstract get elementRef(): ElementRef;
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}

View File

@ -11,7 +11,7 @@ import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/chang
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
import {isPresent} from '../facade/lang';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
import {DirectRenderer, RenderComponentType, RenderDebugInfo, Renderer} from '../render/api';
import {DirectRenderer, RenderComponentType, Renderer} from '../render/api';
import {AnimationViewContext} from './animation_view_context';
import {ComponentRef} from './component_factory';

View File

@ -7,10 +7,8 @@
*/
import {Injector} from '../di/injector';
import {isPresent} from '../facade/lang';
import {ElementRef} from './element_ref';
import {QueryList} from './query_list';
import {AppView} from './view';
import {ViewContainerRef_} from './view_container_ref';
import {ViewType} from './view_type';

View File

@ -7,7 +7,6 @@
*/
import {Injector} from '../di/injector';
import {unimplemented} from '../facade/errors';
import {isPresent} from '../facade/lang';
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
@ -42,11 +41,11 @@ export abstract class ViewContainerRef {
* Anchor element that specifies the location of this container in the containing View.
* <!-- TODO: rename to anchorElement -->
*/
get element(): ElementRef { return <ElementRef>unimplemented(); }
abstract get element(): ElementRef;
get injector(): Injector { return <Injector>unimplemented(); }
abstract get injector(): Injector;
get parentInjector(): Injector { return <Injector>unimplemented(); }
abstract get parentInjector(): Injector;
/**
* Destroys all Views in this container.
@ -61,7 +60,7 @@ export abstract class ViewContainerRef {
/**
* Returns the number of Views currently attached to this container.
*/
get length(): number { return <number>unimplemented(); };
abstract get length(): number;
/**
* Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it
@ -187,7 +186,8 @@ export class ViewContainerRef_ implements ViewContainerRef {
}
indexOf(viewRef: ViewRef): number {
return this._element.nestedViews.indexOf((<ViewRef_<any>>viewRef).internalView);
return this.length ? this._element.nestedViews.indexOf((<ViewRef_<any>>viewRef).internalView) :
-1;
}
/** @internal */
@ -214,7 +214,7 @@ export class ViewContainerRef_ implements ViewContainerRef {
return wtfLeave(s, view.ref);
}
clear() {
clear(): void {
for (let i = this.length - 1; i >= 0; i--) {
this.remove(i);
}

View File

@ -9,7 +9,6 @@
import {AnimationQueue} from '../animation/animation_queue';
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {ChangeDetectorStatus} from '../change_detection/constants';
import {unimplemented} from '../facade/errors';
import {AppView} from './view';
/**
@ -21,7 +20,7 @@ export abstract class ViewRef extends ChangeDetectorRef {
*/
abstract destroy(): void;
get destroyed(): boolean { return <boolean>unimplemented(); }
abstract get destroyed(): boolean;
abstract onDestroy(callback: Function): any /** TODO #9100 */;
}
@ -81,9 +80,9 @@ export abstract class ViewRef extends ChangeDetectorRef {
* @experimental
*/
export abstract class EmbeddedViewRef<C> extends ViewRef {
get context(): C { return unimplemented(); }
abstract get context(): C;
get rootNodes(): any[] { return <any[]>unimplemented(); };
abstract get rootNodes(): any[];
}
export class ViewRef_<C> implements EmbeddedViewRef<C>, ChangeDetectorRef {

View File

@ -15,7 +15,6 @@ import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security';
import {VERSION} from '../version';
import {NgZone} from '../zone/ng_zone';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
import {AppView} from './view';
@ -23,7 +22,6 @@ import {AppView} from './view';
@Injectable()
export class ViewUtils {
sanitizer: Sanitizer;
private _nextCompTypeId: number = 0;
constructor(
private _renderer: RootRenderer, sanitizer: Sanitizer,

View File

@ -10,7 +10,6 @@ import {AnimationKeyframe} from '../../src/animation/animation_keyframe';
import {AnimationPlayer} from '../../src/animation/animation_player';
import {AnimationStyles} from '../../src/animation/animation_styles';
import {Injector} from '../di/injector';
import {unimplemented} from '../facade/errors';
import {ViewEncapsulation} from '../metadata/view';
/**
@ -25,12 +24,12 @@ export class RenderComponentType {
}
export abstract class RenderDebugInfo {
get injector(): Injector { return unimplemented(); }
get component(): any { return unimplemented(); }
get providerTokens(): any[] { return unimplemented(); }
get references(): {[key: string]: any} { return unimplemented(); }
get context(): any { return unimplemented(); }
get source(): string { return unimplemented(); }
abstract get injector(): Injector;
abstract get component(): any;
abstract get providerTokens(): any[];
abstract get references(): {[key: string]: any};
abstract get context(): any;
abstract get source(): string;
}
export interface DirectRenderer {

View File

@ -6,8 +6,21 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Observable} from 'rxjs/Observable';
import {$$observable as symbolObservable} from 'rxjs/symbol/observable';
/**
* Determine if the argument is shaped like a Promise
*/
export function isPromise(obj: any): obj is Promise<any> {
// allow any Promise/A+ compliant thenable.
// It's up to the caller to ensure that obj.then conforms to the spec
return !!obj && typeof obj.then === 'function';
}
/**
* Determine if the argument is an Observable
*/
export function isObservable(obj: any | Observable<any>): obj is Observable<any> {
return !!(obj && obj[symbolObservable]);
}

View File

@ -212,6 +212,11 @@ export function main() {
expect(mockConsole.res[0]).toEqual('EXCEPTION: ' + expectedErrMsg);
});
}));
it('should add bootstrapped module into platform modules list', async(() => {
defaultPlatform.bootstrapModule(createModule({bootstrap: [SomeComponent]}))
.then(module => expect((<any>defaultPlatform)._modules).toContain(module));
}));
});
describe('bootstrapModuleFactory', () => {

View File

@ -7,7 +7,7 @@
*/
import {Inject, Injectable, Injector, Optional, Provider, ReflectiveInjector, ReflectiveKey, Self, forwardRef} from '@angular/core';
import {ReflectiveInjectorDynamicStrategy, ReflectiveInjectorInlineStrategy, ReflectiveInjector_, ReflectiveProtoInjector} from '@angular/core/src/di/reflective_injector';
import {ReflectiveInjector_} from '@angular/core/src/di/reflective_injector';
import {ResolvedReflectiveProvider_} from '@angular/core/src/di/reflective_provider';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -83,14 +83,9 @@ export function main() {
{provide: 'provider10', useValue: 1}
];
[{strategy: 'inline', providers: [], strategyClass: ReflectiveInjectorInlineStrategy}, {
strategy: 'dynamic',
providers: dynamicProviders,
strategyClass: ReflectiveInjectorDynamicStrategy
}].forEach((context) => {
function createInjector(
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ {
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(context['providers']));
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders));
if (isPresent(parent)) {
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);
} else {
@ -98,11 +93,7 @@ export function main() {
}
}
describe(`injector ${context['strategy']}`, () => {
it('should use the right strategy', () => {
const injector = createInjector([]);
expect(injector.internalStrategy).toBeAnInstanceOf(context['strategyClass']);
});
describe(`injector`, () => {
it('should instantiate a class without dependencies', () => {
const injector = createInjector([Engine]);
@ -170,30 +161,6 @@ export function main() {
expect(car.engine).toBeAnInstanceOf(Engine);
});
it('should throw when using a factory with more than 20 dependencies', () => {
function factoryWithTooManyArgs() { return new Car(null); }
const injector = createInjector([
Engine, {
provide: Car,
useFactory: factoryWithTooManyArgs,
deps: [
Engine, Engine, Engine, Engine, Engine, Engine, Engine,
Engine, Engine, Engine, Engine, Engine, Engine, Engine,
Engine, Engine, Engine, Engine, Engine, Engine, Engine
]
}
]);
try {
injector.get(Car);
throw 'Must throw';
} catch (e) {
expect(e.message).toContain(
`Cannot instantiate 'Car' because it has more than 20 dependencies`);
}
});
it('should supporting provider to null', () => {
const injector = createInjector([{provide: Engine, useValue: null}]);
const engine = injector.get(Engine);
@ -202,8 +169,7 @@ export function main() {
it('should provide to an alias', () => {
const injector = createInjector([
Engine, {provide: SportsCar, useClass: SportsCar},
{provide: Car, useExisting: SportsCar}
Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar}
]);
const car = injector.get(Car);
@ -225,8 +191,8 @@ export function main() {
});
it('should support multiProviders that are created using useExisting', () => {
const injector = createInjector(
[Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]);
const injector =
createInjector([Engine, SportsCar, {provide: Car, useExisting: SportsCar, multi: true}]);
const cars = injector.get(Car);
expect(cars.length).toEqual(1);
@ -319,8 +285,7 @@ export function main() {
it('should show the full path when error happens in a constructor', () => {
const providers =
ReflectiveInjector.resolve([Car, {provide: Engine, useClass: BrokenEngine}]);
const proto = new ReflectiveProtoInjector([providers[0], providers[1]]);
const injector = new ReflectiveInjector_(proto);
const injector = new ReflectiveInjector_(providers);
try {
injector.get(Car);
@ -337,8 +302,7 @@ export function main() {
let isBroken = true;
const injector = createInjector([
Car,
{provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())}
Car, {provide: Engine, useFactory: (() => isBroken ? new BrokenEngine() : new Engine())}
]);
expect(() => injector.get(Car))
@ -432,9 +396,8 @@ export function main() {
it('should throw when not requested provider on self', () => {
const parent = ReflectiveInjector.resolveAndCreate([Engine]);
const child = parent.resolveAndCreateChild([
{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}
]);
const child = parent.resolveAndCreateChild(
[{provide: Car, useFactory: (e: Engine) => new Car(e), deps: [[Engine, new Self()]]}]);
expect(() => child.get(Car))
.toThrowError(`No provider for Engine! (${stringify(Car)} -> ${stringify(Engine)})`);
@ -556,5 +519,4 @@ export function main() {
.toEqual('ReflectiveInjector(providers: [ "Engine" , "BrokenEngine" ])');
});
});
});
}

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