Compare commits

...

48 Commits

Author SHA1 Message Date
14a2d1a6f7 docs: add changelog for 4.0.2 2017-04-11 14:56:13 -07:00
61e089931f release: cut the 4.0.2 release 2017-04-11 14:50:12 -07:00
cbf30cb101 ci: add jasonaden to the router 2017-04-11 14:38:46 -07:00
2f41b52e78 fix(router): fix query param parsing 2017-04-11 14:38:46 -07:00
dcf60da16d refactor(router): cleanup & simplifications 2017-04-11 14:38:45 -07:00
9c9f409364 test(router): enable running the campaign in non-ES6 browsers
Closes #15806.
2017-04-11 14:38:45 -07:00
b44b983c1f test(router): test preloading when a module is already loaded 2017-04-11 14:38:45 -07:00
978f80985c fix(router): the preloader use the module from the loaded config 2017-04-11 14:38:45 -07:00
c10e50cf38 fix(tsc-wrapped): ensure valid path separators in metadata
* Fixes that `tsc-wrapped` stores invalid path separators in the bundled metadata files. Previous errors could have been: `Cannot find module '.corecoordinationnique-selection-dispatcher'.` (See https://github.com/angular/material2/issues/3834)
* Fixes failing tests on Windows. Now all tooling tests are green on Windows.

Related to #15403
2017-04-11 14:38:45 -07:00
168a2eb5bf fix(language-service): detect when there isn't a tsconfig.json
Fixes #15874
2017-04-11 14:38:44 -07:00
c9c7acd484 fix(language-service): parse extended i18n forms 2017-04-11 14:34:15 -07:00
5b99533315 fix(language-service): initialize static reflector correctly
Fixes #15768
2017-04-11 14:34:14 -07:00
037805b741 style(router): fix typo in router preloader method 2017-04-11 14:34:14 -07:00
0861fda69c fix(language-service): avoid throwing exceptions when reporting metadata errors 2017-04-11 14:34:13 -07:00
feae7b6059 fix(language-service): resolve any parameter types to any result 2017-04-11 14:34:12 -07:00
e4277a0869 fix(language-service): improve resilience to incomplete information 2017-04-11 14:34:12 -07:00
1864ccb3dd fix(compiler): fix inheritance for AOT with summaries (#15583)
Allows to inherit ctor args, lifecycle hooks and statics from a class
in another compilation unit. 
Will error if trying to inherit from a class in another compilation unit 
that has an `@Component` / `@Directive` / `@Pipe` / `@NgModule`.
2017-04-11 14:34:11 -07:00
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
154 changed files with 3298 additions and 2256 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
@ -185,8 +186,8 @@ groups:
files: files:
- "packages/router/*" - "packages/router/*"
users: users:
- vicb #primary - jasonaden
# needs secondary - vicb
- IgorMinar #fallback - IgorMinar #fallback
- mhevery #fallback - mhevery #fallback

View File

@ -1,3 +1,50 @@
<a name="4.0.2"></a>
## [4.0.2](https://github.com/angular/angular/compare/4.0.1...4.0.2) (2017-04-11)
### Bug Fixes
* **compiler:** fix inheritance for AOT with summaries ([#15583](https://github.com/angular/angular/issues/15583)) ([1864ccb](https://github.com/angular/angular/commit/1864ccb))
* **language-service:** avoid throwing exceptions when reporting metadata errors ([0861fda](https://github.com/angular/angular/commit/0861fda))
* **language-service:** detect when there isn't a tsconfig.json ([168a2eb](https://github.com/angular/angular/commit/168a2eb)), closes [#15874](https://github.com/angular/angular/issues/15874)
* **language-service:** improve resilience to incomplete information ([e4277a0](https://github.com/angular/angular/commit/e4277a0))
* **language-service:** initialize static reflector correctly ([5b99533](https://github.com/angular/angular/commit/5b99533)), closes [#15768](https://github.com/angular/angular/issues/15768)
* **language-service:** parse extended i18n forms ([c9c7acd](https://github.com/angular/angular/commit/c9c7acd))
* **language-service:** resolve any parameter types to any result ([feae7b6](https://github.com/angular/angular/commit/feae7b6))
* **router:** fix query param parsing ([2f41b52](https://github.com/angular/angular/commit/2f41b52))
* **router:** the preloader use the module from the loaded config ([978f809](https://github.com/angular/angular/commit/978f809))
* **tsc-wrapped:** ensure valid path separators in metadata ([c10e50c](https://github.com/angular/angular/commit/c10e50c))
<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) invisible-makeover (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)

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];
@ -75,4 +75,4 @@ function urlParamsToForm() {
} }
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-srcs", "name": "angular-srcs",
"version": "4.0.0", "version": "4.0.2",
"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,
@ -32,4 +34,4 @@
"benchmarks/src/largetable/ng2/index_aot.ts", "benchmarks/src/largetable/ng2/index_aot.ts",
"benchmarks/src/largetable/ng2_switch/index_aot.ts" "benchmarks/src/largetable/ng2_switch/index_aot.ts"
] ]
} }

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.2",
"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,19 +70,25 @@ 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 {
if (!containingFile || !containingFile.length) { const key = m + ':' + (containingFile || '');
if (m.indexOf('.') === 0) { let result = this.moduleFileNames.get(key);
throw new Error('Resolution of relative paths requires a containing file.'); if (!result) {
if (!containingFile || !containingFile.length) {
if (m.indexOf('.') === 0) {
throw new Error('Resolution of relative paths requires a containing file.');
}
// Any containing file gives the same result for absolute imports
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
} }
// Any containing file gives the same result for absolute imports m = m.replace(EXT, '');
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts')); const resolved =
ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule;
result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
this.moduleFileNames.set(key, result);
} }
m = m.replace(EXT, ''); return result;
const resolved =
ts.resolveModuleName(
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
.resolvedModule;
return resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
}; };
/** /**

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;
@ -124,7 +124,7 @@ export class NgTools_InternalApi_NG_2 {
const symbolCache = new StaticSymbolCache(); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache); const summaryResolver = new AotSummaryResolver(ngCompilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver); const symbolResolver = new StaticSymbolResolver(ngCompilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver); const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector); const routeMap = listLazyRoutesOfModule(options.entryModule, ngCompilerHost, staticReflector);
return Object.keys(routeMap).reduce( return Object.keys(routeMap).reduce(

View File

@ -47,7 +47,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const symbolCache = new StaticSymbolCache(); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache); const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache);
const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver); const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(symbolResolver); const staticReflector = new StaticReflector(summaryResolver, symbolResolver);
StaticAndDynamicReflectionCapabilities.install(staticReflector); StaticAndDynamicReflectionCapabilities.install(staticReflector);
const console = new Console(); const console = new Console();
const htmlParser = new I18NHtmlParser( const htmlParser = new I18NHtmlParser(

View File

@ -7,7 +7,11 @@
*/ */
import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger, ɵReflectorReader} from '@angular/core'; import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, HostBinding, HostListener, Inject, Injectable, Input, NgModule, Optional, Output, Pipe, Self, SkipSelf, ViewChild, ViewChildren, animate, group, keyframes, sequence, state, style, transition, trigger, ɵReflectorReader} from '@angular/core';
import {CompileSummaryKind} from '../compile_metadata';
import {SummaryResolver} from '../summary_resolver';
import {syntaxError} from '../util'; import {syntaxError} from '../util';
import {StaticSymbol} from './static_symbol'; import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver'; import {StaticSymbolResolver} from './static_symbol_resolver';
@ -35,8 +39,11 @@ export class StaticReflector implements ɵReflectorReader {
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>(); private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private injectionToken: StaticSymbol; private injectionToken: StaticSymbol;
private opaqueToken: StaticSymbol; private opaqueToken: StaticSymbol;
private annotationForParentClassWithSummaryKind = new Map<CompileSummaryKind, any[]>();
private annotationNames = new Map<any, string>();
constructor( constructor(
private summaryResolver: SummaryResolver<StaticSymbol>,
private symbolResolver: StaticSymbolResolver, private symbolResolver: StaticSymbolResolver,
knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [], knownMetadataClasses: {name: string, filePath: string, ctor: any}[] = [],
knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [], knownMetadataFunctions: {name: string, filePath: string, fn: any}[] = [],
@ -47,6 +54,17 @@ export class StaticReflector implements ɵReflectorReader {
this.getStaticSymbol(kc.filePath, kc.name), kc.ctor)); this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
knownMetadataFunctions.forEach( knownMetadataFunctions.forEach(
(kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn)); (kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
this.annotationForParentClassWithSummaryKind.set(
CompileSummaryKind.Directive, [Directive, Component]);
this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Pipe, [Pipe]);
this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.NgModule, [NgModule]);
this.annotationForParentClassWithSummaryKind.set(
CompileSummaryKind.Injectable, [Injectable, Pipe, Directive, Component, NgModule]);
this.annotationNames.set(Directive, 'Directive');
this.annotationNames.set(Component, 'Component');
this.annotationNames.set(Pipe, 'Pipe');
this.annotationNames.set(NgModule, 'NgModule');
this.annotationNames.set(Injectable, 'Injectable');
} }
importUri(typeOrFunc: StaticSymbol): string { importUri(typeOrFunc: StaticSymbol): string {
@ -96,17 +114,33 @@ export class StaticReflector implements ɵReflectorReader {
if (!annotations) { if (!annotations) {
annotations = []; annotations = [];
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
if (classMetadata['extends']) { const parentType = this.findParentType(type, classMetadata);
const parentType = this.trySimplify(type, classMetadata['extends']); if (parentType) {
if (parentType && (parentType instanceof StaticSymbol)) { const parentAnnotations = this.annotations(parentType);
const parentAnnotations = this.annotations(parentType); annotations.push(...parentAnnotations);
annotations.push(...parentAnnotations);
}
} }
let ownAnnotations: any[] = [];
if (classMetadata['decorators']) { if (classMetadata['decorators']) {
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']); ownAnnotations = this.simplify(type, classMetadata['decorators']);
annotations.push(...ownAnnotations); annotations.push(...ownAnnotations);
} }
if (parentType && !this.summaryResolver.isLibraryFile(type.filePath) &&
this.summaryResolver.isLibraryFile(parentType.filePath)) {
const summary = this.summaryResolver.resolveSummary(parentType);
if (summary && summary.type) {
const requiredAnnotationTypes =
this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind);
const typeHasRequiredAnnotation = requiredAnnotationTypes.some(
requiredType => ownAnnotations.some(ann => ann instanceof requiredType));
if (!typeHasRequiredAnnotation) {
this.reportError(
syntaxError(
`Class ${type.name} in ${type.filePath} extends from a ${CompileSummaryKind[summary.type.summaryKind]} in another compilation unit without duplicating the decorator. ` +
`Please add a ${requiredAnnotationTypes.map(type => this.annotationNames.get(type)).join(' or ')} decorator to the class.`),
type);
}
}
}
this.annotationCache.set(type, annotations.filter(ann => !!ann)); this.annotationCache.set(type, annotations.filter(ann => !!ann));
} }
return annotations; return annotations;
@ -117,14 +151,12 @@ export class StaticReflector implements ɵReflectorReader {
if (!propMetadata) { if (!propMetadata) {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
propMetadata = {}; propMetadata = {};
if (classMetadata['extends']) { const parentType = this.findParentType(type, classMetadata);
const parentType = this.simplify(type, classMetadata['extends']); if (parentType) {
if (parentType instanceof StaticSymbol) { const parentPropMetadata = this.propMetadata(parentType);
const parentPropMetadata = this.propMetadata(parentType); Object.keys(parentPropMetadata).forEach((parentProp) => {
Object.keys(parentPropMetadata).forEach((parentProp) => { propMetadata[parentProp] = parentPropMetadata[parentProp];
propMetadata[parentProp] = parentPropMetadata[parentProp]; });
});
}
} }
const members = classMetadata['members'] || {}; const members = classMetadata['members'] || {};
@ -157,6 +189,7 @@ export class StaticReflector implements ɵReflectorReader {
let parameters = this.parameterCache.get(type); let parameters = this.parameterCache.get(type);
if (!parameters) { if (!parameters) {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
const parentType = this.findParentType(type, classMetadata);
const members = classMetadata ? classMetadata['members'] : null; const members = classMetadata ? classMetadata['members'] : null;
const ctorData = members ? members['__ctor__'] : null; const ctorData = members ? members['__ctor__'] : null;
if (ctorData) { if (ctorData) {
@ -175,11 +208,8 @@ export class StaticReflector implements ɵReflectorReader {
} }
parameters.push(nestedResult); parameters.push(nestedResult);
}); });
} else if (classMetadata['extends']) { } else if (parentType) {
const parentType = this.simplify(type, classMetadata['extends']); parameters = this.parameters(parentType);
if (parentType instanceof StaticSymbol) {
parameters = this.parameters(parentType);
}
} }
if (!parameters) { if (!parameters) {
parameters = []; parameters = [];
@ -198,14 +228,12 @@ export class StaticReflector implements ɵReflectorReader {
if (!methodNames) { if (!methodNames) {
const classMetadata = this.getTypeMetadata(type); const classMetadata = this.getTypeMetadata(type);
methodNames = {}; methodNames = {};
if (classMetadata['extends']) { const parentType = this.findParentType(type, classMetadata);
const parentType = this.simplify(type, classMetadata['extends']); if (parentType) {
if (parentType instanceof StaticSymbol) { const parentMethodNames = this._methodNames(parentType);
const parentMethodNames = this._methodNames(parentType); Object.keys(parentMethodNames).forEach((parentProp) => {
Object.keys(parentMethodNames).forEach((parentProp) => { methodNames[parentProp] = parentMethodNames[parentProp];
methodNames[parentProp] = parentMethodNames[parentProp]; });
});
}
} }
const members = classMetadata['members'] || {}; const members = classMetadata['members'] || {};
@ -219,6 +247,13 @@ export class StaticReflector implements ɵReflectorReader {
return methodNames; return methodNames;
} }
private findParentType(type: StaticSymbol, classMetadata: any): StaticSymbol|null {
const parentType = this.trySimplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
return parentType;
}
}
hasLifecycleHook(type: any, lcProperty: string): boolean { hasLifecycleHook(type: any, lcProperty: string): boolean {
if (!(type instanceof StaticSymbol)) { if (!(type instanceof StaticSymbol)) {
this.reportError( this.reportError(

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,11 +301,23 @@ 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(
sourceSymbol: StaticSymbol, topLevelPath: string, topLevelSymbolNames: Set<string>, sourceSymbol: StaticSymbol, topLevelPath: string, topLevelSymbolNames: Set<string>,
metadata: any): ResolvedStaticSymbol { metadata: any): ResolvedStaticSymbol {
// For classes that don't have Angular summaries / metadata,
// we only keep their arity, but nothing else
// (e.g. their constructor parameters).
// We do this to prevent introducing deep imports
// as we didn't generate .ngfactory.ts files with proper reexports.
if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) && metadata &&
metadata['__symbolic'] === 'class') {
const transformedMeta = {__symbolic: 'class', arity: metadata.arity};
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
}
const self = this; const self = this;
class ReferenceTransformer extends ValueTransformer { class ReferenceTransformer extends ValueTransformer {

View File

@ -53,7 +53,7 @@ export function serializeSummaries(
// (in a minimal way). // (in a minimal way).
types.forEach((typeSummary) => { types.forEach((typeSummary) => {
serializer.addOrMergeSummary( serializer.addOrMergeSummary(
{symbol: typeSummary.type.reference, metadata: {__symbolic: 'class'}, type: typeSummary}); {symbol: typeSummary.type.reference, metadata: null, type: typeSummary});
if (typeSummary.summaryKind === CompileSummaryKind.NgModule) { if (typeSummary.summaryKind === CompileSummaryKind.NgModule) {
const ngModuleSummary = <CompileNgModuleSummary>typeSummary; const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => { ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
@ -94,10 +94,21 @@ class Serializer extends ValueTransformer {
addOrMergeSummary(summary: Summary<StaticSymbol>) { addOrMergeSummary(summary: Summary<StaticSymbol>) {
let symbolMeta = summary.metadata; let symbolMeta = summary.metadata;
if (symbolMeta && symbolMeta.__symbolic === 'class') { if (symbolMeta && symbolMeta.__symbolic === 'class') {
// For classes, we only keep their statics and arity, but not the metadata // For classes, we keep everything except their class decorators.
// of the class itself as that has been captured already via other summaries // We need to keep e.g. the ctor args, method names, method decorators
// (e.g. DirectiveSummary, ...). // so that the class can be extended in another compilation unit.
symbolMeta = {__symbolic: 'class', statics: symbolMeta.statics, arity: symbolMeta.arity}; // We don't keep the class decorators as
// 1) they refer to data
// that should not cause a rebuild of downstream compilation units
// (e.g. inline templates of @Component, or @NgModule.declarations)
// 2) their data is already captured in TypeSummaries, e.g. DirectiveSummary.
const clone: {[key: string]: any} = {};
Object.keys(symbolMeta).forEach((propName) => {
if (propName !== 'decorators') {
clone[propName] = symbolMeta[propName];
}
});
symbolMeta = clone;
} }
let processedSummary = this.processedSummaryBySymbol.get(summary.symbol); let processedSummary = this.processedSummaryBySymbol.get(summary.symbol);

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

@ -94,7 +94,7 @@ export class Extractor {
const symbolCache = new StaticSymbolCache(); const symbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(host, symbolCache); const summaryResolver = new AotSummaryResolver(host, symbolCache);
const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver); const staticSymbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver);
const staticReflector = new StaticReflector(staticSymbolResolver); const staticReflector = new StaticReflector(summaryResolver, staticSymbolResolver);
StaticAndDynamicReflectionCapabilities.install(staticReflector); StaticAndDynamicReflectionCapabilities.install(staticReflector);
const config = const config =

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

@ -529,6 +529,7 @@ export class CompileMetadataResolver {
syntaxError( syntaxError(
`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), `Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`),
moduleType); moduleType);
return;
} }
}); });
@ -627,6 +628,7 @@ export class CompileMetadataResolver {
`Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` + `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
`You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`),
moduleType); moduleType);
return;
} }
this._ngModuleOfTypes.set(type, moduleType); this._ngModuleOfTypes.set(type, moduleType);
} }
@ -860,6 +862,7 @@ export class CompileMetadataResolver {
} else if (provider === void 0) { } else if (provider === void 0) {
this._reportError(syntaxError( this._reportError(syntaxError(
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`)); `Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
return;
} else { } else {
const providersInfo = const providersInfo =
(<string[]>providers.reduce( (<string[]>providers.reduce(
@ -879,6 +882,7 @@ export class CompileMetadataResolver {
syntaxError( syntaxError(
`Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), `Invalid ${debugInfo ? debugInfo : 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`),
type); type);
return;
} }
if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) { if (providerMeta.token === resolveIdentifier(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type)); targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
@ -933,14 +937,12 @@ export class CompileMetadataResolver {
const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType); const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
if (dirMeta && dirMeta.metadata.isComponent) { if (dirMeta && dirMeta.metadata.isComponent) {
return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory}; return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory};
} else {
const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (dirSummary && dirSummary.isComponent) {
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
}
} }
const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (dirSummary && dirSummary.isComponent) {
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
}
if (throwIfNotFound) { if (throwIfNotFound) {
throw syntaxError(`${dirType.name} cannot be used as an entry component.`); throw syntaxError(`${dirType.name} cannot be used as an entry component.`);
} }
@ -1004,8 +1006,10 @@ export class CompileMetadataResolver {
syntaxError( syntaxError(
`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), `Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`),
typeOrFunc); typeOrFunc);
selectors = [];
} else {
selectors = [this._getTokenMetadata(q.selector)];
} }
selectors = [this._getTokenMetadata(q.selector)];
} }
return { return {

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

@ -8,6 +8,7 @@
import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler'; import {AotCompiler, AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
import {RenderComponentType, ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core'; import {RenderComponentType, ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
import {NodeFlags} from '@angular/core/src/view/index';
import {async} from '@angular/core/testing'; import {async} from '@angular/core/testing';
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped'; import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -368,6 +369,293 @@ describe('compiler (unbundled Angular)', () => {
})); }));
}); });
describe('inheritance with summaries', () => {
function compileWithSummaries(
libInput: MockData, appInput: MockData): Promise<GeneratedFile[]> {
const libHost = new MockCompilerHost(['/lib/base.ts'], libInput, angularFiles);
const libAotHost = new MockAotCompilerHost(libHost);
libAotHost.tsFilesOnly();
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
const appAotHost = new MockAotCompilerHost(appHost);
appAotHost.tsFilesOnly();
return compile(libHost, libAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
.then(() => {
libHost.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
libHost.overrides.forEach((value, key) => appHost.override(key, value));
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
});
}
function compileParentAndChild(
{parentClassDecorator, parentModuleDecorator, childClassDecorator, childModuleDecorator}: {
parentClassDecorator: string,
parentModuleDecorator: string,
childClassDecorator: string,
childModuleDecorator: string
}) {
const libInput: MockData = {
'lib': {
'base.ts': `
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
${parentClassDecorator}
export class Base {}
${parentModuleDecorator}
export class BaseModule {}
`
}
};
const appInput: MockData = {
'app': {
'main.ts': `
import {Injectable, Pipe, Directive, Component, NgModule} from '@angular/core';
import {Base} from '../lib/base';
${childClassDecorator}
export class Extends extends Base {}
${childModuleDecorator}
export class MyModule {}
`
}
};
return compileWithSummaries(libInput, appInput)
.then((generatedFiles) => generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts'));
}
it('should inherit ctor and lifecycle hooks from classes in other compilation units',
async(() => {
const libInput: MockData = {
'lib': {
'base.ts': `
export class AParam {}
export class Base {
constructor(a: AParam) {}
ngOnDestroy() {}
}
`
}
};
const appInput: MockData = {
'app': {
'main.ts': `
import {NgModule, Component} from '@angular/core';
import {Base} from '../lib/base';
@Component({template: ''})
export class Extends extends Base {}
@NgModule({
declarations: [Extends]
})
export class MyModule {}
`
}
};
compileWithSummaries(libInput, appInput).then((generatedFiles) => {
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(mainNgFactory.source)
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam]`);
});
}));
it('should inherit ctor and lifecycle hooks from classes in other compilation units over 2 levels',
async(() => {
const lib1Input: MockData = {
'lib1': {
'base.ts': `
export class AParam {}
export class Base {
constructor(a: AParam) {}
ngOnDestroy() {}
}
`
}
};
const lib2Input: MockData = {
'lib2': {
'middle.ts': `
import {Base} from '../lib1/base';
export class Middle extends Base {}
`
}
};
const appInput: MockData = {
'app': {
'main.ts': `
import {NgModule, Component} from '@angular/core';
import {Middle} from '../lib2/middle';
@Component({template: ''})
export class Extends extends Middle {}
@NgModule({
declarations: [Extends]
})
export class MyModule {}
`
}
};
const lib1Host = new MockCompilerHost(['/lib1/base.ts'], lib1Input, angularFiles);
const lib1AotHost = new MockAotCompilerHost(lib1Host);
lib1AotHost.tsFilesOnly();
const lib2Host = new MockCompilerHost(['/lib2/middle.ts'], lib2Input, angularFiles);
const lib2AotHost = new MockAotCompilerHost(lib2Host);
lib2AotHost.tsFilesOnly();
const appHost = new MockCompilerHost(['/app/main.ts'], appInput, angularFiles);
const appAotHost = new MockAotCompilerHost(appHost);
appAotHost.tsFilesOnly();
compile(lib1Host, lib1AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit)
.then(() => {
lib1Host.writtenFiles.forEach((value, key) => lib2Host.writeFile(key, value, false));
lib1Host.overrides.forEach((value, key) => lib2Host.override(key, value));
return compile(
lib2Host, lib2AotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
})
.then(() => {
lib2Host.writtenFiles.forEach((value, key) => appHost.writeFile(key, value, false));
lib2Host.overrides.forEach((value, key) => appHost.override(key, value));
return compile(appHost, appAotHost, expectNoDiagnostics, expectNoDiagnosticsAndEmit);
})
.then((generatedFiles) => {
const mainNgFactory = generatedFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(mainNgFactory.source)
.toContain(`${flags},(null as any),0,import1.Extends,[import2.AParam_2]`);
});
}));
describe('Injectable', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: '@Injectable()',
parentModuleDecorator: '@NgModule({providers: [Base]})',
childClassDecorator: '@Injectable()',
childModuleDecorator: '@NgModule({providers: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
it('should error if the child class has no matching decorator', async(() => {
compileParentAndChild({
parentClassDecorator: '@Injectable()',
parentModuleDecorator: '@NgModule({providers: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({providers: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'Class Extends in /app/main.ts extends from a Injectable in another compilation unit without duplicating the decorator. ' +
'Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.');
});
}));
});
describe('Component', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Component({template: ''})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Component({template: ''})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
it('should error if the child class has no matching decorator', async(() => {
compileParentAndChild({
parentClassDecorator: `@Component({template: ''})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
'Please add a Directive or Component decorator to the class.');
});
}));
});
describe('Directive', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Directive({selector: '[someDir]'})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
it('should error if the child class has no matching decorator', async(() => {
compileParentAndChild({
parentClassDecorator: `@Directive({selector: '[someDir]'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'Class Extends in /app/main.ts extends from a Directive in another compilation unit without duplicating the decorator. ' +
'Please add a Directive or Component decorator to the class.');
});
}));
});
describe('Pipe', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: `@Pipe({name: 'somePipe'})`,
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
it('should error if the child class has no matching decorator', async(() => {
compileParentAndChild({
parentClassDecorator: `@Pipe({name: 'somePipe'})`,
parentModuleDecorator: '@NgModule({declarations: [Base]})',
childClassDecorator: '',
childModuleDecorator: '@NgModule({declarations: [Extends]})',
}).then(fail, (e) => {
expect(e.message).toContain(
'Class Extends in /app/main.ts extends from a Pipe in another compilation unit without duplicating the decorator. ' +
'Please add a Pipe decorator to the class.');
});
}));
});
describe('NgModule', () => {
it('should allow to inherit', async(() => {
compileParentAndChild({
parentClassDecorator: `@NgModule()`,
parentModuleDecorator: '',
childClassDecorator: `@NgModule()`,
childModuleDecorator: '',
}).then((mainNgFactory) => { expect(mainNgFactory).toBeTruthy(); });
}));
it('should error if the child class has no matching decorator', async(() => {
compileParentAndChild({
parentClassDecorator: `@NgModule()`,
parentModuleDecorator: '',
childClassDecorator: '',
childModuleDecorator: '',
}).then(fail, (e) => {
expect(e.message).toContain(
'Class Extends in /app/main.ts extends from a NgModule in another compilation unit without duplicating the decorator. ' +
'Please add a NgModule decorator to the class.');
});
}));
});
});
}); });
describe('compiler (bundled Angular)', () => { describe('compiler (bundled Angular)', () => {
@ -513,6 +801,11 @@ function expectNoDiagnostics(program: ts.Program) {
expectNoDiagnostics(program.getSemanticDiagnostics()); expectNoDiagnostics(program.getSemanticDiagnostics());
} }
function expectNoDiagnosticsAndEmit(program: ts.Program) {
expectNoDiagnostics(program);
program.emit();
}
function isDTS(fileName: string): boolean { function isDTS(fileName: string): boolean {
return /\.d\.ts$/.test(fileName); return /\.d\.ts$/.test(fileName);
} }
@ -544,7 +837,7 @@ function summaryCompile(
function compile( function compile(
host: MockCompilerHost, aotHost: AotCompilerHost, preCompile?: (program: ts.Program) => void, host: MockCompilerHost, aotHost: AotCompilerHost, preCompile?: (program: ts.Program) => void,
postCompile: (program: ts.Program) => void = expectNoDiagnostics, postCompile: (program: ts.Program) => void = expectNoDiagnostics,
options: AotCompilerOptions = {}) { options: AotCompilerOptions = {}): Promise<GeneratedFile[]> {
const scripts = host.scriptNames.slice(0); const scripts = host.scriptNames.slice(0);
const program = ts.createProgram(scripts, settings, host); const program = ts.createProgram(scripts, settings, host);
if (preCompile) preCompile(program); if (preCompile) preCompile(program);

View File

@ -24,9 +24,10 @@ describe('StaticReflector', () => {
errorRecorder?: (error: any, fileName: string) => void, collectorOptions?: CollectorOptions) { errorRecorder?: (error: any, fileName: string) => void, collectorOptions?: CollectorOptions) {
const symbolCache = new StaticSymbolCache(); const symbolCache = new StaticSymbolCache();
host = new MockStaticSymbolResolverHost(testData, collectorOptions); host = new MockStaticSymbolResolverHost(testData, collectorOptions);
symbolResolver = const summaryResolver = new MockSummaryResolver([]);
new StaticSymbolResolver(host, symbolCache, new MockSummaryResolver([]), errorRecorder); spyOn(summaryResolver, 'isLibraryFile').and.returnValue(false);
reflector = new StaticReflector(symbolResolver, decorators, [], errorRecorder); symbolResolver = new StaticSymbolResolver(host, symbolCache, summaryResolver, errorRecorder);
reflector = new StaticReflector(summaryResolver, symbolResolver, decorators, [], errorRecorder);
noContext = reflector.getStaticSymbol('', ''); noContext = reflector.getStaticSymbol('', '');
} }
@ -495,6 +496,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

@ -283,6 +283,31 @@ describe('StaticSymbolResolver', () => {
]); ]);
}); });
it('should only use the arity for classes from libraries without summaries', () => {
init({
'/test.d.ts': [{
'__symbolic': 'module',
'version': 3,
'metadata': {
'AParam': {__symbolic: 'class'},
'AClass': {
__symbolic: 'class',
arity: 1,
members: {
__ctor__: [{
__symbolic: 'constructor',
parameters: [symbolCache.get('/test.d.ts', 'AParam')]
}]
}
}
}
}]
});
expect(symbolResolver.resolveSymbol(symbolCache.get('/test.d.ts', 'AClass')).metadata)
.toEqual({__symbolic: 'class', arity: 1});
});
it('should be able to trace a named export', () => { it('should be able to trace a named export', () => {
const symbol = symbolResolver const symbol = symbolResolver
.resolveSymbol(symbolResolver.getSymbolByModule( .resolveSymbol(symbolResolver.getSymbolByModule(

View File

@ -61,7 +61,8 @@ export function main() {
metadata: { metadata: {
__symbolic: 'class', __symbolic: 'class',
members: {'aMethod': {__symbolic: 'function'}}, members: {'aMethod': {__symbolic: 'function'}},
statics: {aStatic: true} statics: {aStatic: true},
decorators: ['aDecoratorData']
} }
} }
], ],
@ -88,8 +89,12 @@ export function main() {
}); });
expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService')); expect(summaries[1].symbol).toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
// serialization should only keep the statics... // serialization should drop class decorators
expect(summaries[1].metadata).toEqual({__symbolic: 'class', statics: {aStatic: true}}); expect(summaries[1].metadata).toEqual({
__symbolic: 'class',
members: {aMethod: {__symbolic: 'function'}},
statics: {aStatic: true}
});
expect(summaries[1].type.type.reference) expect(summaries[1].type.type.reference)
.toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService')); .toBe(symbolCache.get('/tmp/some_service.d.ts', 'SomeService'));
}); });

View File

@ -177,8 +177,8 @@ export class MockCompilerHost implements ts.CompilerHost {
private angularSourcePath: string|undefined; private angularSourcePath: string|undefined;
private nodeModulesPath: string|undefined; private nodeModulesPath: string|undefined;
private overrides = new Map<string, string>(); public overrides = new Map<string, string>();
private writtenFiles = new Map<string, string>(); public writtenFiles = new Map<string, string>();
private sourceFiles = new Map<string, ts.SourceFile>(); private sourceFiles = new Map<string, ts.SourceFile>();
private assumeExists = new Set<string>(); private assumeExists = new Set<string>();
private traces: string[] = []; private traces: string[] = [];

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

@ -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)) { // Items remaining at the end of the list have been deleted
newSeqRecord = records.get(key); if (insertBefore) {
this._maybeAddToChanges(newSeqRecord, value); if (insertBefore._prev) {
} else { insertBefore._prev._next = null;
newSeqRecord = new KeyValueChangeRecord_<K, V>(key);
records.set(key, newSeqRecord);
newSeqRecord.currentValue = value;
this._addToAdditions(newSeqRecord);
}
} }
if (seqChanged) { this._removalsHead = insertBefore;
if (this._isInRemovals(newSeqRecord)) { this._removalsTail = insertBefore;
this._removeFromRemovals(newSeqRecord);
} for (let record = insertBefore; record !== null; record = record._nextRemoved) {
if (lastNewSeqRecord == null) { if (record === this._mapHead) {
this._mapHead = newSeqRecord; this._mapHead = null;
} else {
lastNewSeqRecord._next = newSeqRecord;
} }
this._records.delete(record.key);
record._nextRemoved = record._next;
record.previousValue = record.currentValue;
record.currentValue = null;
record._prev = null;
record._next = null;
} }
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]);
} }
} }
@ -439,4 +439,4 @@ class RendererAdapter implements RendererV1 {
setText(renderNode: Text, text: string): void { this.delegate.setValue(renderNode, text); } setText(renderNode: Text, text: string): void { this.delegate.setValue(renderNode, text); }
animate(): any { throw new Error('Renderer.animate is no longer supported!'); } animate(): any { throw new Error('Renderer.animate is no longer supported!'); }
} }

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', () => {

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