feat(change_detection): added changeDetection to Component
This commit is contained in:
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
library change_detectoin.change_detection_jit_generator;
|
||||
|
||||
class ChangeDetectorJITGenerator {
|
||||
ChangeDetectorJITGenerator(typeName, records, directiveMementos) {
|
||||
ChangeDetectorJITGenerator(typeName, strategy, records, directiveMementos) {
|
||||
}
|
||||
|
||||
generate() {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
35
modules/angular2/src/change_detection/constants.js
vendored
Normal file
35
modules/angular2/src/change_detection/constants.js
vendored
Normal 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";
|
@ -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;
|
||||
}
|
||||
|
@ -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>) {}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user