feat(change_detection): added changeDetection to Component

This commit is contained in:
vsavkin
2015-03-30 16:54:10 -07:00
parent a11f683e7b
commit 514ba54282
20 changed files with 210 additions and 194 deletions

View File

@ -1,7 +1,8 @@
import {isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {BindingPropagationConfig} from './binding_propagation_config';
import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
import {ChangeDetector} from './interfaces';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
export class AbstractChangeDetector extends ChangeDetector {
lightDomChildren:List;
@ -15,7 +16,7 @@ export class AbstractChangeDetector extends ChangeDetector {
this.lightDomChildren = [];
this.shadowDomChildren = [];
this.bindingPropagationConfig = new BindingPropagationConfig(this);
this.mode = CHECK_ALWAYS;
this.mode = null;
}
addChild(cd:ChangeDetector) {

View File

@ -1,4 +1,5 @@
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './interfaces';
import {ChangeDetector} from './interfaces';
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
/**
* @publicModule angular2/angular2

View File

@ -1,7 +1,7 @@
library change_detectoin.change_detection_jit_generator;
class ChangeDetectorJITGenerator {
ChangeDetectorJITGenerator(typeName, records, directiveMementos) {
ChangeDetectorJITGenerator(typeName, strategy, records, directiveMementos) {
}
generate() {

View File

@ -24,74 +24,10 @@ import {
* that "emulates" what the developer would write by hand to implement the same
* kind of behaviour.
*
* For example: An expression `address.city` will result in the following class:
*
* var ChangeDetector0 = function ChangeDetector0(dispatcher, protos) {
* AbstractChangeDetector.call(this);
* this.dispatcher = dispatcher;
* this.protos = protos;
*
* this.context = ChangeDetectionUtil.unitialized();
* this.address0 = ChangeDetectionUtil.unitialized();
* this.city1 = ChangeDetectionUtil.unitialized();
* }
* ChangeDetector0.prototype = Object.create(AbstractChangeDetector.prototype);
*
* ChangeDetector0.prototype.detectChangesInRecords = function(throwOnChange) {
* var address0;
* var city1;
* var change;
* var changes = null;
* var temp;
* var context = this.context;
*
* address0 = context.address;
* if (address0 !== this.address0) {
* this.address0 = address0;
* }
*
* city1 = address0.city;
* if (city1 !== this.city1) {
* changes = ChangeDetectionUtil.addRecord(changes,
* ChangeDetectionUtil.simpleChangeRecord(this.protos[1].bindingMemento, this.city1, city1));
* this.city1 = city1;
* }
*
* if (changes.length > 0) {
* if(throwOnChange) ChangeDetectionUtil.throwOnChange(this.protos[1], changes[0]);
* this.dispatcher.onRecordChange('address.city', changes);
* changes = null;
* }
* }
*
* ChangeDetector0.prototype.callOnAllChangesDone = function() {}
*
* 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() {
* return this.context !== ChangeDetectionUtil.unitialized();
* }
*
* return ChangeDetector0;
*
*
* The only thing the generated class depends on is the super class AbstractChangeDetector.
*
* The implementation comprises two parts:
* * ChangeDetectorJITGenerator has the logic of how everything fits together.
* * template functions (e.g., constructorTemplate) define what code is generated.
*/
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
var UTIL = "ChangeDetectionUtil";
var DISPATCHER_ACCESSOR = "this.dispatcher";
@ -102,6 +38,7 @@ var CONTEXT_ACCESSOR = "this.context";
var CHANGE_LOCAL = "change";
var CHANGES_LOCAL = "changes";
var LOCALS_ACCESSOR = "this.locals";
var MODE_ACCESSOR = "this.mode";
var TEMP_LOCAL = "temp";
function typeTemplate(type:string, cons:string, detectChanges:string,
@ -137,9 +74,10 @@ function pipeOnDestroyTemplate(pipeNames:List) {
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
}
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
function hydrateTemplate(type:string, mode:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
return `
${type}.prototype.hydrate = function(context, locals) {
${MODE_ACCESSOR} = "${mode}";
${CONTEXT_ACCESSOR} = context;
${LOCALS_ACCESSOR} = locals;
}
@ -265,13 +203,15 @@ export class ChangeDetectorJITGenerator {
typeName:string;
records:List<ProtoRecord>;
directiveMementos:List;
localNames:List<String>;
changeNames:List<String>;
fieldNames:List<String>;
pipeNames:List<String>;
localNames:List<string>;
changeNames:List<string>;
fieldNames:List<string>;
pipeNames:List<string>;
changeDetectionStrategy:stirng;
constructor(typeName:string, records:List<ProtoRecord>, directiveMementos:List) {
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveMementos:List) {
this.typeName = typeName;
this.changeDetectionStrategy = changeDetectionStrategy;
this.records = records;
this.directiveMementos = directiveMementos;
@ -281,7 +221,7 @@ export class ChangeDetectorJITGenerator {
this.pipeNames = this.getPipeNames(this.localNames);
}
getLocalNames(records:List<ProtoRecord>):List<String> {
getLocalNames(records:List<ProtoRecord>):List<string> {
var index = 0;
var names = records.map((r) => {
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
@ -290,15 +230,15 @@ export class ChangeDetectorJITGenerator {
return ["context"].concat(names);
}
getChangeNames(localNames:List<String>):List<String> {
getChangeNames(localNames:List<string>):List<string> {
return localNames.map((n) => `change_${n}`);
}
getFieldNames(localNames:List<String>):List<String> {
getFieldNames(localNames:List<string>):List<string> {
return localNames.map((n) => `this.${n}`);
}
getPipeNames(localNames:List<String>):List<String> {
getPipeNames(localNames:List<string>):List<string> {
return localNames.map((n) => `this.${n}_pipe`);
}
@ -314,7 +254,8 @@ export class ChangeDetectorJITGenerator {
}
genHydrate():string {
return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getNonNullPipeNames()));
}
@ -325,7 +266,7 @@ export class ChangeDetectorJITGenerator {
return fieldDefinitionsTemplate(fields);
}
getNonNullPipeNames():List<String> {
getNonNullPipeNames():List<string> {
var pipes = [];
this.records.forEach((r) => {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {

View File

@ -3,7 +3,8 @@ 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, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
import {ChangeRecord, ChangeDetector} from './interfaces';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
export var uninitialized = new Object();
@ -163,6 +164,10 @@ export class ChangeDetectionUtil {
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;

View File

@ -0,0 +1,35 @@
//TODO:vsavkin Use enums after switching to TypeScript
/**
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
* will become CHECKED.
*/
export const CHECK_ONCE="CHECK_ONCE";
/**
* CHECKED means that the change detector should be skipped until its mode changes to
* CHECK_ONCE or CHECK_ALWAYS.
*/
export const CHECKED="CHECKED";
/**
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
* will remain CHECK_ALWAYS.
*/
export const CHECK_ALWAYS="ALWAYS_CHECK";
/**
* DETACHED means that the change detector sub tree is not a part of the main tree and
* should be skipped.
*/
export const DETACHED="DETACHED";
/**
* ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration.
*/
export const ON_PUSH = "ON_PUSH";
/**
* DEFAULT means that the change detector's mode will be set to CHECK_ALWAYS during hydration.
*/
export const DEFAULT = "DEFAULT";

View File

@ -35,8 +35,10 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
protos:List<ProtoRecord>;
directiveMementos:List;
changeControlStrategy:string;
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>, directiveMementos:List) {
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
protoRecords:List<ProtoRecord>, directiveMementos:List) {
super();
this.dispatcher = dispatcher;
this.pipeRegistry = pipeRegistry;
@ -54,9 +56,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
this.protos = protoRecords;
this.directiveMementos = directiveMementos;
this.changeControlStrategy = changeControlStrategy;
}
hydrate(context:any, locals:any) {
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
this.values[0] = context;
this.locals = locals;
}

View File

@ -1,5 +1,19 @@
import {List} from 'angular2/src/facade/collection';
import {Locals} from './parser/locals';
import {AST} from './parser/ast';
export class ProtoChangeDetector {
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMemento:List):ChangeDetector{
return null;
}
}
export class ChangeDetection {
createProtoChangeDetector(name:string, changeControlStrategy:string):ProtoChangeDetector{
return null;
}
}
export class ChangeRecord {
bindingMemento:any;
@ -20,31 +34,6 @@ export class ChangeRecord {
}
}
/**
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
* will become CHECKED.
*/
export const CHECK_ONCE="CHECK_ONCE";
/**
* CHECKED means that the change detector should be skipped until its mode changes to
* CHECK_ONCE or CHECK_ALWAYS.
*/
export const CHECKED="CHECKED";
/**
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
* will remain CHECK_ALWAYS.
*/
export const CHECK_ALWAYS="ALWAYS_CHECK";
/**
* DETACHED means that the change detector sub tree is not a part of the main tree and
* should be skipped.
*/
export const DETACHED="DETACHED";
export class ChangeDispatcher {
onRecordChange(directiveMemento, records:List<ChangeRecord>) {}
}

View File

@ -22,7 +22,7 @@ import {
PrefixNot
} from './parser/ast';
import {ChangeRecord, ChangeDispatcher, ChangeDetector} from './interfaces';
import {ChangeRecord, ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
@ -45,13 +45,6 @@ import {
RECORD_TYPE_INTERPOLATE
} from './proto_record';
export class ProtoChangeDetector {
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMemento:List):ChangeDetector{
return null;
}
}
export class BindingRecord {
ast:AST;
bindingMemento:any;
@ -67,15 +60,18 @@ export class BindingRecord {
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry:PipeRegistry) {
constructor(pipeRegistry:PipeRegistry, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
this._createRecordsIfNecessary(bindingRecords, variableBindings);
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records, directiveMementos);
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
this._pipeRegistry, this._records, directiveMementos);
}
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
@ -93,11 +89,13 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
export class JitProtoChangeDetector extends ProtoChangeDetector {
_factory:Function;
_pipeRegistry;
_changeControlStrategy:string;
constructor(pipeRegistry) {
constructor(pipeRegistry, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._factory = null;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
@ -114,7 +112,8 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, records, directiveMementos).generate();
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
directiveMementos).generate();
}
}
}