Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
7edc5e96f3 | |||
7b802faa96 | |||
6f039d7a3c | |||
0a82f7d69f | |||
b7f85813d5 | |||
a5bdbed306 | |||
883ca285de | |||
afb7540067 | |||
47df3d681f | |||
cd7fe31fbc | |||
4203b1bb1f | |||
055db5ad6a | |||
62a8618536 | |||
419fe0ca0d | |||
e46a65f8fd | |||
f0e1043a1d | |||
c1938f5af1 | |||
915eae50b4 | |||
68675e625a | |||
d0e1688514 | |||
ee6705af49 | |||
9218812011 | |||
ec77bf9fb0 | |||
fee5b2ad56 | |||
9c70a3cfb1 | |||
ec3b6e9603 | |||
63066f7697 | |||
5f6d0f2340 | |||
4a0e93b03b |
@ -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.
|
||||
|
31
CHANGELOG.md
31
CHANGELOG.md
@ -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)
|
||||
|
||||
|
@ -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.
|
@ -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)
|
||||
```
|
||||
|
@ -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'));
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
41
integration/typings_test_ts23/include-all.ts
Normal file
41
integration/typings_test_ts23/include-all.ts
Normal 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
|
||||
};
|
28
integration/typings_test_ts23/package.json
Normal file
28
integration/typings_test_ts23/package.json
Normal 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"
|
||||
}
|
||||
}
|
24
integration/typings_test_ts23/tsconfig.json
Normal file
24
integration/typings_test_ts23/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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'}
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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'}
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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'}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -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', () => {
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
14
packages/http/testing/public_api.ts
Normal file
14
packages/http/testing/public_api.ts
Normal 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';
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
"baseUrl": ".",
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
|
@ -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
|
||||
},
|
||||
];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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)}"`;
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
}
|
2
tools/public_api_guard/forms/forms.d.ts
vendored
2
tools/public_api_guard/forms/forms.d.ts
vendored
@ -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;
|
||||
|
Reference in New Issue
Block a user