Compare commits

..

29 Commits
4.1.1 ... 4.1.x

Author SHA1 Message Date
7edc5e96f3 docs: add changelog for 4.1.3 2017-05-17 15:44:25 -07:00
7b802faa96 release: cut the 4.1.3 release 2017-05-17 15:42:14 -07:00
6f039d7a3c build: do not create the bundles when updating the public API
- 1.7x faster on my machine (2.7 vs 4.6 min)
- should solve the memory issue
2017-05-17 15:12:12 -07:00
0a82f7d69f fix(platform-server): wait for async app initializers to complete before removing server side styles (#16712)
This fixes a flicker when transitioning from server rendered page to client rendered page in lazy loaded routes by waiting for the lazy loaded route to finish loading, assuming initialNavigation on the route is set to 'enabled'.

Fixes #15716
2017-05-17 15:12:08 -07:00
b7f85813d5 fix(compiler-cli): import routing module with forRoot (#16438) 2017-05-17 15:10:59 -07:00
a5bdbed306 fix: add typescript 2.3.2 typings test (#16738)
Closes #16663
2017-05-17 15:10:59 -07:00
883ca285de fix(router): Wrap Promise-like instances in native Promises (#16759)
Hybrid apps (mix of Angular and AngularJS) might return AngularJS implementation
of Promises that do not play well with the change detection. Wrapping them in
native Promises fix this issue.

This could be the case when a Resolver returns a `$q` promise.
2017-05-17 15:10:58 -07:00
afb7540067 fix(upgrade): Prevent renaming of $inject property (#16706)
Use bracket notation to access $inject in downgradeInjectable to
support property renaming. Since the return type is any,
Closure compiler renames $inject.
2017-05-17 15:10:58 -07:00
47df3d681f fix(upgrade): use quote to prevent ClossureCompiler obfuscating $event. (#16724)
This is critical for AngularJS to get the $event object in template.
2017-05-17 15:10:57 -07:00
cd7fe31fbc docs: add saved replies (#16726) 2017-05-17 15:10:56 -07:00
4203b1bb1f docs(common): remove h1s from API docs
Only one h1 is allowed per document, and this is provided by the template.
So we cannot have any h1 tags (or `#` markdown shorthand) in any API docs.

Closes #16193
2017-05-17 15:10:56 -07:00
055db5ad6a docs(router): Change CanDeactivate to CanLoad (#16237)
fix mistake in docs. CanDeactivate should be CanLoad
2017-05-17 15:10:55 -07:00
62a8618536 docs: add changelog for 4.1.2 2017-05-10 15:44:28 -07:00
419fe0ca0d release: cut the 4.1.2 release 2017-05-10 15:42:11 -07:00
e46a65f8fd build: update key for pushing to PACKAGE-builds repos (#16667) 2017-05-09 17:34:35 -07:00
f0e1043a1d docs(*) fix dangling links in API docs (#16632)
* docs(animations): fix links to `Component` animations

* docs(core): fix links to `ReflectiveInjector` methods

The `resolve` and other methods were moved from the
`Injector` to the `ReflectiveInjector`.

* docs(core): fix links to `Renderer`

The local links were assuming that that methods were on the
current document (e.g. `RootRenderer`), but they are actually
on the `Renderer` class.

* docs(router): fix links to methods

* docs(forms): fix links to methods

* docs(core): fix links to methods

* docs(router): fix API page links and an internal link
2017-05-09 17:34:35 -07:00
c1938f5af1 docs(core): remove link to non-existent class in InjectableDecorator (#16653)
Closes #16640
2017-05-09 17:34:34 -07:00
915eae50b4 fix(router): fix redirect to a URL with a param having multiple values (#16376)
fixes #16310

PR Close #16376
2017-05-09 17:34:33 -07:00
68675e625a test(router): simplify redirect tests (#16376) 2017-05-09 17:34:32 -07:00
d0e1688514 fix(compiler): avoid a ...null spread in extraction (#16547)
This code only runs in ES5 mode in the test suite, so this is difficult to test.
However `updateFromTemplate` is being called with a spread operator, as
`...updateFromTemplate(...)`. The spread operator should fail on `null` values.
This change avoids the problem by always returning a (possibly empty) array.

PR Close #16547
2017-05-09 17:34:31 -07:00
ee6705af49 fix(core): detach projected views when a parent view is destroyed (#16592)
This also clarifies via a test 
that we only update projected views when the view is created or destroyed,
but not when it is attached/detached/moved.

Fixes #15578

PR Close #16592
2017-05-09 17:34:31 -07:00
9218812011 fix(core): projected views should be dirty checked when the declaring component is dirty checked. (#16592)
Previously a projected view was only dirty checked when the
component in which it was inserted was dirty checked.

This fix changes the behavior so that a view is also dirty checked if
the declaring component is dirty checked.

Note: This does not change the order of change detection,
only the fact whether a projected view is dirty checked or not.

Fixes #14321
2017-05-09 17:34:30 -07:00
ec77bf9fb0 docs(core): EventEmitter docs for isAsync defaults (#15780)
- solves #15758
2017-05-09 17:34:29 -07:00
fee5b2ad56 docs(animations): remove duplicate word (#16508) 2017-05-09 17:34:24 -07:00
9c70a3cfb1 fix(http): flatten metadata for @angular/http/testing
@angular/http/testing used to publish a metadata structure which paralleled
the .d.ts structure. This causes ngc to write incorrect imports for this
bundle when compiling providers using MockBackend and other http testing
classes.

This change restructures the @angular/http/testing build a bit, modeling it
after @angular/platform-browser-animations, and produces a FESM structure
that has flat metadata.

Fixes #15521.
2017-05-09 17:33:59 -07:00
ec3b6e9603 fix(http): introduce encodingHint for text() for better ArrayBuffer support
Currently, if a Response has an ArrayBuffer body and text() is called, Angular
attempts to convert the ArrayBuffer to a string. Doing this requires knowing
the encoding of the bytes in the buffer, which is context that we don't have.

Instead, we assume that the buffer is encoded in UTF-16, and attempt to process
it that way. Unfortunately the approach chosen (interpret buffer as Uint16Array and
create a Javascript string from each entry using String.fromCharCode) is incorrect
as it does not handle UTF-16 surrogate pairs. What Angular actually implements, then,
is UCS-2 decoding, which is equivalent to UTF-16 with characters restricted to the
base plane.

No standard way of decoding UTF-8 or UTF-16 exists in the browser today. APIs like
TextDecoder are only supported in a few browsers, and although hacks like using the
FileReader API with a Blob to force browsers to do content encoding detection and
decoding exist, they're slow and not compatible with the synchronous text() API.

Thus, this bug is fixed by introducing an encodingHint parameter to text(). The
default value of this parameter is 'legacy', indicating that the existing broken
behavior should be used - this prevents breaking existing apps. The only other
possible value of the hint is 'iso-8859' which interprets each byte of the buffer
with String.fromCharCode. UTF-8 and UTF-16 are not supported - it is up to the
consumer to get the ArrayBuffer and decode it themselves.

The parameter is a hint, as it's not always used (for example, if the conversion
to text doesn't involve an ArrayBuffer source). Additionally, this leaves the door
open for future implementations to perform more sophisticated encoding detection
and ignore the user-provided value if it can be proven to be incorrect.

Fixes #15932.

PR Close #16420
2017-05-09 17:33:59 -07:00
63066f7697 fix(http): honor RequestArgs.search and RequestArgs.params map type
Currently `new Request({search: ...})` is not honored, and
`new Request({params: {'x': 'y'}) doesn't work either, as this object would have
toString() called. This change allows both of these cases to work, as proved by
the 2 new tests.

Fixes #15761

PR Close #16392
2017-05-09 17:33:58 -07:00
5f6d0f2340 revert: fix: public API golden files (#16414)
This reverts commit dcaa11a88b.
2017-05-04 15:00:09 -07:00
4a0e93b03b revert: test(compiler-cli): add test for missingTranslation parameter
This reverts commit 109229246c.
2017-05-04 14:51:30 -07:00
58 changed files with 796 additions and 223 deletions

View File

@ -32,7 +32,7 @@ env:
global:
# GITHUB_TOKEN_ANGULAR=<github token, a personal access token of the angular-builds account, account access in valentine>
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
- secure: "rNqXoy2gqjbF5tBXlRBy+oiYntO3BtzcxZuEtlLMzNaTNzC4dyMOFub0GkzIPWwOzkARoEU9Kv+bC97fDVbCBUKeyzzEqxqddUKhzRxeaYjsefJ6XeTvBvDxwo7wDwyxZSuWdBeGAe4eARVHm7ypsd+AlvqxtzjyS27TK2BzdL4="
- secure: "aCdHveZuY8AT4Jr1JoJB4LxZsnGWRe/KseZh1YXYe5UtufFCtTVHvUcLn0j2aLBF0KpdyS+hWf0i4np9jthKu2xPKriefoPgCMpisYeC0MFkwbmv+XlgkUbgkgVZMGiVyX7DCYXVahxIoOUjVMEDCbNiHTIrfEuyq24U3ok2tHc="
# FIREBASE_TOKEN
# This is needed for publishing builds to the "aio-staging" firebase site.
# TODO(i): the token was generated using the iminar@google account, we should switch to a shared/role-base account.

View File

@ -1,3 +1,34 @@
<a name="4.1.3"></a>
## [4.1.3](https://github.com/angular/angular/compare/4.1.2...4.1.3) (2017-05-17)
### Bug Fixes
* add typescript 2.3.2 typings test ([#16738](https://github.com/angular/angular/issues/16738)) ([a5bdbed](https://github.com/angular/angular/commit/a5bdbed)), closes [#16663](https://github.com/angular/angular/issues/16663)
* **compiler-cli:** import routing module with forRoot ([#16438](https://github.com/angular/angular/issues/16438)) ([b7f8581](https://github.com/angular/angular/commit/b7f8581))
* **platform-server:** wait for async app initializers to complete before removing server side styles ([#16712](https://github.com/angular/angular/issues/16712)) ([0a82f7d](https://github.com/angular/angular/commit/0a82f7d)), closes [#15716](https://github.com/angular/angular/issues/15716)
* **router:** Wrap Promise-like instances in native Promises ([#16759](https://github.com/angular/angular/issues/16759)) ([883ca28](https://github.com/angular/angular/commit/883ca28))
* **upgrade:** Prevent renaming of $inject property ([#16706](https://github.com/angular/angular/issues/16706)) ([afb7540](https://github.com/angular/angular/commit/afb7540))
* **upgrade:** use quote to prevent ClossureCompiler obfuscating $event. ([#16724](https://github.com/angular/angular/issues/16724)) ([47df3d6](https://github.com/angular/angular/commit/47df3d6))
<a name="4.1.2"></a>
## [4.1.2](https://github.com/angular/angular/compare/4.1.1...4.1.2) (2017-05-10)
### Bug Fixes
* **compiler:** avoid a `...null` spread in extraction ([#16547](https://github.com/angular/angular/issues/16547)) ([d0e1688](https://github.com/angular/angular/commit/d0e1688))
* **core:** detach projected views when a parent view is destroyed ([#16592](https://github.com/angular/angular/issues/16592)) ([ee6705a](https://github.com/angular/angular/commit/ee6705a)), closes [#15578](https://github.com/angular/angular/issues/15578)
* **core:** projected views should be dirty checked when the declaring component is dirty checked. ([#16592](https://github.com/angular/angular/issues/16592)) ([9218812](https://github.com/angular/angular/commit/9218812)), closes [#14321](https://github.com/angular/angular/issues/14321)
* **http:** flatten metadata for [@angular](https://github.com/angular)/http/testing ([9c70a3c](https://github.com/angular/angular/commit/9c70a3c)), closes [#15521](https://github.com/angular/angular/issues/15521)
* **http:** honor RequestArgs.search and RequestArgs.params map type ([63066f7](https://github.com/angular/angular/commit/63066f7)), closes [#15761](https://github.com/angular/angular/issues/15761) [#16392](https://github.com/angular/angular/issues/16392)
* **http:** introduce encodingHint for text() for better ArrayBuffer support ([ec3b6e9](https://github.com/angular/angular/commit/ec3b6e9)), closes [#15932](https://github.com/angular/angular/issues/15932) [#16420](https://github.com/angular/angular/issues/16420)
* **router:** fix redirect to a URL with a param having multiple values ([#16376](https://github.com/angular/angular/issues/16376)) ([915eae5](https://github.com/angular/angular/commit/915eae5)), closes [#16310](https://github.com/angular/angular/issues/16310)
<a name="4.1.1"></a>
## [4.1.1](https://github.com/angular/angular/compare/4.1.0...4.1.1) (2017-05-04)

View File

@ -501,7 +501,7 @@ those callbacks like this:
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
The callbacks receive an `AnimationEvent` that contains useful properties such as
`fromState`, `toState` and `totalTime`.
Those callbacks will fire whether or not an animation is picked up.

View File

@ -19,6 +19,12 @@ I'm sorry but we don't understand the problem you are reporting.
If the problem still exists please open a new issue and provide a plunker reproducing the problem and describing the difference between the expected and current behavior. You can use this plunker template: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
```
## Angular: Plunker Needed (v1)
```
I'm sorry but reported issues require a plunker reproducing the problem.
If this issue persists, please create a plunker using this template and describe the difference between the expected and current behavior and create a new issue: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5?p=catalogue
```
## Angular: Duplicate (v1)
```
@ -38,6 +44,10 @@ If the problem still persists, please file a new issue and ensure you provide al
I'm sorry but this issue is not caused by Angular. Please contact the author(s) of project <PROJECT NAME> or file issue on their issue tracker.
```
## Angular: Behaving as Expected (v1)
```
It appears this behaves as expected. If you still feel there is an issue, please provide further details in a new issue.
```
## Angular: Non-reproducible (v1)
```

View File

@ -29,9 +29,10 @@ function loadTask(fileName, taskName) {
gulp.task('format:enforce', loadTask('format', 'enforce'));
gulp.task('format', loadTask('format', 'format'));
gulp.task('build.sh', loadTask('build'));
gulp.task('build.sh', loadTask('build', 'all'));
gulp.task('build.sh:no-bundle', loadTask('build', 'no-bundle'));
gulp.task('public-api:enforce', loadTask('public-api', 'enforce'));
gulp.task('public-api:update', ['build.sh'], loadTask('public-api', 'update'));
gulp.task('public-api:update', ['build.sh:no-bundle'], loadTask('public-api', 'update'));
gulp.task('lint', ['format:enforce', 'validate-commit-messages', 'tslint']);
gulp.task('tslint', ['tools:build'], loadTask('lint'));
gulp.task('validate-commit-messages', loadTask('validate-commit-message'));

View File

@ -8,18 +8,18 @@
import * as compiler from '@angular/compiler';
import * as compilerTesting from '@angular/compiler/testing';
import * as coreTesting from '@angular/core';
import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as forms from '@angular/forms';
import * as core from '@angular/core/testing';
import * as httpTesting from '@angular/http';
import * as http from '@angular/http/testing';
import * as platformBrowserTesting from '@angular/platform-browser';
import * as http from '@angular/http';
import * as httpTesting from '@angular/http/testing';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowser from '@angular/platform-browser/testing';
import * as platformServerTesting from '@angular/platform-server';
import * as platformServer from '@angular/platform-server/testing';
import * as routerTesting from '@angular/router';
import * as router from '@angular/router/testing';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserTesting from '@angular/platform-browser/testing';
import * as platformServer from '@angular/platform-server';
import * as platformServerTesting from '@angular/platform-server/testing';
import * as router from '@angular/router';
import * as routerTesting from '@angular/router/testing';
import * as upgrade from '@angular/upgrade';
export default {

View File

@ -8,18 +8,18 @@
import * as compiler from '@angular/compiler';
import * as compilerTesting from '@angular/compiler/testing';
import * as coreTesting from '@angular/core';
import * as core from '@angular/core/testing';
import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as forms from '@angular/forms';
import * as httpTesting from '@angular/http';
import * as http from '@angular/http/testing';
import * as platformBrowserTesting from '@angular/platform-browser';
import * as http from '@angular/http';
import * as httpTesting from '@angular/http/testing';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowser from '@angular/platform-browser/testing';
import * as platformServerTesting from '@angular/platform-server';
import * as platformServer from '@angular/platform-server/testing';
import * as routerTesting from '@angular/router';
import * as router from '@angular/router/testing';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserTesting from '@angular/platform-browser/testing';
import * as platformServer from '@angular/platform-server';
import * as platformServerTesting from '@angular/platform-server/testing';
import * as router from '@angular/router';
import * as routerTesting from '@angular/router/testing';
import * as upgrade from '@angular/upgrade';
export default {

View File

@ -0,0 +1,41 @@
/**
* @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 * as compiler from '@angular/compiler';
import * as compilerTesting from '@angular/compiler/testing';
import * as core from '@angular/core';
import * as coreTesting from '@angular/core/testing';
import * as forms from '@angular/forms';
import * as http from '@angular/http';
import * as httpTesting from '@angular/http/testing';
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
import * as platformBrowser from '@angular/platform-browser';
import * as platformBrowserTesting from '@angular/platform-browser/testing';
import * as platformServer from '@angular/platform-server';
import * as platformServerTesting from '@angular/platform-server/testing';
import * as router from '@angular/router';
import * as routerTesting from '@angular/router/testing';
import * as upgrade from '@angular/upgrade';
export default {
compiler,
compilerTesting,
core,
coreTesting,
forms,
http,
httpTesting,
platformBrowser,
platformBrowserTesting,
platformBrowserDynamic,
platformServer,
platformServerTesting,
router,
routerTesting,
upgrade
};

View File

@ -0,0 +1,28 @@
{
"name": "angular-integration",
"description": "Assert that users with TypeScript 2.2 can type-check an Angular application",
"version": "0.0.0",
"license": "MIT",
"dependencies": {
"@angular/animations": "file:../../dist/packages-dist/animations",
"@angular/common": "file:../../dist/packages-dist/common",
"@angular/compiler": "file:../../dist/packages-dist/compiler",
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/forms": "file:../../dist/packages-dist/forms",
"@angular/http": "file:../../dist/packages-dist/http",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"@angular/router": "file:../../dist/packages-dist/router",
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
"@types/jasmine": "2.5.41",
"rxjs": "file:../../node_modules/rxjs",
"typescript": "2.3.2",
"zone.js": "0.7.6"
},
"scripts": {
"test": "tsc"
}
}

View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../../dist/typing-test/",
"rootDir": ".",
"target": "es5",
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.iterable",
"es2015.promise"
],
"types": [],
"strictNullChecks": true
},
"files": [
"include-all.ts",
"node_modules/@types/jasmine/index.d.ts"
]
}

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "4.1.1",
"version": "4.1.3",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",

View File

@ -120,7 +120,7 @@ export interface AnimationGroupMetadata extends AnimationMetadata { steps: Anima
/**
* `trigger` is an animation-specific function that is designed to be used inside of Angular's
animation DSL language. If this information is new, please navigate to the {@link
Component#animations-anchor component animations metadata page} to gain a better understanding of
Component#animations component animations metadata page} to gain a better understanding of
how animations in Angular are used.
*
* `trigger` Creates an animation trigger which will a list of {@link state state} and {@link
@ -128,7 +128,7 @@ export interface AnimationGroupMetadata extends AnimationMetadata { steps: Anima
changes.
*
* Triggers are registered within the component annotation data under the {@link
Component#animations-anchor animations section}. An animation trigger can be placed on an element
Component#animations animations section}. An animation trigger can be placed on an element
within a template by referencing the name of the trigger followed by the expression value that the
trigger is bound to (in the form of `[@triggerName]="expression"`.
*
@ -175,7 +175,7 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
/**
* `animate` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `animate` specifies an animation step that will apply the provided `styles` data for a given
@ -226,7 +226,7 @@ export function animate(
/**
* `group` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `group` specifies a list of animation steps that are all run in parallel. Grouped animations are
@ -261,7 +261,7 @@ export function group(steps: AnimationMetadata[]): AnimationGroupMetadata {
/**
* `sequence` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `sequence` Specifies a list of animation steps that are run one by one. (`sequence` is used by
@ -299,7 +299,7 @@ export function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata
/**
* `style` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `style` declares a key/value object containing CSS properties/styles that can then be used for
@ -347,7 +347,7 @@ export function style(
/**
* `state` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `state` declares an animation state within the given trigger. When a state is active within a
@ -399,7 +399,7 @@ export function state(name: string, styles: AnimationStyleMetadata): AnimationSt
/**
* `keyframes` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `keyframes` specifies a collection of {@link style style} entries each optionally characterized
@ -448,7 +448,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
/**
* `transition` is an animation-specific function that is designed to be used inside of Angular's
* animation DSL language. If this information is new, please navigate to the {@link
* Component#animations-anchor component animations metadata page} to gain a better understanding of
* Component#animations component animations metadata page} to gain a better understanding of
* how animations in Angular are used.
*
* `transition` declares the {@link sequence sequence of animation steps} that will be run when the

View File

@ -49,7 +49,7 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
* ngModuleFactory: moduleFactory;">
* </ng-container>
* ```
* # Example
* ## Example
*
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
*

View File

@ -17,13 +17,13 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
* - `then` template is the inline template of `ngIf` unless bound to a different value.
* - `else` template is blank unless it is bound.
*
* # Most common usage
* ## Most common usage
*
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
* seen in this example:
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
*
* # Showing an alternative template using `else`
* ## Showing an alternative template using `else`
*
* If it is necessary to display a template when the `expression` is falsy use the `else` template
* binding as shown. Note that the `else` binding points to a `<ng-template>` labeled `#elseBlock`.
@ -32,7 +32,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
*
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
*
* # Using non-inlined `then` template
* ## Using non-inlined `then` template
*
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
@ -40,7 +40,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '
*
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
*
* # Storing conditional result in a variable
* ## Storing conditional result in a variable
*
* A common pattern is that we need to show a set of properties from the same object. If the
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against

View File

@ -26,7 +26,7 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef
*
* Note: using the key `$implicit` in the context object will set it's value as default.
*
* # Example
* ## Example
*
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
*

View File

@ -29,7 +29,6 @@ function main() {
Promise.resolve()
.then(() => codeGenTest())
.then(() => codeGenTest(true))
.then(() => i18nTest())
.then(() => lazyRoutesTest())
.then(() => {
@ -43,9 +42,8 @@ function main() {
});
}
function codeGenTest(forceError = false) {
function codeGenTest() {
const basePath = path.join(__dirname, '../ngtools_src');
const srcPath = path.join(__dirname, '../src');
const project = path.join(basePath, 'tsconfig-build.json');
const readResources: string[] = [];
const wroteFiles: string[] = [];
@ -61,9 +59,6 @@ function codeGenTest(forceError = false) {
config.ngOptions.basePath = basePath;
console.log(`>>> running codegen for ${project}`);
if (forceError) {
console.log(`>>> asserting that missingTranslation param with error value throws`);
}
return __NGTOOLS_PRIVATE_API_2
.codeGen({
basePath,
@ -72,10 +67,9 @@ function codeGenTest(forceError = false) {
angularCompilerOptions: config.ngOptions,
// i18n options.
i18nFormat: 'xlf',
i18nFile: path.join(srcPath, 'messages.fi.xlf'),
locale: 'fi',
missingTranslation: forceError ? 'error' : 'ignore',
i18nFormat: undefined,
i18nFile: undefined,
locale: undefined,
readResource: (fileName: string) => {
readResources.push(fileName);
@ -107,17 +101,10 @@ function codeGenTest(forceError = false) {
console.log(`done, no errors.`);
})
.catch((e: Error) => {
if (forceError) {
assert(
e.message.match(`Missing translation for message`),
`Expected error message for missing translations`);
console.log(`done, error catched`);
} else {
console.error(e.stack);
console.error('Compilation failed');
throw e;
}
.catch((e: any) => {
console.error(e.stack);
console.error('Compilation failed');
throw e;
});
}
@ -178,7 +165,7 @@ function i18nTest() {
console.log(`done, no errors.`);
})
.catch((e: Error) => {
.catch((e: any) => {
console.error(e.stack);
console.error('Extraction failed');
throw e;

View File

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

View File

@ -149,8 +149,13 @@ function _extractLazyRoutesFromStaticModule(
return acc;
}, []);
const importedSymbols = ((moduleMetadata.imports || []) as any[])
.filter(i => i instanceof StaticSymbol) as StaticSymbol[];
const importedSymbols =
((moduleMetadata.imports || []) as any[])
.filter(i => i instanceof StaticSymbol || i.ngModule instanceof StaticSymbol)
.map(i => {
if (i instanceof StaticSymbol) return i;
return i.ngModule;
}) as StaticSymbol[];
return importedSymbols
.reduce(

View File

@ -26,7 +26,7 @@ export class MessageBundle {
private _implicitAttrs: {[k: string]: string[]}, private _locale: string|null = null) {}
updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig):
ParseError[]|null {
ParseError[] {
const htmlParserResult = this._htmlParser.parse(html, url, true, interpolationConfig);
if (htmlParserResult.errors.length) {
@ -41,7 +41,7 @@ export class MessageBundle {
}
this._messages.push(...i18nParserResult.messages);
return null;
return [];
}
// Return the message in the internal format

View File

@ -24,23 +24,48 @@ export const APP_INITIALIZER = new InjectionToken<Array<() => void>>('Applicatio
*/
@Injectable()
export class ApplicationInitStatus {
private resolve: Function;
private reject: Function;
private initialized = false;
private _donePromise: Promise<any>;
private _done = false;
constructor(@Inject(APP_INITIALIZER) @Optional() appInits: (() => any)[]) {
constructor(@Inject(APP_INITIALIZER) @Optional() private appInits: (() => any)[]) {
this._donePromise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
});
}
/** @internal */
runInitializers() {
if (this.initialized) {
return;
}
const asyncInitPromises: Promise<any>[] = [];
if (appInits) {
for (let i = 0; i < appInits.length; i++) {
const initResult = appInits[i]();
const complete =
() => {
this._done = true;
this.resolve();
}
if (this.appInits) {
for (let i = 0; i < this.appInits.length; i++) {
const initResult = this.appInits[i]();
if (isPromise(initResult)) {
asyncInitPromises.push(initResult);
}
}
}
this._donePromise = Promise.all(asyncInitPromises).then(() => { this._done = true; });
Promise.all(asyncInitPromises).then(() => { complete(); }).catch(e => { this.reject(e); });
if (asyncInitPromises.length === 0) {
this._done = true;
complete();
}
this.initialized = true;
}
get done(): boolean { return this._done; }

View File

@ -302,6 +302,7 @@ export class PlatformRef_ extends PlatformRef {
ngZone !.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
return _callAndReportToErrorHandler(exceptionHandler, () => {
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
initStatus.runInitializers();
return initStatus.donePromise.then(() => {
this._moduleDoBootstrap(moduleRef);
return moduleRef;

View File

@ -127,7 +127,7 @@ export interface InjectableDecorator {
*
* {@example core/di/ts/metadata_spec.ts region='Injectable'}
*
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
* {@link Injector} will throw an error when trying to instantiate a class that
* does not have `@Injectable` marker, as shown in the example below.
*
* {@example core/di/ts/metadata_spec.ts region='InjectableThrows'}

View File

@ -113,7 +113,7 @@ export abstract class ReflectiveInjector implements Injector {
*
* This function is slower than the corresponding `fromResolvedProviders`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
* See {@link ReflectiveInjector#resolve} and {@link ReflectiveInjector#fromResolvedProviders}.
*/
static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
@ -190,7 +190,7 @@ export abstract class ReflectiveInjector implements Injector {
*
* This function is slower than the corresponding `createChildFromResolved`
* because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
* See {@link ReflectiveInjector#resolve} and {@link ReflectiveInjector#createChildFromResolved}.
*/
abstract resolveAndCreateChild(providers: Provider[]): ReflectiveInjector;

View File

@ -64,8 +64,11 @@ export class EventEmitter<T> extends Subject<T> {
__isAsync: boolean;
/**
* Creates an instance of [EventEmitter], which depending on [isAsync],
* Creates an instance of {@link EventEmitter}, which depending on `isAsync`,
* delivers events synchronously or asynchronously.
*
* @param isAsync By default, events are delivered synchronously (default value: `false`).
* Set to `true` for asynchronous event delivery.
*/
constructor(isAsync: boolean = false) {
super();

View File

@ -61,19 +61,19 @@ export enum ViewEncapsulation {
* {@link Component}
*/
export class ViewMetadata {
/** {@link Component.templateUrl} */
/** {@link Component#templateUrl} */
templateUrl: string|undefined;
/** {@link Component.template} */
/** {@link Component#template} */
template: string|undefined;
/** {@link Component.stylesUrl} */
/** {@link Component#stylesUrl} */
styleUrls: string[]|undefined;
/** {@link Component.styles} */
/** {@link Component#styles} */
styles: string[]|undefined;
/** {@link Component.encapsulation} */
/** {@link Component#encapsulation} */
encapsulation: ViewEncapsulation|undefined;
/** {@link Component.animation} */
/** {@link Component#animation} */
animations: any[]|undefined;
/** {@link Component.interpolation} */
/** {@link Component#interpolation} */
interpolation: [string, string]|undefined;
constructor(

View File

@ -99,7 +99,8 @@ export const Renderer2Interceptor = new InjectionToken<Renderer2[]>('Renderer2In
*
* Use this service to bypass Angular's templating and make custom UI changes that can't be
* expressed declaratively. For example if you need to set a property or an attribute whose name is
* not statically known, use {@link #setElementProperty} or {@link #setElementAttribute}
* not statically known, use {@link Renderer#setElementProperty} or {@link
* Renderer#setElementAttribute}
* respectively.
*
* If you are implementing a custom renderer, you must implement this interface.

View File

@ -141,37 +141,38 @@ export const enum NodeFlags {
None = 0,
TypeElement = 1 << 0,
TypeText = 1 << 1,
ProjectedTemplate = 1 << 2,
CatRenderNode = TypeElement | TypeText,
TypeNgContent = 1 << 2,
TypePipe = 1 << 3,
TypePureArray = 1 << 4,
TypePureObject = 1 << 5,
TypePurePipe = 1 << 6,
TypeNgContent = 1 << 3,
TypePipe = 1 << 4,
TypePureArray = 1 << 5,
TypePureObject = 1 << 6,
TypePurePipe = 1 << 7,
CatPureExpression = TypePureArray | TypePureObject | TypePurePipe,
TypeValueProvider = 1 << 7,
TypeClassProvider = 1 << 8,
TypeFactoryProvider = 1 << 9,
TypeUseExistingProvider = 1 << 10,
LazyProvider = 1 << 11,
PrivateProvider = 1 << 12,
TypeDirective = 1 << 13,
Component = 1 << 14,
TypeValueProvider = 1 << 8,
TypeClassProvider = 1 << 9,
TypeFactoryProvider = 1 << 10,
TypeUseExistingProvider = 1 << 11,
LazyProvider = 1 << 12,
PrivateProvider = 1 << 13,
TypeDirective = 1 << 14,
Component = 1 << 15,
CatProvider = TypeValueProvider | TypeClassProvider | TypeFactoryProvider |
TypeUseExistingProvider | TypeDirective,
OnInit = 1 << 15,
OnDestroy = 1 << 16,
DoCheck = 1 << 17,
OnChanges = 1 << 18,
AfterContentInit = 1 << 19,
AfterContentChecked = 1 << 20,
AfterViewInit = 1 << 21,
AfterViewChecked = 1 << 22,
EmbeddedViews = 1 << 23,
ComponentView = 1 << 24,
TypeContentQuery = 1 << 25,
TypeViewQuery = 1 << 26,
StaticQuery = 1 << 27,
DynamicQuery = 1 << 28,
OnInit = 1 << 16,
OnDestroy = 1 << 17,
DoCheck = 1 << 18,
OnChanges = 1 << 19,
AfterContentInit = 1 << 20,
AfterContentChecked = 1 << 21,
AfterViewInit = 1 << 22,
AfterViewChecked = 1 << 23,
EmbeddedViews = 1 << 24,
ComponentView = 1 << 25,
TypeContentQuery = 1 << 26,
TypeViewQuery = 1 << 27,
StaticQuery = 1 << 28,
DynamicQuery = 1 << 29,
CatQuery = TypeContentQuery | TypeViewQuery,
// mutually exclusive values...
@ -328,7 +329,10 @@ export const enum ViewState {
FirstCheck = 1 << 1,
Attached = 1 << 2,
ChecksEnabled = 1 << 3,
Destroyed = 1 << 4,
IsProjectedView = 1 << 4,
CheckProjectedView = 1 << 5,
CheckProjectedViews = 1 << 6,
Destroyed = 1 << 7,
CatDetectChanges = Attached | ChecksEnabled,
CatInit = BeforeFirstCheck | CatDetectChanges

View File

@ -117,6 +117,14 @@ export function markParentViewsForCheck(view: ViewData) {
}
}
export function markParentViewsForCheckProjectedViews(view: ViewData, endView: ViewData) {
let currView: ViewData|null = view;
while (currView && currView !== endView) {
currView.state |= ViewState.CheckProjectedViews;
currView = currView.viewContainerParent || currView.parent;
}
}
export function dispatchEvent(
view: ViewData, nodeIndex: number, eventName: string, event: any): boolean {
const nodeDef = view.def.nodes[nodeIndex];

View File

@ -17,7 +17,8 @@ import {checkAndUpdateQuery, createQuery} from './query';
import {createTemplateData, createViewContainerData} from './refs';
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
import {NOOP, checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util';
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveViewDefinition} from './util';
import {detachProjectedView} from './view_attach';
export function viewDef(
flags: ViewFlags, nodes: NodeDef[], updateDirectives?: ViewUpdateFn,
@ -314,12 +315,14 @@ function createViewNodes(view: ViewData) {
}
export function checkNoChangesView(view: ViewData) {
markProjectedViewsForCheck(view);
Services.updateDirectives(view, CheckType.CheckNoChanges);
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
Services.updateRenderer(view, CheckType.CheckNoChanges);
execComponentViewsAction(view, ViewAction.CheckNoChanges);
// Note: We don't check queries for changes as we didn't do this in v2.x.
// TODO(tbosch): investigate if we can enable the check again in v5.x with a nicer error message.
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
}
export function checkAndUpdateView(view: ViewData) {
@ -329,6 +332,7 @@ export function checkAndUpdateView(view: ViewData) {
} else {
view.state &= ~ViewState.FirstCheck;
}
markProjectedViewsForCheck(view);
Services.updateDirectives(view, CheckType.CheckAndUpdate);
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction(
@ -343,7 +347,6 @@ export function checkAndUpdateView(view: ViewData) {
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction(
view, NodeFlags.TypeViewQuery, NodeFlags.DynamicQuery, CheckType.CheckAndUpdate);
callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterViewChecked |
(view.state & ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
@ -351,6 +354,7 @@ export function checkAndUpdateView(view: ViewData) {
if (view.def.flags & ViewFlags.OnPush) {
view.state &= ~ViewState.ChecksEnabled;
}
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
}
export function checkAndUpdateNode(
@ -363,6 +367,31 @@ export function checkAndUpdateNode(
}
}
function markProjectedViewsForCheck(view: ViewData) {
const def = view.def;
if (!(def.nodeFlags & NodeFlags.ProjectedTemplate)) {
return;
}
for (let i = 0; i < def.nodes.length; i++) {
const nodeDef = def.nodes[i];
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
const projectedViews = asElementData(view, i).template._projectedViews;
if (projectedViews) {
for (let i = 0; i < projectedViews.length; i++) {
const projectedView = projectedViews[i];
projectedView.state |= ViewState.CheckProjectedView;
markParentViewsForCheckProjectedViews(projectedView, view);
}
}
} else if ((nodeDef.childFlags & NodeFlags.ProjectedTemplate) === 0) {
// a parent with leafs
// no child is a component,
// then skip the children
i += nodeDef.childCount;
}
}
}
function checkAndUpdateNodeInline(
view: ViewData, nodeDef: NodeDef, v0?: any, v1?: any, v2?: any, v3?: any, v4?: any, v5?: any,
v6?: any, v7?: any, v8?: any, v9?: any): boolean {
@ -474,6 +503,7 @@ export function destroyView(view: ViewData) {
view.disposables[i]();
}
}
detachProjectedView(view);
if (view.renderer.destroyNode) {
destroyViewNodes(view);
}
@ -498,7 +528,9 @@ function destroyViewNodes(view: ViewData) {
enum ViewAction {
CreateViewNodes,
CheckNoChanges,
CheckNoChangesProjectedViews,
CheckAndUpdate,
CheckAndUpdateProjectedViews,
Destroy
}
@ -547,18 +579,44 @@ function callViewAction(view: ViewData, action: ViewAction) {
const viewState = view.state;
switch (action) {
case ViewAction.CheckNoChanges:
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges &&
(viewState & ViewState.Destroyed) === 0) {
checkNoChangesView(view);
if ((viewState & ViewState.Destroyed) === 0) {
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
checkNoChangesView(view);
} else if (viewState & ViewState.CheckProjectedViews) {
execProjectedViewsAction(view, ViewAction.CheckNoChangesProjectedViews);
}
}
break;
case ViewAction.CheckNoChangesProjectedViews:
if ((viewState & ViewState.Destroyed) === 0) {
if (viewState & ViewState.CheckProjectedView) {
checkNoChangesView(view);
} else if (viewState & ViewState.CheckProjectedViews) {
execProjectedViewsAction(view, action);
}
}
break;
case ViewAction.CheckAndUpdate:
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges &&
(viewState & ViewState.Destroyed) === 0) {
checkAndUpdateView(view);
if ((viewState & ViewState.Destroyed) === 0) {
if ((viewState & ViewState.CatDetectChanges) === ViewState.CatDetectChanges) {
checkAndUpdateView(view);
} else if (viewState & ViewState.CheckProjectedViews) {
execProjectedViewsAction(view, ViewAction.CheckAndUpdateProjectedViews);
}
}
break;
case ViewAction.CheckAndUpdateProjectedViews:
if ((viewState & ViewState.Destroyed) === 0) {
if (viewState & ViewState.CheckProjectedView) {
checkAndUpdateView(view);
} else if (viewState & ViewState.CheckProjectedViews) {
execProjectedViewsAction(view, action);
}
}
break;
case ViewAction.Destroy:
// Note: destroyView recurses over all views,
// so we don't need to special case projected views here.
destroyView(view);
break;
case ViewAction.CreateViewNodes:
@ -567,6 +625,11 @@ function callViewAction(view: ViewData, action: ViewAction) {
}
}
function execProjectedViewsAction(view: ViewData, action: ViewAction) {
execEmbeddedViewsAction(view, action);
execComponentViewsAction(view, action);
}
function execQueriesAction(
view: ViewData, queryFlags: NodeFlags, staticDynamicQueryFlag: NodeFlags,
checkType: CheckType) {

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ElementData, Services, ViewData} from './types';
import {RenderNodeAction, declaredViewContainer, renderNode, visitRootRenderNodes} from './util';
import {ElementData, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewState} from './types';
import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, visitRootRenderNodes} from './util';
export function attachEmbeddedView(
parentView: ViewData, elementData: ElementData, viewIndex: number | undefined | null,
@ -18,14 +18,7 @@ export function attachEmbeddedView(
}
view.viewContainerParent = parentView;
addToArray(embeddedViews, viewIndex !, view);
const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) {
let projectedViews = dvcElementData.template._projectedViews;
if (!projectedViews) {
projectedViews = dvcElementData.template._projectedViews = [];
}
projectedViews.push(view);
}
attachProjectedView(elementData, view);
Services.dirtyParentQueries(view);
@ -33,6 +26,43 @@ export function attachEmbeddedView(
renderAttachEmbeddedView(elementData, prevView, view);
}
function attachProjectedView(vcElementData: ElementData, view: ViewData) {
const dvcElementData = declaredViewContainer(view);
if (!dvcElementData || dvcElementData === vcElementData ||
view.state & ViewState.IsProjectedView) {
return;
}
// Note: For performance reasons, we
// - add a view to template._projectedViews only 1x throughout its lifetime,
// and remove it not until the view is destroyed.
// (hard, as when a parent view is attached/detached we would need to attach/detach all
// nested projected views as well, even accross component boundaries).
// - don't track the insertion order of views in the projected views array
// (hard, as when the views of the same template are inserted different view containers)
view.state |= ViewState.IsProjectedView;
let projectedViews = dvcElementData.template._projectedViews;
if (!projectedViews) {
projectedViews = dvcElementData.template._projectedViews = [];
}
projectedViews.push(view);
// Note: we are changing the NodeDef here as we cannot calculate
// the fact whether a template is used for projection during compilation.
markNodeAsProjectedTemplate(view.parent !.def, view.parentNodeDef !);
}
function markNodeAsProjectedTemplate(viewDef: ViewDefinition, nodeDef: NodeDef) {
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
return;
}
viewDef.nodeFlags |= NodeFlags.ProjectedTemplate;
nodeDef.flags |= NodeFlags.ProjectedTemplate;
let parentNodeDef = nodeDef.parent;
while (parentNodeDef) {
parentNodeDef.childFlags |= NodeFlags.ProjectedTemplate;
parentNodeDef = parentNodeDef.parent;
}
}
export function detachEmbeddedView(elementData: ElementData, viewIndex?: number): ViewData|null {
const embeddedViews = elementData.viewContainer !._embeddedViews;
if (viewIndex == null || viewIndex >= embeddedViews.length) {
@ -45,12 +75,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex?: number)
view.viewContainerParent = null;
removeFromArray(embeddedViews, viewIndex);
const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) {
const projectedViews = dvcElementData.template._projectedViews;
removeFromArray(projectedViews, projectedViews.indexOf(view));
}
// See attachProjectedView for why we don't update projectedViews here.
Services.dirtyParentQueries(view);
renderDetachView(view);
@ -58,6 +83,20 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex?: number)
return view;
}
export function detachProjectedView(view: ViewData) {
if (!(view.state & ViewState.IsProjectedView)) {
return;
}
const dvcElementData = declaredViewContainer(view);
if (dvcElementData) {
const projectedViews = dvcElementData.template._projectedViews;
if (projectedViews) {
removeFromArray(projectedViews, projectedViews.indexOf(view));
Services.dirtyParentQueries(view);
}
}
}
export function moveEmbeddedView(
elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData {
const embeddedViews = elementData.viewContainer !._embeddedViews;

View File

@ -5,6 +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 {Injector} from '@angular/core';
import {APP_INITIALIZER, ApplicationInitStatus} from '../src/application_init';
import {TestBed, async, inject} from '../testing';
@ -14,11 +15,13 @@ export function main() {
it('should return true for `done`',
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
status.runInitializers();
expect(status.done).toBe(true);
})));
it('should return a promise that resolves immediately for `donePromise`',
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
status.runInitializers();
status.donePromise.then(() => { expect(status.done).toBe(true); });
})));
});
@ -26,15 +29,32 @@ export function main() {
describe('with async initializers', () => {
let resolve: (result: any) => void;
let promise: Promise<any>;
let completerResolver = false;
beforeEach(() => {
let initializerFactory = (injector: Injector) => {
return () => {
const initStatus = injector.get(ApplicationInitStatus);
initStatus.donePromise.then(() => { expect(completerResolver).toBe(true); });
}
};
promise = new Promise((res) => { resolve = res; });
TestBed.configureTestingModule(
{providers: [{provide: APP_INITIALIZER, multi: true, useValue: () => promise}]});
TestBed.configureTestingModule({
providers: [
{provide: APP_INITIALIZER, multi: true, useValue: () => promise},
{
provide: APP_INITIALIZER,
multi: true,
useFactory: initializerFactory,
deps: [Injector]
},
]
});
});
it('should update the status once all async initializers are done',
async(inject([ApplicationInitStatus], (status: ApplicationInitStatus) => {
let completerResolver = false;
status.runInitializers();
setTimeout(() => {
completerResolver = true;
resolve(null);

View File

@ -197,11 +197,9 @@ export function main() {
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]))
.then(() => expect(false).toBe(true), (e) => {
expect(e).toBe('Test');
// Note: if the modules throws an error during construction,
// we don't have an injector and therefore no way of
// getting the exception handler. So
// the error is only rethrown but not logged via the exception handler.
expect(mockConsole.res).toEqual([]);
// Error rethrown will be seen by the exception handler since it's after
// construction.
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
});
}));
@ -294,11 +292,9 @@ export function main() {
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]));
expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test');
// Note: if the modules throws an error during construction,
// we don't have an injector and therefore no way of
// getting the exception handler. So
// the error is only rethrown but not logged via the exception handler.
expect(mockConsole.res).toEqual([]);
// Error rethrown will be seen by the exception handler since it's after
// construction.
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
}));
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',

View File

@ -8,11 +8,12 @@
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/src/test_bindings';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactory2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, DebugElement, Directive, DoCheck, EventEmitter, HostBinding, Inject, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RendererFactory2, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync} 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/src/matchers';
import {DomElementSchemaRegistry} from '../../../compiler/index';
import {MockSchemaRegistry} from '../../../compiler/testing/index';
@ -1293,6 +1294,120 @@ export function main() {
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
describe('projected views', () => {
let log: string[];
@Directive({selector: '[i]'})
class DummyDirective {
@Input()
i: any;
}
@Component({
selector: 'main-cmp',
template:
`<span [i]="log('start')"></span><outer-cmp><ng-template><span [i]="log('tpl')"></span></ng-template></outer-cmp>`
})
class MainComp {
constructor(public cdRef: ChangeDetectorRef) {}
log(id: string) { log.push(`main-${id}`); }
}
@Component({
selector: 'outer-cmp',
template:
`<span [i]="log('start')"></span><inner-cmp [outerTpl]="tpl"><ng-template><span [i]="log('tpl')"></span></ng-template></inner-cmp>`
})
class OuterComp {
@ContentChild(TemplateRef)
tpl: TemplateRef<any>;
constructor(public cdRef: ChangeDetectorRef) {}
log(id: string) { log.push(`outer-${id}`); }
}
@Component({
selector: 'inner-cmp',
template:
`<span [i]="log('start')"></span>><ng-container [ngTemplateOutlet]="outerTpl"></ng-container><ng-container [ngTemplateOutlet]="tpl"></ng-container>`
})
class InnerComp {
@ContentChild(TemplateRef)
tpl: TemplateRef<any>;
@Input()
outerTpl: TemplateRef<any>
constructor(public cdRef: ChangeDetectorRef) {}
log(id: string) { log.push(`inner-${id}`); }
}
let ctx: ComponentFixture<MainComp>;
let mainComp: MainComp;
let outerComp: OuterComp;
let innerComp: InnerComp;
beforeEach(() => {
log = [];
ctx = TestBed
.configureTestingModule(
{declarations: [MainComp, OuterComp, InnerComp, DummyDirective]})
.createComponent(MainComp);
mainComp = ctx.componentInstance;
outerComp = ctx.debugElement.query(By.directive(OuterComp)).injector.get(OuterComp);
innerComp = ctx.debugElement.query(By.directive(InnerComp)).injector.get(InnerComp);
});
it('should dirty check projected views in regular order', () => {
ctx.detectChanges(false);
expect(log).toEqual(
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
log = [];
ctx.detectChanges(false);
expect(log).toEqual(
['main-start', 'outer-start', 'inner-start', 'main-tpl', 'outer-tpl']);
});
it('should not dirty check projected views if neither the declaration nor the insertion place is dirty checked',
() => {
ctx.detectChanges(false);
log = [];
mainComp.cdRef.detach();
ctx.detectChanges(false);
expect(log).toEqual([]);
});
it('should dirty check projected views if the insertion place is dirty checked', () => {
ctx.detectChanges(false);
log = [];
innerComp.cdRef.detectChanges();
expect(log).toEqual(['inner-start', 'main-tpl', 'outer-tpl']);
});
it('should dirty check projected views if the declaration place is dirty checked', () => {
ctx.detectChanges(false);
log = [];
innerComp.cdRef.detach();
mainComp.cdRef.detectChanges();
expect(log).toEqual(['main-start', 'outer-start', 'main-tpl', 'outer-tpl']);
log = [];
outerComp.cdRef.detectChanges();
expect(log).toEqual(['outer-start', 'outer-tpl']);
log = [];
outerComp.cdRef.detach();
mainComp.cdRef.detectChanges();
expect(log).toEqual(['main-start', 'main-tpl']);
});
});
});
describe('class binding', () => {

View File

@ -536,6 +536,52 @@ export function main() {
expect(q.query.length).toBe(0);
});
// Note: This tests is just document our current behavior, which we do
// for performance reasons.
it('should not affected queries for projected templates if views are detached or moved', () => {
const template =
'<manual-projecting #q><ng-template let-x="x"><div [text]="x"></div></ng-template></manual-projecting>';
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references !['q'] as ManualProjecting;
expect(q.query.length).toBe(0);
const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'});
const view2 = q.vc.createEmbeddedView(q.template, {'x': '2'});
view.detectChanges();
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
q.vc.detach(1);
q.vc.detach(0);
view.detectChanges();
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
q.vc.insert(view2);
q.vc.insert(view1);
view.detectChanges();
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2']);
});
it('should remove manually projected templates if their parent view is destroyed', () => {
const template = `
<manual-projecting #q><ng-template #tpl><div text="1"></div></ng-template></manual-projecting>
<div *ngIf="shouldShow">
<ng-container [ngTemplateOutlet]="tpl"></ng-container>
</div>
`;
const view = createTestCmp(MyComp0, template);
const q = view.debugElement.children[0].references !['q'];
view.componentInstance.shouldShow = true;
view.detectChanges();
expect(q.query.length).toBe(1);
view.componentInstance.shouldShow = false;
view.detectChanges();
expect(q.query.length).toBe(0);
});
it('should not throw if a content template is queried and created in the view during change detection',
() => {
@Component(

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleRef, NgZone, Pipe, PlatformRef, Provider, ReflectiveInjector, SchemaMetadata, Type, ɵERROR_COMPONENT_TYPE, ɵstringify as stringify} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';
@ -283,6 +283,9 @@ export class TestBed implements Injector {
const ngZoneInjector = ReflectiveInjector.resolveAndCreate(
[{provide: NgZone, useValue: ngZone}], this.platform.injector);
this._moduleRef = this._moduleWithComponentFactories.ngModuleFactory.create(ngZoneInjector);
// ApplicationInitStatus.runInitializers() is marked @internal to core. So casting to any
// before accessing it.
(this._moduleRef.injector.get(ApplicationInitStatus) as any).runInitializers();
this._instantiated = true;
}

View File

@ -128,7 +128,7 @@ export const formArrayNameProvider: any = {
* status is re-calculated.
*
* **Add new controls**: You can add new controls to the {@link FormArray} dynamically by
* calling its {@link FormArray.push} method.
* calling its {@link FormArray#push} method.
* Ex: `this.form.get('cities').push(new FormControl());`
*
* ### Example

View File

@ -676,8 +676,8 @@ export class FormControl extends AbstractControl {
/**
* Patches the value of a control.
*
* This function is functionally the same as {@link FormControl.setValue} at this level.
* It exists for symmetry with {@link FormGroup.patchValue} on `FormGroups` and `FormArrays`,
* This function is functionally the same as {@link FormControl#setValue} at this level.
* It exists for symmetry with {@link FormGroup#patchValue} on `FormGroups` and `FormArrays`,
* where it does behave differently.
*/
patchValue(value: any, options: {
@ -842,7 +842,7 @@ export class FormGroup extends AbstractControl {
* Registers a control with the group's list of controls.
*
* This method does not update value or validity of the control, so for
* most cases you'll want to use {@link FormGroup.addControl} instead.
* most cases you'll want to use {@link FormGroup#addControl} instead.
*/
registerControl(name: string, control: AbstractControl): AbstractControl {
if (this.controls[name]) return this.controls[name];

View File

@ -37,14 +37,32 @@ export abstract class Body {
/**
* Returns the body as a string, presuming `toString()` can be called on the response body.
*
* When decoding an `ArrayBuffer`, the optional `encodingHint` parameter determines how the
* bytes in the buffer will be interpreted. Valid values are:
*
* - `legacy` - incorrectly interpret the bytes as UTF-16 (technically, UCS-2). Only characters
* in the Basic Multilingual Plane are supported, surrogate pairs are not handled correctly.
* In addition, the endianness of the 16-bit octet pairs in the `ArrayBuffer` is not taken
* into consideration. This is the default behavior to avoid breaking apps, but should be
* considered deprecated.
*
* - `iso-8859` - interpret the bytes as ISO-8859 (which can be used for ASCII encoded text).
*/
text(): string {
text(encodingHint: 'legacy'|'iso-8859' = 'legacy'): string {
if (this._body instanceof URLSearchParams) {
return this._body.toString();
}
if (this._body instanceof ArrayBuffer) {
return String.fromCharCode.apply(null, new Uint16Array(<ArrayBuffer>this._body));
switch (encodingHint) {
case 'legacy':
return String.fromCharCode.apply(null, new Uint16Array(this._body as ArrayBuffer));
case 'iso-8859':
return String.fromCharCode.apply(null, new Uint8Array(this._body as ArrayBuffer));
default:
throw new Error(`Invalid value for encodingHint: ${encodingHint}`);
}
}
if (this._body == null) {

View File

@ -42,6 +42,15 @@ export function getResponseURL(xhr: any): string|null {
return null;
}
export function stringToArrayBuffer8(input: String): ArrayBuffer {
const view = new Uint8Array(input.length);
for (let i = 0, strLen = input.length; i < strLen; i++) {
view[i] = input.charCodeAt(i);
}
return view.buffer;
}
export function stringToArrayBuffer(input: String): ArrayBuffer {
const view = new Uint16Array(input.length);
for (let i = 0, strLen = input.length; i < strLen; i++) {

View File

@ -76,8 +76,14 @@ export class Request extends Body {
// TODO: assert that url is present
const url = requestOptions.url;
this.url = requestOptions.url !;
if (requestOptions.params) {
const params = requestOptions.params.toString();
const paramsArg = requestOptions.params || requestOptions.search;
if (paramsArg) {
let params: string;
if (typeof paramsArg === 'object' && !(paramsArg instanceof URLSearchParams)) {
params = urlEncodeParams(paramsArg).toString();
} else {
params = paramsArg.toString();
}
if (params.length > 0) {
let prefix = '?';
if (this.url.indexOf('?') != -1) {
@ -163,8 +169,22 @@ export class Request extends Body {
}
}
function urlEncodeParams(params: {[key: string]: any}): URLSearchParams {
const searchParams = new URLSearchParams();
Object.keys(params).forEach(key => {
const value = params[key];
if (value && Array.isArray(value)) {
value.forEach(element => searchParams.append(key, element.toString()));
} else {
searchParams.append(key, value.toString());
}
});
return searchParams;
}
const noop = function() {};
const w = typeof window == 'object' ? window : noop;
const FormData = (w as any /** TODO #9100 */)['FormData'] || noop;
const Blob = (w as any /** TODO #9100 */)['Blob'] || noop;
export const ArrayBuffer = (w as any /** TODO #9100 */)['ArrayBuffer'] || noop;
export const ArrayBuffer: ArrayBufferConstructor =
(w as any /** TODO #9100 */)['ArrayBuffer'] || noop;

View File

@ -7,10 +7,12 @@
*/
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {ɵgetDOM as getDOM} from '@angular/platform-browser';
import {RequestOptions} from '../src/base_request_options';
import {ContentType} from '../src/enums';
import {Headers} from '../src/headers';
import {stringToArrayBuffer, stringToArrayBuffer8} from '../src/http_utils';
import {ArrayBuffer, Request} from '../src/static_request';
export function main() {
@ -109,5 +111,28 @@ export function main() {
expect(req.text()).toEqual('');
});
it('should use object params', () => {
const req = new Request({url: 'http://test.com', params: {'a': 3, 'b': ['x', 'y']}});
expect(req.url).toBe('http://test.com?a=3&b=x&b=y');
});
it('should use search if present', () => {
const req = new Request({url: 'http://test.com', search: 'a=1&b=2'});
expect(req.url).toBe('http://test.com?a=1&b=2');
});
if (getDOM().supportsWebAnimation()) {
it('should serialize an ArrayBuffer to string via legacy encoding', () => {
const str = '\u89d2\u5ea6';
expect(new Request({body: stringToArrayBuffer(str), url: '/'}).text()).toEqual(str);
});
it('should serialize an ArrayBuffer to string via iso-8859 encoding', () => {
const str = 'abcd';
expect(new Request({body: stringToArrayBuffer8(str), url: '/'}).text('iso-8859'))
.toEqual(str);
});
}
});
}

View File

@ -0,0 +1,14 @@
/**
* @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
*/
/**
* @module
* @description
* Entry point for all public APIs of the http testing package.
*/
export * from './src/testing';

View File

@ -1,6 +1,7 @@
{
"extends": "../tsconfig-build",
"compilerOptions": {
"strictNullChecks": true,
"paths": {
"@angular/core": ["../../dist/packages/core"],
"@angular/http": ["../../dist/packages/http"],
@ -8,9 +9,12 @@
}
},
"files": [
"index.ts"
"public_api.ts"
],
"angularCompilerOptions": {
"strictMetadataEmit": true
"annotateForClosureCompiler": true,
"strictMetadataEmit": true,
"flatModuleOutFile": "index.js",
"flatModuleId": "@angular/http/testing"
}
}

View File

@ -3,6 +3,7 @@
"baseUrl": ".",
"declaration": true,
"stripInternal": true,
"strictNullChecks": true,
"experimentalDecorators": true,
"module": "es2015",
"moduleResolution": "node",

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_INITIALIZER, Inject, InjectionToken, Provider} from '@angular/core';
import {APP_INITIALIZER, ApplicationInitStatus, Inject, InjectionToken, Injector, Provider} from '@angular/core';
import {getDOM} from '../dom/dom_adapter';
import {DOCUMENT} from '../dom/dom_tokens';
@ -17,22 +17,25 @@ import {DOCUMENT} from '../dom/dom_tokens';
*/
export const TRANSITION_ID = new InjectionToken('TRANSITION_ID');
export function bootstrapListenerFactory(transitionId: string, document: any) {
const factory = () => {
const dom = getDOM();
const styles: any[] =
Array.prototype.slice.apply(dom.querySelectorAll(document, `style[ng-transition]`));
styles.filter(el => dom.getAttribute(el, 'ng-transition') === transitionId)
.forEach(el => dom.remove(el));
export function appInitializerFactory(transitionId: string, document: any, injector: Injector) {
return () => {
// Wait for all application initializers to be completed before removing the styles set by
// the server.
injector.get(ApplicationInitStatus).donePromise.then(() => {
const dom = getDOM();
const styles: any[] =
Array.prototype.slice.apply(dom.querySelectorAll(document, `style[ng-transition]`));
styles.filter(el => dom.getAttribute(el, 'ng-transition') === transitionId)
.forEach(el => dom.remove(el));
});
};
return factory;
}
export const SERVER_TRANSITION_PROVIDERS: Provider[] = [
{
provide: APP_INITIALIZER,
useFactory: bootstrapListenerFactory,
deps: [TRANSITION_ID, DOCUMENT],
useFactory: appInitializerFactory,
deps: [TRANSITION_ID, DOCUMENT, Injector],
multi: true
},
];

View File

@ -358,7 +358,13 @@ class ApplyRedirects {
private createQueryParams(redirectToParams: Params, actualParams: Params): Params {
const res: Params = {};
forEach(redirectToParams, (v: any, k: string) => {
res[k] = v.startsWith(':') ? actualParams[v.substring(1)] : v;
const copySourceValue = typeof v === 'string' && v.startsWith(':');
if (copySourceValue) {
const sourceName = v.substring(1);
res[k] = actualParams[sourceName];
} else {
res[k] = v;
}
});
return res;
}

View File

@ -29,7 +29,7 @@ import {UrlSegment, UrlSegmentGroup} from './url_tree';
* {@link CanActivateChild} for more info.
* - `canDeactivate` is an array of DI tokens used to look up CanDeactivate handlers. See
* {@link CanDeactivate} for more info.
* - `canLoad` is an array of DI tokens used to look up CanDeactivate handlers. See
* - `canLoad` is an array of DI tokens used to look up CanLoad handlers. See
* {@link CanLoad} for more info.
* - `data` is additional data provided to the component via `ActivatedRoute`.
* - `resolve` is a map of DI tokens used to look up data resolvers. See {@link Resolve} for more

View File

@ -72,7 +72,7 @@ import {UrlTree} from '../url_tree';
* - 'merge' merge the queryParams into the current queryParams
* - 'preserve' prserve the current queryParams
* - default / '' use the queryParams only
* same options for {@link NavigationExtras.queryParamsHandling}
* same options for {@link NavigationExtras#queryParamsHandling}
*
* ```
* <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge">
@ -89,7 +89,7 @@ import {UrlTree} from '../url_tree';
*
* @ngModule RouterModule
*
* See {@link Router.createUrlTree} for more information.
* See {@link Router#createUrlTree} for more information.
*
* @stable
*/

View File

@ -97,7 +97,10 @@ export function wrapIntoObservable<T>(value: T | NgModuleFactory<T>| Promise<T>|
}
if (isPromise(value)) {
return fromPromise(value);
// Use `Promise.resolve()` to wrap promise-like instances.
// Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
// change detection.
return fromPromise(Promise.resolve(value));
}
return of (value);

View File

@ -32,13 +32,24 @@ describe('applyRedirects', () => {
],
},
],
'/a/b', (t: UrlTree) => { compareTrees(t, tree('/a/b')); });
'/a/b', (t: UrlTree) => { expectTreeToBe(t, '/a/b'); });
});
it('should add new segments when needed', () => {
checkRedirect(
[{path: 'a/b', redirectTo: 'a/b/c'}, {path: '**', component: ComponentC}], '/a/b',
(t: UrlTree) => { compareTrees(t, tree('/a/b/c')); });
(t: UrlTree) => { expectTreeToBe(t, '/a/b/c'); });
});
it('should support redirecting with to an URL with query parameters', () => {
const config: Routes = [
{path: 'single_value', redirectTo: '/dst?k=v1'},
{path: 'multiple_values', redirectTo: '/dst?k=v1&k=v2'},
{path: '**', component: ComponentA},
];
checkRedirect(config, 'single_value', (t: UrlTree) => expectTreeToBe(t, '/dst?k=v1'));
checkRedirect(config, 'multiple_values', (t: UrlTree) => expectTreeToBe(t, '/dst?k=v1&k=v2'));
});
it('should handle positional parameters', () => {
@ -47,7 +58,7 @@ describe('applyRedirects', () => {
{path: 'a/:aid/b/:bid', redirectTo: 'newa/:aid/newb/:bid'},
{path: '**', component: ComponentC}
],
'/a/1/b/2', (t: UrlTree) => { compareTrees(t, tree('/newa/1/newb/2')); });
'/a/1/b/2', (t: UrlTree) => { expectTreeToBe(t, '/newa/1/newb/2'); });
});
it('should throw when cannot handle a positional parameter', () => {
@ -61,7 +72,7 @@ describe('applyRedirects', () => {
it('should pass matrix parameters', () => {
checkRedirect(
[{path: 'a/:id', redirectTo: 'd/a/:id/e'}, {path: '**', component: ComponentC}],
'/a;p1=1/1;p2=2', (t: UrlTree) => { compareTrees(t, tree('/d/a;p1=1/1;p2=2/e')); });
'/a;p1=1/1;p2=2', (t: UrlTree) => { expectTreeToBe(t, '/d/a;p1=1/1;p2=2/e'); });
});
it('should handle preserve secondary routes', () => {
@ -70,7 +81,7 @@ describe('applyRedirects', () => {
{path: 'a/:id', redirectTo: 'd/a/:id/e'},
{path: 'c/d', component: ComponentA, outlet: 'aux'}, {path: '**', component: ComponentC}
],
'/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/d/a/1/e(aux:c/d)')); });
'/a/1(aux:c/d)', (t: UrlTree) => { expectTreeToBe(t, '/d/a/1/e(aux:c/d)'); });
});
it('should redirect secondary routes', () => {
@ -80,7 +91,7 @@ describe('applyRedirects', () => {
{path: 'c/d', redirectTo: 'f/c/d/e', outlet: 'aux'},
{path: '**', component: ComponentC, outlet: 'aux'}
],
'/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/a/1(aux:f/c/d/e)')); });
'/a/1(aux:c/d)', (t: UrlTree) => { expectTreeToBe(t, '/a/1(aux:f/c/d/e)'); });
});
it('should use the configuration of the route redirected to', () => {
@ -95,7 +106,7 @@ describe('applyRedirects', () => {
},
{path: 'c', redirectTo: 'a'}
],
'c/b', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
'c/b', (t: UrlTree) => { expectTreeToBe(t, 'a/b'); });
});
it('should support redirects with both main and aux', () => {
@ -109,7 +120,7 @@ describe('applyRedirects', () => {
{path: 'b', redirectTo: 'cc', outlet: 'aux'}
]
}],
'a/(b//aux:b)', (t: UrlTree) => { compareTrees(t, tree('a/(bb//aux:cc)')); });
'a/(b//aux:b)', (t: UrlTree) => { expectTreeToBe(t, 'a/(bb//aux:cc)'); });
});
it('should support redirects with both main and aux (with a nested redirect)', () => {
@ -128,7 +139,7 @@ describe('applyRedirects', () => {
{path: 'b', redirectTo: 'cc/d', outlet: 'aux'}
]
}],
'a/(b//aux:b)', (t: UrlTree) => { compareTrees(t, tree('a/(bb//aux:cc/dd)')); });
'a/(b//aux:b)', (t: UrlTree) => { expectTreeToBe(t, 'a/(bb//aux:cc/dd)'); });
});
it('should redirect wild cards', () => {
@ -137,7 +148,7 @@ describe('applyRedirects', () => {
{path: '404', component: ComponentA},
{path: '**', redirectTo: '/404'},
],
'/a/1(aux:c/d)', (t: UrlTree) => { compareTrees(t, tree('/404')); });
'/a/1(aux:c/d)', (t: UrlTree) => { expectTreeToBe(t, '/404'); });
});
it('should support absolute redirects', () => {
@ -150,7 +161,7 @@ describe('applyRedirects', () => {
},
{path: '**', component: ComponentC}
],
'/a/b/1?b=2', (t: UrlTree) => { compareTrees(t, tree('/absolute/1?a=1&b=2#f1')); });
'/a/b/1?b=2', (t: UrlTree) => { expectTreeToBe(t, '/absolute/1?a=1&b=2#f1'); });
});
describe('lazy loading', () => {
@ -166,7 +177,7 @@ describe('applyRedirects', () => {
applyRedirects(testModule.injector, <any>loader, serializer, tree('a/b'), config)
.forEach(r => {
compareTrees(r, tree('/a/b'));
expectTreeToBe(r, '/a/b');
expect(config[0]._loadedConfig).toBe(loadedConfig);
});
});
@ -198,7 +209,7 @@ describe('applyRedirects', () => {
}];
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config).forEach(r => {
compareTrees(r, tree('/a/b'));
expectTreeToBe(r, '/a/b');
});
});
@ -279,8 +290,7 @@ describe('applyRedirects', () => {
[{path: 'a', component: ComponentA, canLoad: ['guard'], loadChildren: 'children'}];
applyRedirects(<any>injector, <any>loader, serializer, tree('a/b'), config)
.subscribe(
(r) => { compareTrees(r, tree('/a/b')); }, (e) => { throw 'Should not reach'; });
.subscribe((r) => { expectTreeToBe(r, '/a/b'); }, (e) => { throw 'Should not reach'; });
});
@ -293,7 +303,7 @@ describe('applyRedirects', () => {
[{path: '', pathMatch: 'full', redirectTo: '/a'}, {path: 'a', loadChildren: 'children'}];
applyRedirects(testModule.injector, <any>loader, serializer, tree(''), config).forEach(r => {
compareTrees(r, tree('a'));
expectTreeToBe(r, 'a');
expect(config[1]._loadedConfig).toBe(loadedConfig);
});
});
@ -318,7 +328,7 @@ describe('applyRedirects', () => {
applyRedirects(testModule.injector, <any>loader, serializer, tree('a?k2'), config)
.subscribe(
r => {
compareTrees(r, tree('a?k2'));
expectTreeToBe(r, 'a?k2');
expect(config[0]._loadedConfig).toBe(loadedConfig);
},
(e) => { throw 'Should not reach'; });
@ -373,7 +383,7 @@ describe('applyRedirects', () => {
},
{path: '', redirectTo: 'a'}
],
'b', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
'b', (t: UrlTree) => { expectTreeToBe(t, 'a/b'); });
});
it('redirect from an empty path should work (absolute redirect)', () => {
@ -388,7 +398,7 @@ describe('applyRedirects', () => {
},
{path: '', redirectTo: '/a/b'}
],
'', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
'', (t: UrlTree) => { expectTreeToBe(t, 'a/b'); });
});
it('should redirect empty path route only when terminal', () => {
@ -419,7 +429,7 @@ describe('applyRedirects', () => {
},
{path: '', redirectTo: 'a'}
],
'', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
'', (t: UrlTree) => { expectTreeToBe(t, 'a/b'); });
});
it('redirect to an empty path should work', () => {
@ -428,7 +438,7 @@ describe('applyRedirects', () => {
{path: '', component: ComponentA, children: [{path: 'b', component: ComponentB}]},
{path: 'a', redirectTo: ''}
],
'a/b', (t: UrlTree) => { compareTrees(t, tree('b')); });
'a/b', (t: UrlTree) => { expectTreeToBe(t, 'b'); });
});
describe('aux split is in the middle', () => {
@ -442,7 +452,7 @@ describe('applyRedirects', () => {
{path: '', redirectTo: 'c', outlet: 'aux'}
]
}],
'a/b', (t: UrlTree) => { compareTrees(t, tree('a/(b//aux:c)')); });
'a/b', (t: UrlTree) => { expectTreeToBe(t, 'a/(b//aux:c)'); });
});
it('should create a new url segment (terminal)', () => {
@ -455,7 +465,7 @@ describe('applyRedirects', () => {
{path: '', pathMatch: 'full', redirectTo: 'c', outlet: 'aux'}
]
}],
'a/b', (t: UrlTree) => { compareTrees(t, tree('a/b')); });
'a/b', (t: UrlTree) => { expectTreeToBe(t, 'a/b'); });
});
});
@ -470,7 +480,7 @@ describe('applyRedirects', () => {
{path: '', redirectTo: 'c', outlet: 'aux'}
]
}],
'a', (t: UrlTree) => { compareTrees(t, tree('a/(b//aux:c)')); });
'a', (t: UrlTree) => { expectTreeToBe(t, 'a/(b//aux:c)'); });
});
it('should create a new child (terminal)', () => {
@ -483,7 +493,7 @@ describe('applyRedirects', () => {
{path: '', pathMatch: 'full', redirectTo: 'c', outlet: 'aux'}
]
}],
'a', (t: UrlTree) => { compareTrees(t, tree('a/(b//aux:c)')); });
'a', (t: UrlTree) => { expectTreeToBe(t, 'a/(b//aux:c)'); });
});
it('should work only only primary outlet', () => {
@ -495,7 +505,7 @@ describe('applyRedirects', () => {
{path: 'c', component: ComponentC, outlet: 'aux'}
]
}],
'a/(aux:c)', (t: UrlTree) => { compareTrees(t, tree('a/(b//aux:c)')); });
'a/(aux:c)', (t: UrlTree) => { expectTreeToBe(t, 'a/(b//aux:c)'); });
});
});
@ -515,7 +525,7 @@ describe('applyRedirects', () => {
{path: '', redirectTo: 'c', outlet: 'aux'}
]
}],
'a/(d//aux:e)', (t: UrlTree) => { compareTrees(t, tree('a/(b/d//aux:c/e)')); });
'a/(d//aux:e)', (t: UrlTree) => { expectTreeToBe(t, 'a/(b/d//aux:c/e)'); });
});
it('should not create a new child (terminal)', () => {
@ -545,7 +555,7 @@ describe('applyRedirects', () => {
it('should not error when no children matching and no url is left', () => {
checkRedirect(
[{path: 'a', component: ComponentA, children: [{path: 'b', component: ComponentB}]}],
'/a', (t: UrlTree) => { compareTrees(t, tree('a')); });
'/a', (t: UrlTree) => { expectTreeToBe(t, 'a'); });
});
it('should not error when no children matching and no url is left (aux routes)', () => {
@ -559,7 +569,7 @@ describe('applyRedirects', () => {
{path: 'c', component: ComponentC, outlet: 'aux'},
]
}],
'/a', (t: UrlTree) => { compareTrees(t, tree('a/(aux:c)')); });
'/a', (t: UrlTree) => { expectTreeToBe(t, 'a/(aux:c)'); });
});
it('should error when no children matching and some url is left', () => {
@ -588,7 +598,7 @@ describe('applyRedirects', () => {
component: ComponentA,
children: [{path: 'b', component: ComponentB}]
}] as any,
'/a/1/b', (t: UrlTree) => { compareTrees(t, tree('a/1/b')); });
'/a/1/b', (t: UrlTree) => { expectTreeToBe(t, 'a/1/b'); });
});
});
@ -600,7 +610,7 @@ describe('applyRedirects', () => {
{path: 'b/:id', component: ComponentB},
{path: 'c/:id', component: ComponentC, outlet: 'aux'}
],
'a/1;p=99', (t: UrlTree) => { compareTrees(t, tree('/b/1;p=99(aux:c/1;p=99)')); });
'a/1;p=99', (t: UrlTree) => { expectTreeToBe(t, '/b/1;p=99(aux:c/1;p=99)'); });
});
it('should work when using absolute redirects (wildcard)', () => {
@ -609,7 +619,7 @@ describe('applyRedirects', () => {
{path: '**', redirectTo: '/b(aux:c)'}, {path: 'b', component: ComponentB},
{path: 'c', component: ComponentC, outlet: 'aux'}
],
'a/1', (t: UrlTree) => { compareTrees(t, tree('/b(aux:c)')); });
'a/1', (t: UrlTree) => { expectTreeToBe(t, '/b(aux:c)'); });
});
it('should throw when using non-absolute redirects', () => {
@ -637,7 +647,8 @@ function tree(url: string): UrlTree {
return new DefaultUrlSerializer().parse(url);
}
function compareTrees(actual: UrlTree, expected: UrlTree): void {
function expectTreeToBe(actual: UrlTree, expectedUrl: string): void {
const expected = tree(expectedUrl);
const serializer = new DefaultUrlSerializer();
const error =
`"${serializer.serialize(actual)}" is not equal to "${serializer.serialize(expected)}"`;

View File

@ -18,7 +18,7 @@ let downgradeCount = 0;
/**
* @whatItDoes
*
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
* *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
* library for hybrid upgrade apps that support AoT compilation*
*
* Allows an Angular component to be used from AngularJS.

View File

@ -165,7 +165,7 @@ export class DowngradeComponentAdapter {
next: assignExpr ?
((setter: any) => (v: any /** TODO #9100 */) => setter(this.scope, v))(setter) :
((getter: any) => (v: any /** TODO #9100 */) =>
getter(this.scope, {$event: v}))(getter)
getter(this.scope, {'$event': v}))(getter)
});
} else {
throw new Error(

View File

@ -12,7 +12,7 @@ import {INJECTOR_KEY} from './constants';
/**
* @whatItDoes
*
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
* *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
* library for hybrid upgrade apps that support AoT compilation*
*
* Allow an Angular service to be accessible from AngularJS.
@ -53,7 +53,7 @@ import {INJECTOR_KEY} from './constants';
*/
export function downgradeInjectable(token: any): Function {
const factory = function(i: Injector) { return i.get(token); };
(factory as any).$inject = [INJECTOR_KEY];
(factory as any)['$inject'] = [INJECTOR_KEY];
return factory;
}

View File

@ -43,7 +43,7 @@ type LifecycleHook = '$doCheck' | '$onChanges' | '$onDestroy' | '$onInit' | '$po
/**
* @whatItDoes
*
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
* *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
* library for hybrid upgrade apps that support AoT compilation*
*
* Allows an AngularJS component to be used from Angular.

View File

@ -18,7 +18,7 @@ import {angular1Providers, setTempInjectorRef} from './angular1_providers';
/**
* @whatItDoes
*
* *Part of the [upgrade/static](/docs/ts/latest/api/#!?query=upgrade%2Fstatic)
* *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
* library for hybrid upgrade apps that support AoT compilation*
*
* Allows AngularJS and Angular components to be used together inside a hybrid upgrade
@ -88,6 +88,7 @@ import {angular1Providers, setTempInjectorRef} from './angular1_providers';
*
* {@example upgrade/static/ts/module.ts region='bootstrap'}
*
* {@a upgrading-an-angular-1-service}
*
* ## Upgrading an AngularJS service
*

View File

@ -1,6 +1,6 @@
{
"name": "@angular/tsc-wrapped",
"version": "4.1.1",
"version": "4.1.3",
"description": "Wraps the tsc CLI, allowing extensions.",
"homepage": "https://github.com/angular/angular/tree/master/tools/tsc-wrapped",
"bugs": "https://github.com/angular/angular/issues",

View File

@ -1,7 +1,14 @@
module.exports = (gulp) => (done) => {
module.exports = {
// build everything and generate bundles
'all': (gulp) => (done) => execBuild(done),
// same as all without the bundling part - faster / lower memory usage
'no-bundle': (gulp) => (done) => execBuild(done, '--bundle=false'),
};
function execBuild(done, args = '') {
const path = require('path');
const childProcess = require('child_process');
// increase maxbuffer to address out of memory exception when running certain tasks
childProcess.exec(path.join(__dirname, '../../build.sh'), {maxBuffer: 300 * 1024}, done);
};
childProcess.exec(path.join(__dirname, `../../build.sh ${args}`), {maxBuffer: 300 * 1024}, done);
}

View File

@ -532,8 +532,8 @@ export interface ValidatorFn {
/** @stable */
export declare class Validators {
static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null;
static compose(validators: null): null;
static compose(validators: (ValidatorFn | null | undefined)[]): ValidatorFn | null;
static composeAsync(validators: (AsyncValidatorFn | null)[]): AsyncValidatorFn | null;
static email(control: AbstractControl): ValidationErrors | null;
static maxLength(maxLength: number): ValidatorFn;