refactor(change_detection): call onChange from the change detector
This commit is contained in:
parent
bcbed2812d
commit
abea92af59
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -89,21 +89,31 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||
detectChangesInRecords(throwOnChange:boolean) {
|
||||
var protos:List<ProtoRecord> = 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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<ElementInjector>) {
|
||||
invoke(currentValue:any, elementInjectors:List<ElementInjector>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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', () => {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue