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 eefff36bfc..008dbd4243 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 @@ -40,6 +40,7 @@ var CHANGES_LOCAL = "changes"; var LOCALS_ACCESSOR = "this.locals"; var MODE_ACCESSOR = "this.mode"; var TEMP_LOCAL = "temp"; +var CURRENT_PROTO = "currentProto"; function typeTemplate(type:string, cons:string, detectChanges:string, notifyOnAllChangesDone:string, setContext:string):string { @@ -113,12 +114,13 @@ function onAllChangesDoneTemplate(index:number):string { } -function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string { +function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string { return ` ${localDefinitions} ${changeDefinitions} var ${TEMP_LOCAL}; var ${CHANGE_LOCAL}; +var ${CURRENT_PROTO}; var ${CHANGES_LOCAL} = null; context = ${CONTEXT_ACCESSOR}; @@ -126,19 +128,11 @@ ${records} `; } -function notifyTemplate(index:number):string{ - return ` -if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) { - if(throwOnChange) ${UTIL}.throwOnChange(${PROTOS_ACCESSOR}[${index}], ${CHANGES_LOCAL}[0]); - ${DISPATCHER_ACCESSOR}.onRecordChange(${PROTOS_ACCESSOR}[${index}].directiveMemento, ${CHANGES_LOCAL}); - ${CHANGES_LOCAL} = null; -} -`; -} - -function pipeCheckTemplate(context:string, bindingPropagationConfig:string, pipe:string, pipeType:string, - value:string, change:string, addRecord:string, notify:string):string{ +function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string, + oldValue:string, newValue:string, change:string, invokeMementoAndAddChange:string, + addToChanges, lastInDirective:string):string{ return ` +${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; if (${pipe} === ${UTIL}.unitialized()) { ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig}); } else if (!${pipe}.supports(${context})) { @@ -146,25 +140,29 @@ if (${pipe} === ${UTIL}.unitialized()) { ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig}); } -${CHANGE_LOCAL} = ${pipe}.transform(${context}); -if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) { - ${value} = ${CHANGE_LOCAL}; +${newValue} = ${pipe}.transform(${context}); +if (! ${UTIL}.noChangeMarker(${newValue})) { ${change} = true; - ${addRecord} + ${invokeMementoAndAddChange} + ${addToChanges} + ${oldValue} = ${newValue}; } -${notify} +${lastInDirective} `; } -function referenceCheckTemplate(assignment, newValue, oldValue, change, addRecord, notify) { +function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string, + invokeMementoAndAddChange:string, addToChanges:string, lastInDirective:string):string { return ` +${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; ${assignment} if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) { ${change} = true; - ${addRecord} + ${invokeMementoAndAddChange} + ${addToChanges} ${oldValue} = ${newValue}; } -${notify} +${lastInDirective} `; } @@ -193,9 +191,25 @@ if (${cond}) { `; } -function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newValue:string) { - return `${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL}, - ${UTIL}.simpleChangeRecord(${PROTOS_ACCESSOR}[${protoIndex}].bindingMemento, ${oldValue}, ${newValue}));`; +function addToChangesTemplate(oldValue:string, newValue:string):string { + return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingMemento, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`; +} + +function invokeBindingMemento(oldValue:string, newValue:string):string { + return ` +if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); + +${DISPATCHER_ACCESSOR}.invokeMementoFor(${CURRENT_PROTO}.bindingMemento, ${newValue}); + `; +} + +function lastInDirectiveTemplate(protoIndex:number):string{ + return ` +if (${CHANGES_LOCAL}) { + ${DISPATCHER_ACCESSOR}.onChange(${PROTOS_ACCESSOR}[${protoIndex}].directiveMemento, ${CHANGES_LOCAL}); +} +${CHANGES_LOCAL} = null; +`; } @@ -277,7 +291,7 @@ export class ChangeDetectorJITGenerator { } genDetectChanges():string { - var body = this.genBody(); + var body = this.genDetectChangesBody(); return detectChangesTemplate(this.typeName, body); } @@ -295,9 +309,9 @@ export class ChangeDetectorJITGenerator { return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n")); } - genBody():string { + genDetectChangesBody():string { var rec = this.records.map((r) => this.genRecord(r)).join("\n"); - return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec); + return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec); } genLocalDefinitions():string { @@ -318,27 +332,33 @@ export class ChangeDetectorJITGenerator { genPipeCheck(r:ProtoRecord):string { var context = this.localNames[r.contextIndex]; - var pipe = this.pipeNames[r.selfIndex]; - var newValue = this.localNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex]; + var newValue = this.localNames[r.selfIndex]; var change = this.changeNames[r.selfIndex]; + + var pipe = this.pipeNames[r.selfIndex]; var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null"; - var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue); - var notify = this.genNotify(r); + var invokeMemento = this.getInvokeMementoAndAddChangeTemplate(r); + var addToChanges = this.genAddToChanges(r); + var lastInDirective = this.genLastInDirective(r); - return pipeCheckTemplate(context, bpc, pipe, r.name, newValue, change, addRecord, notify); + return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change, + invokeMemento, addToChanges, lastInDirective); } genReferenceCheck(r:ProtoRecord):string { - var newValue = this.localNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex]; + var newValue = this.localNames[r.selfIndex]; var change = this.changeNames[r.selfIndex]; var assignment = this.genUpdateCurrentValue(r); - var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue); - var notify = this.genNotify(r); - var check = referenceCheckTemplate(assignment, newValue, oldValue, change, r.lastInBinding ? addRecord : '', notify); + var invokeMemento = this.getInvokeMementoAndAddChangeTemplate(r); + var addToChanges = this.genAddToChanges(r); + var lastInDirective = this.genLastInDirective(r); + + var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change, + invokeMemento, addToChanges, lastInDirective); if (r.isPureFunction()) { return this.ifChangedGuard(r, check); } else { @@ -405,8 +425,22 @@ export class ChangeDetectorJITGenerator { return JSON.stringify(value); } - genNotify(r):string{ - return r.lastInDirective ? notifyTemplate(r.selfIndex - 1) : ''; + getInvokeMementoAndAddChangeTemplate(r:ProtoRecord):string { + var newValue = this.localNames[r.selfIndex]; + var oldValue = this.fieldNames[r.selfIndex]; + return r.lastInBinding ? invokeBindingMemento(oldValue, newValue) : ""; + } + + genAddToChanges(r:ProtoRecord):string { + var newValue = this.localNames[r.selfIndex]; + var oldValue = this.fieldNames[r.selfIndex]; + var callOnChange = r.directiveMemento && r.directiveMemento.callOnChange; + return callOnChange ? addToChangesTemplate(oldValue, newValue) : ""; + } + + genLastInDirective(r:ProtoRecord):string{ + var callOnChange = r.directiveMemento && r.directiveMemento.callOnChange; + return r.lastInDirective && callOnChange ? lastInDirectiveTemplate(r.selfIndex - 1) : ''; } genArgs(r:ProtoRecord):string { diff --git a/modules/angular2/src/change_detection/change_detection_util.js b/modules/angular2/src/change_detection/change_detection_util.js index 5be6d95e6a..e92643b2fc 100644 --- a/modules/angular2/src/change_detection/change_detection_util.js +++ b/modules/angular2/src/change_detection/change_detection_util.js @@ -3,7 +3,6 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {ProtoRecord} from './proto_record'; import {ExpressionChangedAfterItHasBeenChecked} from './exceptions'; import {NO_CHANGE} from './pipes/pipe'; -import {ChangeRecord, ChangeDetector} from './interfaces'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; export var uninitialized = new Object(); @@ -40,31 +39,7 @@ var _simpleChanges = [ new SimpleChange(null, null), new SimpleChange(null, null), new SimpleChange(null, null) -] - -var _changeRecordsIndex = 0; -var _changeRecords = [ - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null), - new ChangeRecord(null, null) -] +]; function _simpleChange(previousValue, currentValue) { var index = _simpleChangesIndex++ % 20; @@ -74,16 +49,6 @@ function _simpleChange(previousValue, currentValue) { return s; } -function _changeRecord(bindingMemento, change) { - var index = _changeRecordsIndex++ % 20; - var s = _changeRecords[index]; - s.bindingMemento = bindingMemento; - s.change = change; - return s; -} - -var _singleElementList = [null]; - export class ChangeDetectionUtil { static unitialized() { return uninitialized; @@ -152,33 +117,19 @@ export class ChangeDetectionUtil { throw new ExpressionChangedAfterItHasBeenChecked(proto, change); } - static simpleChange(previousValue:any, currentValue:any):SimpleChange { - return _simpleChange(previousValue, currentValue); - } - - static changeRecord(memento:any, change:any):ChangeRecord { - return _changeRecord(memento, change); - } - - static simpleChangeRecord(memento:any, previousValue:any, currentValue:any):ChangeRecord { - return _changeRecord(memento, _simpleChange(previousValue, currentValue)); - } - static changeDetectionMode(strategy:string) { return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS; } - static addRecord(updatedRecords:List, changeRecord:ChangeRecord):List { - if (isBlank(updatedRecords)) { - updatedRecords = _singleElementList; - updatedRecords[0] = changeRecord; + static simpleChange(previousValue:any, currentValue:any):SimpleChange { + return _simpleChange(previousValue, currentValue); + } - } else if (updatedRecords === _singleElementList) { - updatedRecords = [_singleElementList[0], changeRecord]; - - } else { - ListWrapper.push(updatedRecords, changeRecord); + static addChange(changes, bindingMemento, change){ + if (isBlank(changes)) { + changes = {}; } - return updatedRecords; + changes[bindingMemento.propertyName] = change; + return changes; } } \ No newline at end of file diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.js b/modules/angular2/src/change_detection/dynamic_change_detector.js index 3e1dc29464..1ec9ac69f7 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.js +++ b/modules/angular2/src/change_detection/dynamic_change_detector.js @@ -89,21 +89,31 @@ export class DynamicChangeDetector extends AbstractChangeDetector { detectChangesInRecords(throwOnChange:boolean) { var protos:List = this.protos; - var updatedRecords = null; + var changes = null; + var currentDirectiveMemento = null; + for (var i = 0; i < protos.length; ++i) { var proto:ProtoRecord = protos[i]; - var change = this._check(proto); - - if (isPresent(change)) { - var record = ChangeDetectionUtil.changeRecord(proto.bindingMemento, change); - updatedRecords = ChangeDetectionUtil.addRecord(updatedRecords, record); + if (isBlank(currentDirectiveMemento)) { + currentDirectiveMemento = proto.directiveMemento; } - if (proto.lastInDirective && isPresent(updatedRecords)) { - if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, updatedRecords[0]); + var change = this._check(proto); + if (isPresent(change)) { + if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change); + this.dispatcher.invokeMementoFor(proto.bindingMemento, change.currentValue); - this.dispatcher.onRecordChange(proto.directiveMemento, updatedRecords); - updatedRecords = null; + if (isPresent(currentDirectiveMemento) && currentDirectiveMemento.callOnChange) { + changes = ChangeDetectionUtil.addChange(changes, proto.bindingMemento, change); + } + } + + if (proto.lastInDirective) { + if (isPresent(changes)) { + this.dispatcher.onChange(currentDirectiveMemento, changes); + } + currentDirectiveMemento = null; + changes = null; } } } @@ -285,3 +295,6 @@ function isSame(a, b) { if ((a !== a) && (b !== b)) return true; return false; } + + + diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index f346a0b02a..acca98cf45 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -230,58 +230,32 @@ export class View { handler(eventObj, this); } - onRecordChange(directiveMemento, records:List) { - this._invokeMementos(records); - if (directiveMemento instanceof DirectiveMemento) { - this._notifyDirectiveAboutChanges(directiveMemento, records); - } - } - - onAllChangesDone(directiveMemento) { + onAllChangesDone(directiveMemento:DirectiveMemento) { var dir = directiveMemento.directive(this.elementInjectors); dir.onAllChangesDone(); } - _invokeMementos(records:List) { - for(var i = 0; i < records.length; ++i) { - this._invokeMementoFor(records[i]); - } - } - - _notifyDirectiveAboutChanges(directiveMemento, records:List) { + onChange(directiveMemento:DirectiveMemento, changes) { var dir = directiveMemento.directive(this.elementInjectors); - if (directiveMemento.callOnChange) { - dir.onChange(this._collectChanges(records)); - } + dir.onChange(changes); } - // dispatch to element injector or text nodes based on context - _invokeMementoFor(record:ChangeRecord) { - var memento = record.bindingMemento; + // dispatch to element injector or text nodes based on context + invokeMementoFor(memento:any, currentValue:any) { if (memento instanceof DirectiveBindingMemento) { var directiveMemento:DirectiveBindingMemento = memento; - directiveMemento.invoke(record, this.elementInjectors); + directiveMemento.invoke(currentValue, this.elementInjectors); } else if (memento instanceof ElementBindingMemento) { var elementMemento:ElementBindingMemento = memento; - elementMemento.invoke(record, this.bindElements); + elementMemento.invoke(currentValue, this.bindElements); } else { // we know it refers to _textNodes. var textNodeIndex:number = memento; - DOM.setText(this.textNodes[textNodeIndex], record.currentValue); + DOM.setText(this.textNodes[textNodeIndex], currentValue); } } - - _collectChanges(records:List) { - var changes = StringMapWrapper.create(); - for(var i = 0; i < records.length; ++i) { - var record = records[i]; - var propertyUpdate = new PropertyUpdate(record.currentValue, record.previousValue); - StringMapWrapper.set(changes, record.bindingMemento._setterName, propertyUpdate); - } - return changes; - } } /** @@ -604,7 +578,7 @@ export class ProtoView { elBinder.hasElementPropertyBindings = true; this.elementsWithBindingCount++; } - var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setterName, setter); + var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setter); ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); } @@ -697,17 +671,15 @@ export class ProtoView { */ export class ElementBindingMemento { _elementIndex:int; - _setterName:string; _setter:SetterFn; - constructor(elementIndex:int, setterName:string, setter:SetterFn) { + constructor(elementIndex:int, setter:SetterFn) { this._elementIndex = elementIndex; - this._setterName = setterName; this._setter = setter; } - invoke(record:ChangeRecord, bindElements:List) { + invoke(currentValue:any, bindElements:List) { var element = bindElements[this._elementIndex]; - this._setter(element, record.currentValue); + this._setter(element, currentValue); } } @@ -716,23 +688,23 @@ export class ElementBindingMemento { export class DirectiveBindingMemento { _elementInjectorIndex:int; _directiveIndex:int; - _setterName:string; + propertyName:string; _setter:SetterFn; constructor( elementInjectorIndex:number, directiveIndex:number, - setterName:string, + propertyName:string, setter:SetterFn) { this._elementInjectorIndex = elementInjectorIndex; this._directiveIndex = directiveIndex; - this._setterName = setterName; + this.propertyName = propertyName; this._setter = setter; } - invoke(record:ChangeRecord, elementInjectors:List) { + invoke(currentValue:any, elementInjectors:List) { var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex]; var directive = elementInjector.getDirectiveAtIndex(this._directiveIndex); - this._setter(directive, record.currentValue); + this._setter(directive, currentValue); } } @@ -754,20 +726,4 @@ class DirectiveMemento { var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex]; return elementInjector.getDirectiveAtIndex(this._directiveIndex); } -} - -/** - */ -export class PropertyUpdate { - currentValue; - previousValue; - - constructor(currentValue, previousValue) { - this.currentValue = currentValue; - this.previousValue = previousValue; - } - - static createWithoutPrevious(currentValue) { - return new PropertyUpdate(currentValue, uninitialized); - } -} +} \ No newline at end of file diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 039283e8a0..de21d77461 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -10,8 +10,6 @@ import {Locals} from 'angular2/src/change_detection/parser/locals'; import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH, DEFAULT} from 'angular2/change_detection'; -import {ChangeDetectionUtil} from 'angular2/src/change_detection/change_detection_util'; - import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/change_detection/proto_change_detector'; @@ -129,21 +127,21 @@ export function main() { it("should support literal array", () => { var c = createChangeDetector('array', '[1,2]'); c["changeDetector"].detectChanges(); - expect(c["dispatcher"].loggedValues).toEqual([[[1, 2]]]); + expect(c["dispatcher"].loggedValues).toEqual([[1, 2]]); c = createChangeDetector('array', '[1,a]', new TestData(2)); c["changeDetector"].detectChanges(); - expect(c["dispatcher"].loggedValues).toEqual([[[1, 2]]]); + expect(c["dispatcher"].loggedValues).toEqual([[1, 2]]); }); it("should support literal maps", () => { var c = createChangeDetector('map', '{z:1}'); c["changeDetector"].detectChanges(); - expect(c["dispatcher"].loggedValues[0][0]['z']).toEqual(1); + expect(c["dispatcher"].loggedValues[0]['z']).toEqual(1); c = createChangeDetector('map', '{z:a}', new TestData(1)); c["changeDetector"].detectChanges(); - expect(c["dispatcher"].loggedValues[0][0]['z']).toEqual(1); + expect(c["dispatcher"].loggedValues[0]['z']).toEqual(1); }); it("should support binary operations", () => { @@ -221,11 +219,7 @@ export function main() { cd.detectChanges(); - var changeRecord = dispatcher.changeRecords[0][0]; - - expect(changeRecord.bindingMemento).toEqual('name'); - expect(changeRecord.change.currentValue).toEqual('bob'); - expect(changeRecord.change.previousValue).toEqual(ChangeDetectionUtil.unitialized()); + expect(dispatcher.loggedValues).toEqual(['bob']); }); }); @@ -240,59 +234,43 @@ export function main() { cd.detectChanges(); - var changeRecord = dispatcher.changeRecords[0][0]; - - expect(changeRecord.bindingMemento).toEqual('name'); - expect(changeRecord.change.currentValue).toEqual('bob state:0'); - expect(changeRecord.change.previousValue).toEqual(ChangeDetectionUtil.unitialized()); + expect(dispatcher.loggedValues).toEqual(['bob state:0']); }); }); - describe("group changes", () => { - var dirMemento1 = new FakeDirectiveMemento(1); - var dirMemento2 = new FakeDirectiveMemento(2); + describe("onChange", () => { + var dirMemento1 = new FakeDirectiveMemento(1, false, true); + var dirMemento2 = new FakeDirectiveMemento(2, false, true); + var dirMementoNoOnChange = new FakeDirectiveMemento(3, false, false); + var memo1 = new FakeBindingMemento("memo1"); + var memo2 = new FakeBindingMemento("memo2"); it("should notify the dispatcher when a group of records changes", () => { var pcd = createProtoChangeDetector(); var cd = instantiate(pcd, dispatcher, [ - new BindingRecord(ast("1 + 2"), "memo", dirMemento1), - new BindingRecord(ast("10 + 20"), "memo", dirMemento1), - new BindingRecord(ast("100 + 200"), "memo", dirMemento2) + new BindingRecord(ast("1 + 2"), memo1, dirMemento1), + new BindingRecord(ast("10 + 20"), memo2, dirMemento1), + new BindingRecord(ast("100 + 200"), memo1, dirMemento2) ]); cd.detectChanges(); - expect(dispatcher.loggedValues).toEqual([[3, 30], [300]]); + expect(dispatcher.loggedOnChange).toEqual([{'memo1': 3, 'memo2': 30}, {'memo1': 300}]); }); - it("should notify the dispatcher before switching to the next group", () => { + it("should not notify the dispatcher when callOnChange is false", () => { var pcd = createProtoChangeDetector(); var cd = instantiate(pcd, dispatcher, [ - new BindingRecord(ast("a()"), "a", dirMemento1), - new BindingRecord(ast("b()"), "b", dirMemento2), - new BindingRecord(ast("c()"), "c", dirMemento2) + new BindingRecord(ast("1"), memo1, dirMemento1), + new BindingRecord(ast("2"), memo1, dirMementoNoOnChange), + new BindingRecord(ast("3"), memo1, dirMemento2) ]); - var tr = new TestRecord(); - tr.a = () => { - dispatcher.logValue('InvokeA'); - return 'a' - }; - tr.b = () => { - dispatcher.logValue('InvokeB'); - return 'b' - }; - tr.c = () => { - dispatcher.logValue('InvokeC'); - return 'c' - }; - cd.hydrate(tr, null); - cd.detectChanges(); - expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]); + expect(dispatcher.loggedOnChange).toEqual([{'memo1': 1}, {'memo1': 3}]); }); }); @@ -350,7 +328,7 @@ export function main() { // the first value is the directive memento passed into onAllChangesDone expect(dispatcher.loggedValues).toEqual([ ["onAllChangesDone", memento], - [1] + 1 ]); }); }); @@ -800,58 +778,55 @@ class TestData { class FakeDirectiveMemento { value:any; callOnAllChangesDone:boolean; + callOnChange:boolean; - constructor(value, callOnAllChangesDone:boolean = false) { + constructor(value, callOnAllChangesDone:boolean = false, callOnChange:boolean = false) { this.value = value; this.callOnAllChangesDone = callOnAllChangesDone; + this.callOnChange = callOnChange; + } +} + +class FakeBindingMemento { + propertyName:string; + + constructor(propertyName:string) { + this.propertyName = propertyName; } } class TestDispatcher extends ChangeDispatcher { log:List; loggedValues:List; - changeRecords:List; - loggedOnAllChangesDone:List; - onChange:Function; + loggedOnChange:List; constructor() { super(); - this.log = null; - this.loggedValues = null; - this.loggedOnAllChangesDone = null; - this.onChange = (_, __) => {}; this.clear(); } clear() { this.log = ListWrapper.create(); this.loggedValues = ListWrapper.create(); - this.loggedOnAllChangesDone = ListWrapper.create(); - this.changeRecords = ListWrapper.create(); + this.loggedOnChange = ListWrapper.create(); } - logValue(value) { - ListWrapper.push(this.loggedValues, value); - } - - onRecordChange(directiveMemento, changeRecords:List) { - var value = changeRecords[0].change.currentValue; - var memento = changeRecords[0].bindingMemento; - ListWrapper.push(this.log, memento + '=' + this._asString(value)); - - var values = ListWrapper.map(changeRecords, (r) => r.change.currentValue); - ListWrapper.push(this.loggedValues, values); - - ListWrapper.push(this.changeRecords, changeRecords); - - this.onChange(directiveMemento, changeRecords); + onChange(directiveMemento, changes) { + var r = {}; + StringMapWrapper.forEach(changes, (c, key) => r[key] = c.currentValue); + ListWrapper.push(this.loggedOnChange, r); } onAllChangesDone(directiveMemento) { ListWrapper.push(this.loggedValues, ["onAllChangesDone", directiveMemento]); } + invokeMementoFor(memento, value) { + ListWrapper.push(this.log, `${memento}=${this._asString(value)}`); + ListWrapper.push(this.loggedValues, value); + } + _asString(value) { return (isBlank(value) ? 'null' : value.toString()); } -} +} \ No newline at end of file diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js index 66fdeb2c41..57e4d894a9 100644 --- a/modules/angular2/test/core/compiler/view_spec.js +++ b/modules/angular2/test/core/compiler/view_spec.js @@ -7,11 +7,11 @@ import {Component, Decorator, Viewport, Directive, onChange, onAllChangesDone} f import {Lexer, Parser, DynamicProtoChangeDetector, ChangeDetector} from 'angular2/change_detection'; import {EventEmitter} from 'angular2/src/core/annotations/di'; -import {List, MapWrapper} from 'angular2/src/facade/collection'; +import {List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {DOM} from 'angular2/src/dom/dom_adapter'; import {int, IMPLEMENTS} from 'angular2/src/facade/lang'; import {Injector} from 'angular2/di'; -import {View, PropertyUpdate} from 'angular2/src/core/compiler/view'; +import {View} from 'angular2/src/core/compiler/view'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone'; import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager'; @@ -624,14 +624,13 @@ export function main() { ctx.b = 0; cd.detectChanges(); - expect(directive.changes).toEqual({ - "a" : PropertyUpdate.createWithoutPrevious(0), - "b" : PropertyUpdate.createWithoutPrevious(0) - }); + expect(directive.changes["a"].currentValue).toEqual(0); + expect(directive.changes["b"].currentValue).toEqual(0); ctx.a = 100; cd.detectChanges(); - expect(directive.changes).toEqual({"a" : new PropertyUpdate(100, 0)}); + expect(directive.changes["a"].currentValue).toEqual(100); + expect(StringMapWrapper.contains(directive.changes, "b")).toBe(false); }); it('should invoke the onAllChangesDone callback', () => { diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.js b/modules/benchmarks/src/change_detection/change_detection_benchmark.js index da5b1dd038..4536e62738 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.js +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.js @@ -109,16 +109,16 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) { var proto = changeDetection.createProtoChangeDetector("proto"); var bindingRecords = [ - new BindingRecord(parser.parseBinding('field0', null), "memo", 0), - new BindingRecord(parser.parseBinding('field1', null), "memo", 1), - new BindingRecord(parser.parseBinding('field2', null), "memo", 2), - new BindingRecord(parser.parseBinding('field3', null), "memo", 3), - new BindingRecord(parser.parseBinding('field4', null), "memo", 4), - new BindingRecord(parser.parseBinding('field5', null), "memo", 5), - new BindingRecord(parser.parseBinding('field6', null), "memo", 6), - new BindingRecord(parser.parseBinding('field7', null), "memo", 7), - new BindingRecord(parser.parseBinding('field8', null), "memo", 8), - new BindingRecord(parser.parseBinding('field9', null), "memo", 9) + new BindingRecord(parser.parseBinding('field0', null), "memo", null), + new BindingRecord(parser.parseBinding('field1', null), "memo", null), + new BindingRecord(parser.parseBinding('field2', null), "memo", null), + new BindingRecord(parser.parseBinding('field3', null), "memo", null), + new BindingRecord(parser.parseBinding('field4', null), "memo", null), + new BindingRecord(parser.parseBinding('field5', null), "memo", null), + new BindingRecord(parser.parseBinding('field6', null), "memo", null), + new BindingRecord(parser.parseBinding('field7', null), "memo", null), + new BindingRecord(parser.parseBinding('field8', null), "memo", null), + new BindingRecord(parser.parseBinding('field9', null), "memo", null) ]; for (var i = 0; i < iterations; ++i) { @@ -215,6 +215,6 @@ export function main () { class DummyDispatcher extends ChangeDispatcher { - onRecordChange(record, context) { + invokeMementoFor(binding, newValue) { } }