From a16954d3a5977bf3c087749f7a0c81810281af08 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 11 Mar 2015 21:11:39 -0700 Subject: [PATCH] refactor(view): separate context and locals --- modules/angular2/change_detection.js | 4 +- .../change_detection_jit_generator.es6 | 54 +++---- .../change_detection/change_detection_util.js | 11 -- .../dynamic_change_detector.js | 29 ++-- .../src/change_detection/interfaces.js | 3 +- .../src/change_detection/parser/ast.js | 144 +++++++++--------- .../parser/context_with_variable_bindings.js | 36 ----- .../src/change_detection/parser/locals.js | 51 +++++++ .../change_detection/proto_change_detector.js | 46 +++--- .../src/change_detection/proto_record.js | 7 +- modules/angular2/src/core/application.js | 2 +- .../compiler/pipeline/proto_view_builder.js | 13 +- modules/angular2/src/core/compiler/view.js | 77 ++++++---- .../src/core/compiler/view_container.js | 3 +- .../change_detection/change_detection_spec.js | 99 ++++++------ .../context_with_variable_bindings_spec.js | 44 ------ .../change_detection/parser/locals_spec.js | 40 +++++ .../change_detection/parser/parser_spec.js | 84 +++++----- .../test/core/compiler/integration_spec.js | 33 ++-- .../pipeline/element_binder_builder_spec.js | 7 +- .../pipeline/proto_view_builder_spec.js | 11 +- .../shadow_dom_emulation_integration_spec.js | 2 +- .../test/core/compiler/view_container_spec.js | 5 +- .../angular2/test/core/compiler/view_spec.js | 40 ++--- .../angular2/test/directives/foreach_spec.js | 10 +- modules/angular2/test/directives/if_spec.js | 2 +- .../test/directives/non_bindable_spec.js | 2 +- .../angular2/test/directives/switch_spec.js | 2 +- .../angular2/test/forms/integration_spec.js | 2 +- .../change_detection_benchmark.js | 6 +- 30 files changed, 450 insertions(+), 419 deletions(-) delete mode 100644 modules/angular2/src/change_detection/parser/context_with_variable_bindings.js create mode 100644 modules/angular2/src/change_detection/parser/locals.js delete mode 100644 modules/angular2/test/change_detection/parser/context_with_variable_bindings_spec.js create mode 100644 modules/angular2/test/change_detection/parser/locals_spec.js diff --git a/modules/angular2/change_detection.js b/modules/angular2/change_detection.js index b330cd5f5d..c8ac4e7093 100644 --- a/modules/angular2/change_detection.js +++ b/modules/angular2/change_detection.js @@ -1,8 +1,8 @@ export {AST} from './src/change_detection/parser/ast'; export {Lexer} from './src/change_detection/parser/lexer'; export {Parser} from './src/change_detection/parser/parser'; -export {ContextWithVariableBindings} - from './src/change_detection/parser/context_with_variable_bindings'; +export {Locals} + from './src/change_detection/parser/locals'; export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions'; export {ChangeRecord, ChangeDispatcher, ChangeDetector, diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 index 2f63bb529b..5866777a24 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 @@ -1,7 +1,6 @@ import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; -import {ContextWithVariableBindings} from './parser/context_with_variable_bindings'; import {AbstractChangeDetector} from './abstract_change_detector'; import {ChangeDetectionUtil} from './change_detection_util'; @@ -9,6 +8,7 @@ import { ProtoRecord, RECORD_TYPE_SELF, RECORD_TYPE_PROPERTY, + RECORD_TYPE_LOCAL, RECORD_TYPE_INVOKE_METHOD, RECORD_TYPE_CONST, RECORD_TYPE_INVOKE_CLOSURE, @@ -44,13 +44,7 @@ import { * var temp; * var context = this.context; * - * temp = ChangeDetectionUtil.findContext("address", context); - * if (temp instanceof ContextWithVariableBindings) { - * address0 = temp.get('address'); - * } else { - * address0 = temp.address; - * } - * + * address0 = context.address; * if (address0 !== this.address0) { * this.address0 = address0; * } @@ -70,14 +64,16 @@ import { * } * * - * ChangeDetector0.prototype.hydrate = function(context) { + * ChangeDetector0.prototype.hydrate = function(context, locals) { * this.context = context; + * this.locals = locals; * } * * ChangeDetector0.prototype.dehydrate = function(context) { * this.context = ChangeDetectionUtil.unitialized(); * this.address0 = ChangeDetectionUtil.unitialized(); * this.city1 = ChangeDetectionUtil.unitialized(); + * this.locals = null; * } * * ChangeDetector0.prototype.hydrated = function() { @@ -99,8 +95,10 @@ var UTIL = "ChangeDetectionUtil"; var DISPATCHER_ACCESSOR = "this.dispatcher"; var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry"; var PROTOS_ACCESSOR = "this.protos"; +var CONTEXT_ACCESSOR = "this.context"; var CHANGE_LOCAL = "change"; var CHANGES_LOCAL = "changes"; +var LOCALS_ACCESSOR = "this.locals"; var TEMP_LOCAL = "temp"; function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string { @@ -135,15 +133,17 @@ function pipeOnDestroyTemplate(pipeNames:List) { function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string { return ` -${type}.prototype.hydrate = function(context) { - this.context = context; +${type}.prototype.hydrate = function(context, locals) { + ${CONTEXT_ACCESSOR} = context; + ${LOCALS_ACCESSOR} = locals; } ${type}.prototype.dehydrate = function() { ${pipeOnDestroy} ${fieldsDefinitions} + ${LOCALS_ACCESSOR} = null; } ${type}.prototype.hydrated = function() { - return this.context !== ${UTIL}.unitialized(); + return ${CONTEXT_ACCESSOR} !== ${UTIL}.unitialized(); } `; } @@ -165,7 +165,7 @@ var ${TEMP_LOCAL}; var ${CHANGE_LOCAL}; var ${CHANGES_LOCAL} = null; -context = this.context; +context = ${CONTEXT_ACCESSOR}; ${records} `; } @@ -216,17 +216,6 @@ function assignmentTemplate(field:string, value:string) { return `${field} = ${value};`; } -function propertyReadTemplate(name:string, context:string, newValue:string) { - return ` -${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context}); -if (${TEMP_LOCAL} instanceof ContextWithVariableBindings) { - ${newValue} = ${TEMP_LOCAL}.get('${name}'); -} else { - ${newValue} = ${TEMP_LOCAL}.${name}; -} -`; -} - function invokeMethodTemplate(name:string, args:string, context:string, newValue:string) { return ` ${TEMP_LOCAL} = ${UTIL}.findContext("${name}", ${context}); @@ -306,7 +295,7 @@ export class ChangeDetectorJITGenerator { generate():Function { var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate()); - return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'ContextWithVariableBindings', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, ContextWithVariableBindings, this.records); + return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records); } genConstructor():string { @@ -403,18 +392,13 @@ export class ChangeDetectorJITGenerator { return `${newValue} = ${this.genLiteral(r.funcOrValue)}`; case RECORD_TYPE_PROPERTY: - if (r.contextIndex == 0) { // only the first property read can be a local - return propertyReadTemplate(r.name, context, newValue); - } else { - return assignmentTemplate(newValue, `${context}.${r.name}`); - } + return assignmentTemplate(newValue, `${context}.${r.name}`); + + case RECORD_TYPE_LOCAL: + return assignmentTemplate(newValue, `${LOCALS_ACCESSOR}.get('${r.name}')`); case RECORD_TYPE_INVOKE_METHOD: - if (r.contextIndex == 0) { // only the first property read can be a local - return invokeMethodTemplate(r.name, args, context, newValue); - } else { - return assignmentTemplate(newValue, `${context}.${r.name}(${args})`); - } + return assignmentTemplate(newValue, `${context}.${r.name}(${args})`); case RECORD_TYPE_INVOKE_CLOSURE: return assignmentTemplate(newValue, `${context}(${args})`); diff --git a/modules/angular2/src/change_detection/change_detection_util.js b/modules/angular2/src/change_detection/change_detection_util.js index fa8bfc5a0a..554ccc97c0 100644 --- a/modules/angular2/src/change_detection/change_detection_util.js +++ b/modules/angular2/src/change_detection/change_detection_util.js @@ -1,6 +1,5 @@ import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; -import {ContextWithVariableBindings} from './parser/context_with_variable_bindings'; import {ProtoRecord} from './proto_record'; import {ExpressionChangedAfterItHasBeenChecked} from './exceptions'; import {NO_CHANGE} from './pipes/pipe'; @@ -144,16 +143,6 @@ export class ChangeDetectionUtil { return obj[args[0]]; } - static findContext(name:string, c){ - while (c instanceof ContextWithVariableBindings) { - if (c.hasBinding(name)) { - return c; - } - c = c.parent; - } - return c; - } - static noChangeMarker(value):boolean { return value === NO_CHANGE; } diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.js b/modules/angular2/src/change_detection/dynamic_change_detector.js index 2243864afc..3cd598a3da 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.js +++ b/modules/angular2/src/change_detection/dynamic_change_detector.js @@ -1,6 +1,5 @@ import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/facade/lang'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; -import {ContextWithVariableBindings} from './parser/context_with_variable_bindings'; import {AbstractChangeDetector} from './abstract_change_detector'; import {PipeRegistry} from './pipes/pipe_registry'; @@ -11,6 +10,7 @@ import { ProtoRecord, RECORD_TYPE_SELF, RECORD_TYPE_PROPERTY, + RECORD_TYPE_LOCAL, RECORD_TYPE_INVOKE_METHOD, RECORD_TYPE_CONST, RECORD_TYPE_INVOKE_CLOSURE, @@ -26,6 +26,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { dispatcher:any; pipeRegistry; + locals:any; values:List; changes:List; pipes:List; @@ -47,12 +48,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector { ListWrapper.fill(this.pipes, null); ListWrapper.fill(this.prevContexts, uninitialized); ListWrapper.fill(this.changes, false); + this.locals = null; this.protos = protoRecords; } - hydrate(context:any) { + hydrate(context:any, locals:any) { this.values[0] = context; + this.locals = locals; } dehydrate() { @@ -61,6 +64,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { ListWrapper.fill(this.changes, false); ListWrapper.fill(this.pipes, null); ListWrapper.fill(this.prevContexts, uninitialized); + this.locals = null; } _destroyPipes() { @@ -143,26 +147,15 @@ export class DynamicChangeDetector extends AbstractChangeDetector { case RECORD_TYPE_PROPERTY: var context = this._readContext(proto); - var c = ChangeDetectionUtil.findContext(proto.name, context); - if (c instanceof ContextWithVariableBindings) { - return c.get(proto.name); - } else { - var propertyGetter:Function = proto.funcOrValue; - return propertyGetter(c); - } - break; + return proto.funcOrValue(context); + + case RECORD_TYPE_LOCAL: + return this.locals.get(proto.name); case RECORD_TYPE_INVOKE_METHOD: var context = this._readContext(proto); var args = this._readArgs(proto); - var c = ChangeDetectionUtil.findContext(proto.name, context); - if (c instanceof ContextWithVariableBindings) { - return FunctionWrapper.apply(c.get(proto.name), args); - } else { - var methodInvoker:Function = proto.funcOrValue; - return methodInvoker(c, args); - } - break; + return proto.funcOrValue(context, args); case RECORD_TYPE_KEYED_ACCESS: var arg = this._readArgs(proto)[0]; diff --git a/modules/angular2/src/change_detection/interfaces.js b/modules/angular2/src/change_detection/interfaces.js index 5f7bbed50d..2d0672545b 100644 --- a/modules/angular2/src/change_detection/interfaces.js +++ b/modules/angular2/src/change_detection/interfaces.js @@ -1,4 +1,5 @@ import {List} from 'angular2/src/facade/collection'; +import {Locals} from './parser/locals'; export class ChangeRecord { bindingMemento:any; @@ -55,7 +56,7 @@ export class ChangeDetector { addChild(cd:ChangeDetector) {} removeChild(cd:ChangeDetector) {} remove() {} - hydrate(context:any) {} + hydrate(context:any, locals:Locals) {} dehydrate() {} markPathToRootAsCheckOnce() {} diff --git a/modules/angular2/src/change_detection/parser/ast.js b/modules/angular2/src/change_detection/parser/ast.js index e404e21c12..f66c51ba5c 100644 --- a/modules/angular2/src/change_detection/parser/ast.js +++ b/modules/angular2/src/change_detection/parser/ast.js @@ -1,9 +1,8 @@ import {autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang"; import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection"; -import {ContextWithVariableBindings} from "./context_with_variable_bindings"; export class AST { - eval(context) { + eval(context, locals) { throw new BaseException("Not supported"); } @@ -11,7 +10,7 @@ export class AST { return false; } - assign(context, value) { + assign(context, locals, value) { throw new BaseException("Not supported"); } @@ -24,7 +23,7 @@ export class AST { } export class EmptyExpr extends AST { - eval(context) { + eval(context, locals) { return null; } @@ -34,7 +33,7 @@ export class EmptyExpr extends AST { } export class ImplicitReceiver extends AST { - eval(context) { + eval(context, locals) { return context; } @@ -53,10 +52,10 @@ export class Chain extends AST { this.expressions = expressions; } - eval(context) { + eval(context, locals) { var result; for (var i = 0; i < this.expressions.length; i++) { - var last = this.expressions[i].eval(context); + var last = this.expressions[i].eval(context, locals); if (isPresent(last)) result = last; } return result; @@ -78,11 +77,11 @@ export class Conditional extends AST { this.falseExp = falseExp; } - eval(context) { - if(this.condition.eval(context)) { - return this.trueExp.eval(context); + eval(context, locals) { + if(this.condition.eval(context, locals)) { + return this.trueExp.eval(context, locals); } else { - return this.falseExp.eval(context); + return this.falseExp.eval(context, locals); } } @@ -104,34 +103,29 @@ export class AccessMember extends AST { this.setter = setter; } - eval(context) { - var evaluatedContext = this.receiver.eval(context); - - while (evaluatedContext instanceof ContextWithVariableBindings) { - if (evaluatedContext.hasBinding(this.name)) { - return evaluatedContext.get(this.name); - } - evaluatedContext = evaluatedContext.parent; + eval(context, locals) { + if (this.receiver instanceof ImplicitReceiver && + isPresent(locals) && locals.contains(this.name)) { + return locals.get(this.name); + } else { + var evaluatedReceiver = this.receiver.eval(context, locals); + return this.getter(evaluatedReceiver); } - - return this.getter(evaluatedContext); } get isAssignable() { return true; } - assign(context, value) { - var evaluatedContext = this.receiver.eval(context); + assign(context, locals, value) { + var evaluatedContext = this.receiver.eval(context, locals); - while (evaluatedContext instanceof ContextWithVariableBindings) { - if (evaluatedContext.hasBinding(this.name)) { - throw new BaseException(`Cannot reassign a variable binding ${this.name}`) - } - evaluatedContext = evaluatedContext.parent; + if (this.receiver instanceof ImplicitReceiver && + isPresent(locals) && locals.contains(this.name)) { + throw new BaseException(`Cannot reassign a variable binding ${this.name}`); + } else { + return this.setter(evaluatedContext, value); } - - return this.setter(evaluatedContext, value); } visit(visitor) { @@ -148,9 +142,9 @@ export class KeyedAccess extends AST { this.key = key; } - eval(context) { - var obj = this.obj.eval(context); - var key = this.key.eval(context); + eval(context, locals) { + var obj = this.obj.eval(context, locals); + var key = this.key.eval(context, locals); return obj[key]; } @@ -158,9 +152,9 @@ export class KeyedAccess extends AST { return true; } - assign(context, value) { - var obj = this.obj.eval(context); - var key = this.key.eval(context); + assign(context, locals, value) { + var obj = this.obj.eval(context, locals); + var key = this.key.eval(context, locals); obj[key] = value; return value; } @@ -193,7 +187,7 @@ export class LiteralPrimitive extends AST { this.value = value; } - eval(context) { + eval(context, locals) { return this.value; } @@ -209,8 +203,8 @@ export class LiteralArray extends AST { this.expressions = expressions; } - eval(context) { - return ListWrapper.map(this.expressions, (e) => e.eval(context)); + eval(context, locals) { + return ListWrapper.map(this.expressions, (e) => e.eval(context, locals)); } visit(visitor) { @@ -227,10 +221,10 @@ export class LiteralMap extends AST { this.values = values; } - eval(context) { + eval(context, locals) { var res = StringMapWrapper.create(); for(var i = 0; i < this.keys.length; ++i) { - StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context)); + StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals)); } return res; } @@ -249,7 +243,7 @@ export class Interpolation extends AST { this.expressions = expressions; } - eval(context) { + eval(context, locals) { throw new BaseException("evaluating an Interpolation is not supported"); } @@ -269,13 +263,13 @@ export class Binary extends AST { this.right = right; } - eval(context) { - var left = this.left.eval(context); + eval(context, locals) { + var left = this.left.eval(context, locals); switch (this.operation) { - case '&&': return left && this.right.eval(context); - case '||': return left || this.right.eval(context); + case '&&': return left && this.right.eval(context, locals); + case '||': return left || this.right.eval(context, locals); } - var right = this.right.eval(context); + var right = this.right.eval(context, locals); switch (this.operation) { case '+' : return left + right; @@ -307,8 +301,8 @@ export class PrefixNot extends AST { this.expression = expression; } - eval(context) { - return !this.expression.eval(context); + eval(context, locals) { + return !this.expression.eval(context, locals); } visit(visitor) { @@ -325,8 +319,8 @@ export class Assignment extends AST { this.value = value; } - eval(context) { - return this.target.assign(context, this.value.eval(context)); + eval(context, locals) { + return this.target.assign(context, locals, this.value.eval(context, locals)); } visit(visitor) { @@ -347,19 +341,16 @@ export class MethodCall extends AST { this.name = name; } - eval(context) { - var evaluatedContext = this.receiver.eval(context); - var evaluatedArgs = evalList(context, this.args); - - while (evaluatedContext instanceof ContextWithVariableBindings) { - if (evaluatedContext.hasBinding(this.name)) { - var fn = evaluatedContext.get(this.name); - return FunctionWrapper.apply(fn, evaluatedArgs); - } - evaluatedContext = evaluatedContext.parent; + eval(context, locals) { + var evaluatedArgs = evalList(context, locals, this.args); + if (this.receiver instanceof ImplicitReceiver && + isPresent(locals) && locals.contains(this.name)) { + var fn = locals.get(this.name); + return FunctionWrapper.apply(fn, evaluatedArgs); + } else { + var evaluatedReceiver = this.receiver.eval(context, locals); + return this.fn(evaluatedReceiver, evaluatedArgs); } - - return this.fn(evaluatedContext, evaluatedArgs); } visit(visitor) { @@ -376,12 +367,12 @@ export class FunctionCall extends AST { this.args = args; } - eval(context) { - var obj = this.target.eval(context); + eval(context, locals) { + var obj = this.target.eval(context, locals); if (! (obj instanceof Function)) { throw new BaseException(`${obj} is not a function`); } - return FunctionWrapper.apply(obj, evalList(context, this.args)); + return FunctionWrapper.apply(obj, evalList(context, locals, this.args)); } visit(visitor) { @@ -400,16 +391,16 @@ export class ASTWithSource extends AST { this.ast = ast; } - eval(context) { - return this.ast.eval(context); + eval(context, locals) { + return this.ast.eval(context, locals); } get isAssignable() { return this.ast.isAssignable; } - assign(context, value) { - return this.ast.assign(context, value); + assign(context, locals, value) { + return this.ast.assign(context, locals, value); } visit(visitor) { @@ -454,12 +445,19 @@ export class AstVisitor { visitPrefixNot(ast:PrefixNot) {} } -var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0]]; -function evalList(context, exps:List){ +var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0], + [0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], + [0,0,0,0,0,0,0,0,0]]; + +function evalList(context, locals, exps:List){ var length = exps.length; + if (length > 10) { + throw new BaseException("Cannot have more than 10 argument"); + } + var result = _evalListCache[length]; for (var i = 0; i < length; i++) { - result[i] = exps[i].eval(context); + result[i] = exps[i].eval(context, locals); } return result; } diff --git a/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js b/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js deleted file mode 100644 index e5de7e2b0c..0000000000 --- a/modules/angular2/src/change_detection/parser/context_with_variable_bindings.js +++ /dev/null @@ -1,36 +0,0 @@ -import {MapWrapper} from 'angular2/src/facade/collection'; -import {BaseException} from 'angular2/src/facade/lang'; - -export class ContextWithVariableBindings { - parent:any; - /// varBindings' keys are read-only. adding/removing keys is not supported. - varBindings:Map; - - constructor(parent:any, varBindings:Map) { - this.parent = parent; - this.varBindings = varBindings; - } - - hasBinding(name:string):boolean { - return MapWrapper.contains(this.varBindings, name); - } - - get(name:string) { - return MapWrapper.get(this.varBindings, name); - } - - set(name:string, value) { - // TODO(rado): consider removing this check if we can guarantee this is not - // exposed to the public API. - if (this.hasBinding(name)) { - MapWrapper.set(this.varBindings, name, value); - } else { - throw new BaseException( - 'VariableBindings do not support setting of new keys post-construction.'); - } - } - - clearValues() { - MapWrapper.clearValues(this.varBindings); - } -} diff --git a/modules/angular2/src/change_detection/parser/locals.js b/modules/angular2/src/change_detection/parser/locals.js new file mode 100644 index 0000000000..7298d29008 --- /dev/null +++ b/modules/angular2/src/change_detection/parser/locals.js @@ -0,0 +1,51 @@ +import {isPresent, BaseException} from 'angular2/src/facade/lang'; +import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; + +export class Locals { + parent:Locals; + current:Map; + + constructor(parent:Locals, current:Map) { + this.parent = parent; + this.current = current; + } + + contains(name:string):boolean { + if (MapWrapper.contains(this.current, name)) { + return true; + } + + if (isPresent(this.parent)) { + return this.parent.contains(name); + } + + return false; + } + + get(name:string) { + if (MapWrapper.contains(this.current, name)) { + return MapWrapper.get(this.current, name); + } + + if (isPresent(this.parent)) { + return this.parent.get(name); + } + + throw new BaseException(`Cannot find '${name}'`); + } + + set(name:string, value) { + // TODO(rado): consider removing this check if we can guarantee this is not + // exposed to the public API. + // TODO: vsavkin maybe it should check only the local map + if (MapWrapper.contains(this.current, name)) { + MapWrapper.set(this.current, name, value); + } else { + throw new BaseException('Setting of new keys post-construction is not supported.'); + } + } + + clearValues() { + MapWrapper.clearValues(this.current); + } +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/proto_change_detector.js b/modules/angular2/src/change_detection/proto_change_detector.js index 52e7522c46..7349e13c3b 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.js +++ b/modules/angular2/src/change_detection/proto_change_detector.js @@ -34,6 +34,7 @@ import { ProtoRecord, RECORD_TYPE_SELF, RECORD_TYPE_PROPERTY, + RECORD_TYPE_LOCAL, RECORD_TYPE_INVOKE_METHOD, RECORD_TYPE_CONST, RECORD_TYPE_INVOKE_CLOSURE, @@ -45,7 +46,7 @@ import { export class ProtoChangeDetector { addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){} - instantiate(dispatcher:any, bindingRecords:List):ChangeDetector{ + instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{ return null; } } @@ -71,16 +72,16 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector { this._pipeRegistry = pipeRegistry; } - instantiate(dispatcher:any, bindingRecords:List) { - this._createRecordsIfNecessary(bindingRecords); + instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) { + this._createRecordsIfNecessary(bindingRecords, variableBindings); return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records); } - _createRecordsIfNecessary(bindingRecords:List) { + _createRecordsIfNecessary(bindingRecords:List, variableBindings:List) { if (isBlank(this._records)) { var recordBuilder = new ProtoRecordBuilder(); ListWrapper.forEach(bindingRecords, (r) => { - recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento); + recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings); }); this._records = coalesce(recordBuilder.records); } @@ -98,16 +99,16 @@ export class JitProtoChangeDetector extends ProtoChangeDetector { this._factory = null; } - instantiate(dispatcher:any, bindingRecords:List) { - this._createFactoryIfNecessary(bindingRecords); + instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) { + this._createFactoryIfNecessary(bindingRecords, variableBindings); return this._factory(dispatcher, this._pipeRegistry); } - _createFactoryIfNecessary(bindingRecords:List) { + _createFactoryIfNecessary(bindingRecords:List, variableBindings:List) { if (isBlank(this._factory)) { var recordBuilder = new ProtoRecordBuilder(); ListWrapper.forEach(bindingRecords, (r) => { - recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento); + recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings); }); var c = _jitProtoChangeDetectorClassCounter++; var records = coalesce(recordBuilder.records); @@ -124,13 +125,13 @@ class ProtoRecordBuilder { this.records = []; } - addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) { + addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, variableBindings:List = null) { var last = ListWrapper.last(this.records); if (isPresent(last) && last.directiveMemento == directiveMemento) { last.lastInDirective = false; } - var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length); + var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length, variableBindings); if (! ListWrapper.isEmpty(pr)) { var last = ListWrapper.last(pr); last.lastInBinding = true; @@ -145,19 +146,21 @@ class _ConvertAstIntoProtoRecords { protoRecords:List; bindingMemento:any; directiveMemento:any; + variableBindings:List; contextIndex:number; expressionAsString:string; - constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string) { + constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string, variableBindings:List) { this.protoRecords = []; this.bindingMemento = bindingMemento; this.directiveMemento = directiveMemento; this.contextIndex = contextIndex; this.expressionAsString = expressionAsString; + this.variableBindings = variableBindings; } - static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number) { - var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString()); + static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number, variableBindings:List) { + var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString(), variableBindings); ast.visit(c); return c.protoRecords; } @@ -178,13 +181,22 @@ class _ConvertAstIntoProtoRecords { visitAccessMember(ast:AccessMember) { var receiver = ast.receiver.visit(this); - return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver); + if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) { + return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver); + } else { + return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver); + } } - visitMethodCall(ast:MethodCall) { + visitMethodCall(ast:MethodCall) {; var receiver = ast.receiver.visit(this); var args = this._visitAll(ast.args); - return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver); + if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) { + var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver); + return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target); + } else { + return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver); + } } visitFunctionCall(ast:FunctionCall) { diff --git a/modules/angular2/src/change_detection/proto_record.js b/modules/angular2/src/change_detection/proto_record.js index d421d39d97..357aec7422 100644 --- a/modules/angular2/src/change_detection/proto_record.js +++ b/modules/angular2/src/change_detection/proto_record.js @@ -4,9 +4,10 @@ export const RECORD_TYPE_SELF = 0; export const RECORD_TYPE_CONST = 1; export const RECORD_TYPE_PRIMITIVE_OP = 2; export const RECORD_TYPE_PROPERTY = 3; -export const RECORD_TYPE_INVOKE_METHOD = 4; -export const RECORD_TYPE_INVOKE_CLOSURE = 5; -export const RECORD_TYPE_KEYED_ACCESS = 6; +export const RECORD_TYPE_LOCAL = 4; +export const RECORD_TYPE_INVOKE_METHOD = 5; +export const RECORD_TYPE_INVOKE_CLOSURE = 6; +export const RECORD_TYPE_KEYED_ACCESS = 7; export const RECORD_TYPE_PIPE = 8; export const RECORD_TYPE_INTERPOLATE = 9; diff --git a/modules/angular2/src/core/application.js b/modules/angular2/src/core/application.js index 5eef459146..b5f66283c0 100644 --- a/modules/angular2/src/core/application.js +++ b/modules/angular2/src/core/application.js @@ -67,7 +67,7 @@ function _injectorBindings(appComponentType): List { // the angular application. Thus the context and lightDomInjector are // empty. var view = appProtoView.instantiate(null, eventManager); - view.hydrate(injector, null, null, new Object()); + view.hydrate(injector, null, null, new Object(), null); return view; }); }, [ChangeDetection, Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken, diff --git a/modules/angular2/src/core/compiler/pipeline/proto_view_builder.js b/modules/angular2/src/core/compiler/pipeline/proto_view_builder.js index 3d2928f855..d549629ad4 100644 --- a/modules/angular2/src/core/compiler/pipeline/proto_view_builder.js +++ b/modules/angular2/src/core/compiler/pipeline/proto_view_builder.js @@ -35,7 +35,8 @@ export class ProtoViewBuilder extends CompileStep { if (current.isViewRoot) { var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy'); inheritedProtoView = new ProtoView(current.element, protoChangeDetector, - this._shadowDomStrategy); + this._shadowDomStrategy, this._getParentProtoView(parent)); + if (isPresent(parent)) { if (isPresent(parent.inheritedElementBinder.nestedProtoView)) { throw new BaseException('Only one nested view per element is allowed'); @@ -55,16 +56,20 @@ export class ProtoViewBuilder extends CompileStep { inheritedProtoView = parent.inheritedProtoView; } - // The view's contextWithLocals needs to have a full set of variable names at construction time + // The view's locals needs to have a full set of variable names at construction time // in order to prevent new variables from being set later in the lifecycle. Since we don't want // to actually create variable bindings for the $implicit bindings, add to the - // protoContextLocals manually. + // protoLocals manually. if (isPresent(current.variableBindings)) { MapWrapper.forEach(current.variableBindings, (mappedName, varName) => { - MapWrapper.set(inheritedProtoView.protoContextLocals, mappedName, null); + MapWrapper.set(inheritedProtoView.protoLocals, mappedName, null); }); } current.inheritedProtoView = inheritedProtoView; } + + _getParentProtoView(parent:CompileElement) { + return isPresent(parent) ? parent.inheritedProtoView : null; + } } diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index 8c09e9eca1..5eff761e20 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -1,7 +1,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {Promise} from 'angular2/src/facade/async'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; -import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, +import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, ChangeRecord, BindingRecord, uninitialized} from 'angular2/change_detection'; import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector'; @@ -47,9 +47,9 @@ export class View { lightDoms: List; proto: ProtoView; context: any; - contextWithLocals:ContextWithVariableBindings; + locals:Locals; - constructor(proto:ProtoView, nodes:List, protoContextLocals:Map) { + constructor(proto:ProtoView, nodes:List, protoLocals:Map) { this.proto = proto; this.nodes = nodes; this.changeDetector = null; @@ -63,9 +63,7 @@ export class View { this.preBuiltObjects = null; this.lightDoms = null; this.context = null; - this.contextWithLocals = (MapWrapper.size(protoContextLocals) > 0) - ? new ContextWithVariableBindings(null, MapWrapper.clone(protoContextLocals)) - : null; + this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this } init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List, @@ -88,29 +86,22 @@ export class View { return; } var templateName = MapWrapper.get(this.proto.variableBindings, contextName); - this.context.set(templateName, value); + this.locals.set(templateName, value); } hydrated() { return isPresent(this.context); } - _hydrateContext(newContext) { - if (isPresent(this.contextWithLocals)) { - this.contextWithLocals.parent = newContext; - this.context = this.contextWithLocals; - } else { - this.context = newContext; - } - // TODO(tbosch): if we have a contextWithLocals we actually only need to - // set the contextWithLocals once. Would it be faster to always use a contextWithLocals - // even if we don't have locals and not update the recordRange here? - this.changeDetector.hydrate(this.context); + _hydrateContext(newContext, locals) { + this.context = newContext; + this.locals.parent = locals; + this.changeDetector.hydrate(this.context, this.locals); } _dehydrateContext() { - if (isPresent(this.contextWithLocals)) { - this.contextWithLocals.clearValues(); + if (isPresent(this.locals)) { + this.locals.clearValues(); } this.context = null; this.changeDetector.dehydrate(); @@ -133,9 +124,9 @@ export class View { * tree. */ hydrate(appInjector: Injector, hostElementInjector: ElementInjector, hostLightDom: LightDom, - context: Object) { + context: Object, locals:Locals) { if (this.hydrated()) throw new BaseException('The view is already hydrated.'); - this._hydrateContext(context); + this._hydrateContext(context, locals); // viewContainers for (var i = 0; i < this.viewContainers.length; i++) { @@ -173,15 +164,15 @@ export class View { // name. var exportImplicitName = elementInjector.getExportImplicitName(); if (elementInjector.isExportingComponent()) { - this.context.set(exportImplicitName, elementInjector.getComponent()); + this.locals.set(exportImplicitName, elementInjector.getComponent()); } else if (elementInjector.isExportingElement()) { - this.context.set(exportImplicitName, elementInjector.getNgElement().domElement); + this.locals.set(exportImplicitName, elementInjector.getNgElement().domElement); } } if (isPresent(componentDirective)) { this.componentChildViews[componentChildViewIndex++].hydrate(shadowDomAppInjector, - elementInjector, this.lightDoms[i], elementInjector.getComponent()); + elementInjector, this.lightDoms[i], elementInjector.getComponent(), null); } } @@ -294,7 +285,7 @@ export class ProtoView { elementBinders:List; protoChangeDetector:ProtoChangeDetector; variableBindings: Map; - protoContextLocals:Map; + protoLocals:Map; textNodesWithBindingCount:int; elementsWithBindingCount:int; instantiateInPlace:boolean; @@ -306,16 +297,19 @@ export class ProtoView { // List>, indexed by binder index eventHandlers:List; bindingRecords:List; + parentProtoView:ProtoView; + _variableBindings:List; constructor( template, protoChangeDetector:ProtoChangeDetector, - shadowDomStrategy:ShadowDomStrategy) { + shadowDomStrategy:ShadowDomStrategy, parentProtoView:ProtoView = null) { this.element = template; this.elementBinders = []; this.variableBindings = MapWrapper.create(); - this.protoContextLocals = MapWrapper.create(); + this.protoLocals = MapWrapper.create(); this.protoChangeDetector = protoChangeDetector; + this.parentProtoView = parentProtoView; this.textNodesWithBindingCount = 0; this.elementsWithBindingCount = 0; this.instantiateInPlace = false; @@ -327,6 +321,7 @@ export class ProtoView { this.stylePromises = []; this.eventHandlers = []; this.bindingRecords = []; + this._variableBindings = null; } // TODO(rado): hostElementInjector should be moved to hydrate phase. @@ -342,6 +337,23 @@ export class ProtoView { } } + // this work should be done the constructor of ProtoView once we separate + // ProtoView and ProtoViewBuilder + _getVariableBindings() { + if (isPresent(this._variableBindings)) { + return this._variableBindings; + } + + this._variableBindings = isPresent(this.parentProtoView) ? + ListWrapper.clone(this.parentProtoView._getVariableBindings()) : []; + + MapWrapper.forEach(this.protoLocals, (v, local) => { + ListWrapper.push(this._variableBindings, local); + }); + + return this._variableBindings; + } + _instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View { var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element); var elementsWithBindingsDynamic; @@ -369,9 +381,8 @@ export class ProtoView { viewNodes = [rootElementClone]; } - var view = new View(this, viewNodes, this.protoContextLocals); - var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords); - + var view = new View(this, viewNodes, this.protoLocals); + var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords, this._getVariableBindings()); var binders = this.elementBinders; var elementInjectors = ListWrapper.createFixedSize(binders.length); var eventHandlers = ListWrapper.createFixedSize(binders.length); @@ -514,7 +525,7 @@ export class ProtoView { } else { context = view.elementInjectors[injectorIdx].getDirectiveAtIndex(directiveIndex); } - expr.eval(new ContextWithVariableBindings(context, locals)); + expr.eval(context, new Locals(view.locals, locals)); }); } } @@ -522,7 +533,7 @@ export class ProtoView { bindVariable(contextName:string, templateName:string) { MapWrapper.set(this.variableBindings, contextName, templateName); - MapWrapper.set(this.protoContextLocals, templateName, null); + MapWrapper.set(this.protoLocals, templateName, null); } bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector, diff --git a/modules/angular2/src/core/compiler/view_container.js b/modules/angular2/src/core/compiler/view_container.js index d79d602435..b95f37aaab 100644 --- a/modules/angular2/src/core/compiler/view_container.js +++ b/modules/angular2/src/core/compiler/view_container.js @@ -85,7 +85,8 @@ export class ViewContainer { var newView = this.defaultProtoView.instantiate(this.hostElementInjector, this._eventManager); // insertion must come before hydration so that element injector trees are attached. this.insert(newView, atIndex); - newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom, this.parentView.context); + newView.hydrate(this.appInjector, this.hostElementInjector, this.hostLightDom, + this.parentView.context, this.parentView.locals); // new content tags might have appeared, we need to redistrubute. if (isPresent(this.hostLightDom)) { diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 491b222fd4..a605acd5cb 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -5,8 +5,9 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {Parser} from 'angular2/src/change_detection/parser/parser'; import {Lexer} from 'angular2/src/change_detection/parser/lexer'; +import {Locals} from 'angular2/src/change_detection/parser/locals'; -import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings, BindingRecord, +import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from 'angular2/change_detection'; import {ChangeDetectionUtil} from 'angular2/src/change_detection/change_detection_util'; @@ -28,17 +29,29 @@ export function main() { return parser.parseBinding(exp, location); } - function createChangeDetector(memo:string, exp:string, context = null, registry = null) { + function convertLocalsToVariableBindings(locals) { + var variableBindings = []; + var loc = locals; + while(isPresent(loc)) { + MapWrapper.forEach(loc.current, (v, k) => ListWrapper.push(variableBindings, k)); + loc = loc.parent; + } + return variableBindings; + } + + function createChangeDetector(memo:string, exp:string, context = null, locals = null, registry = null) { var pcd = createProtoChangeDetector(registry); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast(exp), memo, memo)]); - cd.hydrate(context); + + var variableBindings = convertLocalsToVariableBindings(locals); + var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast(exp), memo, memo)], variableBindings); + cd.hydrate(context, locals); return {"changeDetector" : cd, "dispatcher" : dispatcher}; } - function executeWatch(memo:string, exp:string, context = null) { - var res = createChangeDetector(memo, exp, context); + function executeWatch(memo:string, exp:string, context = null, locals = null) { + var res = createChangeDetector(memo, exp, context, locals); res["changeDetector"].detectChanges(); return res["dispatcher"].log; } @@ -180,8 +193,8 @@ export function main() { var ast = parser.parseInterpolation("B{{a}}A", "location"); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast, "memo", "memo")]); - cd.hydrate(new TestData("value")); + var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast, "memo", "memo")], null); + cd.hydrate(new TestData("value"), null); cd.detectChanges(); @@ -211,7 +224,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new CountingPipe()); var person = new Person('bob'); - var c = createChangeDetector('name', 'name | pipe', person, registry); + var c = createChangeDetector('name', 'name | pipe', person, null, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; @@ -234,7 +247,7 @@ export function main() { new BindingRecord(ast("1 + 2"), "memo", "1"), new BindingRecord(ast("10 + 20"), "memo", "1"), new BindingRecord(ast("100 + 200"), "memo", "2") - ]); + ], null); cd.detectChanges(); @@ -248,7 +261,7 @@ export function main() { new BindingRecord(ast("a()"), "a", "1"), new BindingRecord(ast("b()"), "b", "2"), new BindingRecord(ast("c()"), "c", "2") - ]); + ], null); var tr = new TestRecord(); tr.a = () => { @@ -263,7 +276,7 @@ export function main() { dispatcher.logValue('InvokeC'); return 'c' }; - cd.hydrate(tr); + cd.hydrate(tr, null); cd.detectChanges(); @@ -280,8 +293,8 @@ export function main() { var dispatcher = new TestDispatcher(); var cd = pcd.instantiate(dispatcher, [ new BindingRecord(ast("a"), "a", 1) - ]); - cd.hydrate(new TestData('value')); + ], null); + cd.hydrate(new TestData('value'), null); expect(() => { cd.checkNoChanges(); @@ -295,8 +308,8 @@ export function main() { var pcd = createProtoChangeDetector(); var cd = pcd.instantiate(new TestDispatcher(), [ new BindingRecord(ast("invalidProp", "someComponent"), "a", 1) - ]); - cd.hydrate(null); + ], null); + cd.hydrate(null, null); try { cd.detectChanges(); @@ -309,38 +322,38 @@ export function main() { }); }); - describe("ContextWithVariableBindings", () => { - it('should read a field from ContextWithVariableBindings', () => { - var locals = new ContextWithVariableBindings(null, + describe("Locals", () => { + it('should read a value from locals', () => { + var locals = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - expect(executeWatch('key', 'key', locals)) + expect(executeWatch('key', 'key', null, locals)) .toEqual(['key=value']); }); - it('should invoke a function from ContextWithVariableBindings', () => { - var locals = new ContextWithVariableBindings(null, + it('should invoke a function from local', () => { + var locals = new Locals(null, MapWrapper.createFromPairs([["key", () => "value"]])); - expect(executeWatch('key', 'key()', locals)) + expect(executeWatch('key', 'key()', null, locals)) .toEqual(['key=value']); }); - it('should handle nested ContextWithVariableBindings', () => { - var nested = new ContextWithVariableBindings(null, + it('should handle nested locals', () => { + var nested = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - var locals = new ContextWithVariableBindings(nested, MapWrapper.create()); + var locals = new Locals(nested, MapWrapper.create()); - expect(executeWatch('key', 'key', locals)) + expect(executeWatch('key', 'key', null, locals)) .toEqual(['key=value']); }); - it("should fall back to a regular field read when ContextWithVariableBindings " + - "does not have the requested field", () => { - var locals = new ContextWithVariableBindings(new Person("Jim"), + it("should fall back to a regular field read when the locals map" + + "does not have the requested field", () => { + var locals = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); - expect(executeWatch('name', 'name', locals)) + expect(executeWatch('name', 'name', new Person("Jim"), locals)) .toEqual(['name=Jim']); }); }); @@ -350,10 +363,10 @@ export function main() { beforeEach(() => { var protoParent = createProtoChangeDetector(); - parent = protoParent.instantiate(null, []); + parent = protoParent.instantiate(null, [], null); var protoChild = createProtoChangeDetector(); - child = protoChild.instantiate(null, []); + child = protoChild.instantiate(null, [], null); }); it("should add children", () => { @@ -396,7 +409,7 @@ export function main() { }); it("should change CHECK_ONCE to CHECKED", () => { - var cd = createProtoChangeDetector().instantiate(null, []); + var cd = createProtoChangeDetector().instantiate(null, [], null); cd.mode = CHECK_ONCE; cd.detectChanges(); @@ -405,7 +418,7 @@ export function main() { }); it("should not change the CHECK_ALWAYS", () => { - var cd = createProtoChangeDetector().instantiate(null, []); + var cd = createProtoChangeDetector().instantiate(null, [], null); cd.mode = CHECK_ALWAYS; cd.detectChanges(); @@ -416,7 +429,7 @@ export function main() { describe("markPathToRootAsCheckOnce", () => { function changeDetector(mode, parent) { - var cd = createProtoChangeDetector().instantiate(null, []); + var cd = createProtoChangeDetector().instantiate(null, [], null); cd.mode = mode; if (isPresent(parent)) parent.addChild(cd); return cd; @@ -448,20 +461,20 @@ export function main() { var c = createChangeDetector("memo", "name"); var cd = c["changeDetector"]; - cd.hydrate("some context"); + cd.hydrate("some context", null); expect(cd.hydrated()).toBe(true); cd.dehydrate(); expect(cd.hydrated()).toBe(false); - cd.hydrate("other context"); + cd.hydrate("other context", null); expect(cd.hydrated()).toBe(true); }); it("should destroy all active pipes during dehyration", () => { var pipe = new OncePipe(); var registry = new FakePipeRegistry('pipe', () => pipe); - var c = createChangeDetector("memo", "name | pipe", new Person('bob'), registry); + var c = createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -477,7 +490,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new CountingPipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; @@ -495,7 +508,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new OncePipe()); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -513,7 +526,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => pipe); var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); var cd = c["changeDetector"]; cd.detectChanges(); @@ -528,7 +541,7 @@ export function main() { var registry = new FakePipeRegistry('pipe', () => new IdentityPipe()) var ctx = new Person("Megatron"); - var c = createChangeDetector("memo", "name | pipe", ctx, registry); + var c = createChangeDetector("memo", "name | pipe", ctx, null, registry); var cd = c["changeDetector"]; var dispatcher = c["dispatcher"]; diff --git a/modules/angular2/test/change_detection/parser/context_with_variable_bindings_spec.js b/modules/angular2/test/change_detection/parser/context_with_variable_bindings_spec.js deleted file mode 100644 index a354d7b0c6..0000000000 --- a/modules/angular2/test/change_detection/parser/context_with_variable_bindings_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -import {ddescribe, describe, it, xit, iit, expect, beforeEach} from 'angular2/test_lib'; - -import {ContextWithVariableBindings} from 'angular2/src/change_detection/parser/context_with_variable_bindings'; - -import {BaseException, isBlank, isPresent} from 'angular2/src/facade/lang'; -import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; - -export function main() { - describe('ContextWithVariableBindings', () => { - var locals; - beforeEach(() => { - locals = new ContextWithVariableBindings(null, - MapWrapper.createFromPairs([['key', 'value'], ['nullKey', null]])); - }); - - it('should support getting values', () => { - expect(locals.get('key')).toBe('value'); - - var notPresentValue = locals.get('notPresent'); - expect(isPresent(notPresentValue)).toBe(false); - }); - - it('should support checking if key is persent', () => { - expect(locals.hasBinding('key')).toBe(true); - expect(locals.hasBinding('nullKey')).toBe(true); - expect(locals.hasBinding('notPresent')).toBe(false); - }); - - it('should support setting persent keys', () => { - locals.set('key', 'bar'); - expect(locals.get('key')).toBe('bar'); - }); - - it('should not support setting keys that are not present already', () => { - expect(() => locals.set('notPresent', 'bar')).toThrowError(); - }); - - it('should clearValues', () => { - locals.clearValues(); - expect(locals.get('key')).toBe(null); - }); - }) -} - diff --git a/modules/angular2/test/change_detection/parser/locals_spec.js b/modules/angular2/test/change_detection/parser/locals_spec.js new file mode 100644 index 0000000000..98ed1d687d --- /dev/null +++ b/modules/angular2/test/change_detection/parser/locals_spec.js @@ -0,0 +1,40 @@ +import {ddescribe, describe, it, xit, iit, expect, beforeEach} from 'angular2/test_lib'; + +import {Locals} from 'angular2/src/change_detection/parser/locals'; + +import {MapWrapper} from 'angular2/src/facade/collection'; + +export function main() { + describe('Locals', () => { + var locals; + beforeEach(() => { + locals = new Locals(null, + MapWrapper.createFromPairs([['key', 'value'], ['nullKey', null]])); + }); + + it('should support getting values', () => { + expect(locals.get('key')).toBe('value'); + expect(() => locals.get('notPresent')).toThrowError(new RegExp("Cannot find")); + }); + + it('should support checking if key is present', () => { + expect(locals.contains('key')).toBe(true); + expect(locals.contains('nullKey')).toBe(true); + expect(locals.contains('notPresent')).toBe(false); + }); + + it('should support setting keys', () => { + locals.set('key', 'bar'); + expect(locals.get('key')).toBe('bar'); + }); + + it('should not support setting keys that are not present already', () => { + expect(() => locals.set('notPresent', 'bar')).toThrowError(); + }); + + it('should clearValues', () => { + locals.clearValues(); + expect(locals.get('key')).toBe(null); + }); + }) +} diff --git a/modules/angular2/test/change_detection/parser/parser_spec.js b/modules/angular2/test/change_detection/parser/parser_spec.js index 0ace2d67e4..8b4ca5a76c 100644 --- a/modules/angular2/test/change_detection/parser/parser_spec.js +++ b/modules/angular2/test/change_detection/parser/parser_spec.js @@ -4,7 +4,7 @@ import {reflector} from 'angular2/src/reflection/reflection'; import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; import {Parser} from 'angular2/src/change_detection/parser/parser'; import {Lexer} from 'angular2/src/change_detection/parser/lexer'; -import {ContextWithVariableBindings} from 'angular2/src/change_detection/parser/context_with_variable_bindings'; +import {Locals} from 'angular2/src/change_detection/parser/locals'; import {Pipe, LiteralPrimitive} from 'angular2/src/change_detection/parser/ast'; class TestData { @@ -55,21 +55,27 @@ export function main() { return createParser().addPipes(ast, pipes); } - function expectEval(text, passedInContext = null) { - var c = isBlank(passedInContext) ? td() : passedInContext; - return expect(parseAction(text).eval(c)); + function emptyLocals() { + return new Locals(null, MapWrapper.create()); } - function expectEvalError(text, passedInContext = null) { + function expectEval(text, passedInContext = null, passedInLocals = null) { var c = isBlank(passedInContext) ? td() : passedInContext; - return expect(() => parseAction(text).eval(c)); + var l = isBlank(passedInLocals) ? emptyLocals() : passedInLocals; + return expect(parseAction(text).eval(c, l)); + } + + function expectEvalError(text, passedInContext = null, passedInLocals = null) { + var c = isBlank(passedInContext) ? td() : passedInContext; + var l = isBlank(passedInLocals) ? emptyLocals() : passedInLocals; + return expect(() => parseAction(text).eval(c, l)); } function evalAsts(asts, passedInContext = null) { var c = isBlank(passedInContext) ? td() : passedInContext; var res = []; for (var i=0; i { - var locals = new ContextWithVariableBindings(null, - MapWrapper.createFromPairs([["key", "value"]])); - expectEval("key", locals).toEqual("value"); + it("should read a field from Locals", () => { + var locals = new Locals(null, + MapWrapper.createFromPairs([["key", "value"]])); + expectEval("key", null, locals).toEqual("value"); }); - it("should handle nested ContextWithVariableBindings", () => { - var nested = new ContextWithVariableBindings(null, - MapWrapper.createFromPairs([["key", "value"]])); - var locals = new ContextWithVariableBindings(nested, MapWrapper.create()); - expectEval("key", locals).toEqual("value"); + it("should handle nested Locals", () => { + var nested = new Locals(null, + MapWrapper.createFromPairs([["key", "value"]])); + var locals = new Locals(nested, MapWrapper.create()); + expectEval("key", null, locals).toEqual("value"); }); - it("should fall back to a regular field read when ContextWithVariableBindings "+ - "does not have the requested field", () => { - var locals = new ContextWithVariableBindings(td(999), MapWrapper.create()); - expectEval("a", locals).toEqual(999); + it("should fall back to a regular field read when Locals "+ + "does not have the requested field", () => { + var locals = new Locals(null, MapWrapper.create()); + expectEval("a", td(999), locals).toEqual(999); }); }); @@ -216,25 +222,26 @@ export function main() { expectEval("fn().add(1,2)", td(0, 0, td())).toEqual(3); }); + it('should throw when more than 10 arguments', () => { + expectEvalError("fn(1,2,3,4,5,6,7,8,9,10,11)").toThrowError(new RegExp('more than')); + }); + it('should throw when no method', () => { expectEvalError("blah()").toThrowError(); }); - it('should evaluate a method from ContextWithVariableBindings', () => { - var context = new ContextWithVariableBindings( - td(0, 0, 'parent'), + it('should evaluate a method from Locals', () => { + var locals = new Locals( + null, MapWrapper.createFromPairs([['fn', () => 'child']]) ); - expectEval("fn()", context).toEqual('child'); + expectEval("fn()", td(0, 0, 'parent'), locals).toEqual('child'); }); - it('should fall back to the parent context when ContextWithVariableBindings does not ' + - 'have the requested method', () => { - var context = new ContextWithVariableBindings( - td(0, 0, 'parent'), - MapWrapper.create() - ); - expectEval("fn()", context).toEqual('parent'); + it('should fall back to the parent context when Locals does not ' + + 'have the requested method', () => { + var locals = new Locals(null, MapWrapper.create()); + expectEval("fn()", td(0, 0, 'parent'), locals).toEqual('parent'); }); }); @@ -314,14 +321,14 @@ export function main() { it('should reassign when no variable binding with the given name', () => { var context = td(); - var locals = new ContextWithVariableBindings(context, MapWrapper.create()); - expectEval('a = 200', locals).toEqual(200); + var locals = new Locals(null, MapWrapper.create()); + expectEval('a = 200', context, locals).toEqual(200); expect(context.a).toEqual(200); }); it('should throw when reassigning a variable binding', () => { - var locals = new ContextWithVariableBindings(null, MapWrapper.createFromPairs([["key", "value"]])); - expectEvalError('key = 200', locals).toThrowError(new RegExp("Cannot reassign a variable binding")); + var locals = new Locals(null, MapWrapper.createFromPairs([["key", "value"]])); + expectEvalError('key = 200', null, locals).toThrowError(new RegExp("Cannot reassign a variable binding")); }); }); @@ -346,7 +353,7 @@ export function main() { it('should pass exceptions', () => { expect(() => { - parseAction('a()').eval(td(() => {throw new BaseException("boo to you")})); + parseAction('a()').eval(td(() => {throw new BaseException("boo to you")}), emptyLocals()); }).toThrowError('boo to you'); }); @@ -579,9 +586,8 @@ export function main() { describe('wrapLiteralPrimitive', () => { it('should wrap a literal primitive', () => { - expect(createParser().wrapLiteralPrimitive("foo", null).eval(null)).toEqual("foo"); + expect(createParser().wrapLiteralPrimitive("foo", null).eval(null, emptyLocals())).toEqual("foo"); }); }); }); -} - +} \ No newline at end of file diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index f74b19bd90..f92bc7393a 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -68,7 +68,7 @@ export function main() { function createView(pv) { ctx = new MyComp(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, ctx); + view.hydrate(new Injector([]), null, null, ctx, null); cd = view.changeDetector; } @@ -211,7 +211,7 @@ export function main() { ctx.ctxProp = 'a'; cd.detectChanges(); - var comp = view.contextWithLocals.get("comp"); + var comp = view.locals.get("comp"); // it is doubled twice: once in the binding, second time in the bind config expect(comp.prop).toEqual('aaaa'); @@ -345,8 +345,8 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - expect(view.contextWithLocals).not.toBe(null); - expect(view.contextWithLocals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.locals).not.toBe(null); + expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); async.done(); }) @@ -361,10 +361,10 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - expect(view.contextWithLocals).not.toBe(null); - expect(view.contextWithLocals.get('alice')).toBeAnInstanceOf(ChildComp); - expect(view.contextWithLocals.get('bob')).toBeAnInstanceOf(ChildComp); - expect(view.contextWithLocals.get('alice')).not.toBe(view.contextWithLocals.get('bob')); + expect(view.locals).not.toBe(null); + expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.locals.get('bob')).toBeAnInstanceOf(ChildComp); + expect(view.locals.get('alice')).not.toBe(view.locals.get('bob')); async.done(); }) @@ -379,8 +379,8 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - expect(view.contextWithLocals).not.toBe(null); - expect(view.contextWithLocals.get('alice')).toBeAnInstanceOf(ChildComp); + expect(view.locals).not.toBe(null); + expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp); async.done(); }) @@ -392,9 +392,9 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - expect(view.contextWithLocals).not.toBe(null); + expect(view.locals).not.toBe(null); - var value = view.contextWithLocals.get('alice'); + var value = view.locals.get('alice'); expect(value).not.toBe(null); expect(value.tagName.toLowerCase()).toEqual('div'); @@ -411,7 +411,7 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - var cmp = view.contextWithLocals.get('cmp'); + var cmp = view.locals.get('cmp'); cd.detectChanges(); expect(cmp.numberOfChecks).toEqual(1); @@ -436,7 +436,7 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - var childComponent = view.contextWithLocals.get('child'); + var childComponent = view.locals.get('child'); expect(childComponent.myParent).toBeAnInstanceOf(SomeDirective); async.done(); @@ -457,7 +457,7 @@ export function main() { compiler.compile(MyComp).then((pv) => { createView(pv); - var childComponent = view.contextWithLocals.get('child'); + var childComponent = view.locals.get('child'); expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective); async.done(); @@ -479,9 +479,8 @@ export function main() { createView(pv); cd.detectChanges(); - // Note: viewContainers is a sparse array! var subview = view.viewContainers[1].get(0); - var childComponent = subview.contextWithLocals.get('child'); + var childComponent = subview.locals.get('child'); expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective); async.done(); diff --git a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js index b379b02da1..02b1524a50 100644 --- a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js +++ b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js @@ -83,7 +83,7 @@ export function main() { function instantiateView(protoView) { evalContext = new Context(); view = protoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, evalContext); + view.hydrate(new Injector([]), null, null, evalContext, null); changeDetector = view.changeDetector; } @@ -380,7 +380,7 @@ export function main() { var eventMap = StringMapWrapper.get(pv.elementBinders[0].events, 'event1'); var ast = MapWrapper.get(eventMap, -1); - expect(ast.eval(null)).toBe(2); + expect(ast.eval(null, null)).toBe(2); }); it('should bind directive events', () => { @@ -399,7 +399,7 @@ export function main() { var ast = MapWrapper.get(eventMap, 0); var context = new SomeDecoratorWithEvent(); - expect(ast.eval(context)).toEqual('onEvent() callback'); + expect(ast.eval(context, null)).toEqual('onEvent() callback'); }); it('should bind directive properties', () => { @@ -539,7 +539,6 @@ class SomeDecoratorDirectiveWithBinding { events: {'event': 'onEvent($event)'} }) class SomeDecoratorWithEvent { - // Added here so that we don't have to wrap the content in a ContextWithVariableBindings $event: string; constructor() { diff --git a/modules/angular2/test/core/compiler/pipeline/proto_view_builder_spec.js b/modules/angular2/test/core/compiler/pipeline/proto_view_builder_spec.js index 82271cfffe..316785ba87 100644 --- a/modules/angular2/test/core/compiler/pipeline/proto_view_builder_spec.js +++ b/modules/angular2/test/core/compiler/pipeline/proto_view_builder_spec.js @@ -49,6 +49,15 @@ export function main() { expect(results[1].inheritedElementBinder.nestedProtoView).toBe(results[2].inheritedProtoView); }); + it('should set the parent proto view', () => { + var element = el('
'); + var results = createPipeline().process(element); + + var parentProtoView = results[1].inheritedProtoView; + var nestedProtoView = results[2].inheritedProtoView; + expect(nestedProtoView.parentProtoView).toBe(parentProtoView); + }); + it('should bind variables to the nested ProtoView', () => { var element = el('
'); var results = createPipeline({ @@ -71,7 +80,7 @@ export function main() { }).process(element); var protoView = results[0].inheritedProtoView; - expect(protoView.protoContextLocals).toEqual(MapWrapper.createFromStringMap({ + expect(protoView.protoLocals).toEqual(MapWrapper.createFromStringMap({ 'map2': null, 'map1': null })); diff --git a/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js b/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js index 8200adb28b..a98ff4dbd4 100644 --- a/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js +++ b/modules/angular2/test/core/compiler/shadow_dom/shadow_dom_emulation_integration_spec.js @@ -379,6 +379,6 @@ class MyComp { function createView(pv) { var view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, {}); + view.hydrate(new Injector([]), null, null, {}, null); return view; } diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js index 8ca2e18ef3..8b8c184bec 100644 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ b/modules/angular2/test/core/compiler/view_container_spec.js @@ -11,7 +11,7 @@ import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular function createView(nodes) { var view = new View(null, nodes, MapWrapper.create()); - var cd = new DynamicProtoChangeDetector(null).instantiate(view, []); + var cd = new DynamicProtoChangeDetector(null).instantiate(view, [], null); view.init(cd, [], [], [], [], [], [], [], [], []); return view; } @@ -48,7 +48,8 @@ class HydrateAwareFakeView { return this.isHydrated; } - hydrate(_, __, ___, ____) { + + hydrate(_, __, ___, ____, _____) { this.isHydrated = true; } diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js index 0d94a748cf..cc5aad0ccb 100644 --- a/modules/angular2/test/core/compiler/view_spec.js +++ b/modules/angular2/test/core/compiler/view_spec.js @@ -46,7 +46,7 @@ export function main() { function createView(protoView, eventManager: EventManager = null) { var ctx = new MyEvaluationContext(); var view = protoView.instantiate(null, eventManager); - view.hydrate(null, null, null, ctx); + view.hydrate(null, null, null, ctx, null); return view; } @@ -69,7 +69,7 @@ export function main() { it('should be able to be hydrated and dehydrated', () => { var ctx = new Object(); - view.hydrate(null, null, null, ctx); + view.hydrate(null, null, null, ctx, null); expect(view.hydrated()).toBe(true); view.dehydrate(); @@ -78,7 +78,7 @@ export function main() { it('should hydrate and dehydrate the change detector', () => { var ctx = new Object(); - view.hydrate(null, null, null, ctx); + view.hydrate(null, null, null, ctx, null); expect(view.changeDetector.hydrated()).toBe(true); view.dehydrate(); @@ -104,7 +104,7 @@ export function main() { it('should support setting of declared locals', () => { view.setLocal('context-foo', 'bar'); - expect(view.context.get('template-foo')).toBe('bar'); + expect(view.locals.get('template-foo')).toBe('bar'); }); it('should not throw on undeclared locals', () => { @@ -114,8 +114,8 @@ export function main() { it('when dehydrated should set locals to null', () => { view.setLocal('context-foo', 'bar'); view.dehydrate(); - view.hydrate(null, null, null, new Object()); - expect(view.context.get('template-foo')).toBe(null); + view.hydrate(null, null, null, new Object(), null); + expect(view.locals.get('template-foo')).toBe(null); }); it('should throw when trying to set on dehydrated view', () => { @@ -136,7 +136,7 @@ export function main() { var pv = new ProtoView(templateAwareCreateElement('
'), new DynamicProtoChangeDetector(null), null); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.nodes.length).toBe(1); expect(DOM.getAttribute(view.nodes[0], 'id')).toEqual('1'); }); @@ -150,7 +150,7 @@ export function main() { pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop')); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.bindElements.length).toEqual(1); expect(view.bindElements[0]).toBe(view.nodes[0]); }); @@ -162,7 +162,7 @@ export function main() { pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a')); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.bindElements.length).toEqual(1); expect(view.bindElements[0]).toBe(view.nodes[0].childNodes[1]); }); @@ -179,7 +179,7 @@ export function main() { pv.bindTextNode(2, parser.parseBinding('b', null)); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.textNodes.length).toEqual(2); expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[0]); expect(view.textNodes[1]).toBe(view.nodes[0].childNodes[2]); @@ -192,7 +192,7 @@ export function main() { pv.bindTextNode(0, parser.parseBinding('b', null)); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.textNodes.length).toEqual(1); expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[1].childNodes[0]); }); @@ -207,7 +207,7 @@ export function main() { new NativeShadowDomStrategy(null)); pv.instantiateInPlace = true; var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.nodes[0]).toBe(template); }); @@ -216,7 +216,7 @@ export function main() { var pv = new ProtoView(template, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy(null)) var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.nodes[0]).not.toBe(template); }); }); @@ -236,7 +236,7 @@ export function main() { pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective])); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.elementInjectors.length).toBe(1); expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); }); @@ -249,7 +249,7 @@ export function main() { pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective])); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.elementInjectors.length).toBe(2); expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); expect(view.elementInjectors[1].parent).toBe(view.elementInjectors[0]); @@ -296,7 +296,7 @@ export function main() { pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective])); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.rootElementInjectors.length).toBe(1); expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); }); @@ -308,7 +308,7 @@ export function main() { pv.bindElement(null, 0, new ProtoElementInjector(null, 2, [AnotherDirective])); var view = pv.instantiate(null, null); - view.hydrate(null, null, null, null); + view.hydrate(null, null, null, null, null); expect(view.rootElementInjectors.length).toBe(2) expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true); expect(view.rootElementInjectors[1].get(AnotherDirective) instanceof AnotherDirective).toBe(true); @@ -331,7 +331,7 @@ export function main() { function createNestedView(protoView) { ctx = new MyEvaluationContext(); var view = protoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, ctx); + view.hydrate(new Injector([]), null, null, ctx, null); return view; } @@ -643,7 +643,7 @@ export function main() { someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy(null)); var view = rootProtoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, null); + view.hydrate(new Injector([]), null, null, null, null); expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null); }); @@ -652,7 +652,7 @@ export function main() { someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy(null)); var view = rootProtoView.instantiate(null, null); - view.hydrate(new Injector([]), null, null, null); + view.hydrate(new Injector([]), null, null, null, null); expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi'); }); }); diff --git a/modules/angular2/test/directives/foreach_spec.js b/modules/angular2/test/directives/foreach_spec.js index 61cf60693a..6657814c9d 100644 --- a/modules/angular2/test/directives/foreach_spec.js +++ b/modules/angular2/test/directives/foreach_spec.js @@ -45,7 +45,7 @@ export function main() { function createView(pv) { component = new TestComponent(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, component); + view.hydrate(new Injector([]), null, null, component, null); cd = view.changeDetector; } @@ -201,20 +201,19 @@ export function main() { compileWithTemplate( '
' + '
' + - '{{subitem}};' + + '{{subitem}}-{{item.length}};' + '
|
' ).then((pv) => { createView(pv); - component.items = [['a', 'b'], ['c','d']]; + component.items = [['a', 'b'], ['c']]; cd.detectChanges(); cd.detectChanges(); cd.detectChanges(); - expect(DOM.getText(view.nodes[0])).toEqual('a;b;|c;d;|'); + expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;|c-1;|'); async.done(); }); })); - it('should display indices correctly', inject([AsyncTestCompleter], (async) => { var INDEX_TEMPLATE = '
{{i.toString()}}
'; @@ -243,7 +242,6 @@ class Foo { @Component({selector: 'test-cmp'}) class TestComponent { items: any; - item: any; constructor() { this.items = [1, 2]; } diff --git a/modules/angular2/test/directives/if_spec.js b/modules/angular2/test/directives/if_spec.js index 79fd1d9193..3e753857c4 100644 --- a/modules/angular2/test/directives/if_spec.js +++ b/modules/angular2/test/directives/if_spec.js @@ -57,7 +57,7 @@ export function main() { function createView(pv) { component = new TestComponent(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, component); + view.hydrate(new Injector([]), null, null, component, null); cd = view.changeDetector; } diff --git a/modules/angular2/test/directives/non_bindable_spec.js b/modules/angular2/test/directives/non_bindable_spec.js index 49bf9038e0..b256fb5e10 100644 --- a/modules/angular2/test/directives/non_bindable_spec.js +++ b/modules/angular2/test/directives/non_bindable_spec.js @@ -54,7 +54,7 @@ export function main() { function createView(pv) { component = new TestComponent(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, component); + view.hydrate(new Injector([]), null, null, component, null); cd = view.changeDetector; } diff --git a/modules/angular2/test/directives/switch_spec.js b/modules/angular2/test/directives/switch_spec.js index f1d84844eb..ea5ecaa342 100644 --- a/modules/angular2/test/directives/switch_spec.js +++ b/modules/angular2/test/directives/switch_spec.js @@ -51,7 +51,7 @@ export function main() { function createView(pv) { component = new TestComponent(); view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, component); + view.hydrate(new Injector([]), null, null, component, null); cd = view.changeDetector; } diff --git a/modules/angular2/test/forms/integration_spec.js b/modules/angular2/test/forms/integration_spec.js index 41da4e43a5..066839fbe8 100644 --- a/modules/angular2/test/forms/integration_spec.js +++ b/modules/angular2/test/forms/integration_spec.js @@ -63,7 +63,7 @@ export function main() { compiler.compile(componentType).then((pv) => { var view = pv.instantiate(null, null); - view.hydrate(new Injector([]), null, null, context); + view.hydrate(new Injector([]), null, null, context, null); detectChanges(view); callback(view); }); diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.js b/modules/benchmarks/src/change_detection/change_detection_benchmark.js index 6c3984d7f0..1b2baabe93 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.js +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.js @@ -105,7 +105,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) { var parser = new Parser(new Lexer()); var parentProto = changeDetection.createProtoChangeDetector('parent'); - var parentCd = parentProto.instantiate(dispatcher, []); + var parentCd = parentProto.instantiate(dispatcher, [], []); var proto = changeDetection.createProtoChangeDetector("proto"); var bindingRecords = [ @@ -126,8 +126,8 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) { for (var j = 0; j < 10; ++j) { obj.setField(j, i); } - var cd = proto.instantiate(dispatcher, bindingRecords); - cd.hydrate(obj); + var cd = proto.instantiate(dispatcher, bindingRecords, []); + cd.hydrate(obj, null); parentCd.addChild(cd); } return parentCd;