Compare commits

..

38 Commits
4.0.0 ... 4.0.1

Author SHA1 Message Date
6b79ab5abe docs: add changelog for 4.0.1 2017-03-29 16:27:40 -07:00
53c12a84dc release: cut the 4.0.1 release 2017-03-29 16:26:06 -07:00
ca665303f4 fix(core): fix inheritance in JIT mode for TS 2.1 (#15599)
Fixes #15502
2017-03-29 16:19:15 -07:00
0fe4985756 fix(language-service): correctly determine base members of types (#15600)
Fixes #15460
2017-03-29 16:19:09 -07:00
74087cb39d docs(http): remove deprecated stuff and outdated plunkrs (#15598) 2017-03-29 15:18:03 -07:00
902bb2f026 fix(compiler): throw when a component defines both template and templateUrl (#15572)
Closes #15566
2017-03-29 10:29:00 -07:00
23bf34853c refactor(router): cleanup & simplification (#15436) 2017-03-29 10:15:00 -07:00
8c4b963927 fix(core): Update types for TypeScript nullability support (#15472) 2017-03-29 10:14:55 -07:00
bfa4f70204 fix(language-service): don't require reflect-metadata module to be provided (#15569)
Fixes #15568
2017-03-29 10:14:48 -07:00
d481f6d150 docs(core): fix API docs for Injector.get 2017-03-29 10:14:38 -07:00
fd6114561b perf(router): don't create new serializer every time UrlTree.toString is called (#15565) 2017-03-29 10:11:21 -07:00
c82851172e fix(animations): make sure style calculations are not computed too early (#15540)
Closes #15507
2017-03-29 10:11:06 -07:00
75478b2078 fix(router): should run CanActivate after CanDeactivate guards
Closes #14059
Closes #15467
2017-03-29 10:11:00 -07:00
cdbb3dbd2a refactor(router): fix tests structure 2017-03-29 10:10:53 -07:00
e72124c888 fix(core): fix the key/value differ (#15539)
fixes #15457
2017-03-29 10:10:43 -07:00
b8c0a97e35 fix(core): check for undefined on normalizeDebugBindingValue (#15503)
DebugServices is parsing false atributes values incorrectly.
Parse5 expects a string value for attributes, but currently boolean is being sent.

Closes #15494
2017-03-29 10:10:37 -07:00
5597fd3180 fix(language-service): improve performance of updateModuleAnalysis() (#15543) 2017-03-29 10:10:26 -07:00
a88413f871 fix(compiler): ignore errors when evaluating base classes (#15560)
Fixes #15536
2017-03-29 10:10:22 -07:00
aa116524e6 refactor(router): improve flatten fn
closes #15505
2017-03-29 10:10:10 -07:00
4a5ad7ba30 refactor(router): use object spread operator instead of merge fn 2017-03-29 10:10:06 -07:00
d74e4d0633 fix(core): improve error msg for invalid KeyValueDiffer.diff arg (#15489)
Closes #15402
2017-03-29 10:09:55 -07:00
a2c2b87aa3 fix(language-service): be resilient to invalidate ordering (#15470)
Fixes #15466
2017-03-29 10:09:19 -07:00
8f4ea3e4b8 docs(core): fix typo and example in InjectionToken doc (#15449)
The doc included an example that didn't use InjectionToken.
2017-03-29 10:08:22 -07:00
bf25e94f19 fix(language-service): guard access to Symbol.members (#15529)
Fixes #15528 

What is the current behavior?
The language service access TypeScript's Symbol.members without checking for null or undefined.
What is the new behavior?
The access is guarded.
2017-03-29 10:08:06 -07:00
7983414e6a ci: add tbosch and vicb as approvers of the language service (#15530) 2017-03-29 10:07:58 -07:00
8248eba3e2 docs: in doc comments, replace [aA]ngular2 with Angular (#15463) 2017-03-29 10:07:44 -07:00
a65487528f fix(compiler): allow single quotes into named interpolations (#15461)
Fixes #15318
2017-03-29 10:07:32 -07:00
426b3a19b7 refactor: use object spread operator rather than merge (#15426) 2017-03-29 10:07:14 -07:00
2360676a7b fix(router): shouldn't execute CanLoad when a route has been loaded
Closes #14475
Closes #15438
2017-03-29 10:07:01 -07:00
ce3e03ff1a refactor(router): polishing 2017-03-29 10:06:52 -07:00
858c11cf7b docs: clarify querying all descendants (#15400)
Fixes #14417
Updated example to illustrate @ContentChildren default behavior (only query direct children), and how to query for nested elements/all descendants.
2017-03-29 10:06:09 -07:00
95afaf495b test(compiler): refactor i18n integration test 2017-03-24 14:35:10 -07:00
d92930e975 docs: fixed broken links (#15455)
Closes #15446
2017-03-24 08:14:23 -07:00
c2892dada3 docs: revert the move of CONTRIBUTING.md to docs/CONTRIBUTING.md
Because the root path has a special meeting for GitHub, Jeremy, unicorns and red arrows.
2017-03-23 20:57:49 -07:00
b2b1195534 docs: update the contributing link one more time :-) 2017-03-23 19:26:25 -07:00
e1b09e3bcc docs: update CONTRIBUTING link in README.md 2017-03-23 19:25:46 -07:00
c65b75443e docs: add the 4.0.0 code name to the changelog 2017-03-23 17:46:02 -07:00
db0dca3fc1 docs: add a note about exclusion of symbols prefixed with ɵ from our public api surface (#15440) 2017-03-23 17:21:52 -07:00
140 changed files with 2578 additions and 2113 deletions

View File

@ -176,7 +176,8 @@ groups:
- "packages/language-service/*" - "packages/language-service/*"
users: users:
- chuckjaz #primary - chuckjaz #primary
# needs secondary - tbosch #secondary
- vicb
- IgorMinar #fallback - IgorMinar #fallback
- mhevery #fallback - mhevery #fallback

View File

@ -1,5 +1,33 @@
<a name="4.0.1"></a>
## [4.0.1](https://github.com/angular/angular/compare/4.0.0...4.0.1) (2017-03-29)
### Bug Fixes
* **animations:** make sure style calculations are not computed too early ([#15540](https://github.com/angular/angular/issues/15540)) ([c828511](https://github.com/angular/angular/commit/c828511)), closes [#15507](https://github.com/angular/angular/issues/15507)
* **compiler:** allow single quotes into named interpolations ([#15461](https://github.com/angular/angular/issues/15461)) ([a654875](https://github.com/angular/angular/commit/a654875)), closes [#15318](https://github.com/angular/angular/issues/15318)
* **compiler:** ignore errors when evaluating base classes ([#15560](https://github.com/angular/angular/issues/15560)) ([a88413f](https://github.com/angular/angular/commit/a88413f)), closes [#15536](https://github.com/angular/angular/issues/15536)
* **compiler:** throw when a component defines both template and templateUrl ([#15572](https://github.com/angular/angular/issues/15572)) ([902bb2f](https://github.com/angular/angular/commit/902bb2f)), closes [#15566](https://github.com/angular/angular/issues/15566)
* **core:** check for undefined on normalizeDebugBindingValue ([#15503](https://github.com/angular/angular/issues/15503)) ([b8c0a97](https://github.com/angular/angular/commit/b8c0a97)), closes [#15494](https://github.com/angular/angular/issues/15494)
* **core:** fix inheritance in JIT mode for TS 2.1 ([#15599](https://github.com/angular/angular/issues/15599)) ([ca66530](https://github.com/angular/angular/commit/ca66530)), closes [#15502](https://github.com/angular/angular/issues/15502)
* **core:** fix the key/value differ ([#15539](https://github.com/angular/angular/issues/15539)) ([e72124c](https://github.com/angular/angular/commit/e72124c)), closes [#15457](https://github.com/angular/angular/issues/15457)
* **core:** improve error msg for invalid KeyValueDiffer.diff arg ([#15489](https://github.com/angular/angular/issues/15489)) ([d74e4d0](https://github.com/angular/angular/commit/d74e4d0)), closes [#15402](https://github.com/angular/angular/issues/15402)
* **core:** Update types for TypeScript nullability support ([#15472](https://github.com/angular/angular/issues/15472)) ([8c4b963](https://github.com/angular/angular/commit/8c4b963))
* **language-service:** be resilient to invalidate ordering ([#15470](https://github.com/angular/angular/issues/15470)) ([a2c2b87](https://github.com/angular/angular/commit/a2c2b87)), closes [#15466](https://github.com/angular/angular/issues/15466)
* **language-service:** correctly determine base members of types ([#15600](https://github.com/angular/angular/issues/15600)) ([0fe4985](https://github.com/angular/angular/commit/0fe4985)), closes [#15460](https://github.com/angular/angular/issues/15460)
* **language-service:** don't require `reflect-metadata` module to be provided ([#15569](https://github.com/angular/angular/issues/15569)) ([bfa4f70](https://github.com/angular/angular/commit/bfa4f70)), closes [#15568](https://github.com/angular/angular/issues/15568)
* **language-service:** guard access to `Symbol.members` ([#15529](https://github.com/angular/angular/issues/15529)) ([bf25e94](https://github.com/angular/angular/commit/bf25e94)), closes [#15528](https://github.com/angular/angular/issues/15528)
* **language-service:** improve performance of `updateModuleAnalysis()` ([#15543](https://github.com/angular/angular/issues/15543)) ([5597fd3](https://github.com/angular/angular/commit/5597fd3))
* **router:** should run CanActivate after CanDeactivate guards ([75478b2](https://github.com/angular/angular/commit/75478b2)), closes [#14059](https://github.com/angular/angular/issues/14059) [#15467](https://github.com/angular/angular/issues/15467)
* **router:** shouldn't execute CanLoad when a route has been loaded ([2360676](https://github.com/angular/angular/commit/2360676)), closes [#14475](https://github.com/angular/angular/issues/14475) [#15438](https://github.com/angular/angular/issues/15438)
### Performance Improvements
* **router:** don't create new serializer every time UrlTree.toString is called ([#15565](https://github.com/angular/angular/issues/15565)) ([fd61145](https://github.com/angular/angular/commit/fd61145))
<a name="4.0.0"></a> <a name="4.0.0"></a>
# [4.0.0](https://github.com/angular/angular/compare/4.0.0-rc.6...4.0.0) (2017-03-23) # [4.0.0](https://github.com/angular/angular/compare/4.0.0-rc.6...4.0.0) invisible-makeover (2017-03-23)
### Bug Fixes ### Bug Fixes

View File

@ -147,7 +147,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
* All public API methods **must be documented**. (Details TBC). * All public API methods **must be documented**. (Details TBC).
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at * We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
**100 characters**. An automated formatter is available, see **100 characters**. An automated formatter is available, see
[DEVELOPER.md](DEVELOPER.md#clang-format). [DEVELOPER.md](docs/DEVELOPER.md#clang-format).
## <a name="commit"></a> Commit Message Guidelines ## <a name="commit"></a> Commit Message Guidelines
@ -263,7 +263,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md [coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# [commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html [corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[dev-doc]: https://github.com/angular/angular/blob/master/DEVELOPER.md [dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
[github]: https://github.com/angular/angular [github]: https://github.com/angular/angular
[gitter]: https://gitter.im/angular/angular [gitter]: https://gitter.im/angular/angular
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html [individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html

View File

@ -32,7 +32,7 @@ We explicitly don't consider the following to be our public API surface:
- any file/import paths within our package except for the `/`, `/testing` and `/bundles/*` - any file/import paths within our package except for the `/`, `/testing` and `/bundles/*`
- constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes - constructors of injectable classes (services and directives) - please use DI to obtain instances of these classes
- any class members or symbols marked as `private` or prefixed with underscore - any class members or symbols marked as `private`, or prefixed with underscore (`_`) and [barred latin o](https://en.wikipedia.org/wiki/%C6%9F) (`ɵ`)
- extending any of our classes unless the support for this is specifically documented in the API docs - extending any of our classes unless the support for this is specifically documented in the API docs
- the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed) - the contents and API surface of the code generated by Angular's compiler (with one notable exception: the existence and name of `NgModuleFactory` instances exported from generated code is guaranteed)

View File

@ -26,9 +26,9 @@ Thanks for reporting this issue. However this issue is a duplicate of an existin
``` ```
## Angular: Insufficient Information Provided (v2) ## Angular: Insufficient Information Provided (v1)
``` ```
Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/docs/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information. Thanks for reporting this issue. However, you didn't provide sufficient information for us to understand and reproduce the problem. Please check out [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue) to understand why we can't act on issues that are lacking important information.
If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template. If the problem still persists, please file a new issue and ensure you provide all of the required information when filling out the issue template.
``` ```
@ -39,11 +39,11 @@ I'm sorry but this issue is not caused by Angular. Please contact the author(s)
``` ```
## Angular: Non-reproducible (v2) ## Angular: Non-reproducible (v1)
``` ```
I'm sorry but we can't reproduce the problem following the instructions you provided. I'm sorry but we can't reproduce the problem following the instructions you provided.
If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/docs/CONTRIBUTING.md#-submitting-an-issue). If the problem still exists please open a new issue following [our submission guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue).
``` ```
## Angular: Obsolete (v1) ## Angular: Obsolete (v1)
@ -54,9 +54,9 @@ If the problem still persists, please file a new issue and ensure you provide th
``` ```
## Angular: Support Request (v2) ## Angular: Support Request (v1)
``` ```
Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`. Hello, we reviewed this issue and determined that it doesn't fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on [StackOverflow](http://stackoverflow.com/) using tag `angular`.
If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/docs/CONTRIBUTING.md#-got-a-question-or-problem). If you are wondering why we don't resolve support issues via the issue tracker, please [check out this explanation](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-got-a-question-or-problem).
``` ```

View File

@ -40,7 +40,7 @@ export function init(moduleRef: NgModuleRef<AppModule>) {
const injector = moduleRef.injector; const injector = moduleRef.injector;
appRef = injector.get(ApplicationRef); appRef = injector.get(ApplicationRef);
const numberOfChecksEl = document.getElementById('numberOfChecks'); const numberOfChecksEl = document.getElementById('numberOfChecks') !;
tree = appRef.components[0].instance; tree = appRef.components[0].instance;

View File

@ -13,10 +13,10 @@ export class TreeNode {
children: TreeNode[]; children: TreeNode[];
constructor( constructor(
public value: string, public depth: number, public maxDepth: number, public left: TreeNode, public value: string, public depth: number, public maxDepth: number,
public right: TreeNode) { public left: TreeNode|null, public right: TreeNode|null) {
this.transitiveChildCount = Math.pow(2, (this.maxDepth - this.depth + 1)) - 1; this.transitiveChildCount = Math.pow(2, (this.maxDepth - this.depth + 1)) - 1;
this.children = this.left ? [this.left, this.right] : []; this.children = this.left ? [this.left, this.right !] : [];
} }
// Needed for Polymer as it does not support ternary nor modulo operator // Needed for Polymer as it does not support ternary nor modulo operator

View File

@ -35,7 +35,7 @@ export function getStringParameter(name: string) {
} }
export function bindAction(selector: string, callback: () => void) { export function bindAction(selector: string, callback: () => void) {
document.querySelector(selector).addEventListener('click', callback); document.querySelector(selector) !.addEventListener('click', callback);
} }
@ -60,7 +60,7 @@ export function profile(create: () => void, destroy: () => void, name: string) {
function urlParamsToForm() { function urlParamsToForm() {
const regex = /(\w+)=(\w+)/g; const regex = /(\w+)=(\w+)/g;
const search = decodeURIComponent(location.search); const search = decodeURIComponent(location.search);
let match: any[]; let match: any[]|null;
while (match = regex.exec(search)) { while (match = regex.exec(search)) {
const name = match[1]; const name = match[1];
const value = match[2]; const value = match[2];

View File

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

View File

@ -13,3 +13,4 @@ export {NoopAnimationDriver as ɵNoopAnimationDriver} from './render/animation_d
export {DomAnimationEngine as ɵDomAnimationEngine} from './render/dom_animation_engine'; export {DomAnimationEngine as ɵDomAnimationEngine} from './render/dom_animation_engine';
export {NoopAnimationEngine as ɵNoopAnimationEngine} from './render/noop_animation_engine'; export {NoopAnimationEngine as ɵNoopAnimationEngine} from './render/noop_animation_engine';
export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver'; export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver';
export {WebAnimationsPlayer as ɵWebAnimationsPlayer} from './render/web_animations/web_animations_player';

View File

@ -259,8 +259,6 @@ export class DomAnimationEngine {
const player = this._buildPlayer(element, instruction, previousPlayers, i); const player = this._buildPlayer(element, instruction, previousPlayers, i);
player.onDestroy( player.onDestroy(
() => { deleteFromArrayMap(this._activeElementAnimations, element, player); }); () => { deleteFromArrayMap(this._activeElementAnimations, element, player); });
player.init();
this._markPlayerAsActive(element, player); this._markPlayerAsActive(element, player);
return player; return player;
}); });
@ -354,6 +352,7 @@ export class DomAnimationEngine {
// in the event that an animation throws an error then we do // in the event that an animation throws an error then we do
// not want to re-run animations on any previous animations // not want to re-run animations on any previous animations
// if they have already been kicked off beforehand // if they have already been kicked off beforehand
player.init();
if (!player.hasStarted()) { if (!player.hasStarted()) {
player.play(); player.play();
} }

View File

@ -87,6 +87,22 @@ export function main() {
expect(engine.queuedPlayers.pop() instanceof NoopAnimationPlayer).toBe(true); expect(engine.queuedPlayers.pop() instanceof NoopAnimationPlayer).toBe(true);
}); });
it('should not initialize the animation until the engine has been flushed', () => {
const engine = makeEngine();
engine.registerTrigger(trigger(
'trig', [transition('* => something', [animate(1000, style({color: 'gold'}))])]));
engine.setProperty(element, 'trig', 'something');
const player = engine.queuedPlayers.pop() as MockAnimationPlayer;
let initialized = false;
player.onInit(() => initialized = true);
expect(initialized).toBe(false);
engine.flush();
expect(initialized).toBe(true);
});
it('should not queue an animation if the property value has not changed at all', () => { it('should not queue an animation if the property value has not changed at all', () => {
const engine = makeEngine(); const engine = makeEngine();

View File

@ -31,6 +31,7 @@ export class MockAnimationDriver implements AnimationDriver {
export class MockAnimationPlayer extends NoopAnimationPlayer { export class MockAnimationPlayer extends NoopAnimationPlayer {
private __finished = false; private __finished = false;
public previousStyles: {[key: string]: string | number} = {}; public previousStyles: {[key: string]: string | number} = {};
private _onInitFns: (() => any)[] = [];
constructor( constructor(
public element: any, public keyframes: {[key: string]: string | number}[], public element: any, public keyframes: {[key: string]: string | number}[],
@ -45,6 +46,16 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
}); });
} }
/* @internal */
onInit(fn: () => any) { this._onInitFns.push(fn); }
/* @internal */
init() {
super.init();
this._onInitFns.forEach(fn => fn());
this._onInitFns = [];
}
finish(): void { finish(): void {
super.finish(); super.finish();
this.__finished = true; this.__finished = true;

View File

@ -98,7 +98,7 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
*/ */
export interface AnimationAnimateMetadata extends AnimationMetadata { export interface AnimationAnimateMetadata extends AnimationMetadata {
timings: string|number|AnimateTimings; timings: string|number|AnimateTimings;
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata; styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null;
} }
/** /**
@ -118,10 +118,10 @@ export interface AnimationSequenceMetadata extends AnimationMetadata { steps: An
export interface AnimationGroupMetadata extends AnimationMetadata { steps: AnimationMetadata[]; } export interface AnimationGroupMetadata extends AnimationMetadata { steps: AnimationMetadata[]; }
/** /**
* `trigger` is an animation-specific function that is designed to be used inside of Angular2's * `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 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-anchor component animations metadata page} to gain a better understanding of
how animations in Angular2 are used. how animations in Angular are used.
* *
* `trigger` Creates an animation trigger which will a list of {@link state state} and {@link * `trigger` Creates an animation trigger which will a list of {@link state state} and {@link
transition transition} entries that will be evaluated when the expression bound to the trigger transition transition} entries that will be evaluated when the expression bound to the trigger
@ -173,10 +173,10 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
} }
/** /**
* `animate` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `animate` specifies an animation step that will apply the provided `styles` data for a given * `animate` specifies an animation step that will apply the provided `styles` data for a given
* amount of time based on the provided `timing` expression value. Calls to `animate` are expected * amount of time based on the provided `timing` expression value. Calls to `animate` are expected
@ -218,16 +218,16 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
* @experimental Animation support is experimental. * @experimental Animation support is experimental.
*/ */
export function animate( export function animate(
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata = timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata |
null): AnimationAnimateMetadata { null = null): AnimationAnimateMetadata {
return {type: AnimationMetadataType.Animate, styles: styles, timings: timings}; return {type: AnimationMetadataType.Animate, styles: styles, timings: timings};
} }
/** /**
* `group` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `group` specifies a list of animation steps that are all run in parallel. Grouped animations are * `group` specifies a list of animation steps that are all run in parallel. Grouped animations are
* useful when a series of styles must be animated/closed off at different statrting/ending times. * useful when a series of styles must be animated/closed off at different statrting/ending times.
@ -259,10 +259,10 @@ export function group(steps: AnimationMetadata[]): AnimationGroupMetadata {
} }
/** /**
* `sequence` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `sequence` Specifies a list of animation steps that are run one by one. (`sequence` is used by * `sequence` Specifies a list of animation steps that are run one by one. (`sequence` is used by
* default when an array is passed as animation data into {@link transition transition}.) * default when an array is passed as animation data into {@link transition transition}.)
@ -297,10 +297,10 @@ export function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata
} }
/** /**
* `style` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `style` declares a key/value object containing CSS properties/styles that can then be used for * `style` declares a key/value object containing CSS properties/styles that can then be used for
* {@link state animation states}, within an {@link sequence animation sequence}, or as styling data * {@link state animation states}, within an {@link sequence animation sequence}, or as styling data
@ -345,10 +345,10 @@ export function style(
} }
/** /**
* `state` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `state` declares an animation state within the given trigger. When a state is active within a * `state` declares an animation state within the given trigger. When a state is active within a
* component then its associated styles will persist on the element that the trigger is attached to * component then its associated styles will persist on the element that the trigger is attached to
@ -397,10 +397,10 @@ export function state(name: string, styles: AnimationStyleMetadata): AnimationSt
} }
/** /**
* `keyframes` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `keyframes` specifies a collection of {@link style style} entries each optionally characterized * `keyframes` specifies a collection of {@link style style} entries each optionally characterized
* by an `offset` value. * by an `offset` value.
@ -446,10 +446,10 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
} }
/** /**
* `transition` is an animation-specific function that is designed to be used inside of Angular2's * `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 * 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-anchor component animations metadata page} to gain a better understanding of
* how animations in Angular2 are used. * how animations in Angular are used.
* *
* `transition` declares the {@link sequence sequence of animation steps} that will be run when the * `transition` declares the {@link sequence sequence of animation steps} that will be run when the
* provided `stateChangeExpr` value is satisfied. The `stateChangeExpr` consists of a `state1 => * provided `stateChangeExpr` value is satisfied. The `stateChangeExpr` consists of a `state1 =>

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core'; export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';

View File

@ -72,7 +72,7 @@ export class NgLocaleLocalization extends NgLocalization {
} }
// This is generated code DO NOT MODIFY // This is generated code DO NOT MODIFY
// see angular2/script/cldr/gen_plural_rules.js // see angular/script/cldr/gen_plural_rules.js
/** @experimental */ /** @experimental */
export enum Plural { export enum Plural {

View File

@ -30,7 +30,7 @@ function getSourcePositionForStack(stack: string): {source: string, line: number
const htmlLocations = stack const htmlLocations = stack
.split('\n') .split('\n')
// e.g. at View_MyComp_0 (...html:153:40) // e.g. at View_MyComp_0 (...html:153:40)
.map(line => /\((.*\.html):(\d+):(\d+)/.exec(line)) .map(line => /\((.*\.html):(\d+):(\d+)/.exec(line) !)
.filter(match => !!match) .filter(match => !!match)
.map(match => ({ .map(match => ({
source: match[1], source: match[1],

View File

@ -8,7 +8,7 @@
*/ */
/* tslint:disable:no-console */ /* tslint:disable:no-console */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
import * as path from 'path'; import * as path from 'path';
@ -67,9 +67,9 @@ function codeGenTest() {
angularCompilerOptions: config.ngOptions, angularCompilerOptions: config.ngOptions,
// i18n options. // i18n options.
i18nFormat: null, i18nFormat: undefined,
i18nFile: null, i18nFile: undefined,
locale: null, locale: undefined,
readResource: (fileName: string) => { readResource: (fileName: string) => {
readResources.push(fileName); readResources.push(fileName);
@ -131,8 +131,8 @@ function i18nTest() {
compilerOptions: config.parsed.options, program, host, compilerOptions: config.parsed.options, program, host,
angularCompilerOptions: config.ngOptions, angularCompilerOptions: config.ngOptions,
i18nFormat: 'xlf', i18nFormat: 'xlf',
locale: null, locale: undefined,
outFile: null, outFile: undefined,
readResource: (fileName: string) => { readResource: (fileName: string) => {
readResources.push(fileName); readResources.push(fileName);
return hostContext.readResource(fileName); return hostContext.readResource(fileName);

View File

@ -8,7 +8,7 @@
*/ */
/* tslint:disable:no-console */ /* tslint:disable:no-console */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
import * as path from 'path'; import * as path from 'path';
@ -34,7 +34,8 @@ function main() {
if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName) && if (/.*\/node_modules\/.*/.test(fileName) && !/.*ngsummary\.json$/.test(fileName) &&
!/package\.json$/.test(fileName)) { !/package\.json$/.test(fileName)) {
// Only allow to read summaries and package.json files from node_modules // Only allow to read summaries and package.json files from node_modules
return null; // TODO (mhevery): Fix this. TypeScript.d.ts does not allow returning null.
return null !;
} }
readFiles.push(path.relative(basePath, fileName)); readFiles.push(path.relative(basePath, fileName));
return super.readFile(fileName); return super.readFile(fileName);

View File

@ -13,7 +13,7 @@ import {platformServer} from '@angular/platform-server';
import {MainModule} from '../src/module'; import {MainModule} from '../src/module';
import {MainModuleNgFactory} from '../src/module.ngfactory'; import {MainModuleNgFactory} from '../src/module.ngfactory';
let mainModuleRef: NgModuleRef<MainModule> = null; let mainModuleRef: NgModuleRef<MainModule> = null !;
beforeEach((done) => { beforeEach((done) => {
platformServer().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => { platformServer().bootstrapModuleFactory(MainModuleNgFactory).then((moduleRef: any) => {
mainModuleRef = moduleRef; mainModuleRef = moduleRef;
@ -29,5 +29,5 @@ export function createComponent<C>(comp: {new (...args: any[]): C}): ComponentFi
const moduleRef = createModule(); const moduleRef = createModule();
const compRef = const compRef =
moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector); moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector);
return new ComponentFixture(compRef, null, null); return new ComponentFixture(compRef, null, false);
} }

View File

@ -10,6 +10,8 @@
"target": "es5", "target": "es5",
"experimentalDecorators": true, "experimentalDecorators": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true,
"skipLibCheck": true,
"moduleResolution": "node", "moduleResolution": "node",
"rootDir": "", "rootDir": "",
"declaration": true, "declaration": true,

View File

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

View File

@ -33,6 +33,7 @@ export class CompilerHost implements AotCompilerHost {
private resolverCache = new Map<string, ModuleMetadata[]>(); private resolverCache = new Map<string, ModuleMetadata[]>();
private bundleIndexCache = new Map<string, boolean>(); private bundleIndexCache = new Map<string, boolean>();
private bundleIndexNames = new Set<string>(); private bundleIndexNames = new Set<string>();
private moduleFileNames = new Map<string, string>();
protected resolveModuleNameHost: CompilerHostContext; protected resolveModuleNameHost: CompilerHostContext;
constructor( constructor(
@ -69,6 +70,9 @@ export class CompilerHost implements AotCompilerHost {
getCanonicalFileName(fileName: string): string { return fileName; } getCanonicalFileName(fileName: string): string { return fileName; }
moduleNameToFileName(m: string, containingFile: string): string|null { moduleNameToFileName(m: string, containingFile: string): string|null {
const key = m + ':' + (containingFile || '');
let result = this.moduleFileNames.get(key);
if (!result) {
if (!containingFile || !containingFile.length) { if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) { if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.'); throw new Error('Resolution of relative paths requires a containing file.');
@ -81,7 +85,10 @@ export class CompilerHost implements AotCompilerHost {
ts.resolveModuleName( ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost) m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule; .resolvedModule;
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null; result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
this.moduleFileNames.set(key, result);
}
return result;
}; };
/** /**

View File

@ -11,7 +11,7 @@
/** /**
* Extract i18n messages from source code * Extract i18n messages from source code
*/ */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
import * as tsc from '@angular/tsc-wrapped'; import * as tsc from '@angular/tsc-wrapped';

View File

@ -10,7 +10,7 @@
/** /**
* Extract i18n messages from source code * Extract i18n messages from source code
*/ */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
import * as compiler from '@angular/compiler'; import * as compiler from '@angular/compiler';

View File

@ -8,7 +8,7 @@
*/ */
// Must be imported first, because angular2 decorators throws on load. // Must be imported first, because Angular decorators throw on load.
import 'reflect-metadata'; import 'reflect-metadata';
import * as ts from 'typescript'; import * as ts from 'typescript';

View File

@ -32,9 +32,9 @@ export interface NgTools_InternalApi_NG2_CodeGen_Options {
angularCompilerOptions: AngularCompilerOptions; angularCompilerOptions: AngularCompilerOptions;
// i18n options. // i18n options.
i18nFormat: string; i18nFormat?: string;
i18nFile: string; i18nFile?: string;
locale: string; locale?: string;
readResource: (fileName: string) => Promise<string>; readResource: (fileName: string) => Promise<string>;
@ -58,7 +58,7 @@ export interface NgTools_InternalApi_NG2_ExtractI18n_Options {
program: ts.Program; program: ts.Program;
host: ts.CompilerHost; host: ts.CompilerHost;
angularCompilerOptions: AngularCompilerOptions; angularCompilerOptions: AngularCompilerOptions;
i18nFormat: string; i18nFormat?: string;
readResource: (fileName: string) => Promise<string>; readResource: (fileName: string) => Promise<string>;
// Every new property under this line should be optional. // Every new property under this line should be optional.
locale?: string; locale?: string;

View File

@ -118,7 +118,7 @@ export class StaticReflector implements ɵReflectorReader {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
propMetadata = {}; propMetadata = {};
if (classMetadata['extends']) { if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']); const parentType = this.trySimplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) { if (parentType instanceof StaticSymbol) {
const parentPropMetadata = this.propMetadata(parentType); const parentPropMetadata = this.propMetadata(parentType);
Object.keys(parentPropMetadata).forEach((parentProp) => { Object.keys(parentPropMetadata).forEach((parentProp) => {
@ -176,7 +176,7 @@ export class StaticReflector implements ɵReflectorReader {
parameters.push(nestedResult); parameters.push(nestedResult);
}); });
} else if (classMetadata['extends']) { } else if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']); const parentType = this.trySimplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) { if (parentType instanceof StaticSymbol) {
parameters = this.parameters(parentType); parameters = this.parameters(parentType);
} }
@ -199,7 +199,7 @@ export class StaticReflector implements ɵReflectorReader {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
methodNames = {}; methodNames = {};
if (classMetadata['extends']) { if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']); const parentType = this.trySimplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) { if (parentType instanceof StaticSymbol) {
const parentMethodNames = this._methodNames(parentType); const parentMethodNames = this._methodNames(parentType);
Object.keys(parentMethodNames).forEach((parentProp) => { Object.keys(parentMethodNames).forEach((parentProp) => {

View File

@ -59,6 +59,7 @@ export class StaticSymbolResolver {
// Note: this will only contain StaticSymbols without members! // Note: this will only contain StaticSymbols without members!
private importAs = new Map<StaticSymbol, StaticSymbol>(); private importAs = new Map<StaticSymbol, StaticSymbol>();
private symbolResourcePaths = new Map<StaticSymbol, string>(); private symbolResourcePaths = new Map<StaticSymbol, string>();
private symbolFromFile = new Map<string, StaticSymbol[]>();
constructor( constructor(
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache, private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
@ -143,6 +144,25 @@ export class StaticSymbolResolver {
this.importAs.set(sourceSymbol, targetSymbol); this.importAs.set(sourceSymbol, targetSymbol);
} }
/**
* Invalidate all information derived from the given file.
*
* @param fileName the file to invalidate
*/
invalidateFile(fileName: string) {
this.metadataCache.delete(fileName);
this.resolvedFilePaths.delete(fileName);
const symbols = this.symbolFromFile.get(fileName);
if (symbols) {
this.symbolFromFile.delete(fileName);
for (const symbol of symbols) {
this.resolvedSymbols.delete(symbol);
this.importAs.delete(symbol);
this.symbolResourcePaths.delete(symbol);
}
}
}
private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol { private _resolveSymbolMembers(staticSymbol: StaticSymbol): ResolvedStaticSymbol {
const members = staticSymbol.members; const members = staticSymbol.members;
const baseResolvedSymbol = const baseResolvedSymbol =
@ -281,6 +301,7 @@ export class StaticSymbolResolver {
} }
resolvedSymbols.forEach( resolvedSymbols.forEach(
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol)); (resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
this.symbolFromFile.set(filePath, resolvedSymbols.map(resolvedSymbol => resolvedSymbol.symbol));
} }
private createResolvedSymbol( private createResolvedSymbol(

View File

@ -65,6 +65,10 @@ export class DirectiveNormalizer {
let normalizedTemplateSync: CompileTemplateMetadata = null; let normalizedTemplateSync: CompileTemplateMetadata = null;
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>; let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
if (prenormData.template != null) { if (prenormData.template != null) {
if (prenormData.templateUrl != null) {
throw syntaxError(
`'${stringify(prenormData.componentType)}' component cannot define both template and templateUrl`);
}
if (typeof prenormData.template !== 'string') { if (typeof prenormData.template !== 'string') {
throw syntaxError( throw syntaxError(
`The template specified for component ${stringify(prenormData.componentType)} is not a string`); `The template specified for component ${stringify(prenormData.componentType)} is not a string`);

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵmerge as merge, ɵreflector, ɵstringify as stringify} from '@angular/core'; import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵreflector, ɵstringify as stringify} from '@angular/core';
import {CompilerInjectable} from './injectable'; import {CompilerInjectable} from './injectable';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
@ -124,8 +124,8 @@ export class DirectiveResolver {
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs); this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
const mergedOutputs = const mergedOutputs =
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs); this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
const mergedHost = directive.host ? merge(directive.host, host) : host; const mergedHost = directive.host ? {...directive.host, ...host} : host;
const mergedQueries = directive.queries ? merge(directive.queries, queries) : queries; const mergedQueries = directive.queries ? {...directive.queries, ...queries} : queries;
if (directive instanceof Component) { if (directive instanceof Component) {
return new Component({ return new Component({

View File

@ -161,8 +161,9 @@ class _I18nVisitor implements html.Visitor {
} }
} }
const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*"([\s\S]*?)"[\s\S]*\)/g; const _CUSTOM_PH_EXP =
/\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
function _extractPlaceholderName(input: string): string { function _extractPlaceholderName(input: string): string {
return input.split(_CUSTOM_PH_EXP)[1]; return input.split(_CUSTOM_PH_EXP)[2];
} }

View File

@ -644,8 +644,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
return EventHandlerVars.event; return EventHandlerVars.event;
} }
let currViewExpr: o.Expression = VIEW_VAR; let currViewExpr: o.Expression = VIEW_VAR;
for (let currBuilder: ViewBuilder = this; currBuilder; for (let currBuilder: ViewBuilder = this; currBuilder; currBuilder = currBuilder.parent,
currBuilder = currBuilder.parent, currViewExpr = currViewExpr.prop('parent')) { currViewExpr = currViewExpr.prop('parent').cast(o.DYNAMIC_TYPE)) {
// check references // check references
const refNodeIndex = currBuilder.refNodeIndices[name]; const refNodeIndex = currBuilder.refNodeIndices[name];
if (refNodeIndex != null) { if (refNodeIndex != null) {
@ -717,7 +717,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
let compBuilder: ViewBuilder = this; let compBuilder: ViewBuilder = this;
while (compBuilder.parent) { while (compBuilder.parent) {
compBuilder = compBuilder.parent; compBuilder = compBuilder.parent;
compViewExpr = compViewExpr.prop('parent'); compViewExpr = compViewExpr.prop('parent').cast(o.DYNAMIC_TYPE);
} }
const pipeNodeIndex = compBuilder.purePipeNodeIndices[name]; const pipeNodeIndex = compBuilder.purePipeNodeIndices[name];
const pipeValueExpr: o.Expression = const pipeValueExpr: o.Expression =

View File

@ -495,6 +495,31 @@ describe('StaticReflector', () => {
expect(() => reflector.propMetadata(appComponent)).not.toThrow(); expect(() => reflector.propMetadata(appComponent)).not.toThrow();
}); });
it('should not throw with an invalid extends', () => {
const data = Object.create(DEFAULT_TEST_DATA);
const file = '/tmp/src/invalid-component.ts';
data[file] = `
import {Component} from '@angular/core';
function InvalidParent() {
return InvalidParent;
}
@Component({
selector: 'tmp',
template: '',
})
export class BadComponent extends InvalidParent() {
}
`;
init(data);
const badComponent = reflector.getStaticSymbol(file, 'BadComponent');
expect(reflector.propMetadata(badComponent)).toEqual({});
expect(reflector.parameters(badComponent)).toEqual([]);
expect(reflector.hasLifecycleHook(badComponent, 'onDestroy')).toEqual(false);
});
it('should produce a annotation even if it contains errors', () => { it('should produce a annotation even if it contains errors', () => {
const data = Object.create(DEFAULT_TEST_DATA); const data = Object.create(DEFAULT_TEST_DATA);
const file = '/tmp/src/invalid-component.ts'; const file = '/tmp/src/invalid-component.ts';

View File

@ -51,6 +51,16 @@ export function main() {
templateUrl: <any>{} templateUrl: <any>{}
})).toThrowError('The templateUrl specified for component SomeComp is not a string'); })).toThrowError('The templateUrl specified for component SomeComp is not a string');
})); }));
it('should throw if both template and templateUrl are defined',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(() => normalizer.normalizeTemplate({
ngModuleType: null,
componentType: SomeComp,
moduleUrl: SOME_MODULE_URL,
template: '',
templateUrl: '',
})).toThrowError(`'SomeComp' component cannot define both template and templateUrl`);
}));
}); });
describe('normalizeTemplateSync', () => { describe('normalizeTemplateSync', () => {

View File

@ -125,6 +125,12 @@ export function main() {
.toEqual([ .toEqual([
[['[before, <ph name="TEST"> exp //i18n(ph="teSt") </ph>, after]'], 'm', 'd'], [['[before, <ph name="TEST"> exp //i18n(ph="teSt") </ph>, after]'], 'm', 'd'],
]); ]);
expect(
_humanizeMessages('<div i18n=\'m|d\'>before{{ exp //i18n(ph=\'teSt\') }}after</div>'))
.toEqual([
[[`[before, <ph name="TEST"> exp //i18n(ph='teSt') </ph>, after]`], 'm', 'd'],
]);
}); });
}); });

View File

@ -0,0 +1,158 @@
/**
* @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 {NgLocalization} from '@angular/common';
import {Component, DebugElement} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@Component({
selector: 'i18n-cmp',
template: '',
})
export class I18nComponent {
count: number;
sex: string;
sexB: string;
response: any = {getItemsList: (): any[] => []};
}
export class FrLocalization extends NgLocalization {
getPluralCategory(value: number): string {
switch (value) {
case 0:
case 1:
return 'one';
default:
return 'other';
}
}
}
export function validateHtml(
tb: ComponentFixture<I18nComponent>, cmp: I18nComponent, el: DebugElement) {
expectHtml(el, 'h1').toBe('<h1>attributs i18n sur les balises</h1>');
expectHtml(el, '#i18n-1').toBe('<div id="i18n-1"><p>imbriqué</p></div>');
expectHtml(el, '#i18n-2').toBe('<div id="i18n-2"><p>imbriqué</p></div>');
expectHtml(el, '#i18n-3').toBe('<div id="i18n-3"><p><i>avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-3b')
.toBe(
'<div id="i18n-3b"><p><i class="preserved-on-placeholders">avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-4').toBe('<p id="i18n-4" title="sur des balises non traductibles"></p>');
expectHtml(el, '#i18n-5').toBe('<p id="i18n-5" title="sur des balises traductibles"></p>');
expectHtml(el, '#i18n-6').toBe('<p id="i18n-6" title=""></p>');
cmp.count = 0;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('zero');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('zero');
cmp.count = 1;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('un');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('un');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('un');
cmp.count = 2;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('deux');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('deux');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('deux');
cmp.count = 3;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('beaucoup');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('beaucoup');
cmp.sex = 'm';
cmp.sexB = 'f';
tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('homme');
expect(el.query(By.css('#i18n-8b')).nativeElement).toHaveText('femme');
cmp.sex = 'f';
tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('femme');
cmp.count = 123;
tb.detectChanges();
expectHtml(el, '#i18n-9').toEqual('<div id="i18n-9">count = 123</div>');
cmp.sex = 'f';
tb.detectChanges();
expectHtml(el, '#i18n-10').toEqual('<div id="i18n-10">sexe = f</div>');
expectHtml(el, '#i18n-11').toEqual('<div id="i18n-11">custom name</div>');
expectHtml(el, '#i18n-12').toEqual('<h1 id="i18n-12">Balises dans les commentaires html</h1>');
expectHtml(el, '#i18n-13').toBe('<div id="i18n-13" title="dans une section traductible"></div>');
expectHtml(el, '#i18n-15').toMatch(/ca <b>devrait<\/b> marcher/);
expectHtml(el, '#i18n-16').toMatch(/avec un ID explicite/);
expectHtml(el, '#i18n-18')
.toEqual('<div id="i18n-18">FOO<a title="dans une section traductible">BAR</a></div>');
}
function expectHtml(el: DebugElement, cssSelector: string): any {
return expect(stringifyElement(el.query(By.css(cssSelector)).nativeElement));
}
export const HTML = `
<div>
<h1 i18n>i18n attribute on tags</h1>
<div id="i18n-1"><p i18n>nested</p></div>
<div id="i18n-2"><p i18n="different meaning|">nested</p></div>
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
<div>
<p id="i18n-4" i18n-title title="on not translatable node"></p>
<p id="i18n-5" i18n i18n-title title="on translatable node"></p>
<p id="i18n-6" i18n-title title></p>
</div>
<!-- no ph below because the ICU node is the only child of the div, i.e. no text nodes -->
<div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<div i18n id="i18n-8">
{sex, select, m {male} f {female}}
</div>
<div i18n id="i18n-8b">
{sexB, select, m {male} f {female}}
</div>
<div i18n id="i18n-9">{{ "count = " + count }}</div>
<div i18n id="i18n-10">sex = {{ sex }}</div>
<div i18n id="i18n-11">{{ "custom name" //i18n(ph="CUSTOM_NAME") }}</div>
</div>
<!-- i18n -->
<h1 id="i18n-12" >Markers in html comments</h1>
<div id="i18n-13" i18n-title title="in a translatable section"></div>
<div id="i18n-14">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<!-- /i18n -->
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
<div id="i18n-16" i18n="@@i18n16">with an explicit ID</div>
<div id="i18n-17" i18n="@@i18n17">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<!-- make sure that ICU messages are not treated as text nodes -->
<div i18n="desc">{
response.getItemsList().length,
plural,
=0 {Found no results}
=1 {Found one result}
other {Found {{response.getItemsList().length}} results}
}</div>
<div i18n id="i18n-18">foo<a i18n-title title="in a translatable section">bar</a></div>
<div i18n>{{ 'test' //i18n(ph="map name") }}</div>
`;

View File

@ -12,16 +12,16 @@ import {MessageBundle} from '@angular/compiler/src/i18n/message_bundle';
import {Xmb} from '@angular/compiler/src/i18n/serializers/xmb'; import {Xmb} from '@angular/compiler/src/i18n/serializers/xmb';
import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser';
import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config';
import {Component, DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; import {DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core';
import {TestBed, async} from '@angular/core/testing'; import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {expect} from '@angular/platform-browser/testing/src/matchers'; import {expect} from '@angular/platform-browser/testing/src/matchers';
import {SpyResourceLoader} from '../spies'; import {SpyResourceLoader} from '../spies';
import {FrLocalization, HTML, I18nComponent, validateHtml} from './integration_common';
export function main() { export function main() {
describe('i18n integration spec', () => { describe('i18n XMB/XTB integration spec', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureCompiler({ TestBed.configureCompiler({
@ -45,100 +45,16 @@ export function main() {
}); });
it('should translate templates', () => { it('should translate templates', () => {
const tb = TestBed.overrideTemplate(I18nComponent, HTML).createComponent(I18nComponent); const tb: ComponentFixture<I18nComponent> =
const cmp = tb.componentInstance; TestBed.overrideTemplate(I18nComponent, HTML).createComponent(I18nComponent);
const el = tb.debugElement; const cmp: I18nComponent = tb.componentInstance;
const el: DebugElement = tb.debugElement;
expectHtml(el, 'h1').toBe('<h1>attributs i18n sur les balises</h1>'); validateHtml(tb, cmp, el);
expectHtml(el, '#i18n-1').toBe('<div id="i18n-1"><p>imbriqué</p></div>');
expectHtml(el, '#i18n-2').toBe('<div id="i18n-2"><p>imbriqué</p></div>');
expectHtml(el, '#i18n-3')
.toBe('<div id="i18n-3"><p><i>avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-3b')
.toBe(
'<div id="i18n-3b"><p><i class="preserved-on-placeholders">avec des espaces réservés</i></p></div>');
expectHtml(el, '#i18n-4')
.toBe('<p id="i18n-4" title="sur des balises non traductibles"></p>');
expectHtml(el, '#i18n-5').toBe('<p id="i18n-5" title="sur des balises traductibles"></p>');
expectHtml(el, '#i18n-6').toBe('<p id="i18n-6" title=""></p>');
cmp.count = 0;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('zero');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('zero');
cmp.count = 1;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('un');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('un');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('un');
cmp.count = 2;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('deux');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('deux');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('deux');
cmp.count = 3;
tb.detectChanges();
expect(el.query(By.css('#i18n-7')).nativeElement).toHaveText('beaucoup');
expect(el.query(By.css('#i18n-14')).nativeElement).toHaveText('beaucoup');
expect(el.query(By.css('#i18n-17')).nativeElement).toHaveText('beaucoup');
cmp.sex = 'm';
cmp.sexB = 'f';
tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('homme');
expect(el.query(By.css('#i18n-8b')).nativeElement).toHaveText('femme');
cmp.sex = 'f';
tb.detectChanges();
expect(el.query(By.css('#i18n-8')).nativeElement).toHaveText('femme');
cmp.count = 123;
tb.detectChanges();
expectHtml(el, '#i18n-9').toEqual('<div id="i18n-9">count = 123</div>');
cmp.sex = 'f';
tb.detectChanges();
expectHtml(el, '#i18n-10').toEqual('<div id="i18n-10">sexe = f</div>');
expectHtml(el, '#i18n-11').toEqual('<div id="i18n-11">custom name</div>');
expectHtml(el, '#i18n-12')
.toEqual('<h1 id="i18n-12">Balises dans les commentaires html</h1>');
expectHtml(el, '#i18n-13')
.toBe('<div id="i18n-13" title="dans une section traductible"></div>');
expectHtml(el, '#i18n-15').toMatch(/ca <b>devrait<\/b> marcher/);
expectHtml(el, '#i18n-16').toMatch(/avec un ID explicite/);
expectHtml(el, '#i18n-18')
.toEqual('<div id="i18n-18">FOO<a title="dans une section traductible">BAR</a></div>');
}); });
}); });
} }
function expectHtml(el: DebugElement, cssSelector: string): any {
return expect(stringifyElement(el.query(By.css(cssSelector)).nativeElement));
}
@Component({
selector: 'i18n-cmp',
template: '',
})
class I18nComponent {
count: number;
sex: string;
sexB: string;
response: any = {getItemsList: (): any[] => []};
}
class FrLocalization extends NgLocalization {
getPluralCategory(value: number): string {
switch (value) {
case 0:
case 1:
return 'one';
default:
return 'other';
}
}
}
const XTB = ` const XTB = `
<translationbundle> <translationbundle>
<translation id="615790887472569365">attributs i18n sur les balises</translation> <translation id="615790887472569365">attributs i18n sur les balises</translation>
@ -194,60 +110,3 @@ const XMB = ` <msg id="615790887472569365">i18n attribute on tags</msg>
<msg id="4085484936881858615" desc="desc">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg> <msg id="4085484936881858615" desc="desc">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
<msg id="4035252431381981115">foo<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>bar<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></msg> <msg id="4035252431381981115">foo<ph name="START_LINK"><ex>&lt;a&gt;</ex></ph>bar<ph name="CLOSE_LINK"><ex>&lt;/a&gt;</ex></ph></msg>
<msg id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`; <msg id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`;
const HTML = `
<div>
<h1 i18n>i18n attribute on tags</h1>
<div id="i18n-1"><p i18n>nested</p></div>
<div id="i18n-2"><p i18n="different meaning|">nested</p></div>
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
<div>
<p id="i18n-4" i18n-title title="on not translatable node"></p>
<p id="i18n-5" i18n i18n-title title="on translatable node"></p>
<p id="i18n-6" i18n-title title></p>
</div>
<!-- no ph below because the ICU node is the only child of the div, i.e. no text nodes -->
<div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<div i18n id="i18n-8">
{sex, select, m {male} f {female}}
</div>
<div i18n id="i18n-8b">
{sexB, select, m {male} f {female}}
</div>
<div i18n id="i18n-9">{{ "count = " + count }}</div>
<div i18n id="i18n-10">sex = {{ sex }}</div>
<div i18n id="i18n-11">{{ "custom name" //i18n(ph="CUSTOM_NAME") }}</div>
</div>
<!-- i18n -->
<h1 id="i18n-12" >Markers in html comments</h1>
<div id="i18n-13" i18n-title title="in a translatable section"></div>
<div id="i18n-14">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<!-- /i18n -->
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
<div id="i18n-16" i18n="@@i18n16">with an explicit ID</div>
<div id="i18n-17" i18n="@@i18n17">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
<!-- make sure that ICU messages are not treated as text nodes -->
<div i18n="desc">{
response.getItemsList().length,
plural,
=0 {Found no results}
=1 {Found one result}
other {Found {{response.getItemsList().length}} results}
}</div>
<div i18n id="i18n-18">foo<a i18n-title title="in a translatable section">bar</a></div>
<div i18n>{{ 'test' //i18n(ph="map name") }}</div>
`;

View File

@ -62,7 +62,7 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
*/ */
export interface AnimationAnimateMetadata extends AnimationMetadata { export interface AnimationAnimateMetadata extends AnimationMetadata {
timings: string|number|AnimateTimings; timings: string|number|AnimateTimings;
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata; styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null;
} }
/** /**
@ -86,8 +86,8 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
* @deprecated This symbol has moved. Please Import from @angular/animations instead! * @deprecated This symbol has moved. Please Import from @angular/animations instead!
*/ */
export function animate( export function animate(
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata = timings: string | number, styles?: AnimationStyleMetadata |
null): AnimationAnimateMetadata { AnimationKeyframesSequenceMetadata): AnimationAnimateMetadata {
return _animate(timings, styles); return _animate(timings, styles);
} }

View File

@ -99,7 +99,7 @@ export function createPlatform(injector: Injector): PlatformRef {
* @experimental APIs related to application bootstrap are currently under review. * @experimental APIs related to application bootstrap are currently under review.
*/ */
export function createPlatformFactory( export function createPlatformFactory(
parentPlatformFactory: (extraProviders?: Provider[]) => PlatformRef, name: string, parentPlatformFactory: ((extraProviders?: Provider[]) => PlatformRef) | null, name: string,
providers: Provider[] = []): (extraProviders?: Provider[]) => PlatformRef { providers: Provider[] = []): (extraProviders?: Provider[]) => PlatformRef {
const marker = new InjectionToken(`Platform: ${name}`); const marker = new InjectionToken(`Platform: ${name}`);
return (extraProviders: Provider[] = []) => { return (extraProviders: Provider[] = []) => {
@ -153,7 +153,7 @@ export function destroyPlatform(): void {
* *
* @experimental APIs related to application bootstrap are currently under review. * @experimental APIs related to application bootstrap are currently under review.
*/ */
export function getPlatform(): PlatformRef { export function getPlatform(): PlatformRef|null {
return _platform && !_platform.destroyed ? _platform : null; return _platform && !_platform.destroyed ? _platform : null;
} }
@ -278,10 +278,10 @@ export class PlatformRef_ extends PlatformRef {
} }
bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> { bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>): Promise<NgModuleRef<M>> {
return this._bootstrapModuleFactoryWithZone(moduleFactory, null); return this._bootstrapModuleFactoryWithZone(moduleFactory);
} }
private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone: NgZone): private _bootstrapModuleFactoryWithZone<M>(moduleFactory: NgModuleFactory<M>, ngZone?: NgZone):
Promise<NgModuleRef<M>> { Promise<NgModuleRef<M>> {
// Note: We need to create the NgZone _before_ we instantiate the module, // Note: We need to create the NgZone _before_ we instantiate the module,
// as instantiating the module creates some providers eagerly. // as instantiating the module creates some providers eagerly.
@ -299,7 +299,7 @@ export class PlatformRef_ extends PlatformRef {
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?'); throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
} }
moduleRef.onDestroy(() => remove(this._modules, moduleRef)); moduleRef.onDestroy(() => remove(this._modules, moduleRef));
ngZone.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }}); ngZone !.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
return _callAndReportToErrorHandler(exceptionHandler, () => { return _callAndReportToErrorHandler(exceptionHandler, () => {
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
return initStatus.donePromise.then(() => { return initStatus.donePromise.then(() => {
@ -312,12 +312,12 @@ export class PlatformRef_ extends PlatformRef {
bootstrapModule<M>(moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = []): bootstrapModule<M>(moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = []):
Promise<NgModuleRef<M>> { Promise<NgModuleRef<M>> {
return this._bootstrapModuleWithZone(moduleType, compilerOptions, null); return this._bootstrapModuleWithZone(moduleType, compilerOptions);
} }
private _bootstrapModuleWithZone<M>( private _bootstrapModuleWithZone<M>(
moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [], moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [],
ngZone: NgZone = null): Promise<NgModuleRef<M>> { ngZone?: NgZone): Promise<NgModuleRef<M>> {
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory); const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
const compiler = compilerFactory.createCompiler( const compiler = compilerFactory.createCompiler(
Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]); Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]);
@ -500,7 +500,8 @@ export class ApplicationRef_ extends ApplicationRef {
if (componentOrFactory instanceof ComponentFactory) { if (componentOrFactory instanceof ComponentFactory) {
componentFactory = componentOrFactory; componentFactory = componentOrFactory;
} else { } else {
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory); componentFactory =
this._componentFactoryResolver.resolveComponentFactory(componentOrFactory) !;
} }
this._rootComponentTypes.push(componentFactory.componentType); this._rootComponentTypes.push(componentFactory.componentType);

View File

@ -15,16 +15,15 @@ import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFac
export class DefaultIterableDifferFactory implements IterableDifferFactory { export class DefaultIterableDifferFactory implements IterableDifferFactory {
constructor() {} constructor() {}
supports(obj: Object): boolean { return isListLikeIterable(obj); } supports(obj: Object|null|undefined): boolean { return isListLikeIterable(obj); }
create<V>(trackByFn?: TrackByFunction<any>): DefaultIterableDiffer<V>; create<V>(trackByFn?: TrackByFunction<V>): DefaultIterableDiffer<V>;
/** /**
* @deprecated v4.0.0 - ChangeDetectorRef is not used and is no longer a parameter * @deprecated v4.0.0 - ChangeDetectorRef is not used and is no longer a parameter
*/ */
create<V>( create<V>(cdRefOrTrackBy?: ChangeDetectorRef|TrackByFunction<V>, trackByFn?: TrackByFunction<V>):
cdRefOrTrackBy?: ChangeDetectorRef|TrackByFunction<any>, DefaultIterableDiffer<V> {
trackByFn?: TrackByFunction<any>): DefaultIterableDiffer<V> {
return new DefaultIterableDiffer<V>(trackByFn || <TrackByFunction<any>>cdRefOrTrackBy); return new DefaultIterableDiffer<V>(trackByFn || <TrackByFunction<any>>cdRefOrTrackBy);
} }
} }
@ -35,27 +34,28 @@ const trackByIdentity = (index: number, item: any) => item;
* @deprecated v4.0.0 - Should not be part of public API. * @deprecated v4.0.0 - Should not be part of public API.
*/ */
export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> { export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChanges<V> {
private _length: number = null; private _length: number = 0;
private _collection: NgIterable<V> = null; private _collection: NgIterable<V>|null = null;
// Keeps track of the used records at any point in time (during & across `_check()` calls) // Keeps track of the used records at any point in time (during & across `_check()` calls)
private _linkedRecords: _DuplicateMap<V> = null; private _linkedRecords: _DuplicateMap<V>|null = null;
// Keeps track of the removed records at any point in time during `_check()` calls. // Keeps track of the removed records at any point in time during `_check()` calls.
private _unlinkedRecords: _DuplicateMap<V> = null; private _unlinkedRecords: _DuplicateMap<V>|null = null;
private _previousItHead: IterableChangeRecord_<V> = null; private _previousItHead: IterableChangeRecord_<V>|null = null;
private _itHead: IterableChangeRecord_<V> = null; private _itHead: IterableChangeRecord_<V>|null = null;
private _itTail: IterableChangeRecord_<V> = null; private _itTail: IterableChangeRecord_<V>|null = null;
private _additionsHead: IterableChangeRecord_<V> = null; private _additionsHead: IterableChangeRecord_<V>|null = null;
private _additionsTail: IterableChangeRecord_<V> = null; private _additionsTail: IterableChangeRecord_<V>|null = null;
private _movesHead: IterableChangeRecord_<V> = null; private _movesHead: IterableChangeRecord_<V>|null = null;
private _movesTail: IterableChangeRecord_<V> = null; private _movesTail: IterableChangeRecord_<V>|null = null;
private _removalsHead: IterableChangeRecord_<V> = null; private _removalsHead: IterableChangeRecord_<V>|null = null;
private _removalsTail: IterableChangeRecord_<V> = null; private _removalsTail: IterableChangeRecord_<V>|null = null;
// Keeps track of records where custom track by is the same, but item identity has changed // Keeps track of records where custom track by is the same, but item identity has changed
private _identityChangesHead: IterableChangeRecord_<V> = null; private _identityChangesHead: IterableChangeRecord_<V>|null = null;
private _identityChangesTail: IterableChangeRecord_<V> = null; private _identityChangesTail: IterableChangeRecord_<V>|null = null;
private _trackByFn: TrackByFunction<V>
constructor(private _trackByFn?: TrackByFunction<V>) { constructor(trackByFn?: TrackByFunction<V>) {
this._trackByFn = this._trackByFn || trackByIdentity; this._trackByFn = trackByFn || trackByIdentity;
} }
get collection() { return this._collection; } get collection() { return this._collection; }
@ -63,25 +63,27 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
get length(): number { return this._length; } get length(): number { return this._length; }
forEachItem(fn: (record: IterableChangeRecord_<V>) => void) { forEachItem(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._itHead; record !== null; record = record._next) { for (record = this._itHead; record !== null; record = record._next) {
fn(record); fn(record);
} }
} }
forEachOperation( forEachOperation(
fn: (item: IterableChangeRecord_<V>, previousIndex: number, currentIndex: number) => void) { fn: (item: IterableChangeRecord<V>, previousIndex: number|null, currentIndex: number|null) =>
void) {
let nextIt = this._itHead; let nextIt = this._itHead;
let nextRemove = this._removalsHead; let nextRemove = this._removalsHead;
let addRemoveOffset = 0; let addRemoveOffset = 0;
let moveOffsets: number[] = null; let moveOffsets: number[]|null = null;
while (nextIt || nextRemove) { while (nextIt || nextRemove) {
// Figure out which is the next record to process // Figure out which is the next record to process
// Order: remove, add, move // Order: remove, add, move
const record = !nextRemove || const record: IterableChangeRecord<V> = !nextRemove ||
nextIt && nextIt &&
nextIt.currentIndex < getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ? nextIt.currentIndex ! <
nextIt : getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
nextIt ! :
nextRemove; nextRemove;
const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets); const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
const currentIndex = record.currentIndex; const currentIndex = record.currentIndex;
@ -91,14 +93,14 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
addRemoveOffset--; addRemoveOffset--;
nextRemove = nextRemove._nextRemoved; nextRemove = nextRemove._nextRemoved;
} else { } else {
nextIt = nextIt._next; nextIt = nextIt !._next;
if (record.previousIndex == null) { if (record.previousIndex == null) {
addRemoveOffset++; addRemoveOffset++;
} else { } else {
// INVARIANT: currentIndex < previousIndex // INVARIANT: currentIndex < previousIndex
if (!moveOffsets) moveOffsets = []; if (!moveOffsets) moveOffsets = [];
const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset; const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
const localCurrentIndex = currentIndex - addRemoveOffset; const localCurrentIndex = currentIndex ! - addRemoveOffset;
if (localMovePreviousIndex != localCurrentIndex) { if (localMovePreviousIndex != localCurrentIndex) {
for (let i = 0; i < localMovePreviousIndex; i++) { for (let i = 0; i < localMovePreviousIndex; i++) {
const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0); const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
@ -120,44 +122,45 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
} }
forEachPreviousItem(fn: (record: IterableChangeRecord_<V>) => void) { forEachPreviousItem(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._previousItHead; record !== null; record = record._nextPrevious) { for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
fn(record); fn(record);
} }
} }
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void) { forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._additionsHead; record !== null; record = record._nextAdded) { for (record = this._additionsHead; record !== null; record = record._nextAdded) {
fn(record); fn(record);
} }
} }
forEachMovedItem(fn: (record: IterableChangeRecord_<V>) => void) { forEachMovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._movesHead; record !== null; record = record._nextMoved) { for (record = this._movesHead; record !== null; record = record._nextMoved) {
fn(record); fn(record);
} }
} }
forEachRemovedItem(fn: (record: IterableChangeRecord_<V>) => void) { forEachRemovedItem(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._removalsHead; record !== null; record = record._nextRemoved) { for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
fn(record); fn(record);
} }
} }
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void) { forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) { for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
fn(record); fn(record);
} }
} }
diff(collection: NgIterable<V>): DefaultIterableDiffer<V> { diff(collection: NgIterable<V>): DefaultIterableDiffer<V>|null {
if (collection == null) collection = []; if (collection == null) collection = [];
if (!isListLikeIterable(collection)) { if (!isListLikeIterable(collection)) {
throw new Error(`Error trying to diff '${collection}'`); throw new Error(
`Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed`);
} }
if (this.check(collection)) { if (this.check(collection)) {
@ -173,7 +176,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
check(collection: NgIterable<V>): boolean { check(collection: NgIterable<V>): boolean {
this._reset(); this._reset();
let record: IterableChangeRecord_<V> = this._itHead; let record: IterableChangeRecord_<V>|null = this._itHead;
let mayBeDirty: boolean = false; let mayBeDirty: boolean = false;
let index: number; let index: number;
let item: V; let item: V;
@ -240,8 +243,8 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
*/ */
_reset() { _reset() {
if (this.isDirty) { if (this.isDirty) {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
let nextRecord: IterableChangeRecord_<V>; let nextRecord: IterableChangeRecord_<V>|null;
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) { for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
record._nextPrevious = record._next; record._nextPrevious = record._next;
@ -275,15 +278,15 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
* *
* @internal * @internal
*/ */
_mismatch(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number): _mismatch(record: IterableChangeRecord_<V>|null, item: V, itemTrackBy: any, index: number):
IterableChangeRecord_<V> { IterableChangeRecord_<V> {
// The previous record after which we will append the current one. // The previous record after which we will append the current one.
let previousRecord: IterableChangeRecord_<V>; let previousRecord: IterableChangeRecord_<V>;
if (record === null) { if (record === null) {
previousRecord = this._itTail; previousRecord = this._itTail !;
} else { } else {
previousRecord = record._prev; previousRecord = record._prev !;
// Remove the record from the collection since we know it does not match the item. // Remove the record from the collection since we know it does not match the item.
this._remove(record); this._remove(record);
} }
@ -298,7 +301,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
this._moveAfter(record, previousRecord, index); this._moveAfter(record, previousRecord, index);
} else { } else {
// Never seen it, check evicted list. // Never seen it, check evicted list.
record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy); record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
if (record !== null) { if (record !== null) {
// It is an item which we have evicted earlier: reinsert it back into the list. // It is an item which we have evicted earlier: reinsert it back into the list.
// But first we need to check if identity changed, so we can update in view if necessary // But first we need to check if identity changed, so we can update in view if necessary
@ -343,10 +346,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
*/ */
_verifyReinsertion(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number): _verifyReinsertion(record: IterableChangeRecord_<V>, item: V, itemTrackBy: any, index: number):
IterableChangeRecord_<V> { IterableChangeRecord_<V> {
let reinsertRecord: IterableChangeRecord_<V> = let reinsertRecord: IterableChangeRecord_<V>|null =
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy); this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
if (reinsertRecord !== null) { if (reinsertRecord !== null) {
record = this._reinsertAfter(reinsertRecord, record._prev, index); record = this._reinsertAfter(reinsertRecord, record._prev !, index);
} else if (record.currentIndex != index) { } else if (record.currentIndex != index) {
record.currentIndex = index; record.currentIndex = index;
this._addToMoves(record, index); this._addToMoves(record, index);
@ -361,10 +364,10 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
* *
* @internal * @internal
*/ */
_truncate(record: IterableChangeRecord_<V>) { _truncate(record: IterableChangeRecord_<V>|null) {
// Anything after that needs to be removed; // Anything after that needs to be removed;
while (record !== null) { while (record !== null) {
const nextRecord: IterableChangeRecord_<V> = record._next; const nextRecord: IterableChangeRecord_<V>|null = record._next;
this._addToRemovals(this._unlink(record)); this._addToRemovals(this._unlink(record));
record = nextRecord; record = nextRecord;
} }
@ -451,7 +454,8 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
// assert(record._next === null); // assert(record._next === null);
// assert(record._prev === null); // assert(record._prev === null);
const next: IterableChangeRecord_<V> = prevRecord === null ? this._itHead : prevRecord._next; const next: IterableChangeRecord_<V>|null =
prevRecord === null ? this._itHead : prevRecord._next;
// todo(vicb) // todo(vicb)
// assert(next != record); // assert(next != record);
// assert(prevRecord != record); // assert(prevRecord != record);
@ -598,29 +602,29 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
* @stable * @stable
*/ */
export class IterableChangeRecord_<V> implements IterableChangeRecord<V> { export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
currentIndex: number = null; currentIndex: number|null = null;
previousIndex: number = null; previousIndex: number|null = null;
/** @internal */ /** @internal */
_nextPrevious: IterableChangeRecord_<V> = null; _nextPrevious: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_prev: IterableChangeRecord_<V> = null; _prev: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_next: IterableChangeRecord_<V> = null; _next: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_prevDup: IterableChangeRecord_<V> = null; _prevDup: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_nextDup: IterableChangeRecord_<V> = null; _nextDup: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_prevRemoved: IterableChangeRecord_<V> = null; _prevRemoved: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_nextRemoved: IterableChangeRecord_<V> = null; _nextRemoved: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_nextAdded: IterableChangeRecord_<V> = null; _nextAdded: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_nextMoved: IterableChangeRecord_<V> = null; _nextMoved: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_nextIdentityChange: IterableChangeRecord_<V> = null; _nextIdentityChange: IterableChangeRecord_<V>|null = null;
constructor(public item: V, public trackById: any) {} constructor(public item: V, public trackById: any) {}
@ -635,9 +639,9 @@ export class IterableChangeRecord_<V> implements IterableChangeRecord<V> {
// A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item // A linked list of CollectionChangeRecords with the same IterableChangeRecord_.item
class _DuplicateItemRecordList<V> { class _DuplicateItemRecordList<V> {
/** @internal */ /** @internal */
_head: IterableChangeRecord_<V> = null; _head: IterableChangeRecord_<V>|null = null;
/** @internal */ /** @internal */
_tail: IterableChangeRecord_<V> = null; _tail: IterableChangeRecord_<V>|null = null;
/** /**
* Append the record to the list of duplicates. * Append the record to the list of duplicates.
@ -653,7 +657,7 @@ class _DuplicateItemRecordList<V> {
// todo(vicb) // todo(vicb)
// assert(record.item == _head.item || // assert(record.item == _head.item ||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN); // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
this._tail._nextDup = record; this._tail !._nextDup = record;
record._prevDup = this._tail; record._prevDup = this._tail;
record._nextDup = null; record._nextDup = null;
this._tail = record; this._tail = record;
@ -662,8 +666,8 @@ class _DuplicateItemRecordList<V> {
// Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
// IterableChangeRecord_.currentIndex >= afterIndex // IterableChangeRecord_.currentIndex >= afterIndex
get(trackById: any, afterIndex: number): IterableChangeRecord_<V> { get(trackById: any, afterIndex: number|null): IterableChangeRecord_<V>|null {
let record: IterableChangeRecord_<V>; let record: IterableChangeRecord_<V>|null;
for (record = this._head; record !== null; record = record._nextDup) { for (record = this._head; record !== null; record = record._nextDup) {
if ((afterIndex === null || afterIndex < record.currentIndex) && if ((afterIndex === null || afterIndex < record.currentIndex) &&
looseIdentical(record.trackById, trackById)) { looseIdentical(record.trackById, trackById)) {
@ -688,8 +692,8 @@ class _DuplicateItemRecordList<V> {
// return false; // return false;
//}); //});
const prev: IterableChangeRecord_<V> = record._prevDup; const prev: IterableChangeRecord_<V>|null = record._prevDup;
const next: IterableChangeRecord_<V> = record._nextDup; const next: IterableChangeRecord_<V>|null = record._nextDup;
if (prev === null) { if (prev === null) {
this._head = next; this._head = next;
} else { } else {
@ -725,7 +729,7 @@ class _DuplicateMap<V> {
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
* have any more `a`s needs to return the last `a` not the first or second. * have any more `a`s needs to return the last `a` not the first or second.
*/ */
get(trackById: any, afterIndex: number = null): IterableChangeRecord_<V> { get(trackById: any, afterIndex: number|null): IterableChangeRecord_<V>|null {
const key = trackById; const key = trackById;
const recordList = this.map.get(key); const recordList = this.map.get(key);
return recordList ? recordList.get(trackById, afterIndex) : null; return recordList ? recordList.get(trackById, afterIndex) : null;
@ -738,7 +742,7 @@ class _DuplicateMap<V> {
*/ */
remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> { remove(record: IterableChangeRecord_<V>): IterableChangeRecord_<V> {
const key = record.trackById; const key = record.trackById;
const recordList: _DuplicateItemRecordList<V> = this.map.get(key); const recordList: _DuplicateItemRecordList<V> = this.map.get(key) !;
// Remove the list of duplicates when it gets empty // Remove the list of duplicates when it gets empty
if (recordList.remove(record)) { if (recordList.remove(record)) {
this.map.delete(key); this.map.delete(key);
@ -753,7 +757,8 @@ class _DuplicateMap<V> {
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; } toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
} }
function getPreviousIndex(item: any, addRemoveOffset: number, moveOffsets: number[]): number { function getPreviousIndex(
item: any, addRemoveOffset: number, moveOffsets: number[] | null): number {
const previousIndex = item.previousIndex; const previousIndex = item.previousIndex;
if (previousIndex === null) return previousIndex; if (previousIndex === null) return previousIndex;
let moveOffset = 0; let moveOffset = 0;

View File

@ -9,7 +9,6 @@
import {looseIdentical, stringify} from '../../util'; import {looseIdentical, stringify} from '../../util';
import {isJsObject} from '../change_detection_util'; import {isJsObject} from '../change_detection_util';
import {ChangeDetectorRef} from '../change_detector_ref'; import {ChangeDetectorRef} from '../change_detector_ref';
import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs'; import {KeyValueChangeRecord, KeyValueChanges, KeyValueDiffer, KeyValueDifferFactory} from './keyvalue_differs';
@ -28,15 +27,17 @@ export class DefaultKeyValueDifferFactory<K, V> implements KeyValueDifferFactory
} }
export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyValueChanges<K, V> { export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyValueChanges<K, V> {
private _records: Map<K, V> = new Map<K, V>(); private _records = new Map<K, KeyValueChangeRecord_<K, V>>();
private _mapHead: KeyValueChangeRecord_<K, V> = null; private _mapHead: KeyValueChangeRecord_<K, V>|null = null;
private _previousMapHead: KeyValueChangeRecord_<K, V> = null; // _appendAfter is used in the check loop
private _changesHead: KeyValueChangeRecord_<K, V> = null; private _appendAfter: KeyValueChangeRecord_<K, V>|null = null;
private _changesTail: KeyValueChangeRecord_<K, V> = null; private _previousMapHead: KeyValueChangeRecord_<K, V>|null = null;
private _additionsHead: KeyValueChangeRecord_<K, V> = null; private _changesHead: KeyValueChangeRecord_<K, V>|null = null;
private _additionsTail: KeyValueChangeRecord_<K, V> = null; private _changesTail: KeyValueChangeRecord_<K, V>|null = null;
private _removalsHead: KeyValueChangeRecord_<K, V> = null; private _additionsHead: KeyValueChangeRecord_<K, V>|null = null;
private _removalsTail: KeyValueChangeRecord_<K, V> = null; private _additionsTail: KeyValueChangeRecord_<K, V>|null = null;
private _removalsHead: KeyValueChangeRecord_<K, V>|null = null;
private _removalsTail: KeyValueChangeRecord_<K, V>|null = null;
get isDirty(): boolean { get isDirty(): boolean {
return this._additionsHead !== null || this._changesHead !== null || return this._additionsHead !== null || this._changesHead !== null ||
@ -44,45 +45,46 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
} }
forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void) { forEachItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._mapHead; record !== null; record = record._next) { for (record = this._mapHead; record !== null; record = record._next) {
fn(record); fn(record);
} }
} }
forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void) { forEachPreviousItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) { for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
fn(record); fn(record);
} }
} }
forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) { forEachChangedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._changesHead; record !== null; record = record._nextChanged) { for (record = this._changesHead; record !== null; record = record._nextChanged) {
fn(record); fn(record);
} }
} }
forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) { forEachAddedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._additionsHead; record !== null; record = record._nextAdded) { for (record = this._additionsHead; record !== null; record = record._nextAdded) {
fn(record); fn(record);
} }
} }
forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) { forEachRemovedItem(fn: (r: KeyValueChangeRecord<K, V>) => void) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._removalsHead; record !== null; record = record._nextRemoved) { for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
fn(record); fn(record);
} }
} }
diff(map: Map<any, any>|{[k: string]: any}): any { diff(map?: Map<any, any>|{[k: string]: any}|null): any {
if (!map) { if (!map) {
map = new Map(); map = new Map();
} else if (!(map instanceof Map || isJsObject(map))) { } else if (!(map instanceof Map || isJsObject(map))) {
throw new Error(`Error trying to diff '${map}'`); throw new Error(
`Error trying to diff '${stringify(map)}'. Only maps and objects are allowed`);
} }
return this.check(map) ? this : null; return this.check(map) ? this : null;
@ -90,67 +92,130 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
onDestroy() {} onDestroy() {}
/**
* Check the current state of the map vs the previous.
* The algorithm is optimised for when the keys do no change.
*/
check(map: Map<any, any>|{[k: string]: any}): boolean { check(map: Map<any, any>|{[k: string]: any}): boolean {
this._reset(); this._reset();
const records = this._records;
let oldSeqRecord: KeyValueChangeRecord_<K, V> = this._mapHead; let insertBefore = this._mapHead;
let lastOldSeqRecord: KeyValueChangeRecord_<K, V> = null; this._appendAfter = null;
let lastNewSeqRecord: KeyValueChangeRecord_<K, V> = null;
let seqChanged: boolean = false;
this._forEach(map, (value: any, key: any) => { this._forEach(map, (value: any, key: any) => {
let newSeqRecord: any; if (insertBefore && insertBefore.key === key) {
if (oldSeqRecord && key === oldSeqRecord.key) { this._maybeAddToChanges(insertBefore, value);
newSeqRecord = oldSeqRecord; this._appendAfter = insertBefore;
this._maybeAddToChanges(newSeqRecord, value); insertBefore = insertBefore._next;
} else { } else {
seqChanged = true; const record = this._getOrCreateRecordForKey(key, value);
if (oldSeqRecord !== null) { insertBefore = this._insertBeforeOrAppend(insertBefore, record);
this._removeFromSeq(lastOldSeqRecord, oldSeqRecord);
this._addToRemovals(oldSeqRecord);
} }
if (records.has(key)) { });
newSeqRecord = records.get(key);
this._maybeAddToChanges(newSeqRecord, value); // Items remaining at the end of the list have been deleted
} else { if (insertBefore) {
newSeqRecord = new KeyValueChangeRecord_<K, V>(key); if (insertBefore._prev) {
records.set(key, newSeqRecord); insertBefore._prev._next = null;
newSeqRecord.currentValue = value; }
this._addToAdditions(newSeqRecord);
this._removalsHead = insertBefore;
this._removalsTail = insertBefore;
for (let record = insertBefore; record !== null; record = record._nextRemoved) {
if (record === this._mapHead) {
this._mapHead = null;
}
this._records.delete(record.key);
record._nextRemoved = record._next;
record.previousValue = record.currentValue;
record.currentValue = null;
record._prev = null;
record._next = null;
} }
} }
if (seqChanged) {
if (this._isInRemovals(newSeqRecord)) {
this._removeFromRemovals(newSeqRecord);
}
if (lastNewSeqRecord == null) {
this._mapHead = newSeqRecord;
} else {
lastNewSeqRecord._next = newSeqRecord;
}
}
lastOldSeqRecord = oldSeqRecord;
lastNewSeqRecord = newSeqRecord;
oldSeqRecord = oldSeqRecord && oldSeqRecord._next;
});
this._truncate(lastOldSeqRecord, oldSeqRecord);
return this.isDirty; return this.isDirty;
} }
/**
* Inserts a record before `before` or append at the end of the list when `before` is null.
*
* Notes:
* - This method appends at `this._appendAfter`,
* - This method updates `this._appendAfter`,
* - The return value is the new value for the insertion pointer.
*/
private _insertBeforeOrAppend(
before: KeyValueChangeRecord_<K, V>,
record: KeyValueChangeRecord_<K, V>): KeyValueChangeRecord_<K, V> {
if (before) {
const prev = before._prev;
record._next = before;
record._prev = prev;
before._prev = record;
if (prev) {
prev._next = record;
}
if (before === this._mapHead) {
this._mapHead = record;
}
this._appendAfter = before;
return before;
}
if (this._appendAfter) {
this._appendAfter._next = record;
record._prev = this._appendAfter;
} else {
this._mapHead = record;
}
this._appendAfter = record;
return null;
}
private _getOrCreateRecordForKey(key: K, value: V): KeyValueChangeRecord_<K, V> {
if (this._records.has(key)) {
const record = this._records.get(key);
this._maybeAddToChanges(record, value);
const prev = record._prev;
const next = record._next;
if (prev) {
prev._next = next;
}
if (next) {
next._prev = prev;
}
record._next = null;
record._prev = null;
return record;
}
const record = new KeyValueChangeRecord_<K, V>(key);
this._records.set(key, record);
record.currentValue = value;
this._addToAdditions(record);
return record;
}
/** @internal */ /** @internal */
_reset() { _reset() {
if (this.isDirty) { if (this.isDirty) {
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
// Record the state of the mapping // let `_previousMapHead` contain the state of the map before the changes
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) { this._previousMapHead = this._mapHead;
for (record = this._previousMapHead; record !== null; record = record._next) {
record._nextPrevious = record._next; record._nextPrevious = record._next;
} }
// Update `record.previousValue` with the value of the item before the changes
// We need to update all changed items (that's those which have been added and changed)
for (record = this._changesHead; record !== null; record = record._nextChanged) { for (record = this._changesHead; record !== null; record = record._nextChanged) {
record.previousValue = record.currentValue; record.previousValue = record.currentValue;
} }
for (record = this._additionsHead; record != null; record = record._nextAdded) { for (record = this._additionsHead; record != null; record = record._nextAdded) {
record.previousValue = record.currentValue; record.previousValue = record.currentValue;
} }
@ -161,27 +226,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
} }
} }
private _truncate(lastRecord: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) { // Add the record or a given key to the list of changes only when the value has actually changed
while (record !== null) {
if (lastRecord === null) {
this._mapHead = null;
} else {
lastRecord._next = null;
}
const nextRecord = record._next;
this._addToRemovals(record);
lastRecord = record;
record = nextRecord;
}
for (let rec: KeyValueChangeRecord_<K, V> = this._removalsHead; rec !== null;
rec = rec._nextRemoved) {
rec.previousValue = rec.currentValue;
rec.currentValue = null;
this._records.delete(rec.key);
}
}
private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void { private _maybeAddToChanges(record: KeyValueChangeRecord_<K, V>, newValue: any): void {
if (!looseIdentical(newValue, record.currentValue)) { if (!looseIdentical(newValue, record.currentValue)) {
record.previousValue = record.currentValue; record.previousValue = record.currentValue;
@ -190,52 +235,11 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
} }
} }
private _isInRemovals(record: KeyValueChangeRecord_<K, V>) {
return record === this._removalsHead || record._nextRemoved !== null ||
record._prevRemoved !== null;
}
private _addToRemovals(record: KeyValueChangeRecord_<K, V>) {
if (this._removalsHead === null) {
this._removalsHead = this._removalsTail = record;
} else {
this._removalsTail._nextRemoved = record;
record._prevRemoved = this._removalsTail;
this._removalsTail = record;
}
}
private _removeFromSeq(prev: KeyValueChangeRecord_<K, V>, record: KeyValueChangeRecord_<K, V>) {
const next = record._next;
if (prev === null) {
this._mapHead = next;
} else {
prev._next = next;
}
record._next = null;
}
private _removeFromRemovals(record: KeyValueChangeRecord_<K, V>) {
const prev = record._prevRemoved;
const next = record._nextRemoved;
if (prev === null) {
this._removalsHead = next;
} else {
prev._nextRemoved = next;
}
if (next === null) {
this._removalsTail = prev;
} else {
next._prevRemoved = prev;
}
record._prevRemoved = record._nextRemoved = null;
}
private _addToAdditions(record: KeyValueChangeRecord_<K, V>) { private _addToAdditions(record: KeyValueChangeRecord_<K, V>) {
if (this._additionsHead === null) { if (this._additionsHead === null) {
this._additionsHead = this._additionsTail = record; this._additionsHead = this._additionsTail = record;
} else { } else {
this._additionsTail._nextAdded = record; this._additionsTail !._nextAdded = record;
this._additionsTail = record; this._additionsTail = record;
} }
} }
@ -244,7 +248,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
if (this._changesHead === null) { if (this._changesHead === null) {
this._changesHead = this._changesTail = record; this._changesHead = this._changesTail = record;
} else { } else {
this._changesTail._nextChanged = record; this._changesTail !._nextChanged = record;
this._changesTail = record; this._changesTail = record;
} }
} }
@ -255,7 +259,7 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
const changes: any[] = []; const changes: any[] = [];
const additions: any[] = []; const additions: any[] = [];
const removals: any[] = []; const removals: any[] = [];
let record: KeyValueChangeRecord_<K, V>; let record: KeyValueChangeRecord_<K, V>|null;
for (record = this._mapHead; record !== null; record = record._next) { for (record = this._mapHead; record !== null; record = record._next) {
items.push(stringify(record)); items.push(stringify(record));
@ -295,21 +299,21 @@ export class DefaultKeyValueDiffer<K, V> implements KeyValueDiffer<K, V>, KeyVal
* @stable * @stable
*/ */
class KeyValueChangeRecord_<K, V> implements KeyValueChangeRecord<K, V> { class KeyValueChangeRecord_<K, V> implements KeyValueChangeRecord<K, V> {
previousValue: V = null; previousValue: V|null = null;
currentValue: V = null; currentValue: V|null = null;
/** @internal */ /** @internal */
_nextPrevious: KeyValueChangeRecord_<K, V> = null; _nextPrevious: KeyValueChangeRecord_<K, V>|null = null;
/** @internal */ /** @internal */
_next: KeyValueChangeRecord_<K, V> = null; _next: KeyValueChangeRecord_<K, V>|null = null;
/** @internal */ /** @internal */
_nextAdded: KeyValueChangeRecord_<K, V> = null; _prev: KeyValueChangeRecord_<K, V>|null = null;
/** @internal */ /** @internal */
_nextRemoved: KeyValueChangeRecord_<K, V> = null; _nextAdded: KeyValueChangeRecord_<K, V>|null = null;
/** @internal */ /** @internal */
_prevRemoved: KeyValueChangeRecord_<K, V> = null; _nextRemoved: KeyValueChangeRecord_<K, V>|null = null;
/** @internal */ /** @internal */
_nextChanged: KeyValueChangeRecord_<K, V> = null; _nextChanged: KeyValueChangeRecord_<K, V>|null = null;
constructor(public key: K) {} constructor(public key: K) {}

View File

@ -30,7 +30,7 @@ export interface IterableDiffer<V> {
* @returns an object describing the difference. The return value is only valid until the next * @returns an object describing the difference. The return value is only valid until the next
* `diff()` invocation. * `diff()` invocation.
*/ */
diff(object: NgIterable<V>): IterableChanges<V>; diff(object: NgIterable<V>): IterableChanges<V>|null;
} }
/** /**
@ -92,20 +92,16 @@ export interface IterableChanges<V> {
*/ */
export interface IterableChangeRecord<V> { export interface IterableChangeRecord<V> {
/** Current index of the item in `Iterable` or null if removed. */ /** Current index of the item in `Iterable` or null if removed. */
// TODO(TS2.1): make readonly once we move to TS v2.1 readonly currentIndex: number|null;
/* readonly */ currentIndex: number;
/** Previous index of the item in `Iterable` or null if added. */ /** Previous index of the item in `Iterable` or null if added. */
// TODO(TS2.1): make readonly once we move to TS v2.1 readonly previousIndex: number|null;
/* readonly */ previousIndex: number;
/** The item. */ /** The item. */
// TODO(TS2.1): make readonly once we move to TS v2.1 readonly item: V;
/* readonly */ item: V;
/** Track by identity as computed by the `trackByFn`. */ /** Track by identity as computed by the `trackByFn`. */
// TODO(TS2.1): make readonly once we move to TS v2.1 readonly trackById: any;
/* readonly */ trackById: any;
} }
/** /**

View File

@ -82,17 +82,17 @@ export interface KeyValueChangeRecord<K, V> {
/** /**
* Current key in the Map. * Current key in the Map.
*/ */
/* readonly */ key: K; readonly key: K;
/** /**
* Current value for the key or `undefined` if removed. * Current value for the key or `null` if removed.
*/ */
/* readonly */ currentValue: V; readonly currentValue: V|null;
/** /**
* Previous value for the key or `undefined` if added. * Previous value for the key or `null` if added.
*/ */
/* readonly */ previousValue: V; readonly previousValue: V|null;
} }
/** /**

View File

@ -25,5 +25,5 @@ export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn}
export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api'; export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api';
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util'; export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
export {makeDecorator as ɵmakeDecorator} from './util/decorators'; export {makeDecorator as ɵmakeDecorator} from './util/decorators';
export {isObservable as ɵisObservable, isPromise as ɵisPromise, merge as ɵmerge} from './util/lang'; export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider'; export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';

View File

@ -17,9 +17,9 @@ export class EventListener { constructor(public name: string, public callback: F
export class DebugNode { export class DebugNode {
nativeNode: any; nativeNode: any;
listeners: EventListener[]; listeners: EventListener[];
parent: DebugElement; parent: DebugElement|null;
constructor(nativeNode: any, parent: DebugNode, private _debugContext: DebugContext) { constructor(nativeNode: any, parent: DebugNode|null, private _debugContext: DebugContext) {
this.nativeNode = nativeNode; this.nativeNode = nativeNode;
if (parent && parent instanceof DebugElement) { if (parent && parent instanceof DebugElement) {
parent.addChild(this); parent.addChild(this);
@ -29,19 +29,15 @@ export class DebugNode {
this.listeners = []; this.listeners = [];
} }
get injector(): Injector { return this._debugContext ? this._debugContext.injector : null; } get injector(): Injector { return this._debugContext.injector; }
get componentInstance(): any { return this._debugContext ? this._debugContext.component : null; } get componentInstance(): any { return this._debugContext.component; }
get context(): any { return this._debugContext ? this._debugContext.context : null; } get context(): any { return this._debugContext.context; }
get references(): {[key: string]: any} { get references(): {[key: string]: any} { return this._debugContext.references; }
return this._debugContext ? this._debugContext.references : null;
}
get providerTokens(): any[] { get providerTokens(): any[] { return this._debugContext.providerTokens; }
return this._debugContext ? this._debugContext.providerTokens : null;
}
/** /**
* @deprecated since v4 * @deprecated since v4
@ -55,9 +51,9 @@ export class DebugNode {
export class DebugElement extends DebugNode { export class DebugElement extends DebugNode {
name: string; name: string;
properties: {[key: string]: any}; properties: {[key: string]: any};
attributes: {[key: string]: string}; attributes: {[key: string]: string | null};
classes: {[key: string]: boolean}; classes: {[key: string]: boolean};
styles: {[key: string]: string}; styles: {[key: string]: string | null};
childNodes: DebugNode[]; childNodes: DebugNode[];
nativeElement: any; nativeElement: any;
@ -181,8 +177,8 @@ const _nativeNodeToDebugNode = new Map<any, DebugNode>();
/** /**
* @experimental * @experimental
*/ */
export function getDebugNode(nativeNode: any): DebugNode { export function getDebugNode(nativeNode: any): DebugNode|null {
return _nativeNodeToDebugNode.get(nativeNode); return _nativeNodeToDebugNode.get(nativeNode) || null;
} }
export function getAllDebugNodes(): DebugNode[] { export function getAllDebugNodes(): DebugNode[] {
@ -203,4 +199,4 @@ export function removeDebugNodeFromIndex(node: DebugNode) {
* *
* @experimental All debugging apis are currently experimental. * @experimental All debugging apis are currently experimental.
*/ */
export interface Predicate<T> { (value: T, index?: number, array?: T[]): boolean; } export interface Predicate<T> { (value: T): boolean; }

View File

@ -42,8 +42,8 @@ export class OpaqueToken {
* runtime representation) such as when injecting an interface, callable type, array or * runtime representation) such as when injecting an interface, callable type, array or
* parametrized type. * parametrized type.
* *
* `InjectionToken` is parametrize on `T` which is the type of object which will be returned by the * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
* `Injector`. This provides additional level of type safety. * the `Injector`. This provides additional level of type safety.
* *
* ``` * ```
* interface MyInterface {...} * interface MyInterface {...}
@ -53,7 +53,7 @@ export class OpaqueToken {
* *
* ### Example * ### Example
* *
* {@example core/di/ts/injector_spec.ts region='Injector'} * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
* *
* @stable * @stable
*/ */

View File

@ -56,7 +56,7 @@ export abstract class Injector {
*/ */
abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T; abstract get<T>(token: Type<T>|InjectionToken<T>, notFoundValue?: T): T;
/** /**
* @deprecated from v4.0.0 use Type<T> or InjectToken<T> * @deprecated from v4.0.0 use Type<T> or InjectionToken<T>
* @suppress {duplicate} * @suppress {duplicate}
*/ */
abstract get(token: any, notFoundValue?: any): any; abstract get(token: any, notFoundValue?: any): any;

View File

@ -115,7 +115,7 @@ export abstract class ReflectiveInjector implements Injector {
* because it needs to resolve the passed-in providers first. * because it needs to resolve the passed-in providers first.
* See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}. * See {@link Injector#resolve} and {@link Injector#fromResolvedProviders}.
*/ */
static resolveAndCreate(providers: Provider[], parent: Injector = null): ReflectiveInjector { static resolveAndCreate(providers: Provider[], parent?: Injector): ReflectiveInjector {
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent); return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
} }
@ -143,7 +143,7 @@ export abstract class ReflectiveInjector implements Injector {
* ``` * ```
* @experimental * @experimental
*/ */
static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent: Injector = null): static fromResolvedProviders(providers: ResolvedReflectiveProvider[], parent?: Injector):
ReflectiveInjector { ReflectiveInjector {
return new ReflectiveInjector_(providers, parent); return new ReflectiveInjector_(providers, parent);
} }
@ -163,7 +163,7 @@ export abstract class ReflectiveInjector implements Injector {
* expect(child.parent).toBe(parent); * expect(child.parent).toBe(parent);
* ``` * ```
*/ */
abstract get parent(): Injector; abstract get parent(): Injector|null;
/** /**
* Resolves an array of providers and creates a child injector from those providers. * Resolves an array of providers and creates a child injector from those providers.
@ -282,16 +282,16 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
/** @internal */ /** @internal */
public _providers: ResolvedReflectiveProvider[]; public _providers: ResolvedReflectiveProvider[];
/** @internal */ /** @internal */
public _parent: Injector; public _parent: Injector|null;
keyIds: number[]; keyIds: number[];
objs: any[]; objs: any[];
/** /**
* Private * Private
*/ */
constructor(_providers: ResolvedReflectiveProvider[], _parent: Injector = null) { constructor(_providers: ResolvedReflectiveProvider[], _parent?: Injector) {
this._providers = _providers; this._providers = _providers;
this._parent = _parent; this._parent = _parent || null;
const len = _providers.length; const len = _providers.length;
@ -308,7 +308,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
return this._getByKey(ReflectiveKey.get(token), null, notFoundValue); return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
} }
get parent(): Injector { return this._parent; } get parent(): Injector|null { return this._parent; }
resolveAndCreateChild(providers: Provider[]): ReflectiveInjector { resolveAndCreateChild(providers: Provider[]): ReflectiveInjector {
const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
@ -388,7 +388,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND); return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
} }
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf, notFoundValue: any): any { private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any {
if (key === INJECTOR_KEY) { if (key === INJECTOR_KEY) {
return this; return this;
} }
@ -431,8 +431,8 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
} }
/** @internal */ /** @internal */
_getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf): any { _getByKeyDefault(key: ReflectiveKey, notFoundValue: any, visibility: Self|SkipSelf|null): any {
let inj: Injector; let inj: Injector|null;
if (visibility instanceof SkipSelf) { if (visibility instanceof SkipSelf) {
inj = this._parent; inj = this._parent;

View File

@ -64,7 +64,7 @@ export class KeyRegistry {
if (token instanceof ReflectiveKey) return token; if (token instanceof ReflectiveKey) return token;
if (this._allKeys.has(token)) { if (this._allKeys.has(token)) {
return this._allKeys.get(token); return this._allKeys.get(token) !;
} }
const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys); const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);

View File

@ -26,7 +26,7 @@ interface NormalizedProvider extends TypeProvider, ValueProvider, ClassProvider,
*/ */
export class ReflectiveDependency { export class ReflectiveDependency {
constructor( constructor(
public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf) {} public key: ReflectiveKey, public optional: boolean, public visibility: Self|SkipSelf|null) {}
static fromKey(key: ReflectiveKey): ReflectiveDependency { static fromKey(key: ReflectiveKey): ReflectiveDependency {
return new ReflectiveDependency(key, false, null); return new ReflectiveDependency(key, false, null);
@ -128,7 +128,8 @@ function resolveReflectiveFactory(provider: NormalizedProvider): ResolvedReflect
*/ */
function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider { function resolveReflectiveProvider(provider: NormalizedProvider): ResolvedReflectiveProvider {
return new ResolvedReflectiveProvider_( return new ResolvedReflectiveProvider_(
ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi); ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)],
provider.multi || false);
} }
/** /**
@ -198,7 +199,7 @@ function _normalizeProviders(providers: Provider[], res: Provider[]): Provider[]
} }
export function constructDependencies( export function constructDependencies(
typeOrFunc: any, dependencies: any[]): ReflectiveDependency[] { typeOrFunc: any, dependencies?: any[]): ReflectiveDependency[] {
if (!dependencies) { if (!dependencies) {
return _dependenciesFor(typeOrFunc); return _dependenciesFor(typeOrFunc);
} else { } else {
@ -230,7 +231,7 @@ function _extractToken(
} }
} }
let visibility: Self|SkipSelf = null; let visibility: Self|SkipSelf|null = null;
for (let i = 0; i < metadata.length; ++i) { for (let i = 0; i < metadata.length; ++i) {
const paramMetadata = metadata[i]; const paramMetadata = metadata[i];
@ -261,6 +262,6 @@ function _extractToken(
} }
function _createDependency( function _createDependency(
token: any, optional: boolean, visibility: Self | SkipSelf): ReflectiveDependency { token: any, optional: boolean, visibility: Self | SkipSelf | null): ReflectiveDependency {
return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility); return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
} }

View File

@ -56,7 +56,7 @@ export class CodegenComponentFactoryResolver implements ComponentFactoryResolver
resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> { resolveComponentFactory<T>(component: {new (...args: any[]): T}): ComponentFactory<T> {
let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component); let factory = this._factories.get(component) || this._parent.resolveComponentFactory(component);
return factory ? new ComponentFactoryBoundToModule(factory, this._ngModule) : null; return new ComponentFactoryBoundToModule(factory, this._ngModule);
} }
} }

View File

@ -60,7 +60,7 @@ export class NgModuleFactory<T> {
get moduleType(): Type<T> { return this._moduleType; } get moduleType(): Type<T> { return this._moduleType; }
create(parentInjector: Injector): NgModuleRef<T> { create(parentInjector: Injector|null): NgModuleRef<T> {
const instance = new this._injectorClass(parentInjector || Injector.NULL); const instance = new this._injectorClass(parentInjector || Injector.NULL);
instance.create(); instance.create();
return instance; return instance;

View File

@ -63,7 +63,9 @@ export class QueryList<T>/* implements Iterable<T> */ {
* See * See
* [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
*/ */
find(fn: (item: T, index: number, array: T[]) => boolean): T { return this._results.find(fn); } find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined {
return this._results.find(fn);
}
/** /**
* See * See

View File

@ -51,7 +51,7 @@ export abstract class ViewContainerRef {
/** /**
* Returns the {@link ViewRef} for the View located in this container at the specified index. * Returns the {@link ViewRef} for the View located in this container at the specified index.
*/ */
abstract get(index: number): ViewRef; abstract get(index: number): ViewRef|null;
/** /**
* Returns the number of Views currently attached to this container. * Returns the number of Views currently attached to this container.
@ -120,5 +120,5 @@ export abstract class ViewContainerRef {
* *
* If the `index` param is omitted, the last {@link ViewRef} is detached. * If the `index` param is omitted, the last {@link ViewRef} is detached.
*/ */
abstract detach(index?: number): ViewRef; abstract detach(index?: number): ViewRef|null;
} }

View File

@ -639,7 +639,7 @@ export interface Component extends Directive {
* ### DSL Animation Functions * ### DSL Animation Functions
* *
* Please visit each of the animation DSL functions listed below to gain a better understanding * Please visit each of the animation DSL functions listed below to gain a better understanding
* of how and why they are used for crafting animations in Angular2: * of how and why they are used for crafting animations in Angular:
* *
* - {@link trigger trigger()} * - {@link trigger trigger()}
* - {@link state state()} * - {@link state state()}

View File

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

View File

@ -53,7 +53,9 @@ export function createScope(signature: string, flags: any = null): any {
return events.createScope(signature, flags); return events.createScope(signature, flags);
} }
export function leave<T>(scope: Scope, returnValue?: T): T { export function leave<T>(scope: Scope): void;
export function leave<T>(scope: Scope, returnValue?: T): T;
export function leave<T>(scope: Scope, returnValue?: any): any {
trace.leaveScope(scope, returnValue); trace.leaveScope(scope, returnValue);
return returnValue; return returnValue;
} }

View File

@ -21,6 +21,6 @@ export interface PlatformReflectionCapabilities {
method(name: string): MethodFn; method(name: string): MethodFn;
importUri(type: Type<any>): string; importUri(type: Type<any>): string;
resourceUri(type: Type<any>): string; resourceUri(type: Type<any>): string;
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any; resolveIdentifier(name: string, moduleUrl: string, members: string[]|null, runtime: any): any;
resolveEnum(enumIdentifier: any, name: string): any; resolveEnum(enumIdentifier: any, name: string): any;
} }

View File

@ -14,8 +14,7 @@ import {GetterFn, MethodFn, SetterFn} from './types';
/** /**
* Attention: This regex has to hold even if the code is minified! * Attention: This regex has to hold even if the code is minified!
*/ */
export const DELEGATE_CTOR = export const DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*arguments\)/;
/^function\s+\S+\(\)\s*{\s*("use strict";)?\s*(return\s+)?(\S+\s+!==\s+null\s+&&\s+)?\S+\.apply\(this,\s*arguments\)/;
export class ReflectionCapabilities implements PlatformReflectionCapabilities { export class ReflectionCapabilities implements PlatformReflectionCapabilities {
private _reflect: any; private _reflect: any;
@ -54,7 +53,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
return result; return result;
} }
private _ownParameters(type: Type<any>, parentCtor: any): any[][] { private _ownParameters(type: Type<any>, parentCtor: any): any[][]|null {
// If we have no decorators, we only have function.length as metadata. // If we have no decorators, we only have function.length as metadata.
// In that case, to detect whether a child class declared an own constructor or not, // In that case, to detect whether a child class declared an own constructor or not,
// we need to look inside of that constructor to check whether it is // we need to look inside of that constructor to check whether it is
@ -115,7 +114,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
return parameters || []; return parameters || [];
} }
private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[] { private _ownAnnotations(typeOrFunc: Type<any>, parentCtor: any): any[]|null {
// Prefer the direct API. // Prefer the direct API.
if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) { if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) {
let annotations = (<any>typeOrFunc).annotations; let annotations = (<any>typeOrFunc).annotations;
@ -134,6 +133,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
if (this._reflect && this._reflect.getOwnMetadata) { if (this._reflect && this._reflect.getOwnMetadata) {
return this._reflect.getOwnMetadata('annotations', typeOrFunc); return this._reflect.getOwnMetadata('annotations', typeOrFunc);
} }
return null;
} }
annotations(typeOrFunc: Type<any>): any[] { annotations(typeOrFunc: Type<any>): any[] {
@ -146,7 +146,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
return parentAnnotations.concat(ownAnnotations); return parentAnnotations.concat(ownAnnotations);
} }
private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]} { private _ownPropMetadata(typeOrFunc: any, parentCtor: any): {[key: string]: any[]}|null {
// Prefer the direct API. // Prefer the direct API.
if ((<any>typeOrFunc).propMetadata && if ((<any>typeOrFunc).propMetadata &&
(<any>typeOrFunc).propMetadata !== parentCtor.propMetadata) { (<any>typeOrFunc).propMetadata !== parentCtor.propMetadata) {
@ -172,6 +172,7 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
if (this._reflect && this._reflect.getOwnMetadata) { if (this._reflect && this._reflect.getOwnMetadata) {
return this._reflect.getOwnMetadata('propMetadata', typeOrFunc); return this._reflect.getOwnMetadata('propMetadata', typeOrFunc);
} }
return null;
} }
propMetadata(typeOrFunc: any): {[key: string]: any[]} { propMetadata(typeOrFunc: any): {[key: string]: any[]} {

View File

@ -51,7 +51,7 @@ export class Reflector extends ReflectorReader {
resourceUri(type: any): string { return this.reflectionCapabilities.resourceUri(type); } resourceUri(type: any): string { return this.reflectionCapabilities.resourceUri(type); }
resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any { resolveIdentifier(name: string, moduleUrl: string, members: string[]|null, runtime: any): any {
return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime); return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
} }

View File

@ -14,7 +14,7 @@ export abstract class ReflectorReader {
abstract parameters(typeOrFunc: /*Type*/ any): any[][]; abstract parameters(typeOrFunc: /*Type*/ any): any[][];
abstract annotations(typeOrFunc: /*Type*/ any): any[]; abstract annotations(typeOrFunc: /*Type*/ any): any[];
abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]}; abstract propMetadata(typeOrFunc: /*Type*/ any): {[key: string]: any[]};
abstract importUri(typeOrFunc: /*Type*/ any): string; abstract importUri(typeOrFunc: /*Type*/ any): string|null;
abstract resourceUri(typeOrFunc: /*Type*/ any): string; abstract resourceUri(typeOrFunc: /*Type*/ any): string;
abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any; abstract resolveIdentifier(name: string, moduleUrl: string, members: string[], runtime: any): any;
abstract resolveEnum(identifier: any, name: string): any; abstract resolveEnum(identifier: any, name: string): any;

View File

@ -126,7 +126,7 @@ export interface RendererType2 {
* @experimental * @experimental
*/ */
export abstract class RendererFactory2 { export abstract class RendererFactory2 {
abstract createRenderer(hostElement: any, type: RendererType2): Renderer2; abstract createRenderer(hostElement: any, type: RendererType2|null): Renderer2;
} }
/** /**
@ -148,7 +148,7 @@ export abstract class Renderer2 {
abstract get data(): {[key: string]: any}; abstract get data(): {[key: string]: any};
abstract destroy(): void; abstract destroy(): void;
abstract createElement(name: string, namespace?: string): any; abstract createElement(name: string, namespace?: string|null): any;
abstract createComment(value: string): any; abstract createComment(value: string): any;
abstract createText(value: string): any; abstract createText(value: string): any;
/** /**
@ -156,7 +156,7 @@ export abstract class Renderer2 {
* in which case the view engine won't call it. * in which case the view engine won't call it.
* This is used as a performance optimization for production mode. * This is used as a performance optimization for production mode.
*/ */
destroyNode: (node: any) => void | null; destroyNode: ((node: any) => void)|null;
abstract appendChild(parent: any, newChild: any): void; abstract appendChild(parent: any, newChild: any): void;
abstract insertBefore(parent: any, newChild: any, refChild: any): void; abstract insertBefore(parent: any, newChild: any, refChild: any): void;
abstract removeChild(parent: any, oldChild: any): void; abstract removeChild(parent: any, oldChild: any): void;
@ -173,8 +173,8 @@ export abstract class Renderer2 {
* the caller can't rely on checking whether this is null or not. * the caller can't rely on checking whether this is null or not.
*/ */
abstract nextSibling(node: any): any; abstract nextSibling(node: any): any;
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void; abstract setAttribute(el: any, name: string, value: string, namespace?: string|null): void;
abstract removeAttribute(el: any, name: string, namespace?: string): void; abstract removeAttribute(el: any, name: string, namespace?: string|null): void;
abstract addClass(el: any, name: string): void; abstract addClass(el: any, name: string): void;
abstract removeClass(el: any, name: string): void; abstract removeClass(el: any, name: string): void;
abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void; abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void;

View File

@ -92,7 +92,7 @@ export class Testability implements PublicTestability {
// Schedules the call backs in a new frame so that it is always async. // Schedules the call backs in a new frame so that it is always async.
scheduleMicroTask(() => { scheduleMicroTask(() => {
while (this._callbacks.length !== 0) { while (this._callbacks.length !== 0) {
(this._callbacks.pop())(this._didWork); (this._callbacks.pop() !)(this._didWork);
} }
this._didWork = false; this._didWork = false;
}); });
@ -136,13 +136,13 @@ export class TestabilityRegistry {
this._applications.set(token, testability); this._applications.set(token, testability);
} }
getTestability(elem: any): Testability { return this._applications.get(elem); } getTestability(elem: any): Testability|null { return this._applications.get(elem) || null; }
getAllTestabilities(): Testability[] { return Array.from(this._applications.values()); } getAllTestabilities(): Testability[] { return Array.from(this._applications.values()); }
getAllRootElements(): any[] { return Array.from(this._applications.keys()); } getAllRootElements(): any[] { return Array.from(this._applications.keys()); }
findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability { findTestabilityInTree(elem: Node, findInAncestors: boolean = true): Testability|null {
return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors); return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors);
} }
} }
@ -157,13 +157,13 @@ export class TestabilityRegistry {
export interface GetTestability { export interface GetTestability {
addToWindow(registry: TestabilityRegistry): void; addToWindow(registry: TestabilityRegistry): void;
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean): findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean):
Testability; Testability|null;
} }
class _NoopGetTestability implements GetTestability { class _NoopGetTestability implements GetTestability {
addToWindow(registry: TestabilityRegistry): void {} addToWindow(registry: TestabilityRegistry): void {}
findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean): findTestabilityInTree(registry: TestabilityRegistry, elem: any, findInAncestors: boolean):
Testability { Testability|null {
return null; return null;
} }
} }

View File

@ -96,7 +96,7 @@ function extractAnnotation(annotation: any): any {
return annotation; return annotation;
} }
function applyParams(fnOrArray: (Function | any[]), key: string): Function { function applyParams(fnOrArray: Function | any[] | undefined, key: string): Function {
if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function || if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||
fnOrArray === Number || fnOrArray === Array) { fnOrArray === Number || fnOrArray === Array) {
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`); throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
@ -107,7 +107,7 @@ function applyParams(fnOrArray: (Function | any[]), key: string): Function {
} }
if (Array.isArray(fnOrArray)) { if (Array.isArray(fnOrArray)) {
const annotations: any[] = fnOrArray; const annotations: any[] = fnOrArray as any[];
const annoLength = annotations.length - 1; const annoLength = annotations.length - 1;
const fn: Function = fnOrArray[annoLength]; const fn: Function = fnOrArray[annoLength];
if (typeof fn !== 'function') { if (typeof fn !== 'function') {
@ -263,7 +263,7 @@ export function Class(clsDef: ClassDefinition): Type<any> {
*/ */
export function makeDecorator( export function makeDecorator(
name: string, props: {[name: string]: any}, parentClass?: any, name: string, props: {[name: string]: any}, parentClass?: any,
chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any { chainFn?: (fn: Function) => void): (...args: any[]) => (cls: any) => any {
const metaCtor = makeMetadataCtor([props]); const metaCtor = makeMetadataCtor([props]);
function DecoratorFactory(objOrType: any): (cls: any) => any { function DecoratorFactory(objOrType: any): (cls: any) => any {
@ -332,7 +332,7 @@ export function makeParamDecorator(
return ParamDecorator; return ParamDecorator;
function ParamDecorator(cls: any, unusedKey: any, index: number): any { function ParamDecorator(cls: any, unusedKey: any, index: number): any {
const parameters: any[][] = Reflect.getOwnMetadata('parameters', cls) || []; const parameters: (any[] | null)[] = Reflect.getOwnMetadata('parameters', cls) || [];
// there might be gaps if some in between parameters do not have annotations. // there might be gaps if some in between parameters do not have annotations.
// we pad with nulls. // we pad with nulls.
@ -341,7 +341,7 @@ export function makeParamDecorator(
} }
parameters[index] = parameters[index] || []; parameters[index] = parameters[index] || [];
parameters[index].push(annotationInstance); parameters[index] !.push(annotationInstance);
Reflect.defineMetadata('parameters', parameters, cls); Reflect.defineMetadata('parameters', parameters, cls);
return cls; return cls;

View File

@ -24,18 +24,3 @@ export function isObservable(obj: any | Observable<any>): obj is Observable<any>
// TODO use Symbol.observable when https://github.com/ReactiveX/rxjs/issues/2415 will be resolved // TODO use Symbol.observable when https://github.com/ReactiveX/rxjs/issues/2415 will be resolved
return !!obj && typeof obj.subscribe === 'function'; return !!obj && typeof obj.subscribe === 'function';
} }
// TODO(misko): replace with Object.assign once we require ES6.
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
const m: {[key: string]: V} = {};
for (const k of Object.keys(m1)) {
m[k] = m1[k];
}
for (const k of Object.keys(m2)) {
m[k] = m2[k];
}
return m;
}

View File

@ -16,20 +16,17 @@ export function anchorDef(
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
ngContentIndex: number, childCount: number, handleEvent?: ElementHandleEventFn, ngContentIndex: number, childCount: number, handleEvent?: ElementHandleEventFn,
templateFactory?: ViewDefinitionFactory): NodeDef { templateFactory?: ViewDefinitionFactory): NodeDef {
if (!handleEvent) {
handleEvent = NOOP;
}
flags |= NodeFlags.TypeElement; flags |= NodeFlags.TypeElement;
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
const template = templateFactory ? resolveViewDefinition(templateFactory) : null; const template = templateFactory ? resolveViewDefinition(templateFactory) : null;
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
@ -39,19 +36,20 @@ export function anchorDef(
bindingFlags: 0, bindingFlags: 0,
outputs: [], outputs: [],
element: { element: {
ns: undefined, ns: null,
name: undefined, name: null,
attrs: undefined, template, attrs: null, template,
componentProvider: undefined, componentProvider: null,
componentView: undefined, componentView: null,
componentRendererType: undefined, componentRendererType: null,
publicProviders: undefined, publicProviders: null,
allProviders: undefined, handleEvent allProviders: null,
handleEvent: handleEvent || NOOP
}, },
provider: undefined, provider: null,
text: undefined, text: null,
query: undefined, query: null,
ngContent: undefined ngContent: null
}; };
} }
@ -61,13 +59,13 @@ export function elementDef(
fixedAttrs: [string, string][] = [], fixedAttrs: [string, string][] = [],
bindings?: [BindingFlags, string, string | SecurityContext][], outputs?: ([string, string])[], bindings?: [BindingFlags, string, string | SecurityContext][], outputs?: ([string, string])[],
handleEvent?: ElementHandleEventFn, componentView?: ViewDefinitionFactory, handleEvent?: ElementHandleEventFn, componentView?: ViewDefinitionFactory,
componentRendererType?: RendererType2): NodeDef { componentRendererType?: RendererType2 | null): NodeDef {
if (!handleEvent) { if (!handleEvent) {
handleEvent = NOOP; handleEvent = NOOP;
} }
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
let ns: string; let ns: string = null !;
let name: string; let name: string = null !;
if (namespaceAndName) { if (namespaceAndName) {
[ns, name] = splitNamespace(namespaceAndName); [ns, name] = splitNamespace(namespaceAndName);
} }
@ -77,8 +75,8 @@ export function elementDef(
const [bindingFlags, namespaceAndName, suffixOrSecurityContext] = bindings[i]; const [bindingFlags, namespaceAndName, suffixOrSecurityContext] = bindings[i];
const [ns, name] = splitNamespace(namespaceAndName); const [ns, name] = splitNamespace(namespaceAndName);
let securityContext: SecurityContext; let securityContext: SecurityContext = undefined !;
let suffix: string; let suffix: string = undefined !;
switch (bindingFlags & BindingFlags.Types) { switch (bindingFlags & BindingFlags.Types) {
case BindingFlags.TypeElementStyle: case BindingFlags.TypeElementStyle:
suffix = <string>suffixOrSecurityContext; suffix = <string>suffixOrSecurityContext;
@ -98,7 +96,7 @@ export function elementDef(
outputDefs[i] = { outputDefs[i] = {
type: OutputType.ElementOutput, type: OutputType.ElementOutput,
target: <any>target, eventName, target: <any>target, eventName,
propName: undefined propName: null
}; };
} }
fixedAttrs = fixedAttrs || []; fixedAttrs = fixedAttrs || [];
@ -113,11 +111,11 @@ export function elementDef(
flags |= NodeFlags.TypeElement; flags |= NodeFlags.TypeElement;
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
@ -130,21 +128,24 @@ export function elementDef(
ns, ns,
name, name,
attrs, attrs,
template: undefined, template: null,
// will bet set by the view definition // will bet set by the view definition
componentProvider: undefined, componentView, componentRendererType, componentProvider: null,
publicProviders: undefined, componentView: componentView || null,
allProviders: undefined, handleEvent, componentRendererType: componentRendererType,
publicProviders: null,
allProviders: null,
handleEvent: handleEvent || NOOP,
}, },
provider: undefined, provider: null,
text: undefined, text: null,
query: undefined, query: null,
ngContent: undefined ngContent: null
}; };
} }
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData { export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
const elDef = def.element; const elDef = def.element !;
const rootSelectorOrNode = view.root.selectorOrNode; const rootSelectorOrNode = view.root.selectorOrNode;
const renderer = view.renderer; const renderer = view.renderer;
let el: any; let el: any;
@ -175,7 +176,7 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def:
const output = def.outputs[i]; const output = def.outputs[i];
const handleEventClosure = renderEventHandlerClosure( const handleEventClosure = renderEventHandlerClosure(
view, def.index, elementEventFullName(output.target, output.eventName)); view, def.index, elementEventFullName(output.target, output.eventName));
let listenTarget = output.target; let listenTarget: 'window'|'document'|'body'|'component'|null = output.target;
let listenerView = view; let listenerView = view;
if (output.target === 'component') { if (output.target === 'component') {
listenTarget = null; listenTarget = null;
@ -183,7 +184,7 @@ export function listenToElementOutputs(view: ViewData, compView: ViewData, def:
} }
const disposable = const disposable =
<any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure); <any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure);
view.disposables[def.outputIndex + i] = disposable; view.disposables ![def.outputIndex + i] = disposable;
} }
} }
@ -225,7 +226,7 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
const binding = def.bindings[bindingIdx]; const binding = def.bindings[bindingIdx];
const elData = asElementData(view, def.index); const elData = asElementData(view, def.index);
const renderNode = elData.renderElement; const renderNode = elData.renderElement;
const name = binding.name; const name = binding.name !;
switch (binding.flags & BindingFlags.Types) { switch (binding.flags & BindingFlags.Types) {
case BindingFlags.TypeElementAttribute: case BindingFlags.TypeElementAttribute:
setElementAttribute(view, binding, renderNode, binding.ns, name, value); setElementAttribute(view, binding, renderNode, binding.ns, name, value);
@ -248,7 +249,8 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
} }
function setElementAttribute( function setElementAttribute(
view: ViewData, binding: BindingDef, renderNode: any, ns: string, name: string, value: any) { view: ViewData, binding: BindingDef, renderNode: any, ns: string | null, name: string,
value: any) {
const securityContext = binding.securityContext; const securityContext = binding.securityContext;
let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value; let renderValue = securityContext ? view.root.sanitizer.sanitize(securityContext, value) : value;
renderValue = renderValue != null ? renderValue.toString() : null; renderValue = renderValue != null ? renderValue.toString() : null;
@ -271,7 +273,7 @@ function setElementClass(view: ViewData, renderNode: any, name: string, value: b
function setElementStyle( function setElementStyle(
view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) { view: ViewData, binding: BindingDef, renderNode: any, name: string, value: any) {
let renderValue = view.root.sanitizer.sanitize(SecurityContext.STYLE, value); let renderValue: string|null = view.root.sanitizer.sanitize(SecurityContext.STYLE, value);
if (renderValue != null) { if (renderValue != null) {
renderValue = renderValue.toString(); renderValue = renderValue.toString();
const unit = binding.suffix; const unit = binding.suffix;

View File

@ -12,11 +12,11 @@ import {RenderNodeAction, getParentRenderElement, visitProjectedRenderNodes} fro
export function ngContentDef(ngContentIndex: number, index: number): NodeDef { export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags: NodeFlags.TypeNgContent, flags: NodeFlags.TypeNgContent,
childFlags: 0, childFlags: 0,
@ -29,10 +29,10 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
bindings: [], bindings: [],
bindingFlags: 0, bindingFlags: 0,
outputs: [], outputs: [],
element: undefined, element: null,
provider: undefined, provider: null,
text: undefined, text: null,
query: undefined, query: null,
ngContent: {index} ngContent: {index}
}; };
} }
@ -43,7 +43,7 @@ export function appendNgContent(view: ViewData, renderHost: any, def: NodeDef) {
// Nothing to do if there is no parent element. // Nothing to do if there is no parent element.
return; return;
} }
const ngContentIndex = def.ngContent.index; const ngContentIndex = def.ngContent !.index;
visitProjectedRenderNodes( visitProjectedRenderNodes(
view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, undefined, undefined); view, ngContentIndex, RenderNodeAction.AppendChild, parentEl, null, undefined);
} }

View File

@ -38,9 +38,9 @@ export function directiveDef(
bindings[bindingIndex] = { bindings[bindingIndex] = {
flags: BindingFlags.TypeProperty, flags: BindingFlags.TypeProperty,
name: prop, nonMinifiedName, name: prop, nonMinifiedName,
ns: undefined, ns: null,
securityContext: undefined, securityContext: null,
suffix: undefined suffix: null
}; };
} }
} }
@ -67,9 +67,9 @@ export function providerDef(
} }
export function _def( export function _def(
flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][], childCount: number, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][] | null,
token: any, value: any, deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], childCount: number, token: any, value: any, deps: ([DepFlags, any] | any)[],
outputs?: OutputDef[]): NodeDef { bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef {
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl); const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
if (!outputs) { if (!outputs) {
outputs = []; outputs = [];
@ -92,23 +92,23 @@ export function _def(
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
directChildFlags: 0, directChildFlags: 0,
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
ngContentIndex: undefined, childCount, bindings, ngContentIndex: -1, childCount, bindings,
bindingFlags: calcBindingFlags(bindings), outputs, bindingFlags: calcBindingFlags(bindings), outputs,
element: undefined, element: null,
provider: {token, tokenKey: tokenKey(token), value, deps: depDefs}, provider: {token, tokenKey: tokenKey(token), value, deps: depDefs},
text: undefined, text: null,
query: undefined, query: null,
ngContent: undefined ngContent: null
}; };
} }
@ -126,22 +126,22 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
const allowPrivateServices = true; const allowPrivateServices = true;
// pipes are always eager and classes! // pipes are always eager and classes!
return createClass( return createClass(
compView.parent, viewParentEl(compView), allowPrivateServices, def.provider.value, compView.parent !, viewParentEl(compView) !, allowPrivateServices, def.provider !.value,
def.provider.deps); def.provider !.deps);
} }
export function createDirectiveInstance(view: ViewData, def: NodeDef): any { export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
// components can see other private services, other directives can't. // components can see other private services, other directives can't.
const allowPrivateServices = (def.flags & NodeFlags.Component) > 0; const allowPrivateServices = (def.flags & NodeFlags.Component) > 0;
// directives are always eager and classes! // directives are always eager and classes!
const instance = const instance = createClass(
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps); view, def.parent !, allowPrivateServices, def.provider !.value, def.provider !.deps);
if (def.outputs.length) { if (def.outputs.length) {
for (let i = 0; i < def.outputs.length; i++) { for (let i = 0; i < def.outputs.length; i++) {
const output = def.outputs[i]; const output = def.outputs[i];
const subscription = instance[output.propName].subscribe( const subscription = instance[output.propName !].subscribe(
eventHandlerClosure(view, def.parent.index, output.eventName)); eventHandlerClosure(view, def.parent !.index, output.eventName));
view.disposables[def.outputIndex + i] = subscription.unsubscribe.bind(subscription); view.disposables ![def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
} }
} }
return instance; return instance;
@ -157,48 +157,48 @@ export function checkAndUpdateDirectiveInline(
const providerData = asProviderData(view, def.index); const providerData = asProviderData(view, def.index);
const directive = providerData.instance; const directive = providerData.instance;
let changed = false; let changed = false;
let changes: SimpleChanges; let changes: SimpleChanges = undefined !;
const bindLen = def.bindings.length; const bindLen = def.bindings.length;
if (bindLen > 0 && checkBinding(view, def, 0, v0)) { if (bindLen > 0 && checkBinding(view, def, 0, v0)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 0, v0, changes); changes = updateProp(view, providerData, def, 0, v0, changes);
}; }
if (bindLen > 1 && checkBinding(view, def, 1, v1)) { if (bindLen > 1 && checkBinding(view, def, 1, v1)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 1, v1, changes); changes = updateProp(view, providerData, def, 1, v1, changes);
}; }
if (bindLen > 2 && checkBinding(view, def, 2, v2)) { if (bindLen > 2 && checkBinding(view, def, 2, v2)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 2, v2, changes); changes = updateProp(view, providerData, def, 2, v2, changes);
}; }
if (bindLen > 3 && checkBinding(view, def, 3, v3)) { if (bindLen > 3 && checkBinding(view, def, 3, v3)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 3, v3, changes); changes = updateProp(view, providerData, def, 3, v3, changes);
}; }
if (bindLen > 4 && checkBinding(view, def, 4, v4)) { if (bindLen > 4 && checkBinding(view, def, 4, v4)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 4, v4, changes); changes = updateProp(view, providerData, def, 4, v4, changes);
}; }
if (bindLen > 5 && checkBinding(view, def, 5, v5)) { if (bindLen > 5 && checkBinding(view, def, 5, v5)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 5, v5, changes); changes = updateProp(view, providerData, def, 5, v5, changes);
}; }
if (bindLen > 6 && checkBinding(view, def, 6, v6)) { if (bindLen > 6 && checkBinding(view, def, 6, v6)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 6, v6, changes); changes = updateProp(view, providerData, def, 6, v6, changes);
}; }
if (bindLen > 7 && checkBinding(view, def, 7, v7)) { if (bindLen > 7 && checkBinding(view, def, 7, v7)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 7, v7, changes); changes = updateProp(view, providerData, def, 7, v7, changes);
}; }
if (bindLen > 8 && checkBinding(view, def, 8, v8)) { if (bindLen > 8 && checkBinding(view, def, 8, v8)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 8, v8, changes); changes = updateProp(view, providerData, def, 8, v8, changes);
}; }
if (bindLen > 9 && checkBinding(view, def, 9, v9)) { if (bindLen > 9 && checkBinding(view, def, 9, v9)) {
changed = true; changed = true;
changes = updateProp(view, providerData, def, 9, v9, changes); changes = updateProp(view, providerData, def, 9, v9, changes);
}; }
if (changes) { if (changes) {
directive.ngOnChanges(changes); directive.ngOnChanges(changes);
} }
@ -216,7 +216,7 @@ export function checkAndUpdateDirectiveDynamic(
const providerData = asProviderData(view, def.index); const providerData = asProviderData(view, def.index);
const directive = providerData.instance; const directive = providerData.instance;
let changed = false; let changed = false;
let changes: SimpleChanges; let changes: SimpleChanges = undefined !;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
if (checkBinding(view, def, i, values[i])) { if (checkBinding(view, def, i, values[i])) {
changed = true; changed = true;
@ -242,18 +242,18 @@ function _createProviderInstance(view: ViewData, def: NodeDef): any {
let injectable: any; let injectable: any;
switch (def.flags & NodeFlags.Types) { switch (def.flags & NodeFlags.Types) {
case NodeFlags.TypeClassProvider: case NodeFlags.TypeClassProvider:
injectable = injectable = createClass(
createClass(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps); view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
break; break;
case NodeFlags.TypeFactoryProvider: case NodeFlags.TypeFactoryProvider:
injectable = injectable = callFactory(
callFactory(view, def.parent, allowPrivateServices, providerDef.value, providerDef.deps); view, def.parent !, allowPrivateServices, providerDef !.value, providerDef !.deps);
break; break;
case NodeFlags.TypeUseExistingProvider: case NodeFlags.TypeUseExistingProvider:
injectable = resolveDep(view, def.parent, allowPrivateServices, providerDef.deps[0]); injectable = resolveDep(view, def.parent !, allowPrivateServices, providerDef !.deps[0]);
break; break;
case NodeFlags.TypeValueProvider: case NodeFlags.TypeValueProvider:
injectable = providerDef.value; injectable = providerDef !.value;
break; break;
} }
return injectable; return injectable;
@ -345,7 +345,7 @@ export const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
export function resolveDep( export function resolveDep(
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef,
notFoundValue = Injector.THROW_IF_NOT_FOUND): any { notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
if (depDef.flags & DepFlags.Value) { if (depDef.flags & DepFlags.Value) {
return depDef.token; return depDef.token;
} }
@ -357,7 +357,7 @@ export function resolveDep(
if (elDef && (depDef.flags & DepFlags.SkipSelf)) { if (elDef && (depDef.flags & DepFlags.SkipSelf)) {
allowPrivateServices = false; allowPrivateServices = false;
elDef = elDef.parent; elDef = elDef.parent !;
} }
while (view) { while (view) {
@ -376,7 +376,7 @@ export function resolveDep(
case ViewContainerRefTokenKey: case ViewContainerRefTokenKey:
return asElementData(view, elDef.index).viewContainer; return asElementData(view, elDef.index).viewContainer;
case TemplateRefTokenKey: { case TemplateRefTokenKey: {
if (elDef.element.template) { if (elDef.element !.template) {
return asElementData(view, elDef.index).template; return asElementData(view, elDef.index).template;
} }
break; break;
@ -389,8 +389,8 @@ export function resolveDep(
return createInjector(view, elDef); return createInjector(view, elDef);
default: default:
const providerDef = const providerDef =
(allowPrivateServices ? elDef.element.allProviders : (allowPrivateServices ? elDef.element !.allProviders :
elDef.element.publicProviders)[tokenKey]; elDef.element !.publicProviders) ![tokenKey];
if (providerDef) { if (providerDef) {
const providerData = asProviderData(view, providerDef.index); const providerData = asProviderData(view, providerDef.index);
if (providerData.instance === NOT_CREATED) { if (providerData.instance === NOT_CREATED) {
@ -401,8 +401,8 @@ export function resolveDep(
} }
} }
allowPrivateServices = isComponentView(view); allowPrivateServices = isComponentView(view);
elDef = viewParentEl(view); elDef = viewParentEl(view) !;
view = view.parent; view = view.parent !;
} }
const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR); const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);
@ -437,13 +437,13 @@ function updateProp(
view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any, view: ViewData, providerData: ProviderData, def: NodeDef, bindingIdx: number, value: any,
changes: SimpleChanges): SimpleChanges { changes: SimpleChanges): SimpleChanges {
if (def.flags & NodeFlags.Component) { if (def.flags & NodeFlags.Component) {
const compView = asElementData(view, def.parent.index).componentView; const compView = asElementData(view, def.parent !.index).componentView;
if (compView.def.flags & ViewFlags.OnPush) { if (compView.def.flags & ViewFlags.OnPush) {
compView.state |= ViewState.ChecksEnabled; compView.state |= ViewState.ChecksEnabled;
} }
} }
const binding = def.bindings[bindingIdx]; const binding = def.bindings[bindingIdx];
const propName = binding.name; const propName = binding.name !;
// Note: This is still safe with Closure Compiler as // Note: This is still safe with Closure Compiler as
// the user passed in the property name as an object has to `providerDef`, // the user passed in the property name as an object has to `providerDef`,
// so Closure Compiler will have renamed the property correctly already. // so Closure Compiler will have renamed the property correctly already.
@ -455,7 +455,7 @@ function updateProp(
oldValue = oldValue.wrapped; oldValue = oldValue.wrapped;
} }
const binding = def.bindings[bindingIdx]; const binding = def.bindings[bindingIdx];
changes[binding.nonMinifiedName] = changes[binding.nonMinifiedName !] =
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0); new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
} }
view.oldValues[def.bindingIndex + bindingIdx] = value; view.oldValues[def.bindingIndex + bindingIdx] = value;

View File

@ -29,19 +29,19 @@ function _pureExpressionDef(flags: NodeFlags, propertyNames: string[]): NodeDef
bindings[i] = { bindings[i] = {
flags: BindingFlags.TypeProperty, flags: BindingFlags.TypeProperty,
name: prop, name: prop,
ns: undefined, ns: null,
nonMinifiedName: prop, nonMinifiedName: prop,
securityContext: undefined, securityContext: null,
suffix: undefined suffix: null
}; };
} }
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
@ -50,15 +50,15 @@ function _pureExpressionDef(flags: NodeFlags, propertyNames: string[]): NodeDef
matchedQueries: {}, matchedQueries: {},
matchedQueryIds: 0, matchedQueryIds: 0,
references: {}, references: {},
ngContentIndex: undefined, ngContentIndex: -1,
childCount: 0, bindings, childCount: 0, bindings,
bindingFlags: calcBindingFlags(bindings), bindingFlags: calcBindingFlags(bindings),
outputs: [], outputs: [],
element: undefined, element: null,
provider: undefined, provider: null,
text: undefined, text: null,
query: undefined, query: null,
ngContent: undefined ngContent: null
}; };
} }
@ -102,16 +102,16 @@ export function checkAndUpdatePureExpressionInline(
break; break;
case NodeFlags.TypePureObject: case NodeFlags.TypePureObject:
value = {}; value = {};
if (bindLen > 0) value[bindings[0].name] = v0; if (bindLen > 0) value[bindings[0].name !] = v0;
if (bindLen > 1) value[bindings[1].name] = v1; if (bindLen > 1) value[bindings[1].name !] = v1;
if (bindLen > 2) value[bindings[2].name] = v2; if (bindLen > 2) value[bindings[2].name !] = v2;
if (bindLen > 3) value[bindings[3].name] = v3; if (bindLen > 3) value[bindings[3].name !] = v3;
if (bindLen > 4) value[bindings[4].name] = v4; if (bindLen > 4) value[bindings[4].name !] = v4;
if (bindLen > 5) value[bindings[5].name] = v5; if (bindLen > 5) value[bindings[5].name !] = v5;
if (bindLen > 6) value[bindings[6].name] = v6; if (bindLen > 6) value[bindings[6].name !] = v6;
if (bindLen > 7) value[bindings[7].name] = v7; if (bindLen > 7) value[bindings[7].name !] = v7;
if (bindLen > 8) value[bindings[8].name] = v8; if (bindLen > 8) value[bindings[8].name !] = v8;
if (bindLen > 9) value[bindings[9].name] = v9; if (bindLen > 9) value[bindings[9].name !] = v9;
break; break;
case NodeFlags.TypePurePipe: case NodeFlags.TypePurePipe:
const pipe = v0; const pipe = v0;
@ -175,7 +175,7 @@ export function checkAndUpdatePureExpressionDynamic(
case NodeFlags.TypePureObject: case NodeFlags.TypePureObject:
value = {}; value = {};
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
value[bindings[i].name] = values[i]; value[bindings[i].name !] = values[i];
} }
break; break;
case NodeFlags.TypePurePipe: case NodeFlags.TypePurePipe:

View File

@ -22,17 +22,17 @@ export function queryDef(
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
directChildFlags: 0, directChildFlags: 0,
childMatchedQueries: 0, childMatchedQueries: 0,
ngContentIndex: undefined, ngContentIndex: -1,
matchedQueries: {}, matchedQueries: {},
matchedQueryIds: 0, matchedQueryIds: 0,
references: {}, references: {},
@ -40,11 +40,11 @@ export function queryDef(
bindings: [], bindings: [],
bindingFlags: 0, bindingFlags: 0,
outputs: [], outputs: [],
element: undefined, element: null,
provider: undefined, provider: null,
text: undefined, text: null,
query: {id, filterId: filterQueryId(id), bindings: bindingDefs}, query: {id, filterId: filterQueryId(id), bindings: bindingDefs},
ngContent: undefined ngContent: null
}; };
} }
@ -55,7 +55,7 @@ export function createQuery(): QueryList<any> {
export function dirtyParentQueries(view: ViewData) { export function dirtyParentQueries(view: ViewData) {
const queryIds = view.def.nodeMatchedQueries; const queryIds = view.def.nodeMatchedQueries;
while (view.parent && isEmbeddedView(view)) { while (view.parent && isEmbeddedView(view)) {
let tplDef = view.parentNodeDef; let tplDef = view.parentNodeDef !;
view = view.parent; view = view.parent;
// content queries // content queries
const end = tplDef.index + tplDef.childCount; const end = tplDef.index + tplDef.childCount;
@ -63,7 +63,7 @@ export function dirtyParentQueries(view: ViewData) {
const nodeDef = view.def.nodes[i]; const nodeDef = view.def.nodes[i];
if ((nodeDef.flags & NodeFlags.TypeContentQuery) && if ((nodeDef.flags & NodeFlags.TypeContentQuery) &&
(nodeDef.flags & NodeFlags.DynamicQuery) && (nodeDef.flags & NodeFlags.DynamicQuery) &&
(nodeDef.query.filterId & queryIds) === nodeDef.query.filterId) { (nodeDef.query !.filterId & queryIds) === nodeDef.query !.filterId) {
asQueryList(view, i).setDirty(); asQueryList(view, i).setDirty();
} }
if ((nodeDef.flags & NodeFlags.TypeElement && i + nodeDef.childCount < tplDef.index) || if ((nodeDef.flags & NodeFlags.TypeElement && i + nodeDef.childCount < tplDef.index) ||
@ -94,18 +94,18 @@ export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) {
return; return;
} }
let directiveInstance: any; let directiveInstance: any;
let newValues: any[]; let newValues: any[] = undefined !;
if (nodeDef.flags & NodeFlags.TypeContentQuery) { if (nodeDef.flags & NodeFlags.TypeContentQuery) {
const elementDef = nodeDef.parent.parent; const elementDef = nodeDef.parent !.parent !;
newValues = calcQueryValues( newValues = calcQueryValues(
view, elementDef.index, elementDef.index + elementDef.childCount, nodeDef.query, []); view, elementDef.index, elementDef.index + elementDef.childCount, nodeDef.query !, []);
directiveInstance = asProviderData(view, nodeDef.parent.index).instance; directiveInstance = asProviderData(view, nodeDef.parent !.index).instance;
} else if (nodeDef.flags & NodeFlags.TypeViewQuery) { } else if (nodeDef.flags & NodeFlags.TypeViewQuery) {
newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query, []); newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query !, []);
directiveInstance = view.component; directiveInstance = view.component;
} }
queryList.reset(newValues); queryList.reset(newValues);
const bindings = nodeDef.query.bindings; const bindings = nodeDef.query !.bindings;
let notify = false; let notify = false;
for (let i = 0; i < bindings.length; i++) { for (let i = 0; i < bindings.length; i++) {
const binding = bindings[i]; const binding = bindings[i];
@ -135,12 +135,13 @@ function calcQueryValues(
if (valueType != null) { if (valueType != null) {
values.push(getQueryValue(view, nodeDef, valueType)); values.push(getQueryValue(view, nodeDef, valueType));
} }
if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element.template && if (nodeDef.flags & NodeFlags.TypeElement && nodeDef.element !.template &&
(nodeDef.element.template.nodeMatchedQueries & queryDef.filterId) === queryDef.filterId) { (nodeDef.element !.template !.nodeMatchedQueries & queryDef.filterId) ===
queryDef.filterId) {
// check embedded views that were attached at the place of their template. // check embedded views that were attached at the place of their template.
const elementData = asElementData(view, i); const elementData = asElementData(view, i);
if (nodeDef.flags & NodeFlags.EmbeddedViews) { if (nodeDef.flags & NodeFlags.EmbeddedViews) {
const embeddedViews = elementData.viewContainer._embeddedViews; const embeddedViews = elementData.viewContainer !._embeddedViews;
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
const embeddedView = embeddedViews[k]; const embeddedView = embeddedViews[k];
const dvc = declaredViewContainer(embeddedView); const dvc = declaredViewContainer(embeddedView);

View File

@ -85,7 +85,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
throw new Error('ngModule should be provided'); throw new Error('ngModule should be provided');
} }
const viewDef = resolveViewDefinition(this.viewDefFactory); const viewDef = resolveViewDefinition(this.viewDefFactory);
const componentNodeIndex = viewDef.nodes[0].element.componentProvider.index; const componentNodeIndex = viewDef.nodes[0].element !.componentProvider !.index;
const view = Services.createRootView( const view = Services.createRootView(
injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT); injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT);
const component = asProviderData(view, componentNodeIndex).instance; const component = asProviderData(view, componentNodeIndex).instance;
@ -135,7 +135,7 @@ class ViewContainerRef_ implements ViewContainerData {
let elDef = this._elDef.parent; let elDef = this._elDef.parent;
while (!elDef && view) { while (!elDef && view) {
elDef = viewParentEl(view); elDef = viewParentEl(view);
view = view.parent; view = view.parent !;
} }
return view ? new Injector_(view, elDef) : new Injector_(this._view, null); return view ? new Injector_(view, elDef) : new Injector_(this._view, null);
@ -144,12 +144,12 @@ class ViewContainerRef_ implements ViewContainerData {
clear(): void { clear(): void {
const len = this._embeddedViews.length; const len = this._embeddedViews.length;
for (let i = len - 1; i >= 0; i--) { for (let i = len - 1; i >= 0; i--) {
const view = detachEmbeddedView(this._data, i); const view = detachEmbeddedView(this._data, i) !;
Services.destroyView(view); Services.destroyView(view);
} }
} }
get(index: number): ViewRef { get(index: number): ViewRef|null {
const view = this._embeddedViews[index]; const view = this._embeddedViews[index];
if (view) { if (view) {
const ref = new ViewRef_(view); const ref = new ViewRef_(view);
@ -206,7 +206,7 @@ class ViewContainerRef_ implements ViewContainerData {
} }
} }
detach(index?: number): ViewRef { detach(index?: number): ViewRef|null {
const view = detachEmbeddedView(this._data, index); const view = detachEmbeddedView(this._data, index);
return view ? new ViewRef_(view) : null; return view ? new ViewRef_(view) : null;
} }
@ -219,8 +219,8 @@ export function createChangeDetectorRef(view: ViewData): ChangeDetectorRef {
export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef { export class ViewRef_ implements EmbeddedViewRef<any>, InternalViewRef {
/** @internal */ /** @internal */
_view: ViewData; _view: ViewData;
private _viewContainerRef: ViewContainerRef; private _viewContainerRef: ViewContainerRef|null;
private _appRef: ApplicationRef; private _appRef: ApplicationRef|null;
constructor(_view: ViewData) { constructor(_view: ViewData) {
this._view = _view; this._view = _view;
@ -317,7 +317,7 @@ export function nodeValue(view: ViewData, index: number): any {
const def = view.def.nodes[index]; const def = view.def.nodes[index];
if (def.flags & NodeFlags.TypeElement) { if (def.flags & NodeFlags.TypeElement) {
const elData = asElementData(view, def.index); const elData = asElementData(view, def.index);
return def.element.template ? elData.template : elData.renderElement; return def.element !.template ? elData.template : elData.renderElement;
} else if (def.flags & NodeFlags.TypeText) { } else if (def.flags & NodeFlags.TypeText) {
return asTextData(view, def.index).renderText; return asTextData(view, def.index).renderText;
} else if (def.flags & (NodeFlags.CatProvider | NodeFlags.TypePipe)) { } else if (def.flags & (NodeFlags.CatProvider | NodeFlags.TypePipe)) {
@ -387,7 +387,7 @@ class RendererAdapter implements RendererV1 {
destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[]) { destroyView(hostElement: Element|DocumentFragment, viewAllNodes: Node[]) {
for (let i = 0; i < viewAllNodes.length; i++) { for (let i = 0; i < viewAllNodes.length; i++) {
this.delegate.destroyNode(viewAllNodes[i]); this.delegate.destroyNode !(viewAllNodes[i]);
} }
} }

View File

@ -159,9 +159,9 @@ enum DebugAction {
let _currentAction: DebugAction; let _currentAction: DebugAction;
let _currentView: ViewData; let _currentView: ViewData;
let _currentNodeIndex: number; let _currentNodeIndex: number|null;
function debugSetCurrentNode(view: ViewData, nodeIndex: number) { function debugSetCurrentNode(view: ViewData, nodeIndex: number | null) {
_currentView = view; _currentView = view;
_currentNodeIndex = nodeIndex; _currentNodeIndex = nodeIndex;
} }
@ -231,13 +231,13 @@ function debugCheckAndUpdateNode(
const binding = nodeDef.bindings[i]; const binding = nodeDef.bindings[i];
const value = values[i]; const value = values[i];
if (binding.flags & BindingFlags.TypeProperty) { if (binding.flags & BindingFlags.TypeProperty) {
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] = bindingValues[normalizeDebugBindingName(binding.nonMinifiedName !)] =
normalizeDebugBindingValue(value); normalizeDebugBindingValue(value);
} }
} }
const elDef = nodeDef.parent; const elDef = nodeDef.parent !;
const el = asElementData(view, elDef.index).renderElement; const el = asElementData(view, elDef.index).renderElement;
if (!elDef.element.name) { if (!elDef.element !.name) {
// a comment. // a comment.
view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`); view.renderer.setValue(el, `bindings=${JSON.stringify(bindingValues, null, 2)}`);
} else { } else {
@ -275,37 +275,37 @@ function camelCaseToDashCase(input: string): string {
function normalizeDebugBindingValue(value: any): string { function normalizeDebugBindingValue(value: any): string {
try { try {
// Limit the size of the value as otherwise the DOM just gets polluted. // Limit the size of the value as otherwise the DOM just gets polluted.
return value ? value.toString().slice(0, 30) : value; return value != null ? value.toString().slice(0, 30) : value;
} catch (e) { } catch (e) {
return '[ERROR] Exception while trying to serialize the value'; return '[ERROR] Exception while trying to serialize the value';
} }
} }
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number { function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number|null {
for (let i = nodeIndex; i < view.def.nodes.length; i++) { for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i]; const nodeDef = view.def.nodes[i];
if (nodeDef.flags & NodeFlags.TypeDirective && nodeDef.bindings && nodeDef.bindings.length) { if (nodeDef.flags & NodeFlags.TypeDirective && nodeDef.bindings && nodeDef.bindings.length) {
return i; return i;
} }
} }
return undefined; return null;
} }
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number { function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number|null {
for (let i = nodeIndex; i < view.def.nodes.length; i++) { for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i]; const nodeDef = view.def.nodes[i];
if ((nodeDef.flags & NodeFlags.CatRenderNode) && nodeDef.bindings && nodeDef.bindings.length) { if ((nodeDef.flags & NodeFlags.CatRenderNode) && nodeDef.bindings && nodeDef.bindings.length) {
return i; return i;
} }
} }
return undefined; return null;
} }
class DebugContext_ implements DebugContext { class DebugContext_ implements DebugContext {
private nodeDef: NodeDef; private nodeDef: NodeDef;
private elView: ViewData; private elView: ViewData;
private elDef: NodeDef; private elDef: NodeDef;
constructor(public view: ViewData, public nodeIndex: number) { constructor(public view: ViewData, public nodeIndex: number|null) {
if (nodeIndex == null) { if (nodeIndex == null) {
this.nodeIndex = nodeIndex = 0; this.nodeIndex = nodeIndex = 0;
} }
@ -313,12 +313,12 @@ class DebugContext_ implements DebugContext {
let elDef = this.nodeDef; let elDef = this.nodeDef;
let elView = view; let elView = view;
while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) { while (elDef && (elDef.flags & NodeFlags.TypeElement) === 0) {
elDef = elDef.parent; elDef = elDef.parent !;
} }
if (!elDef) { if (!elDef) {
while (!elDef && elView) { while (!elDef && elView) {
elDef = viewParentEl(elView); elDef = viewParentEl(elView) !;
elView = elView.parent; elView = elView.parent !;
} }
} }
this.elDef = elDef; this.elDef = elDef;
@ -337,7 +337,7 @@ class DebugContext_ implements DebugContext {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) { for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i]; const childDef = this.elView.def.nodes[i];
if (childDef.flags & NodeFlags.CatProvider) { if (childDef.flags & NodeFlags.CatProvider) {
tokens.push(childDef.provider.token); tokens.push(childDef.provider !.token);
} }
i += childDef.childCount; i += childDef.childCount;
} }
@ -389,7 +389,7 @@ class DebugContext_ implements DebugContext {
return NOOP; return NOOP;
} }
}; };
logViewDef.factory(nodeLogger); logViewDef.factory !(nodeLogger);
if (currRenderNodeIndex < renderNodeIndex) { if (currRenderNodeIndex < renderNodeIndex) {
console.error('Illegal state: the ViewDefinitionFactory did not call the logger!'); console.error('Illegal state: the ViewDefinitionFactory did not call the logger!');
(<any>console.error)(...values); (<any>console.error)(...values);
@ -408,14 +408,14 @@ function getRenderNodeIndex(viewDef: ViewDefinition, nodeIndex: number): number
return renderNodeIndex; return renderNodeIndex;
} }
function findHostElement(view: ViewData): ElementData { function findHostElement(view: ViewData): ElementData|null {
while (view && !isComponentView(view)) { while (view && !isComponentView(view)) {
view = view.parent; view = view.parent !;
} }
if (view.parent) { if (view.parent) {
return asElementData(view.parent, viewParentEl(view).index); return asElementData(view.parent, viewParentEl(view) !.index);
} }
return undefined; return null;
} }
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) { function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
@ -440,11 +440,11 @@ function callWithDebugContext(action: DebugAction, fn: any, self: any, args: any
throw e; throw e;
} }
_currentView.state |= ViewState.Errored; _currentView.state |= ViewState.Errored;
throw viewWrappedDebugError(e, getCurrentDebugContext()); throw viewWrappedDebugError(e, getCurrentDebugContext() !);
} }
} }
export function getCurrentDebugContext(): DebugContext { export function getCurrentDebugContext(): DebugContext|null {
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null; return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
} }
@ -452,7 +452,7 @@ export function getCurrentDebugContext(): DebugContext {
class DebugRendererFactory2 implements RendererFactory2 { class DebugRendererFactory2 implements RendererFactory2 {
constructor(private delegate: RendererFactory2) {} constructor(private delegate: RendererFactory2) {}
createRenderer(element: any, renderData: RendererType2): Renderer2 { createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
return new DebugRenderer2(this.delegate.createRenderer(element, renderData)); return new DebugRenderer2(this.delegate.createRenderer(element, renderData));
} }
} }
@ -464,7 +464,7 @@ class DebugRenderer2 implements Renderer2 {
get data() { return this.delegate.data; } get data() { return this.delegate.data; }
destroyNode(node: any) { destroyNode(node: any) {
removeDebugNodeFromIndex(getDebugNode(node)); removeDebugNodeFromIndex(getDebugNode(node) !);
if (this.delegate.destroyNode) { if (this.delegate.destroyNode) {
this.delegate.destroyNode(node); this.delegate.destroyNode(node);
} }
@ -513,7 +513,7 @@ class DebugRenderer2 implements Renderer2 {
insertBefore(parent: any, newChild: any, refChild: any): void { insertBefore(parent: any, newChild: any, refChild: any): void {
const debugEl = getDebugNode(parent); const debugEl = getDebugNode(parent);
const debugChildEl = getDebugNode(newChild); const debugChildEl = getDebugNode(newChild);
const debugRefEl = getDebugNode(refChild); const debugRefEl = getDebugNode(refChild) !;
if (debugEl && debugChildEl && debugEl instanceof DebugElement) { if (debugEl && debugChildEl && debugEl instanceof DebugElement) {
debugEl.insertBefore(debugRefEl, debugChildEl); debugEl.insertBefore(debugRefEl, debugChildEl);
} }

View File

@ -14,21 +14,21 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
for (let i = 1; i < constants.length; i++) { for (let i = 1; i < constants.length; i++) {
bindings[i - 1] = { bindings[i - 1] = {
flags: BindingFlags.TypeProperty, flags: BindingFlags.TypeProperty,
name: undefined, name: null,
ns: undefined, ns: null,
nonMinifiedName: undefined, nonMinifiedName: null,
securityContext: undefined, securityContext: null,
suffix: constants[i] suffix: constants[i]
}; };
} }
const flags = NodeFlags.TypeText; const flags = NodeFlags.TypeText;
return { return {
// will bet set by the view definition // will bet set by the view definition
index: undefined, index: -1,
parent: undefined, parent: null,
renderParent: undefined, renderParent: null,
bindingIndex: undefined, bindingIndex: -1,
outputIndex: undefined, outputIndex: -1,
// regular values // regular values
flags, flags,
childFlags: 0, childFlags: 0,
@ -40,18 +40,18 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
childCount: 0, bindings, childCount: 0, bindings,
bindingFlags: calcBindingFlags(bindings), bindingFlags: calcBindingFlags(bindings),
outputs: [], outputs: [],
element: undefined, element: null,
provider: undefined, provider: null,
text: {prefix: constants[0]}, text: {prefix: constants[0]},
query: undefined, query: null,
ngContent: undefined ngContent: null
}; };
} }
export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData { export function createText(view: ViewData, renderHost: any, def: NodeDef): TextData {
let renderNode: any; let renderNode: any;
const renderer = view.renderer; const renderer = view.renderer;
renderNode = renderer.createText(def.text.prefix); renderNode = renderer.createText(def.text !.prefix);
const parentEl = getParentRenderElement(view, renderHost, def); const parentEl = getParentRenderElement(view, renderHost, def);
if (parentEl) { if (parentEl) {
renderer.appendChild(parentEl, renderNode); renderer.appendChild(parentEl, renderNode);
@ -77,7 +77,7 @@ export function checkAndUpdateTextInline(
if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true; if (bindLen > 9 && checkAndUpdateBinding(view, def, 9, v9)) changed = true;
if (changed) { if (changed) {
let value = def.text.prefix; let value = def.text !.prefix;
if (bindLen > 0) value += _addInterpolationPart(v0, bindings[0]); if (bindLen > 0) value += _addInterpolationPart(v0, bindings[0]);
if (bindLen > 1) value += _addInterpolationPart(v1, bindings[1]); if (bindLen > 1) value += _addInterpolationPart(v1, bindings[1]);
if (bindLen > 2) value += _addInterpolationPart(v2, bindings[2]); if (bindLen > 2) value += _addInterpolationPart(v2, bindings[2]);
@ -109,7 +109,7 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
value = value + _addInterpolationPart(values[i], bindings[i]); value = value + _addInterpolationPart(values[i], bindings[i]);
} }
value = def.text.prefix + value; value = def.text !.prefix + value;
const renderNode = asTextData(view, def.index).renderText; const renderNode = asTextData(view, def.index).renderText;
view.renderer.setValue(renderNode, value); view.renderer.setValue(renderNode, value);
} }

View File

@ -19,7 +19,7 @@ import {Sanitizer, SecurityContext} from '../security';
// ------------------------------------- // -------------------------------------
export interface ViewDefinition { export interface ViewDefinition {
factory: ViewDefinitionFactory; factory: ViewDefinitionFactory|null;
flags: ViewFlags; flags: ViewFlags;
updateDirectives: ViewUpdateFn; updateDirectives: ViewUpdateFn;
updateRenderer: ViewUpdateFn; updateRenderer: ViewUpdateFn;
@ -32,7 +32,7 @@ export interface ViewDefinition {
/** aggregated NodeFlags for all nodes **/ /** aggregated NodeFlags for all nodes **/
nodeFlags: NodeFlags; nodeFlags: NodeFlags;
rootNodeFlags: NodeFlags; rootNodeFlags: NodeFlags;
lastRenderRootNode: NodeDef; lastRenderRootNode: NodeDef|null;
bindingCount: number; bindingCount: number;
outputCount: number; outputCount: number;
/** /**
@ -91,8 +91,8 @@ export const enum ViewFlags {
export interface NodeDef { export interface NodeDef {
flags: NodeFlags; flags: NodeFlags;
index: number; index: number;
parent: NodeDef; parent: NodeDef|null;
renderParent: NodeDef; renderParent: NodeDef|null;
/** this is checked against NgContentDef.index to find matched nodes */ /** this is checked against NgContentDef.index to find matched nodes */
ngContentIndex: number; ngContentIndex: number;
/** number of transitive children */ /** number of transitive children */
@ -123,11 +123,11 @@ export interface NodeDef {
* Used as a bloom filter. * Used as a bloom filter.
*/ */
childMatchedQueries: number; childMatchedQueries: number;
element: ElementDef; element: ElementDef|null;
provider: ProviderDef; provider: ProviderDef|null;
text: TextDef; text: TextDef|null;
query: QueryDef; query: QueryDef|null;
ngContent: NgContentDef; ngContent: NgContentDef|null;
} }
/** /**
@ -179,11 +179,11 @@ export const enum NodeFlags {
export interface BindingDef { export interface BindingDef {
flags: BindingFlags; flags: BindingFlags;
ns: string; ns: string|null;
name: string; name: string|null;
nonMinifiedName: string; nonMinifiedName: string|null;
securityContext: SecurityContext; securityContext: SecurityContext|null;
suffix: string; suffix: string|null;
} }
export const enum BindingFlags { export const enum BindingFlags {
@ -201,9 +201,9 @@ export const enum BindingFlags {
export interface OutputDef { export interface OutputDef {
type: OutputType; type: OutputType;
target: 'window'|'document'|'body'|'component'; target: 'window'|'document'|'body'|'component'|null;
eventName: string; eventName: string;
propName: string; propName: string|null;
} }
export const enum OutputType {ElementOutput, DirectiveOutput} export const enum OutputType {ElementOutput, DirectiveOutput}
@ -217,26 +217,26 @@ export const enum QueryValueType {
} }
export interface ElementDef { export interface ElementDef {
name: string; name: string|null;
ns: string; ns: string|null;
/** ns, name, value */ /** ns, name, value */
attrs: [string, string, string][]; attrs: [string, string, string][]|null;
template: ViewDefinition; template: ViewDefinition|null;
componentProvider: NodeDef; componentProvider: NodeDef|null;
componentRendererType: RendererType2; componentRendererType: RendererType2|null;
// closure to allow recursive components // closure to allow recursive components
componentView: ViewDefinitionFactory; componentView: ViewDefinitionFactory|null;
/** /**
* visible public providers for DI in the view, * visible public providers for DI in the view,
* as see from this element. This does not include private providers. * as see from this element. This does not include private providers.
*/ */
publicProviders: {[tokenKey: string]: NodeDef}; publicProviders: {[tokenKey: string]: NodeDef}|null;
/** /**
* same as visiblePublicProviders, but also includes private providers * same as visiblePublicProviders, but also includes private providers
* that are located on this element. * that are located on this element.
*/ */
allProviders: {[tokenKey: string]: NodeDef}; allProviders: {[tokenKey: string]: NodeDef}|null;
handleEvent: ElementHandleEventFn; handleEvent: ElementHandleEventFn|null;
} }
export interface ElementHandleEventFn { (view: ViewData, eventName: string, event: any): boolean; } export interface ElementHandleEventFn { (view: ViewData, eventName: string, event: any): boolean; }
@ -303,9 +303,9 @@ export interface ViewData {
root: RootData; root: RootData;
renderer: Renderer2; renderer: Renderer2;
// index of component provider / anchor. // index of component provider / anchor.
parentNodeDef: NodeDef; parentNodeDef: NodeDef|null;
parent: ViewData; parent: ViewData|null;
viewContainerParent: ViewData; viewContainerParent: ViewData|null;
component: any; component: any;
context: any; context: any;
// Attention: Never loop over this, as this will // Attention: Never loop over this, as this will
@ -316,7 +316,7 @@ export interface ViewData {
nodes: {[key: number]: NodeData}; nodes: {[key: number]: NodeData};
state: ViewState; state: ViewState;
oldValues: any[]; oldValues: any[];
disposables: DisposableFn[]; disposables: DisposableFn[]|null;
} }
/** /**
@ -366,7 +366,7 @@ export function asTextData(view: ViewData, index: number): TextData {
export interface ElementData { export interface ElementData {
renderElement: any; renderElement: any;
componentView: ViewData; componentView: ViewData;
viewContainer: ViewContainerData; viewContainer: ViewContainerData|null;
template: TemplateData; template: TemplateData;
} }
@ -434,7 +434,7 @@ export interface RootData {
export abstract class DebugContext { export abstract class DebugContext {
abstract get view(): ViewData; abstract get view(): ViewData;
abstract get nodeIndex(): number; abstract get nodeIndex(): number|null;
abstract get injector(): Injector; abstract get injector(): Injector;
abstract get component(): any; abstract get component(): any;
abstract get providerTokens(): any[]; abstract get providerTokens(): any[];
@ -461,7 +461,7 @@ export interface Services {
checkNoChangesView(view: ViewData): void; checkNoChangesView(view: ViewData): void;
destroyView(view: ViewData): void; destroyView(view: ViewData): void;
resolveDep( resolveDep(
view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, view: ViewData, elDef: NodeDef|null, allowPrivateServices: boolean, depDef: DepDef,
notFoundValue?: any): any; notFoundValue?: any): any;
createDebugContext(view: ViewData, nodeIndex: number): DebugContext; createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
handleEvent: ViewHandleEventFn; handleEvent: ViewHandleEventFn;
@ -475,16 +475,16 @@ export interface Services {
* debug mode can hook it. It is lazily filled when `isDevMode` is known. * debug mode can hook it. It is lazily filled when `isDevMode` is known.
*/ */
export const Services: Services = { export const Services: Services = {
setCurrentNode: undefined, setCurrentNode: undefined !,
createRootView: undefined, createRootView: undefined !,
createEmbeddedView: undefined, createEmbeddedView: undefined !,
checkAndUpdateView: undefined, checkAndUpdateView: undefined !,
checkNoChangesView: undefined, checkNoChangesView: undefined !,
destroyView: undefined, destroyView: undefined !,
resolveDep: undefined, resolveDep: undefined !,
createDebugContext: undefined, createDebugContext: undefined !,
handleEvent: undefined, handleEvent: undefined !,
updateDirectives: undefined, updateDirectives: undefined !,
updateRenderer: undefined, updateRenderer: undefined !,
dirtyParentQueries: undefined, dirtyParentQueries: undefined !,
}; };

View File

@ -60,7 +60,7 @@ export function createRendererType2(values: {
let _renderCompCount = 0; let _renderCompCount = 0;
export function resolveRendererType2(type: RendererType2): RendererType2 { export function resolveRendererType2(type?: RendererType2 | null): RendererType2|null {
if (type && type.id === UNDEFINED_RENDERER_TYPE_ID) { if (type && type.id === UNDEFINED_RENDERER_TYPE_ID) {
// first time we see this RendererType2. Initialize it... // first time we see this RendererType2. Initialize it...
const isFilled = const isFilled =
@ -75,7 +75,7 @@ export function resolveRendererType2(type: RendererType2): RendererType2 {
if (type && type.id === EMPTY_RENDERER_TYPE_ID) { if (type && type.id === EMPTY_RENDERER_TYPE_ID) {
type = null; type = null;
} }
return type; return type || null;
} }
export function checkBinding( export function checkBinding(
@ -108,7 +108,7 @@ export function checkBindingNoChanges(
} }
export function markParentViewsForCheck(view: ViewData) { export function markParentViewsForCheck(view: ViewData) {
let currView = view; let currView: ViewData|null = view;
while (currView) { while (currView) {
if (currView.def.flags & ViewFlags.OnPush) { if (currView.def.flags & ViewFlags.OnPush) {
currView.state |= ViewState.ChecksEnabled; currView.state |= ViewState.ChecksEnabled;
@ -126,12 +126,12 @@ export function dispatchEvent(
return Services.handleEvent(view, nodeIndex, eventName, event); return Services.handleEvent(view, nodeIndex, eventName, event);
} }
export function declaredViewContainer(view: ViewData): ElementData { export function declaredViewContainer(view: ViewData): ElementData|null {
if (view.parent) { if (view.parent) {
const parentView = view.parent; const parentView = view.parent;
return asElementData(parentView, view.parentNodeDef.index); return asElementData(parentView, view.parentNodeDef !.index);
} }
return undefined; return null;
} }
/** /**
@ -139,10 +139,10 @@ export function declaredViewContainer(view: ViewData): ElementData {
* for embedded views, this is the index of the parent node * for embedded views, this is the index of the parent node
* that contains the view container. * that contains the view container.
*/ */
export function viewParentEl(view: ViewData): NodeDef { export function viewParentEl(view: ViewData): NodeDef|null {
const parentView = view.parent; const parentView = view.parent;
if (parentView) { if (parentView) {
return view.parentNodeDef.parent; return view.parentNodeDef !.parent;
} else { } else {
return null; return null;
} }
@ -157,23 +157,24 @@ export function renderNode(view: ViewData, def: NodeDef): any {
} }
} }
export function elementEventFullName(target: string, name: string): string { export function elementEventFullName(target: string | null, name: string): string {
return target ? `${target}:${name}` : name; return target ? `${target}:${name}` : name;
} }
export function isComponentView(view: ViewData): boolean { export function isComponentView(view: ViewData): boolean {
return !!view.parent && !!(view.parentNodeDef.flags & NodeFlags.Component); return !!view.parent && !!(view.parentNodeDef !.flags & NodeFlags.Component);
} }
export function isEmbeddedView(view: ViewData): boolean { export function isEmbeddedView(view: ViewData): boolean {
return !!view.parent && !(view.parentNodeDef.flags & NodeFlags.Component); return !!view.parent && !(view.parentNodeDef !.flags & NodeFlags.Component);
} }
export function filterQueryId(queryId: number): number { export function filterQueryId(queryId: number): number {
return 1 << (queryId % 32); return 1 << (queryId % 32);
} }
export function splitMatchedQueriesDsl(matchedQueriesDsl: [string | number, QueryValueType][]): { export function splitMatchedQueriesDsl(
matchedQueriesDsl: [string | number, QueryValueType][] | null): {
matchedQueries: {[queryId: string]: QueryValueType}, matchedQueries: {[queryId: string]: QueryValueType},
references: {[refId: string]: QueryValueType}, references: {[refId: string]: QueryValueType},
matchedQueryIds: number matchedQueryIds: number
@ -199,11 +200,12 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod
if (renderParent) { if (renderParent) {
if ((renderParent.flags & NodeFlags.TypeElement) === 0 || if ((renderParent.flags & NodeFlags.TypeElement) === 0 ||
(renderParent.flags & NodeFlags.ComponentView) === 0 || (renderParent.flags & NodeFlags.ComponentView) === 0 ||
(renderParent.element.componentRendererType && (renderParent.element !.componentRendererType &&
renderParent.element.componentRendererType.encapsulation === ViewEncapsulation.Native)) { renderParent.element !.componentRendererType !.encapsulation ===
ViewEncapsulation.Native)) {
// only children of non components, or children of components with native encapsulation should // only children of non components, or children of components with native encapsulation should
// be attached. // be attached.
return asElementData(view, def.renderParent.index).renderElement; return asElementData(view, def.renderParent !.index).renderElement;
} }
} else { } else {
return renderHost; return renderHost;
@ -213,7 +215,7 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod
const VIEW_DEFINITION_CACHE = new WeakMap<any, ViewDefinition>(); const VIEW_DEFINITION_CACHE = new WeakMap<any, ViewDefinition>();
export function resolveViewDefinition(factory: ViewDefinitionFactory): ViewDefinition { export function resolveViewDefinition(factory: ViewDefinitionFactory): ViewDefinition {
let value: ViewDefinition = VIEW_DEFINITION_CACHE.get(factory); let value: ViewDefinition = VIEW_DEFINITION_CACHE.get(factory) !;
if (!value) { if (!value) {
value = factory(() => NOOP); value = factory(() => NOOP);
value.factory = factory; value.factory = factory;
@ -231,10 +233,10 @@ export function rootRenderNodes(view: ViewData): any[] {
export const enum RenderNodeAction {Collect, AppendChild, InsertBefore, RemoveChild} export const enum RenderNodeAction {Collect, AppendChild, InsertBefore, RemoveChild}
export function visitRootRenderNodes( export function visitRootRenderNodes(
view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target: any[]) { view: ViewData, action: RenderNodeAction, parentNode: any, nextSibling: any, target?: any[]) {
// We need to re-compute the parent node in case the nodes have been moved around manually // We need to re-compute the parent node in case the nodes have been moved around manually
if (action === RenderNodeAction.RemoveChild) { if (action === RenderNodeAction.RemoveChild) {
parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRenderRootNode)); parentNode = view.renderer.parentNode(renderNode(view, view.def.lastRenderRootNode !));
} }
visitSiblingRenderNodes( visitSiblingRenderNodes(
view, action, 0, view.def.nodes.length - 1, parentNode, nextSibling, target); view, action, 0, view.def.nodes.length - 1, parentNode, nextSibling, target);
@ -242,7 +244,7 @@ export function visitRootRenderNodes(
export function visitSiblingRenderNodes( export function visitSiblingRenderNodes(
view: ViewData, action: RenderNodeAction, startIndex: number, endIndex: number, parentNode: any, view: ViewData, action: RenderNodeAction, startIndex: number, endIndex: number, parentNode: any,
nextSibling: any, target: any[]) { nextSibling: any, target?: any[]) {
for (let i = startIndex; i <= endIndex; i++) { for (let i = startIndex; i <= endIndex; i++) {
const nodeDef = view.def.nodes[i]; const nodeDef = view.def.nodes[i];
if (nodeDef.flags & (NodeFlags.TypeElement | NodeFlags.TypeText | NodeFlags.TypeNgContent)) { if (nodeDef.flags & (NodeFlags.TypeElement | NodeFlags.TypeText | NodeFlags.TypeNgContent)) {
@ -255,24 +257,24 @@ export function visitSiblingRenderNodes(
export function visitProjectedRenderNodes( export function visitProjectedRenderNodes(
view: ViewData, ngContentIndex: number, action: RenderNodeAction, parentNode: any, view: ViewData, ngContentIndex: number, action: RenderNodeAction, parentNode: any,
nextSibling: any, target: any[]) { nextSibling: any, target?: any[]) {
let compView = view; let compView: ViewData|null = view;
while (compView && !isComponentView(compView)) { while (compView && !isComponentView(compView)) {
compView = compView.parent; compView = compView.parent;
} }
const hostView = compView.parent; const hostView = compView !.parent;
const hostElDef = viewParentEl(compView); const hostElDef = viewParentEl(compView !);
const startIndex = hostElDef.index + 1; const startIndex = hostElDef !.index + 1;
const endIndex = hostElDef.index + hostElDef.childCount; const endIndex = hostElDef !.index + hostElDef !.childCount;
for (let i = startIndex; i <= endIndex; i++) { for (let i = startIndex; i <= endIndex; i++) {
const nodeDef = hostView.def.nodes[i]; const nodeDef = hostView !.def.nodes[i];
if (nodeDef.ngContentIndex === ngContentIndex) { if (nodeDef.ngContentIndex === ngContentIndex) {
visitRenderNode(hostView, nodeDef, action, parentNode, nextSibling, target); visitRenderNode(hostView !, nodeDef, action, parentNode, nextSibling, target);
} }
// jump to next sibling // jump to next sibling
i += nodeDef.childCount; i += nodeDef.childCount;
} }
if (!hostView.parent) { if (!hostView !.parent) {
// a root view // a root view
const projectedNodes = view.root.projectableNodes[ngContentIndex]; const projectedNodes = view.root.projectableNodes[ngContentIndex];
if (projectedNodes) { if (projectedNodes) {
@ -285,10 +287,10 @@ export function visitProjectedRenderNodes(
function visitRenderNode( function visitRenderNode(
view: ViewData, nodeDef: NodeDef, action: RenderNodeAction, parentNode: any, nextSibling: any, view: ViewData, nodeDef: NodeDef, action: RenderNodeAction, parentNode: any, nextSibling: any,
target: any[]) { target?: any[]) {
if (nodeDef.flags & NodeFlags.TypeNgContent) { if (nodeDef.flags & NodeFlags.TypeNgContent) {
visitProjectedRenderNodes( visitProjectedRenderNodes(
view, nodeDef.ngContent.index, action, parentNode, nextSibling, target); view, nodeDef.ngContent !.index, action, parentNode, nextSibling, target);
} else { } else {
const rn = renderNode(view, nodeDef); const rn = renderNode(view, nodeDef);
if (action === RenderNodeAction.RemoveChild && (nodeDef.flags & NodeFlags.ComponentView) && if (action === RenderNodeAction.RemoveChild && (nodeDef.flags & NodeFlags.ComponentView) &&
@ -305,12 +307,12 @@ function visitRenderNode(
execRenderNodeAction(view, rn, action, parentNode, nextSibling, target); execRenderNodeAction(view, rn, action, parentNode, nextSibling, target);
} }
if (nodeDef.flags & NodeFlags.EmbeddedViews) { if (nodeDef.flags & NodeFlags.EmbeddedViews) {
const embeddedViews = asElementData(view, nodeDef.index).viewContainer._embeddedViews; const embeddedViews = asElementData(view, nodeDef.index).viewContainer !._embeddedViews;
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
visitRootRenderNodes(embeddedViews[k], action, parentNode, nextSibling, target); visitRootRenderNodes(embeddedViews[k], action, parentNode, nextSibling, target);
} }
} }
if (nodeDef.flags & NodeFlags.TypeElement && !nodeDef.element.name) { if (nodeDef.flags & NodeFlags.TypeElement && !nodeDef.element !.name) {
visitSiblingRenderNodes( visitSiblingRenderNodes(
view, action, nodeDef.index + 1, nodeDef.index + nodeDef.childCount, parentNode, view, action, nodeDef.index + 1, nodeDef.index + nodeDef.childCount, parentNode,
nextSibling, target); nextSibling, target);
@ -320,7 +322,7 @@ function visitRenderNode(
function execRenderNodeAction( function execRenderNodeAction(
view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any, view: ViewData, renderNode: any, action: RenderNodeAction, parentNode: any, nextSibling: any,
target: any[]) { target?: any[]) {
const renderer = view.renderer; const renderer = view.renderer;
switch (action) { switch (action) {
case RenderNodeAction.AppendChild: case RenderNodeAction.AppendChild:
@ -333,7 +335,7 @@ function execRenderNodeAction(
renderer.removeChild(parentNode, renderNode); renderer.removeChild(parentNode, renderNode);
break; break;
case RenderNodeAction.Collect: case RenderNodeAction.Collect:
target.push(renderNode); target !.push(renderNode);
break; break;
} }
} }
@ -342,7 +344,7 @@ const NS_PREFIX_RE = /^:([^:]+):(.+)$/;
export function splitNamespace(name: string): string[] { export function splitNamespace(name: string): string[] {
if (name[0] === ':') { if (name[0] === ':') {
const match = name.match(NS_PREFIX_RE); const match = name.match(NS_PREFIX_RE) !;
return [match[1], match[2]]; return [match[1], match[2]];
} }
return ['', name]; return ['', name];

View File

@ -28,15 +28,15 @@ export function viewDef(
let viewNodeFlags = 0; let viewNodeFlags = 0;
let viewRootNodeFlags = 0; let viewRootNodeFlags = 0;
let viewMatchedQueries = 0; let viewMatchedQueries = 0;
let currentParent: NodeDef = null; let currentParent: NodeDef|null = null;
let currentElementHasPublicProviders = false; let currentElementHasPublicProviders = false;
let currentElementHasPrivateProviders = false; let currentElementHasPrivateProviders = false;
let lastRenderRootNode: NodeDef = null; let lastRenderRootNode: NodeDef|null = null;
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
while (currentParent && i > currentParent.index + currentParent.childCount) { while (currentParent && i > currentParent.index + currentParent.childCount) {
const newParent = currentParent.parent; const newParent: NodeDef|null = currentParent.parent;
if (newParent) { if (newParent) {
newParent.childFlags |= currentParent.childFlags; newParent.childFlags |= currentParent.childFlags !;
newParent.childMatchedQueries |= currentParent.childMatchedQueries; newParent.childMatchedQueries |= currentParent.childMatchedQueries;
} }
currentParent = newParent; currentParent = newParent;
@ -48,9 +48,9 @@ export function viewDef(
node.outputIndex = viewDisposableCount; node.outputIndex = viewDisposableCount;
// renderParent needs to account for ng-container! // renderParent needs to account for ng-container!
let currentRenderParent: NodeDef; let currentRenderParent: NodeDef|null;
if (currentParent && currentParent.flags & NodeFlags.TypeElement && if (currentParent && currentParent.flags & NodeFlags.TypeElement &&
!currentParent.element.name) { !currentParent.element !.name) {
currentRenderParent = currentParent.renderParent; currentRenderParent = currentParent.renderParent;
} else { } else {
currentRenderParent = currentParent; currentRenderParent = currentParent;
@ -60,7 +60,7 @@ export function viewDef(
if (node.element) { if (node.element) {
const elDef = node.element; const elDef = node.element;
elDef.publicProviders = elDef.publicProviders =
currentParent ? currentParent.element.publicProviders : Object.create(null); currentParent ? currentParent.element !.publicProviders : Object.create(null);
elDef.allProviders = elDef.publicProviders; elDef.allProviders = elDef.publicProviders;
// Note: We assume that all providers of an element are before any child element! // Note: We assume that all providers of an element are before any child element!
currentElementHasPublicProviders = false; currentElementHasPublicProviders = false;
@ -94,24 +94,25 @@ export function viewDef(
if (!currentElementHasPublicProviders) { if (!currentElementHasPublicProviders) {
currentElementHasPublicProviders = true; currentElementHasPublicProviders = true;
// Use prototypical inheritance to not get O(n^2) complexity... // Use prototypical inheritance to not get O(n^2) complexity...
currentParent.element.publicProviders = currentParent !.element !.publicProviders =
Object.create(currentParent.element.publicProviders); Object.create(currentParent !.element !.publicProviders);
currentParent.element.allProviders = currentParent.element.publicProviders; currentParent !.element !.allProviders = currentParent !.element !.publicProviders;
} }
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0; const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
const isComponent = (node.flags & NodeFlags.Component) !== 0; const isComponent = (node.flags & NodeFlags.Component) !== 0;
if (!isPrivateService || isComponent) { if (!isPrivateService || isComponent) {
currentParent.element.publicProviders[node.provider.tokenKey] = node; currentParent !.element !.publicProviders ![node.provider !.tokenKey] = node;
} else { } else {
if (!currentElementHasPrivateProviders) { if (!currentElementHasPrivateProviders) {
currentElementHasPrivateProviders = true; currentElementHasPrivateProviders = true;
// Use protoyypical inheritance to not get O(n^2) complexity... // Use protoyypical inheritance to not get O(n^2) complexity...
currentParent.element.allProviders = Object.create(currentParent.element.publicProviders); currentParent !.element !.allProviders =
Object.create(currentParent !.element !.publicProviders);
} }
currentParent.element.allProviders[node.provider.tokenKey] = node; currentParent !.element !.allProviders ![node.provider !.tokenKey] = node;
} }
if (isComponent) { if (isComponent) {
currentParent.element.componentProvider = node; currentParent !.element !.componentProvider = node;
} }
} }
if (node.childCount) { if (node.childCount) {
@ -127,10 +128,10 @@ export function viewDef(
currentParent = newParent; currentParent = newParent;
} }
const handleEvent: ViewHandleEventFn = (view, nodeIndex, eventName, event) => const handleEvent: ViewHandleEventFn = (view, nodeIndex, eventName, event) =>
nodes[nodeIndex].element.handleEvent(view, eventName, event); nodes[nodeIndex].element !.handleEvent !(view, eventName, event);
return { return {
// Will be filled later... // Will be filled later...
factory: undefined, factory: null,
nodeFlags: viewNodeFlags, nodeFlags: viewNodeFlags,
rootNodeFlags: viewRootNodeFlags, rootNodeFlags: viewRootNodeFlags,
nodeMatchedQueries: viewMatchedQueries, flags, nodeMatchedQueries: viewMatchedQueries, flags,
@ -143,7 +144,7 @@ export function viewDef(
}; };
} }
function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) { function validateNode(parent: NodeDef | null, node: NodeDef, nodeCount: number) {
const template = node.element && node.element.template; const template = node.element && node.element.template;
if (template) { if (template) {
if (!template.lastRenderRootNode) { if (!template.lastRenderRootNode) {
@ -156,7 +157,7 @@ function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
} }
} }
if (node.flags & NodeFlags.CatProvider) { if (node.flags & NodeFlags.CatProvider) {
const parentFlags = parent ? parent.flags : null; const parentFlags = parent ? parent.flags : 0;
if ((parentFlags & NodeFlags.TypeElement) === 0) { if ((parentFlags & NodeFlags.TypeElement) === 0) {
throw new Error( throw new Error(
`Illegal State: Provider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`); `Illegal State: Provider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`);
@ -186,7 +187,7 @@ export function createEmbeddedView(parent: ViewData, anchorDef: NodeDef, context
// embedded views are seen as siblings to the anchor, so we need // embedded views are seen as siblings to the anchor, so we need
// to get the parent of the anchor and use it as parentIndex. // to get the parent of the anchor and use it as parentIndex.
const view = const view =
createView(parent.root, parent.renderer, parent, anchorDef, anchorDef.element.template); createView(parent.root, parent.renderer, parent, anchorDef, anchorDef.element !.template !);
initView(view, parent.component, context); initView(view, parent.component, context);
createViewNodes(view); createViewNodes(view);
return view; return view;
@ -200,16 +201,16 @@ export function createRootView(root: RootData, def: ViewDefinition, context?: an
} }
function createView( function createView(
root: RootData, renderer: Renderer2, parent: ViewData, parentNodeDef: NodeDef, root: RootData, renderer: Renderer2, parent: ViewData | null, parentNodeDef: NodeDef | null,
def: ViewDefinition): ViewData { def: ViewDefinition): ViewData {
const nodes: NodeData[] = new Array(def.nodes.length); const nodes: NodeData[] = new Array(def.nodes.length);
const disposables = def.outputCount ? new Array(def.outputCount) : undefined; const disposables = def.outputCount ? new Array(def.outputCount) : null;
const view: ViewData = { const view: ViewData = {
def, def,
parent, parent,
viewContainerParent: undefined, parentNodeDef, viewContainerParent: null, parentNodeDef,
context: undefined, context: null,
component: undefined, nodes, component: null, nodes,
state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer, state: ViewState.FirstCheck | ViewState.ChecksEnabled, root, renderer,
oldValues: new Array(def.bindingCount), disposables oldValues: new Array(def.bindingCount), disposables
}; };
@ -225,7 +226,7 @@ function createViewNodes(view: ViewData) {
let renderHost: any; let renderHost: any;
if (isComponentView(view)) { if (isComponentView(view)) {
const hostDef = view.parentNodeDef; const hostDef = view.parentNodeDef;
renderHost = asElementData(view.parent, hostDef.parent.index).renderElement; renderHost = asElementData(view.parent !, hostDef !.parent !.index).renderElement;
} }
const def = view.def; const def = view.def;
const nodes = view.nodes; const nodes = view.nodes;
@ -236,10 +237,10 @@ function createViewNodes(view: ViewData) {
switch (nodeDef.flags & NodeFlags.Types) { switch (nodeDef.flags & NodeFlags.Types) {
case NodeFlags.TypeElement: case NodeFlags.TypeElement:
const el = createElement(view, renderHost, nodeDef) as any; const el = createElement(view, renderHost, nodeDef) as any;
let componentView: ViewData; let componentView: ViewData = undefined !;
if (nodeDef.flags & NodeFlags.ComponentView) { if (nodeDef.flags & NodeFlags.ComponentView) {
const compViewDef = resolveViewDefinition(nodeDef.element.componentView); const compViewDef = resolveViewDefinition(nodeDef.element !.componentView !);
const rendererType = nodeDef.element.componentRendererType; const rendererType = nodeDef.element !.componentRendererType;
let compRenderer: Renderer2; let compRenderer: Renderer2;
if (!rendererType) { if (!rendererType) {
compRenderer = view.root.renderer; compRenderer = view.root.renderer;
@ -247,14 +248,14 @@ function createViewNodes(view: ViewData) {
compRenderer = view.root.rendererFactory.createRenderer(el, rendererType); compRenderer = view.root.rendererFactory.createRenderer(el, rendererType);
} }
componentView = createView( componentView = createView(
view.root, compRenderer, view, nodeDef.element.componentProvider, compViewDef); view.root, compRenderer, view, nodeDef.element !.componentProvider, compViewDef);
} }
listenToElementOutputs(view, componentView, nodeDef, el); listenToElementOutputs(view, componentView, nodeDef, el);
nodeData = <ElementData>{ nodeData = <ElementData>{
renderElement: el, renderElement: el,
componentView, componentView,
viewContainer: undefined, viewContainer: null,
template: nodeDef.element.template ? createTemplateData(view, nodeDef) : undefined template: nodeDef.element !.template ? createTemplateData(view, nodeDef) : undefined
}; };
if (nodeDef.flags & NodeFlags.EmbeddedViews) { if (nodeDef.flags & NodeFlags.EmbeddedViews) {
nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData); nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData);
@ -280,7 +281,7 @@ function createViewNodes(view: ViewData) {
const instance = createDirectiveInstance(view, nodeDef); const instance = createDirectiveInstance(view, nodeDef);
nodeData = <ProviderData>{instance}; nodeData = <ProviderData>{instance};
if (nodeDef.flags & NodeFlags.Component) { if (nodeDef.flags & NodeFlags.Component) {
const compView = asElementData(view, nodeDef.parent.index).componentView; const compView = asElementData(view, nodeDef.parent !.index).componentView;
initView(compView, instance, instance); initView(compView, instance, instance);
} }
break; break;
@ -451,8 +452,8 @@ function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
const queryList = asQueryList(view, nodeDef.index); const queryList = asQueryList(view, nodeDef.index);
if (queryList.dirty) { if (queryList.dirty) {
throw expressionChangedAfterItHasBeenCheckedError( throw expressionChangedAfterItHasBeenCheckedError(
Services.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query.id} not dirty`, Services.createDebugContext(view, nodeDef.index), `Query ${nodeDef.query!.id} not dirty`,
`Query ${nodeDef.query.id} dirty`, (view.state & ViewState.FirstCheck) !== 0); `Query ${nodeDef.query!.id} dirty`, (view.state & ViewState.FirstCheck) !== 0);
} }
} }
@ -482,9 +483,9 @@ function destroyViewNodes(view: ViewData) {
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const def = view.def.nodes[i]; const def = view.def.nodes[i];
if (def.flags & NodeFlags.TypeElement) { if (def.flags & NodeFlags.TypeElement) {
view.renderer.destroyNode(asElementData(view, i).renderElement); view.renderer.destroyNode !(asElementData(view, i).renderElement);
} else if (def.flags & NodeFlags.TypeText) { } else if (def.flags & NodeFlags.TypeText) {
view.renderer.destroyNode(asTextData(view, i).renderText); view.renderer.destroyNode !(asTextData(view, i).renderText);
} }
} }
} }
@ -524,7 +525,7 @@ function execEmbeddedViewsAction(view: ViewData, action: ViewAction) {
const nodeDef = def.nodes[i]; const nodeDef = def.nodes[i];
if (nodeDef.flags & NodeFlags.EmbeddedViews) { if (nodeDef.flags & NodeFlags.EmbeddedViews) {
// a leaf // a leaf
const embeddedViews = asElementData(view, i).viewContainer._embeddedViews; const embeddedViews = asElementData(view, i).viewContainer !._embeddedViews;
for (let k = 0; k < embeddedViews.length; k++) { for (let k = 0; k < embeddedViews.length; k++) {
callViewAction(embeddedViews[k], action); callViewAction(embeddedViews[k], action);
} }

View File

@ -10,13 +10,14 @@ import {ElementData, Services, ViewData} from './types';
import {RenderNodeAction, declaredViewContainer, renderNode, visitRootRenderNodes} from './util'; import {RenderNodeAction, declaredViewContainer, renderNode, visitRootRenderNodes} from './util';
export function attachEmbeddedView( export function attachEmbeddedView(
parentView: ViewData, elementData: ElementData, viewIndex: number, view: ViewData) { parentView: ViewData, elementData: ElementData, viewIndex: number | undefined | null,
let embeddedViews = elementData.viewContainer._embeddedViews; view: ViewData) {
if (viewIndex == null) { let embeddedViews = elementData.viewContainer !._embeddedViews;
if (viewIndex === null || viewIndex === undefined) {
viewIndex = embeddedViews.length; viewIndex = embeddedViews.length;
} }
view.viewContainerParent = parentView; view.viewContainerParent = parentView;
addToArray(embeddedViews, viewIndex, view); addToArray(embeddedViews, viewIndex !, view);
const dvcElementData = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
if (dvcElementData && dvcElementData !== elementData) { if (dvcElementData && dvcElementData !== elementData) {
let projectedViews = dvcElementData.template._projectedViews; let projectedViews = dvcElementData.template._projectedViews;
@ -28,12 +29,12 @@ export function attachEmbeddedView(
Services.dirtyParentQueries(view); Services.dirtyParentQueries(view);
const prevView = viewIndex > 0 ? embeddedViews[viewIndex - 1] : null; const prevView = viewIndex ! > 0 ? embeddedViews[viewIndex ! - 1] : null;
renderAttachEmbeddedView(elementData, prevView, view); renderAttachEmbeddedView(elementData, prevView, view);
} }
export function detachEmbeddedView(elementData: ElementData, viewIndex: number): ViewData { export function detachEmbeddedView(elementData: ElementData, viewIndex?: number): ViewData|null {
const embeddedViews = elementData.viewContainer._embeddedViews; const embeddedViews = elementData.viewContainer !._embeddedViews;
if (viewIndex == null || viewIndex >= embeddedViews.length) { if (viewIndex == null || viewIndex >= embeddedViews.length) {
viewIndex = embeddedViews.length - 1; viewIndex = embeddedViews.length - 1;
} }
@ -41,7 +42,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
return null; return null;
} }
const view = embeddedViews[viewIndex]; const view = embeddedViews[viewIndex];
view.viewContainerParent = undefined; view.viewContainerParent = null;
removeFromArray(embeddedViews, viewIndex); removeFromArray(embeddedViews, viewIndex);
const dvcElementData = declaredViewContainer(view); const dvcElementData = declaredViewContainer(view);
@ -59,7 +60,7 @@ export function detachEmbeddedView(elementData: ElementData, viewIndex: number):
export function moveEmbeddedView( export function moveEmbeddedView(
elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData { elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData {
const embeddedViews = elementData.viewContainer._embeddedViews; const embeddedViews = elementData.viewContainer !._embeddedViews;
const view = embeddedViews[oldViewIndex]; const view = embeddedViews[oldViewIndex];
removeFromArray(embeddedViews, oldViewIndex); removeFromArray(embeddedViews, oldViewIndex);
if (newViewIndex == null) { if (newViewIndex == null) {
@ -79,9 +80,10 @@ export function moveEmbeddedView(
return view; return view;
} }
function renderAttachEmbeddedView(elementData: ElementData, prevView: ViewData, view: ViewData) { function renderAttachEmbeddedView(
const prevRenderNode = elementData: ElementData, prevView: ViewData | null, view: ViewData) {
prevView ? renderNode(prevView, prevView.def.lastRenderRootNode) : elementData.renderElement; const prevRenderNode = prevView ? renderNode(prevView, prevView.def.lastRenderRootNode !) :
elementData.renderElement;
const parentNode = view.renderer.parentNode(prevRenderNode); const parentNode = view.renderer.parentNode(prevRenderNode);
const nextSibling = view.renderer.nextSibling(prevRenderNode); const nextSibling = view.renderer.nextSibling(prevRenderNode);
// Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be! // Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be!

View File

@ -60,7 +60,7 @@ export function main() {
engine.flush(); engine.flush();
expect(getLog().length).toEqual(1); expect(getLog().length).toEqual(1);
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}
]); ]);
}); });
@ -96,7 +96,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, width: '0px'}, {offset: 1, width: '100px'} {offset: 0, width: '0px'}, {offset: 1, width: '100px'}
]); ]);
@ -105,7 +105,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, height: '0px'}, {offset: 1, height: '100px'} {offset: 0, height: '0px'}, {offset: 1, height: '100px'}
]); ]);
@ -114,7 +114,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, height: '0px'}, {offset: 1, height: '100px'} {offset: 0, height: '0px'}, {offset: 1, height: '100px'}
]); ]);
@ -123,7 +123,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, height: '0px'}, {offset: 1, height: '100px'} {offset: 0, height: '0px'}, {offset: 1, height: '100px'}
]); ]);
@ -132,7 +132,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, height: '0px'}, {offset: 1, height: '100px'} {offset: 0, height: '0px'}, {offset: 1, height: '100px'}
]); ]);
@ -142,7 +142,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, width: '0px'}, {offset: 1, width: '100px'} {offset: 0, width: '0px'}, {offset: 1, width: '100px'}
]); ]);
}); });
@ -174,7 +174,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'} {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}
]); ]);
@ -182,7 +182,7 @@ export function main() {
fixture.detectChanges(); fixture.detectChanges();
engine.flush(); engine.flush();
expect(getLog().pop().keyframes).toEqual([ expect(getLog().pop() !.keyframes).toEqual([
{offset: 0, opacity: '1'}, {offset: 1, opacity: '0'} {offset: 0, opacity: '1'}, {offset: 1, opacity: '0'}
]); ]);
}); });
@ -233,7 +233,7 @@ export function main() {
engine.flush(); engine.flush();
expect(getLog().length).toEqual(1); expect(getLog().length).toEqual(1);
const data = getLog().pop(); const data = getLog().pop() !;
expect(data.element).toEqual(fixture.elementRef.nativeElement); expect(data.element).toEqual(fixture.elementRef.nativeElement);
expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]); expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]);
})); }));
@ -679,8 +679,8 @@ export function main() {
expect(getLog().length).toEqual(2); expect(getLog().length).toEqual(2);
const player2 = getLog().pop(); const player2 = getLog().pop() !;
const player1 = getLog().pop(); const player1 = getLog().pop() !;
expect(player2.keyframes).toEqual([ expect(player2.keyframes).toEqual([
{width: AUTO_STYLE, offset: 0}, {width: AUTO_STYLE, offset: 0},
@ -712,7 +712,7 @@ export function main() {
] ]
}) })
class Cmp { class Cmp {
public exp: string; public exp: string|null;
} }
TestBed.configureTestingModule({declarations: [Cmp]}); TestBed.configureTestingModule({declarations: [Cmp]});

View File

@ -0,0 +1,94 @@
/**
* @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 {animate, style, transition, trigger} from '@angular/animations';
import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser';
import {ɵDomAnimationEngine, ɵWebAnimationsDriver, ɵWebAnimationsPlayer, ɵsupportsWebAnimations} from '@angular/animations/browser'
import {Component, ViewChild} from '@angular/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {TestBed} from '../../testing';
export function main() {
// these tests are only mean't to be run within the DOM (for now)
if (typeof Element == 'undefined' || !ɵsupportsWebAnimations()) return;
describe('animation integration tests using web animations', function() {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}],
imports: [BrowserAnimationsModule]
});
});
it('should animate a component that captures height during an animation', () => {
@Component({
selector: 'if-cmp',
template: `
<div *ngIf="exp" #element [@myAnimation]="exp">
hello {{ text }}
</div>
`,
animations: [trigger(
'myAnimation',
[
transition('* => *', [style({height: '0px'}), animate(1000, style({height: '*'}))]),
])]
})
class Cmp {
exp: any = false;
text: string;
@ViewChild('element') public element: any;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = 1;
cmp.text = '';
fixture.detectChanges();
engine.flush();
const element = cmp.element.nativeElement;
element.style.lineHeight = '20px';
element.style.width = '50px';
cmp.exp = 2;
cmp.text = '12345';
fixture.detectChanges();
engine.flush();
let player = engine.activePlayers.pop() as ɵWebAnimationsPlayer;
player.setPosition(1);
assertStyleBetween(element, 'height', 15, 25);
cmp.exp = 3;
cmp.text = '12345-12345-12345-12345';
fixture.detectChanges();
engine.flush();
player = engine.activePlayers.pop() as ɵWebAnimationsPlayer;
player.setPosition(1);
assertStyleBetween(element, 'height', 35, 45);
});
});
}
function assertStyleBetween(
element: any, prop: string, start: string | number, end: string | number) {
const style = (window.getComputedStyle(element) as any)[prop] as string;
if (typeof start == 'number' && typeof end == 'number') {
const value = parseFloat(style);
expect(value).toBeGreaterThan(start);
expect(value).toBeLessThan(end);
}
}

View File

@ -45,7 +45,7 @@ export function main() {
function createModule(providers?: any[]): Type<any>; function createModule(providers?: any[]): Type<any>;
function createModule(options: CreateModuleOptions): Type<any>; function createModule(options: CreateModuleOptions): Type<any>;
function createModule(providersOrOptions: any[] | CreateModuleOptions): Type<any> { function createModule(providersOrOptions: any[] | CreateModuleOptions | undefined): Type<any> {
let options: CreateModuleOptions = {}; let options: CreateModuleOptions = {};
if (providersOrOptions instanceof Array) { if (providersOrOptions instanceof Array) {
options = {providers: providersOrOptions}; options = {providers: providersOrOptions};
@ -92,7 +92,8 @@ export function main() {
createRootEl(); createRootEl();
const modFactory = compiler.compileModuleSync(SomeModule); const modFactory = compiler.compileModuleSync(SomeModule);
const module = modFactory.create(TestBed); const module = modFactory.create(TestBed);
const cmpFactory = module.componentFactoryResolver.resolveComponentFactory(SomeComponent); const cmpFactory =
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
const component = app.bootstrap(cmpFactory); const component = app.bootstrap(cmpFactory);
// The component should see the child module providers // The component should see the child module providers
@ -388,7 +389,7 @@ export function main() {
vc.insert(hostView); vc.insert(hostView);
expect(() => appRef.attachView(hostView)) expect(() => appRef.attachView(hostView))
.toThrowError('This view is already attached to a ViewContainer!'); .toThrowError('This view is already attached to a ViewContainer!');
hostView = vc.detach(0); hostView = vc.detach(0) !;
appRef.attachView(hostView); appRef.attachView(hostView);
expect(() => vc.insert(hostView)) expect(() => vc.insert(hostView))

View File

@ -309,7 +309,7 @@ export function main() {
function modifyArrayUsingOperation( function modifyArrayUsingOperation(
arr: number[], endData: any[], prev: number, next: number) { arr: number[], endData: any[], prev: number, next: number) {
let value: number = null; let value: number = null !;
if (prev == null) { if (prev == null) {
value = endData[next]; value = endData[next];
arr.splice(next, 0, value); arr.splice(next, 0, value);
@ -472,7 +472,7 @@ export function main() {
}); });
it('should throw when given an invalid collection', () => { it('should throw when given an invalid collection', () => {
expect(() => differ.diff('invalid')).toThrowError('Error trying to diff \'invalid\''); expect(() => differ.diff('invalid')).toThrowError(/Error trying to diff 'invalid'/);
}); });
}); });
}); });

View File

@ -7,7 +7,6 @@
*/ */
import {DefaultKeyValueDiffer, DefaultKeyValueDifferFactory} from '@angular/core/src/change_detection/differs/default_keyvalue_differ'; import {DefaultKeyValueDiffer, DefaultKeyValueDifferFactory} from '@angular/core/src/change_detection/differs/default_keyvalue_differ';
import {afterEach, beforeEach, describe, expect, it} from '@angular/core/testing/src/testing_internal';
import {kvChangesAsString} from '../../change_detection/util'; import {kvChangesAsString} from '../../change_detection/util';
// todo(vicb): Update the code & tests for object equality // todo(vicb): Update the code & tests for object equality
@ -22,7 +21,7 @@ export function main() {
m = new Map(); m = new Map();
}); });
afterEach(() => { differ = null; }); afterEach(() => { differ = null !; });
it('should detect additions', () => { it('should detect additions', () => {
differ.check(m); differ.check(m);
@ -55,21 +54,17 @@ export function main() {
}); });
it('should expose previous and current value', () => { it('should expose previous and current value', () => {
let previous: any /** TODO #9100 */, current: any /** TODO #9100 */;
m.set(1, 10); m.set(1, 10);
differ.check(m); differ.check(m);
m.set(1, 20); m.set(1, 20);
differ.check(m); differ.check(m);
differ.forEachChangedItem((record: any /** TODO #9100 */) => { differ.forEachChangedItem((record: any) => {
previous = record.previousValue; expect(record.previousValue).toEqual(10);
current = record.currentValue; expect(record.currentValue).toEqual(20);
}); });
expect(previous).toEqual(10);
expect(current).toEqual(20);
}); });
it('should do basic map watching', () => { it('should do basic map watching', () => {
@ -198,6 +193,19 @@ export function main() {
changes: ['b[0->1]', 'a[0->1]'] changes: ['b[0->1]', 'a[0->1]']
})); }));
}); });
it('should when the first item is moved', () => {
differ.check({a: 'a', b: 'b'});
differ.check({c: 'c', a: 'a'});
expect(differ.toString()).toEqual(kvChangesAsString({
map: ['c[null->c]', 'a'],
previous: ['a', 'b[b->null]'],
additions: ['c[null->c]'],
removals: ['b[b->null]']
}));
});
}); });
describe('diff', () => { describe('diff', () => {
@ -220,8 +228,7 @@ export function main() {
}); });
it('should throw when given an invalid collection', () => { it('should throw when given an invalid collection', () => {
expect(() => differ.diff(<any>'invalid')) expect(() => differ.diff(<any>'invalid')).toThrowError(/Error trying to diff 'invalid'/);
.toThrowError('Error trying to diff \'invalid\'');
}); });
}); });
}); });

View File

@ -318,7 +318,7 @@ export function main() {
fixture = TestBed.createComponent(LocalsComp); fixture = TestBed.createComponent(LocalsComp);
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.children[0].references['alice']).toBeAnInstanceOf(MyDir); expect(fixture.debugElement.children[0].references !['alice']).toBeAnInstanceOf(MyDir);
}); });
it('should allow injecting from the element injector', () => { it('should allow injecting from the element injector', () => {

View File

@ -79,7 +79,7 @@ export function main() {
]; ];
function createInjector( function createInjector(
providers: Provider[], parent: ReflectiveInjector = null): ReflectiveInjector_ { providers: Provider[], parent?: ReflectiveInjector | null): ReflectiveInjector_ {
const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders)); const resolvedProviders = ReflectiveInjector.resolve(providers.concat(dynamicProviders));
if (parent != null) { if (parent != null) {
return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders); return <ReflectiveInjector_>parent.createChildFromResolved(resolvedProviders);

View File

@ -100,7 +100,7 @@ export function main() {
const headEl = defaultDoc.head; const headEl = defaultDoc.head;
getDOM().appendChild(headEl, baseEl); getDOM().appendChild(headEl, baseEl);
const baseHref = getDOM().getBaseHref(defaultDoc); const baseHref = getDOM().getBaseHref(defaultDoc) !;
getDOM().removeChild(headEl, baseEl); getDOM().removeChild(headEl, baseEl);
getDOM().resetBaseElement(); getDOM().resetBaseElement();

View File

@ -261,14 +261,14 @@ export function main() {
describe('safe navigation operator', () => { describe('safe navigation operator', () => {
it('should support reading properties of nulls', fakeAsync(() => { it('should support reading properties of nulls', fakeAsync(() => {
const ctx = _bindSimpleValue('address?.city', Person); const ctx = _bindSimpleValue('address?.city', Person);
ctx.componentInstance.address = null; ctx.componentInstance.address = null !;
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.log).toEqual(['someProp=null']); expect(renderLog.log).toEqual(['someProp=null']);
})); }));
it('should support calling methods on nulls', fakeAsync(() => { it('should support calling methods on nulls', fakeAsync(() => {
const ctx = _bindSimpleValue('address?.toString()', Person); const ctx = _bindSimpleValue('address?.toString()', Person);
ctx.componentInstance.address = null; ctx.componentInstance.address = null !;
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.log).toEqual(['someProp=null']); expect(renderLog.log).toEqual(['someProp=null']);
})); }));
@ -289,7 +289,7 @@ export function main() {
it('should support short-circuting safe navigation', fakeAsync(() => { it('should support short-circuting safe navigation', fakeAsync(() => {
const ctx = _bindSimpleValue('value?.address.city', PersonHolder); const ctx = _bindSimpleValue('value?.address.city', PersonHolder);
ctx.componentInstance.value = null; ctx.componentInstance.value = null !;
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.log).toEqual(['someProp=null']); expect(renderLog.log).toEqual(['someProp=null']);
})); }));
@ -317,7 +317,7 @@ export function main() {
expect(() => { expect(() => {
const ctx = _bindSimpleValue('value?.address.city', PersonHolder); const ctx = _bindSimpleValue('value?.address.city', PersonHolder);
const person = new Person(); const person = new Person();
person.address = null; person.address = null !;
ctx.componentInstance.value = person; ctx.componentInstance.value = person;
ctx.detectChanges(false); ctx.detectChanges(false);
}).toThrow(); }).toThrow();
@ -553,7 +553,7 @@ export function main() {
it('should call pure pipes only if the arguments change', fakeAsync(() => { it('should call pure pipes only if the arguments change', fakeAsync(() => {
const ctx = _bindSimpleValue('name | countingPipe', Person); const ctx = _bindSimpleValue('name | countingPipe', Person);
// change from undefined -> null // change from undefined -> null
ctx.componentInstance.name = null; ctx.componentInstance.name = null !;
ctx.detectChanges(false); ctx.detectChanges(false);
expect(renderLog.loggedValues).toEqual(['null state:0']); expect(renderLog.loggedValues).toEqual(['null state:0']);
ctx.detectChanges(false); ctx.detectChanges(false);
@ -1620,10 +1620,10 @@ class TestLocals {
class Person { class Person {
age: number; age: number;
name: string; name: string;
address: Address = null; address: Address|null = null;
phones: number[]; phones: number[];
init(name: string, address: Address = null) { init(name: string, address: Address|null = null) {
this.name = name; this.name = name;
this.address = address; this.address = address;
} }

View File

@ -39,7 +39,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const compFixture = TestBed.createComponent(MainComp); const compFixture = TestBed.createComponent(MainComp);
const mainComp: MainComp = compFixture.componentInstance; const mainComp: MainComp = compFixture.componentInstance;
expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr); expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr);
const cf = mainComp.cfr.resolveComponentFactory(ChildComp); const cf = mainComp.cfr.resolveComponentFactory(ChildComp) !;
expect(cf.componentType).toBe(ChildComp); expect(cf.componentType).toBe(ChildComp);
}); });
@ -51,8 +51,8 @@ function declareTests({useJit}: {useJit: boolean}) {
const mainComp: CompWithAnalyzeEntryComponentsProvider = compFixture.componentInstance; const mainComp: CompWithAnalyzeEntryComponentsProvider = compFixture.componentInstance;
const cfr: ComponentFactoryResolver = const cfr: ComponentFactoryResolver =
compFixture.componentRef.injector.get(ComponentFactoryResolver); compFixture.componentRef.injector.get(ComponentFactoryResolver);
expect(cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); expect(cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
expect(cfr.resolveComponentFactory(NestedChildComp).componentType).toBe(NestedChildComp); expect(cfr.resolveComponentFactory(NestedChildComp) !.componentType).toBe(NestedChildComp);
}); });
it('should be able to get a component form a parent component (view hiearchy)', () => { it('should be able to get a component form a parent component (view hiearchy)', () => {
@ -62,10 +62,10 @@ function declareTests({useJit}: {useJit: boolean}) {
const childCompEl = compFixture.debugElement.children[0]; const childCompEl = compFixture.debugElement.children[0];
const childComp: ChildComp = childCompEl.componentInstance; const childComp: ChildComp = childCompEl.componentInstance;
// declared on ChildComp directly // declared on ChildComp directly
expect(childComp.cfr.resolveComponentFactory(NestedChildComp).componentType) expect(childComp.cfr.resolveComponentFactory(NestedChildComp) !.componentType)
.toBe(NestedChildComp); .toBe(NestedChildComp);
// inherited from MainComp // inherited from MainComp
expect(childComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); expect(childComp.cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
}); });
it('should not be able to get components from a parent component (content hierarchy)', () => { it('should not be able to get components from a parent component (content hierarchy)', () => {
@ -75,7 +75,8 @@ function declareTests({useJit}: {useJit: boolean}) {
const compFixture = TestBed.createComponent(MainComp); const compFixture = TestBed.createComponent(MainComp);
const nestedChildCompEl = compFixture.debugElement.children[0].children[0]; const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance; const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp).componentType).toBe(ChildComp); expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
.toBe(ChildComp);
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp)) expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
.toThrow(noComponentFactoryError(NestedChildComp)); .toThrow(noComponentFactoryError(NestedChildComp));
}); });

View File

@ -56,7 +56,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const template = '<div>{{null}}{{ctxProp}}</div>'; const template = '<div>{{null}}{{ctxProp}}</div>';
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
fixture.componentInstance.ctxProp = null; fixture.componentInstance.ctxProp = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.nativeElement).toHaveText(''); expect(fixture.nativeElement).toHaveText('');
@ -134,7 +134,7 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(getDOM().getAttribute(fixture.debugElement.children[0].nativeElement, 'foo')) expect(getDOM().getAttribute(fixture.debugElement.children[0].nativeElement, 'foo'))
.toEqual('bar'); .toEqual('bar');
fixture.componentInstance.ctxProp = null; fixture.componentInstance.ctxProp = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(getDOM().hasAttribute(fixture.debugElement.children[0].nativeElement, 'foo')) expect(getDOM().hasAttribute(fixture.debugElement.children[0].nativeElement, 'foo'))
.toBeFalsy(); .toBeFalsy();
@ -151,7 +151,7 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height')) expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height'))
.toEqual('10px'); .toEqual('10px');
fixture.componentInstance.ctxProp = null; fixture.componentInstance.ctxProp = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height')) expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height'))
.toEqual(''); .toEqual('');
@ -264,7 +264,7 @@ function declareTests({useJit}: {useJit: boolean}) {
fixture.componentInstance.ctxProp = 'a'; fixture.componentInstance.ctxProp = 'a';
fixture.detectChanges(); fixture.detectChanges();
const dir = fixture.debugElement.children[0].references['dir']; const dir = fixture.debugElement.children[0].references !['dir'];
expect(dir.dirProp).toEqual('aa'); expect(dir.dirProp).toEqual('aa');
}); });
}); });
@ -450,7 +450,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
expect(fixture.debugElement.children[0].children[0].references['alice']) expect(fixture.debugElement.children[0].children[0].references !['alice'])
.toBeAnInstanceOf(ChildComp); .toBeAnInstanceOf(ChildComp);
}); });
@ -460,7 +460,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
expect(fixture.debugElement.children[0].children[0].references['localdir']) expect(fixture.debugElement.children[0].children[0].references !['localdir'])
.toBeAnInstanceOf(ExportDir); .toBeAnInstanceOf(ExportDir);
}); });
@ -486,8 +486,8 @@ function declareTests({useJit}: {useJit: boolean}) {
const pEl = fixture.debugElement.children[0]; const pEl = fixture.debugElement.children[0];
const alice = pEl.children[0].references['alice']; const alice = pEl.children[0].references !['alice'];
const bob = pEl.children[1].references['bob']; const bob = pEl.children[1].references !['bob'];
expect(alice).toBeAnInstanceOf(ChildComp); expect(alice).toBeAnInstanceOf(ChildComp);
expect(bob).toBeAnInstanceOf(ChildComp); expect(bob).toBeAnInstanceOf(ChildComp);
expect(alice).not.toBe(bob); expect(alice).not.toBe(bob);
@ -499,7 +499,8 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
expect(fixture.debugElement.children[0].references['alice']).toBeAnInstanceOf(ChildComp); expect(fixture.debugElement.children[0].references !['alice'])
.toBeAnInstanceOf(ChildComp);
}); });
it('should assign the element instance to a user-defined variable', () => { it('should assign the element instance to a user-defined variable', () => {
@ -508,7 +509,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const value = fixture.debugElement.children[0].children[0].references['alice']; const value = fixture.debugElement.children[0].children[0].references !['alice'];
expect(value).not.toBe(null); expect(value).not.toBe(null);
expect(value.tagName.toLowerCase()).toEqual('div'); expect(value.tagName.toLowerCase()).toEqual('div');
}); });
@ -520,7 +521,7 @@ function declareTests({useJit}: {useJit: boolean}) {
MyComp, {set: {template: '<ng-template ref-alice></ng-template>'}}) MyComp, {set: {template: '<ng-template ref-alice></ng-template>'}})
.createComponent(MyComp); .createComponent(MyComp);
const value = fixture.debugElement.childNodes[0].references['alice']; const value = fixture.debugElement.childNodes[0].references !['alice'];
expect(value.createEmbeddedView).toBeTruthy(); expect(value.createEmbeddedView).toBeTruthy();
}); });
@ -530,7 +531,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
expect(fixture.debugElement.children[0].children[0].references['superAlice']) expect(fixture.debugElement.children[0].children[0].references !['superAlice'])
.toBeAnInstanceOf(ChildComp); .toBeAnInstanceOf(ChildComp);
}); });
}); });
@ -559,7 +560,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const cmp = fixture.debugElement.children[0].references['cmp']; const cmp = fixture.debugElement.children[0].references !['cmp'];
fixture.detectChanges(); fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1); expect(cmp.numberOfChecks).toEqual(1);
@ -580,7 +581,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const cmp = fixture.debugElement.children[0].references['cmp']; const cmp = fixture.debugElement.children[0].references !['cmp'];
fixture.componentInstance.ctxProp = 'one'; fixture.componentInstance.ctxProp = 'one';
fixture.detectChanges(); fixture.detectChanges();
@ -654,7 +655,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const cmp = fixture.debugElement.children[0].references['cmp']; const cmp = fixture.debugElement.children[0].references !['cmp'];
fixture.componentInstance.ctxProp = 'one'; fixture.componentInstance.ctxProp = 'one';
fixture.detectChanges(); fixture.detectChanges();
@ -675,7 +676,8 @@ function declareTests({useJit}: {useJit: boolean}) {
tick(); tick();
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references['cmp']; const cmp: PushCmpWithAsyncPipe =
fixture.debugElement.children[0].references !['cmp'];
fixture.detectChanges(); fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1); expect(cmp.numberOfChecks).toEqual(1);
@ -707,7 +709,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const childComponent = const childComponent =
fixture.debugElement.children[0].children[0].children[0].references['child']; fixture.debugElement.children[0].children[0].children[0].references !['child'];
expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective); expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective);
}); });
@ -729,7 +731,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const tc = fixture.debugElement.children[0].children[0].children[0]; const tc = fixture.debugElement.children[0].children[0].children[0];
const childComponent = tc.references['child']; const childComponent = tc.references !['child'];
expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective); expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective);
}); });
@ -1232,7 +1234,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const comp = fixture.debugElement.children[0].children[0].references['consuming']; const comp = fixture.debugElement.children[0].children[0].references !['consuming'];
expect(comp.injectable).toBeAnInstanceOf(InjectableService); expect(comp.injectable).toBeAnInstanceOf(InjectableService);
}); });
@ -1248,7 +1250,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}}); TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}});
const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView); const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView);
const comp = fixture.debugElement.children[0].references['consuming']; const comp = fixture.debugElement.children[0].references !['consuming'];
expect(comp.injectable).toBeAnInstanceOf(InjectableService); expect(comp.injectable).toBeAnInstanceOf(InjectableService);
}); });
@ -1276,7 +1278,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const comp = fixture.debugElement.children[0].children[0].references['dir']; const comp = fixture.debugElement.children[0].children[0].references !['dir'];
expect(comp.directive.injectable).toBeAnInstanceOf(InjectableService); expect(comp.directive.injectable).toBeAnInstanceOf(InjectableService);
}); });
@ -1326,7 +1328,7 @@ function declareTests({useJit}: {useJit: boolean}) {
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const providing = fixture.debugElement.children[0].references['providing']; const providing = fixture.debugElement.children[0].references !['providing'];
expect(providing.created).toBe(false); expect(providing.created).toBe(false);
fixture.componentInstance.ctxBoolProp = true; fixture.componentInstance.ctxBoolProp = true;
@ -1363,7 +1365,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
it('should use a default element name for components without selectors', () => { it('should use a default element name for components without selectors', () => {
let noSelectorComponentFactory: ComponentFactory<SomeComponent>; let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined !;
@Component({template: '----'}) @Component({template: '----'})
class NoSelectorComponent { class NoSelectorComponent {
@ -1374,7 +1376,7 @@ function declareTests({useJit}: {useJit: boolean}) {
constructor(componentFactoryResolver: ComponentFactoryResolver) { constructor(componentFactoryResolver: ComponentFactoryResolver) {
// grab its own component factory // grab its own component factory
noSelectorComponentFactory = noSelectorComponentFactory =
componentFactoryResolver.resolveComponentFactory(NoSelectorComponent); componentFactoryResolver.resolveComponentFactory(NoSelectorComponent) !;
} }
} }
@ -1843,7 +1845,8 @@ class DynamicViewport {
this.injector = ReflectiveInjector.resolveAndCreate( this.injector = ReflectiveInjector.resolveAndCreate(
[{provide: MyService, useValue: myService}], vc.injector); [{provide: MyService, useValue: myService}], vc.injector);
this.componentFactory = componentFactoryResolver.resolveComponentFactory(ChildCompUsingService); this.componentFactory =
componentFactoryResolver.resolveComponentFactory(ChildCompUsingService) !;
} }
create() { this.vc.createComponent(this.componentFactory, this.vc.length, this.injector); } create() { this.vc.createComponent(this.componentFactory, this.vc.length, this.injector); }
@ -1970,7 +1973,7 @@ class MyComp {
}) })
class ChildComp { class ChildComp {
ctxProp: string; ctxProp: string;
dirProp: string; dirProp: string|null;
constructor(service: MyService) { constructor(service: MyService) {
this.ctxProp = service.greeting; this.ctxProp = service.greeting;
this.dirProp = null; this.dirProp = null;
@ -2006,7 +2009,7 @@ class CompWithHost {
@Component({selector: '[child-cmp2]', viewProviders: [MyService]}) @Component({selector: '[child-cmp2]', viewProviders: [MyService]})
class ChildComp2 { class ChildComp2 {
ctxProp: string; ctxProp: string;
dirProp: string; dirProp: string|null;
constructor(service: MyService) { constructor(service: MyService) {
this.ctxProp = service.greeting; this.ctxProp = service.greeting;
this.dirProp = null; this.dirProp = null;
@ -2277,7 +2280,7 @@ class EventBus {
@Directive({ @Directive({
selector: 'grand-parent-providing-event-bus', selector: 'grand-parent-providing-event-bus',
providers: [{provide: EventBus, useValue: new EventBus(null, 'grandparent')}] providers: [{provide: EventBus, useValue: new EventBus(null !, 'grandparent')}]
}) })
class GrandParentProvidingEventBus { class GrandParentProvidingEventBus {
bus: EventBus; bus: EventBus;
@ -2313,7 +2316,7 @@ class ChildConsumingEventBus {
@Directive({selector: '[someImpvp]', inputs: ['someImpvp']}) @Directive({selector: '[someImpvp]', inputs: ['someImpvp']})
class SomeImperativeViewport { class SomeImperativeViewport {
view: EmbeddedViewRef<Object>; view: EmbeddedViewRef<Object>|null;
anchor: any; anchor: any;
constructor( constructor(
public vc: ViewContainerRef, public templateRef: TemplateRef<Object>, public vc: ViewContainerRef, public templateRef: TemplateRef<Object>,

View File

@ -132,7 +132,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges(); fixture.detectChanges();
const q = fixture.debugElement.children[0].references['q']; const q = fixture.debugElement.children[0].references !['q'];
fixture.detectChanges(); fixture.detectChanges();
expect(q.textDirChildren.length).toEqual(1); expect(q.textDirChildren.length).toEqual(1);
@ -145,7 +145,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges(); fixture.detectChanges();
const q = fixture.debugElement.children[0].references['q']; const q = fixture.debugElement.children[0].references !['q'];
fixture.detectChanges(); fixture.detectChanges();
expect(q.textDirChildren.length).toEqual(1); expect(q.textDirChildren.length).toEqual(1);
@ -156,7 +156,7 @@ function declareTests({useJit}: {useJit: boolean}) {
@Directive({selector: '[text]'}) @Directive({selector: '[text]'})
class TextDirective { class TextDirective {
@Input() public text: string = null; @Input() public text: string|null = null;
} }
@Component({selector: 'needs-content-children', template: ''}) @Component({selector: 'needs-content-children', template: ''})

View File

@ -125,18 +125,19 @@ function declareTests({useJit}: {useJit: boolean}) {
injector = _injector; injector = _injector;
})); }));
function createModule<T>(moduleType: Type<T>, parentInjector: Injector = null): NgModuleRef<T> { function createModule<T>(
moduleType: Type<T>, parentInjector?: Injector | null): NgModuleRef<T> {
return compiler.compileModuleSync(moduleType).create(parentInjector); return compiler.compileModuleSync(moduleType).create(parentInjector);
} }
function createComp<T>(compType: Type<T>, moduleType: Type<any>): ComponentFixture<T> { function createComp<T>(compType: Type<T>, moduleType: Type<any>): ComponentFixture<T> {
const ngModule = createModule(moduleType, injector); const ngModule = createModule(moduleType, injector);
const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType); const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType) !;
const comp = cf.create(Injector.NULL); const comp = cf.create(Injector.NULL);
return new ComponentFixture(comp, null, false); return new ComponentFixture(comp, null !, false);
} }
describe('errors', () => { describe('errors', () => {
@ -291,7 +292,7 @@ function declareTests({useJit}: {useJit: boolean}) {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
.toBe(SomeComp); .toBe(SomeComp);
expect(ngModule.injector.get(ComponentFactoryResolver) expect(ngModule.injector.get(ComponentFactoryResolver)
.resolveComponentFactory(SomeComp) .resolveComponentFactory(SomeComp)
@ -341,7 +342,7 @@ function declareTests({useJit}: {useJit: boolean}) {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
.toBe(SomeComp); .toBe(SomeComp);
expect(ngModule.injector.get(ComponentFactoryResolver) expect(ngModule.injector.get(ComponentFactoryResolver)
.resolveComponentFactory(SomeComp) .resolveComponentFactory(SomeComp)
@ -359,7 +360,7 @@ function declareTests({useJit}: {useJit: boolean}) {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
.toBe(SomeComp); .toBe(SomeComp);
expect(ngModule.injector.get(ComponentFactoryResolver) expect(ngModule.injector.get(ComponentFactoryResolver)
.resolveComponentFactory(SomeComp) .resolveComponentFactory(SomeComp)
@ -377,7 +378,7 @@ function declareTests({useJit}: {useJit: boolean}) {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
.toBe(SomeComp); .toBe(SomeComp);
expect(ngModule.injector.get(ComponentFactoryResolver) expect(ngModule.injector.get(ComponentFactoryResolver)
.resolveComponentFactory(SomeComp) .resolveComponentFactory(SomeComp)
@ -394,7 +395,7 @@ function declareTests({useJit}: {useJit: boolean}) {
} }
const ngModule = createModule(SomeModule); const ngModule = createModule(SomeModule);
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp).componentType) expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
.toBe(SomeComp); .toBe(SomeComp);
}); });
@ -605,7 +606,7 @@ function declareTests({useJit}: {useJit: boolean}) {
let moduleType: any = null; let moduleType: any = null;
function createInjector(providers: Provider[], parent: Injector = null): Injector { function createInjector(providers: Provider[], parent?: Injector | null): Injector {
@NgModule({providers: providers}) @NgModule({providers: providers})
class SomeModule { class SomeModule {
} }

View File

@ -234,7 +234,7 @@ export function main() {
}); });
it('should support moving non projected light dom around', () => { it('should support moving non projected light dom around', () => {
let sourceDirective: ManualViewportDirective; let sourceDirective: ManualViewportDirective = undefined !;
@Directive({selector: '[manual]'}) @Directive({selector: '[manual]'})
class ManualViewportDirective { class ManualViewportDirective {

View File

@ -67,7 +67,7 @@ export function main() {
'<needs-content-children #q><div text="foo"></div></needs-content-children>'; '<needs-content-children #q><div text="foo"></div></needs-content-children>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
view.detectChanges(); view.detectChanges();
expect(q.textDirChildren.length).toEqual(1); expect(q.textDirChildren.length).toEqual(1);
expect(q.numberOfChildrenAfterContentInit).toEqual(1); expect(q.numberOfChildrenAfterContentInit).toEqual(1);
@ -79,7 +79,7 @@ export function main() {
const view = createTestCmp(MyComp0, template); const view = createTestCmp(MyComp0, template);
view.componentInstance.shouldShow = true; view.componentInstance.shouldShow = true;
view.detectChanges(); view.detectChanges();
const q: NeedsContentChild = view.debugElement.children[0].references['q']; const q: NeedsContentChild = view.debugElement.children[0].references !['q'];
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
view.componentInstance.shouldShow = false; view.componentInstance.shouldShow = false;
@ -93,7 +93,7 @@ export function main() {
const template = '<needs-view-child #q></needs-view-child>'; const template = '<needs-view-child #q></needs-view-child>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewChild = view.debugElement.children[0].references['q']; const q: NeedsViewChild = view.debugElement.children[0].references !['q'];
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
q.shouldShow = false; q.shouldShow = false;
@ -107,7 +107,7 @@ export function main() {
const template = const template =
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>'; '<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
const view = createTestCmp(MyComp0, template); const view = createTestCmp(MyComp0, template);
const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q']; const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references !['q'];
expect(q.contentChild.text).toBeFalsy(); expect(q.contentChild.text).toBeFalsy();
expect(q.viewChild.text).toBeFalsy(); expect(q.viewChild.text).toBeFalsy();
@ -128,7 +128,7 @@ export function main() {
const view = TestBed.createComponent(MyComp0); const view = TestBed.createComponent(MyComp0);
view.detectChanges(); view.detectChanges();
const q: NeedsViewChild = view.debugElement.children[0].references['q']; const q: NeedsViewChild = view.debugElement.children[0].references !['q'];
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
q.shouldShow = false; q.shouldShow = false;
@ -312,7 +312,7 @@ export function main() {
'</needs-query>'; '</needs-query>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
q.query.changes.subscribe({ q.query.changes.subscribe({
next: () => { next: () => {
@ -333,14 +333,14 @@ export function main() {
view.componentInstance.shouldShow = true; view.componentInstance.shouldShow = true;
view.detectChanges(); view.detectChanges();
const q: NeedsQuery = view.debugElement.children[0].references['q']; const q: NeedsQuery = view.debugElement.children[0].references !['q'];
expect(q.query.length).toEqual(1); expect(q.query.length).toEqual(1);
view.componentInstance.shouldShow = false; view.componentInstance.shouldShow = false;
view.detectChanges(); view.detectChanges();
view.componentInstance.shouldShow = true; view.componentInstance.shouldShow = true;
view.detectChanges(); view.detectChanges();
const q2: NeedsQuery = view.debugElement.children[0].references['q']; const q2: NeedsQuery = view.debugElement.children[0].references !['q'];
expect(q2.query.length).toEqual(1); expect(q2.query.length).toEqual(1);
}); });
@ -353,7 +353,7 @@ export function main() {
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' + '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>'; '</needs-query-by-ref-binding>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
view.componentInstance.list = ['1d', '2d']; view.componentInstance.list = ['1d', '2d'];
view.detectChanges(); view.detectChanges();
@ -367,7 +367,7 @@ export function main() {
'<div text="two" #textLabel2="textDir"></div>' + '<div text="two" #textLabel2="textDir"></div>' +
'</needs-query-by-ref-bindings>'; '</needs-query-by-ref-bindings>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
expect(q.query.first.text).toEqual('one'); expect(q.query.first.text).toEqual('one');
expect(q.query.last.text).toEqual('two'); expect(q.query.last.text).toEqual('two');
@ -378,7 +378,7 @@ export function main() {
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' + '<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>'; '</needs-query-by-ref-binding>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
view.componentInstance.list = ['1d', '2d']; view.componentInstance.list = ['1d', '2d'];
view.detectChanges(); view.detectChanges();
@ -394,7 +394,7 @@ export function main() {
'</div>' + '</div>' +
'</needs-query-by-ref-binding>'; '</needs-query-by-ref-binding>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
view.componentInstance.list = ['1d', '2d']; view.componentInstance.list = ['1d', '2d'];
view.detectChanges(); view.detectChanges();
@ -415,14 +415,14 @@ export function main() {
const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>'; const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q']; const q: NeedsViewQueryByLabel = view.debugElement.children[0].references !['q'];
expect(q.query.first.nativeElement).toHaveText('text'); expect(q.query.first.nativeElement).toHaveText('text');
}); });
it('should contain all child directives in the view dom', () => { it('should contain all child directives in the view dom', () => {
const template = '<needs-view-children #q></needs-view-children>'; const template = '<needs-view-children #q></needs-view-children>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
expect(q.textDirChildren.length).toEqual(1); expect(q.textDirChildren.length).toEqual(1);
expect(q.numberOfChildrenAfterViewInit).toEqual(1); expect(q.numberOfChildrenAfterViewInit).toEqual(1);
}); });
@ -432,21 +432,21 @@ export function main() {
it('should contain all the elements in the view with that have the given directive', () => { it('should contain all the elements in the view with that have the given directive', () => {
const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>'; const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQuery = view.debugElement.children[0].references['q']; const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
}); });
it('should not include directive present on the host element', () => { it('should not include directive present on the host element', () => {
const template = '<needs-view-query #q text="self"></needs-view-query>'; const template = '<needs-view-query #q text="self"></needs-view-query>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQuery = view.debugElement.children[0].references['q']; const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
}); });
it('should reflect changes in the component', () => { it('should reflect changes in the component', () => {
const template = '<needs-view-query-if #q></needs-view-query-if>'; const template = '<needs-view-query-if #q></needs-view-query-if>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryIf = view.debugElement.children[0].references['q']; const q: NeedsViewQueryIf = view.debugElement.children[0].references !['q'];
expect(q.query.length).toBe(0); expect(q.query.length).toBe(0);
q.show = true; q.show = true;
@ -458,7 +458,7 @@ export function main() {
it('should not be affected by other changes in the component', () => { it('should not be affected by other changes in the component', () => {
const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>'; const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q']; const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references !['q'];
expect(q.query.length).toEqual(1); expect(q.query.length).toEqual(1);
expect(q.query.first.text).toEqual('1'); expect(q.query.first.text).toEqual('1');
@ -473,7 +473,7 @@ export function main() {
() => { () => {
const template = '<needs-view-query-order #q></needs-view-query-order>'; const template = '<needs-view-query-order #q></needs-view-query-order>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
@ -486,7 +486,7 @@ export function main() {
() => { () => {
const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>'; const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q']; const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references !['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
q.list = ['-3', '2']; q.list = ['-3', '2'];
@ -497,7 +497,7 @@ export function main() {
it('should handle long ngFor cycles', () => { it('should handle long ngFor cycles', () => {
const template = '<needs-view-query-order #q></needs-view-query-order>'; const template = '<needs-view-query-order #q></needs-view-query-order>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q'];
// no significance to 50, just a reasonably large cycle. // no significance to 50, just a reasonably large cycle.
for (let i = 0; i < 50; i++) { for (let i = 0; i < 50; i++) {
@ -511,7 +511,7 @@ export function main() {
it('should support more than three queries', () => { it('should support more than three queries', () => {
const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>'; const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
expect(q.query1).toBeDefined(); expect(q.query1).toBeDefined();
expect(q.query2).toBeDefined(); expect(q.query2).toBeDefined();
expect(q.query3).toBeDefined(); expect(q.query3).toBeDefined();
@ -524,7 +524,7 @@ export function main() {
const template = const template =
'<manual-projecting #q><ng-template><div text="1"></div></ng-template></manual-projecting>'; '<manual-projecting #q><ng-template><div text="1"></div></ng-template></manual-projecting>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
expect(q.query.length).toBe(0); expect(q.query.length).toBe(0);
q.create(); q.create();
@ -553,7 +553,7 @@ export function main() {
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>'; '<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
const view = createTestCmpAndDetectChanges(MyComp0, template); const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q']; const q = view.debugElement.children[0].references !['q'];
// This should be 1, but due to // This should be 1, but due to
// https://github.com/angular/angular/issues/15117 this is 0. // https://github.com/angular/angular/issues/15117 this is 0.
expect(q.query.length).toBe(0); expect(q.query.length).toBe(0);
@ -812,7 +812,7 @@ class NeedsViewContainerWithRead {
@Component({selector: 'has-null-query-condition', template: '<div></div>'}) @Component({selector: 'has-null-query-condition', template: '<div></div>'})
class HasNullQueryCondition { class HasNullQueryCondition {
@ContentChildren(null) errorTrigger: any; @ContentChildren(null !) errorTrigger: any;
} }
@Component({selector: 'my-comp', template: ''}) @Component({selector: 'my-comp', template: ''})

View File

@ -75,7 +75,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
// should not throw for inputs starting with "on" // should not throw for inputs starting with "on"
let cmp: ComponentFixture<SecuredComponent>; let cmp: ComponentFixture<SecuredComponent> = undefined !;
expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow(); expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow();
// must bind to the directive not to the property of the div // must bind to the directive not to the property of the div

View File

@ -26,15 +26,15 @@ export function main() {
}); });
function getErrorLoggerStack(e: Error): string { function getErrorLoggerStack(e: Error): string {
let logStack: string; let logStack: string = undefined !;
getErrorLogger(e)(<any>{error: () => logStack = new Error().stack}, e.message); getErrorLogger(e)(<any>{error: () => logStack = new Error().stack !}, e.message);
return logStack; return logStack;
} }
function getSourceMap(genFile: string): SourceMap { function getSourceMap(genFile: string): SourceMap {
const jitSources = jitSpy.calls.all().map((call) => call.args[call.args.length - 1]); const jitSources = jitSpy.calls.all().map((call) => call.args[call.args.length - 1]);
return jitSources.map(source => extractSourceMap(source)) return jitSources.map(source => extractSourceMap(source))
.find(map => map && map.file === genFile); .find(map => !!(map && map.file === genFile)) !;
} }
function getSourcePositionForStack(stack: string): function getSourcePositionForStack(stack: string):
@ -46,9 +46,9 @@ export function main() {
.map(line => /\((.*\.ngfactory\.js):(\d+):(\d+)/.exec(line)) .map(line => /\((.*\.ngfactory\.js):(\d+):(\d+)/.exec(line))
.filter(match => !!match) .filter(match => !!match)
.map(match => ({ .map(match => ({
file: match[1], file: match ![1],
line: parseInt(match[2], 10), line: parseInt(match ![2], 10),
column: parseInt(match[3], 10) column: parseInt(match ![3], 10)
})); }));
const ngFactoryLocation = ngFactoryLocations[0]; const ngFactoryLocation = ngFactoryLocations[0];

View File

@ -184,19 +184,19 @@ class TestComp {
export function main() { export function main() {
function createComponentFixture<T>( function createComponentFixture<T>(
template: string, providers: Provider[] = null, comp: Type<T> = null): ComponentFixture<T> { template: string, providers?: Provider[] | null, comp?: Type<T>): ComponentFixture<T> {
if (!comp) { if (!comp) {
comp = <any>TestComp; comp = <any>TestComp;
} }
TestBed.overrideComponent(comp, {set: {template}}); TestBed.overrideComponent(comp !, {set: {template}});
if (providers && providers.length) { if (providers && providers.length) {
TestBed.overrideComponent(comp, {add: {providers: providers}}); TestBed.overrideComponent(comp !, {add: {providers: providers}});
} }
return TestBed.createComponent(comp); return TestBed.createComponent(comp !);
} }
function createComponent( function createComponent(
template: string, providers: Provider[] = null, comp: Type<any> = null): DebugElement { template: string, providers?: Provider[], comp?: Type<any>): DebugElement {
const fixture = createComponentFixture(template, providers, comp); const fixture = createComponentFixture(template, providers, comp);
fixture.detectChanges(); fixture.detectChanges();
return fixture.debugElement; return fixture.debugElement;

View File

@ -8,6 +8,7 @@
import {Reflector} from '@angular/core/src/reflection/reflection'; import {Reflector} from '@angular/core/src/reflection/reflection';
import {DELEGATE_CTOR, ReflectionCapabilities} from '@angular/core/src/reflection/reflection_capabilities'; import {DELEGATE_CTOR, ReflectionCapabilities} from '@angular/core/src/reflection/reflection_capabilities';
import {global} from '@angular/core/src/util';
import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators'; import {makeDecorator, makeParamDecorator, makePropDecorator} from '@angular/core/src/util/decorators';
interface ClassDecoratorFactory { interface ClassDecoratorFactory {
@ -171,8 +172,12 @@ export function main() {
class ChildWithCtor extends Parent { class ChildWithCtor extends Parent {
constructor() { super(); } constructor() { super(); }
} }
class ChildNoCtorPrivateProps extends Parent {
private x = 10;
}
expect(DELEGATE_CTOR.exec(ChildNoCtor.toString())).toBeTruthy(); expect(DELEGATE_CTOR.exec(ChildNoCtor.toString())).toBeTruthy();
expect(DELEGATE_CTOR.exec(ChildNoCtorPrivateProps.toString())).toBeTruthy();
expect(DELEGATE_CTOR.exec(ChildWithCtor.toString())).toBeFalsy(); expect(DELEGATE_CTOR.exec(ChildWithCtor.toString())).toBeFalsy();
}); });
}); });
@ -205,7 +210,7 @@ export function main() {
expect(reflector.annotations(NoDecorators)).toEqual([]); expect(reflector.annotations(NoDecorators)).toEqual([]);
expect(reflector.annotations(<any>{})).toEqual([]); expect(reflector.annotations(<any>{})).toEqual([]);
expect(reflector.annotations(<any>1)).toEqual([]); expect(reflector.annotations(<any>1)).toEqual([]);
expect(reflector.annotations(null)).toEqual([]); expect(reflector.annotations(null !)).toEqual([]);
}); });
it('should inherit parameters', () => { it('should inherit parameters', () => {
@ -222,15 +227,24 @@ export function main() {
class Child extends Parent {} class Child extends Parent {}
@ClassDecorator({value: 'child'})
class ChildWithDecorator extends Parent {
}
@ClassDecorator({value: 'child'})
class ChildWithDecoratorAndProps extends Parent {
private x = 10;
}
// Note: We need the class decorator as well, // Note: We need the class decorator as well,
// as otherwise TS won't capture the ctor arguments! // as otherwise TS won't capture the ctor arguments!
@ClassDecorator({value: 'child'}) @ClassDecorator({value: 'child'})
class ChildWithCtor extends Parent { class ChildWithCtor extends Parent {
constructor(@ParamDecorator('c') c: C) { super(null, null); } constructor(@ParamDecorator('c') c: C) { super(null !, null !); }
} }
class ChildWithCtorNoDecorator extends Parent { class ChildWithCtorNoDecorator extends Parent {
constructor(a: any, b: any, c: any) { super(null, null); } constructor(a: any, b: any, c: any) { super(null !, null !); }
} }
class NoDecorators {} class NoDecorators {}
@ -244,6 +258,14 @@ export function main() {
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')] [A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
]); ]);
expect(reflector.parameters(ChildWithDecorator)).toEqual([
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
]);
expect(reflector.parameters(ChildWithDecoratorAndProps)).toEqual([
[A, new ParamDecorator('a')], [B, new ParamDecorator('b')]
]);
expect(reflector.parameters(ChildWithCtor)).toEqual([[C, new ParamDecorator('c')]]); expect(reflector.parameters(ChildWithCtor)).toEqual([[C, new ParamDecorator('c')]]);
// If we have no decorator, we don't get metadata about the ctor params. // If we have no decorator, we don't get metadata about the ctor params.
@ -255,7 +277,7 @@ export function main() {
expect(reflector.parameters(NoDecorators)).toEqual([]); expect(reflector.parameters(NoDecorators)).toEqual([]);
expect(reflector.parameters(<any>{})).toEqual([]); expect(reflector.parameters(<any>{})).toEqual([]);
expect(reflector.parameters(<any>1)).toEqual([]); expect(reflector.parameters(<any>1)).toEqual([]);
expect(reflector.parameters(null)).toEqual([]); expect(reflector.parameters(null !)).toEqual([]);
}); });
it('should inherit property metadata', () => { it('should inherit property metadata', () => {
@ -294,7 +316,7 @@ export function main() {
expect(reflector.propMetadata(NoDecorators)).toEqual({}); expect(reflector.propMetadata(NoDecorators)).toEqual({});
expect(reflector.propMetadata(<any>{})).toEqual({}); expect(reflector.propMetadata(<any>{})).toEqual({});
expect(reflector.propMetadata(<any>1)).toEqual({}); expect(reflector.propMetadata(<any>1)).toEqual({});
expect(reflector.propMetadata(null)).toEqual({}); expect(reflector.propMetadata(null !)).toEqual({});
}); });
it('should inherit lifecycle hooks', () => { it('should inherit lifecycle hooks', () => {

View File

@ -11,9 +11,9 @@ import {stringify} from '../src/util';
export function main() { export function main() {
describe('stringify', () => { describe('stringify', () => {
it('should return string undefined when toString returns undefined', it('should return string undefined when toString returns undefined',
() => expect(stringify({toString: (): string => undefined})).toBe('undefined')); () => expect(stringify({toString: (): any => undefined})).toBe('undefined'));
it('should return string null when toString returns null', it('should return string null when toString returns null',
() => expect(stringify({toString: (): string => null})).toBe('null')); () => expect(stringify({toString: (): any => null})).toBe('null'));
}); });
} }

View File

@ -30,23 +30,23 @@ export function main() {
describe('create', () => { describe('create', () => {
it('should create anchor nodes without parents', () => { it('should create anchor nodes without parents', () => {
const rootNodes = createAndGetRootNodes(compViewDef([ const rootNodes = createAndGetRootNodes(compViewDef([
anchorDef(NodeFlags.None, null, null, 0) anchorDef(NodeFlags.None, null !, null !, 0)
])).rootNodes; ])).rootNodes;
expect(rootNodes.length).toBe(1); expect(rootNodes.length).toBe(1);
}); });
it('should create views with multiple root anchor nodes', () => { it('should create views with multiple root anchor nodes', () => {
const rootNodes = const rootNodes = createAndGetRootNodes(compViewDef([
createAndGetRootNodes(compViewDef([ anchorDef(NodeFlags.None, null !, null !, 0),
anchorDef(NodeFlags.None, null, null, 0), anchorDef(NodeFlags.None, null, null, 0) anchorDef(NodeFlags.None, null !, null !, 0)
])).rootNodes; ])).rootNodes;
expect(rootNodes.length).toBe(2); expect(rootNodes.length).toBe(2);
}); });
it('should create anchor nodes with parents', () => { it('should create anchor nodes with parents', () => {
const rootNodes = createAndGetRootNodes(compViewDef([ const rootNodes = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null !, null !, 1, 'div'),
anchorDef(NodeFlags.None, null, null, 0), anchorDef(NodeFlags.None, null !, null !, 0),
])).rootNodes; ])).rootNodes;
expect(getDOM().childNodes(rootNodes[0]).length).toBe(1); expect(getDOM().childNodes(rootNodes[0]).length).toBe(1);
}); });
@ -54,8 +54,8 @@ export function main() {
it('should add debug information to the renderer', () => { it('should add debug information to the renderer', () => {
const someContext = new Object(); const someContext = new Object();
const {view, rootNodes} = createAndGetRootNodes( const {view, rootNodes} = createAndGetRootNodes(
compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext); compViewDef([anchorDef(NodeFlags.None, null !, null !, 0)]), someContext);
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement); expect(getDebugNode(rootNodes[0]) !.nativeNode).toBe(asElementData(view, 0).renderElement);
}); });
}); });
}); });

View File

@ -13,34 +13,32 @@ import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {createRootView, isBrowser, removeNodes} from './helper'; import {createRootView, isBrowser, removeNodes} from './helper';
export function main() { export function main() {
describe( describe(`Component Views`, () => {
`Component Views`, () => {
function compViewDef( function compViewDef(
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn, nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition { viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer); return viewDef(viewFlags, nodes, updateDirectives, updateRenderer);
} }
function createAndGetRootNodes(viewDef: ViewDefinition): function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
{rootNodes: any[], view: ViewData} {
const view = createRootView(viewDef); const view = createRootView(viewDef);
const rootNodes = rootRenderNodes(view); const rootNodes = rootRenderNodes(view);
return {rootNodes, view}; return {rootNodes, view};
} }
it('should create and attach component views', () => { it('should create and attach component views', () => {
let instance: AComp; let instance: AComp = undefined !;
class AComp { class AComp {
constructor() { instance = this; } constructor() { instance = this; }
} }
const {view, rootNodes} = createAndGetRootNodes(compViewDef([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef( elementDef(
NodeFlags.None, null, null, 1, 'div', null, null, null, null, NodeFlags.None, null !, null !, 1, 'div', null !, null !, null !, null !,
() => compViewDef([ () => compViewDef([
elementDef(NodeFlags.None, null, null, 0, 'span'), elementDef(NodeFlags.None, null !, null !, 0, 'span'),
])), ])),
directiveDef(NodeFlags.Component, null, 0, AComp, []), directiveDef(NodeFlags.Component, null !, 0, AComp, []),
])); ]));
const compView = asElementData(view, 0).componentView; const compView = asElementData(view, 0).componentView;
@ -64,7 +62,7 @@ export function main() {
it('should select root elements based on a selector', () => { it('should select root elements based on a selector', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 0, 'div'), elementDef(NodeFlags.None, null !, null !, 0, 'div'),
]), ]),
{}, [], 'root'); {}, [], 'root');
const rootNodes = rootRenderNodes(view); const rootNodes = rootRenderNodes(view);
@ -74,7 +72,7 @@ export function main() {
it('should select root elements based on a node', () => { it('should select root elements based on a node', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 0, 'div'), elementDef(NodeFlags.None, null !, null !, 0, 'div'),
]), ]),
{}, [], rootNode); {}, [], rootNode);
const rootNodes = rootRenderNodes(view); const rootNodes = rootRenderNodes(view);
@ -84,7 +82,7 @@ export function main() {
it('should set attributes on the root node', () => { it('should set attributes on the root node', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]), elementDef(NodeFlags.None, null !, null !, 0, 'div', [['a', 'b']]),
]), ]),
{}, [], rootNode); {}, [], rootNode);
expect(rootNode.getAttribute('a')).toBe('b'); expect(rootNode.getAttribute('a')).toBe('b');
@ -94,7 +92,7 @@ export function main() {
rootNode.appendChild(document.createElement('div')); rootNode.appendChild(document.createElement('div'));
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]), elementDef(NodeFlags.None, null !, null !, 0, 'div', [['a', 'b']]),
]), ]),
{}, [], rootNode); {}, [], rootNode);
expect(rootNode.childNodes.length).toBe(0); expect(rootNode.childNodes.length).toBe(0);
@ -102,28 +100,26 @@ export function main() {
}); });
} }
describe( describe('data binding', () => {
'data binding', () => { it('should dirty check component views', () => {
it('should dirty check component views',
() => {
let value: any; let value: any;
class AComp { class AComp {
a: any; a: any;
} }
const update = jasmine.createSpy('updater').and.callFake( const update =
(check: NodeCheckFn, view: ViewData) => { jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
check(view, 0, ArgumentType.Inline, value); check(view, 0, ArgumentType.Inline, value);
}); });
const {view, rootNodes} = createAndGetRootNodes( const {view, rootNodes} = createAndGetRootNodes(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', null, null, null, null, () => compViewDef( elementDef(NodeFlags.None, null!, null!, 1, 'div', null!, null!, null!, null!, () => compViewDef(
[ [
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]), elementDef(NodeFlags.None, null!, null!, 0, 'span', null!, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]),
], null, update ], null!, update
)), )),
directiveDef(NodeFlags.Component, null, 0, AComp, []), directiveDef(NodeFlags.Component, null!, 0, AComp, []),
])); ]));
const compView = asElementData(view, 0).componentView; const compView = asElementData(view, 0).componentView;
@ -143,8 +139,7 @@ export function main() {
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`); `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
}); });
it('should support detaching and attaching component views for dirty checking', it('should support detaching and attaching component views for dirty checking', () => {
() => {
class AComp { class AComp {
a: any; a: any;
} }
@ -153,13 +148,13 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef( elementDef(
NodeFlags.None, null, null, 1, 'div', null, null, null, null, NodeFlags.None, null !, null !, 1, 'div', null !, null !, null !, null !,
() => compViewDef( () => compViewDef(
[ [
elementDef(NodeFlags.None, null, null, 0, 'span'), elementDef(NodeFlags.None, null !, null !, 0, 'span'),
], ],
update)), update)),
directiveDef(NodeFlags.Component, null, 0, AComp, [], null, null), directiveDef(NodeFlags.Component, null !, 0, AComp, [], null !, null !),
])); ]));
const compView = asElementData(view, 0).componentView; const compView = asElementData(view, 0).componentView;
@ -185,23 +180,22 @@ export function main() {
const update = jasmine.createSpy('updater'); const update = jasmine.createSpy('updater');
const addListenerSpy = const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
const {view} = createAndGetRootNodes(compViewDef( const {view} = createAndGetRootNodes(compViewDef(
[ [
elementDef( elementDef(
NodeFlags.None, null, null, 1, 'div', null, null, null, null, NodeFlags.None, null !, null !, 1, 'div', null !, null !, null !, null !,
() => { () => {
return compViewDef( return compViewDef(
[ [
elementDef( elementDef(
NodeFlags.None, null, null, 0, 'span', null, null, NodeFlags.None, null !, null !, 0, 'span', null !, null !,
[[null, 'click']]), [[null !, 'click']]),
], ],
update, null, ViewFlags.OnPush); update, null !, ViewFlags.OnPush);
}), }),
directiveDef(NodeFlags.Component, null, 0, AComp, [], {a: [0, 'a']}), directiveDef(NodeFlags.Component, null !, 0, AComp, [], {a: [0, 'a']}),
], ],
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); })); (check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
@ -236,8 +230,7 @@ export function main() {
}); });
} }
it('should stop dirty checking views that threw errors in change detection', it('should stop dirty checking views that threw errors in change detection', () => {
() => {
class AComp { class AComp {
a: any; a: any;
} }
@ -245,20 +238,19 @@ export function main() {
const update = jasmine.createSpy('updater'); const update = jasmine.createSpy('updater');
const {view, rootNodes} = createAndGetRootNodes(compViewDef([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', null, null, null, null, () => compViewDef( elementDef(NodeFlags.None, null!, null!, 1, 'div', null!, null!, null!, null!, () => compViewDef(
[ [
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]), elementDef(NodeFlags.None, null!, null!, 0, 'span', null!, [[BindingFlags.TypeElementAttribute, 'a', SecurityContext.NONE]]),
], ],
null, update)), null!, update)),
directiveDef( directiveDef(
NodeFlags.Component, null, 0, AComp, [], null, null, NodeFlags.Component, null!, 0, AComp, [], null!, null!,
), ),
])); ]));
const compView = asElementData(view, 0).componentView; const compView = asElementData(view, 0).componentView;
update.and.callFake( update.and.callFake((check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
(check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test'); expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
expect(update).toHaveBeenCalled(); expect(update).toHaveBeenCalled();
@ -281,12 +273,12 @@ export function main() {
const {view, rootNodes} = createAndGetRootNodes(compViewDef([ const {view, rootNodes} = createAndGetRootNodes(compViewDef([
elementDef( elementDef(
NodeFlags.None, null, null, 1, 'div', null, null, null, null, NodeFlags.None, null !, null !, 1, 'div', null !, null !, null !, null !,
() => compViewDef([ () => compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'span'), elementDef(NodeFlags.None, null !, null !, 1, 'span'),
directiveDef(NodeFlags.OnDestroy, null, 0, ChildProvider, []) directiveDef(NodeFlags.OnDestroy, null !, 0, ChildProvider, [])
])), ])),
directiveDef(NodeFlags.Component, null, 0, AComp, [], null, null, ), directiveDef(NodeFlags.Component, null !, 0, AComp, [], null !, null !, ),
])); ]));
Services.destroyView(view); Services.destroyView(view);
@ -297,7 +289,7 @@ export function main() {
it('should throw on dirty checking destroyed views', () => { it('should throw on dirty checking destroyed views', () => {
const {view, rootNodes} = createAndGetRootNodes(compViewDef( const {view, rootNodes} = createAndGetRootNodes(compViewDef(
[ [
elementDef(NodeFlags.None, null, null, 0, 'div'), elementDef(NodeFlags.None, null !, null !, 0, 'div'),
], ],
(view) => {})); (view) => {}));

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