feat(change_detection): implement hydration/dehydration
This commit is contained in:
@ -30,9 +30,9 @@ import {
|
||||
* this.dispatcher = dispatcher;
|
||||
* this.protos = protos;
|
||||
*
|
||||
* this.context = null;
|
||||
* this.address0 = null;
|
||||
* this.city1 = null;
|
||||
* this.context = ChangeDetectionUtil.unitialized();
|
||||
* this.address0 = ChangeDetectionUtil.unitialized();
|
||||
* this.city1 = ChangeDetectionUtil.unitialized();
|
||||
* }
|
||||
* ChangeDetector0.prototype = Object.create(AbstractChangeDetector.prototype);
|
||||
*
|
||||
@ -70,10 +70,20 @@ import {
|
||||
* }
|
||||
*
|
||||
*
|
||||
* ChangeDetector0.prototype.setContext = function(context) {
|
||||
* ChangeDetector0.prototype.hydrate = function(context) {
|
||||
* this.context = context;
|
||||
* }
|
||||
*
|
||||
* ChangeDetector0.prototype.dehydrate = function(context) {
|
||||
* this.context = ChangeDetectionUtil.unitialized();
|
||||
* this.address0 = ChangeDetectionUtil.unitialized();
|
||||
* this.city1 = ChangeDetectionUtil.unitialized();
|
||||
* }
|
||||
*
|
||||
* ChangeDetector0.prototype.hydrated = function() {
|
||||
* return this.context !== ChangeDetectionUtil.unitialized();
|
||||
* }
|
||||
*
|
||||
* return ChangeDetector0;
|
||||
*
|
||||
*
|
||||
@ -119,11 +129,22 @@ ${type}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
|
||||
`;
|
||||
}
|
||||
|
||||
function setContextTemplate(type:string):string {
|
||||
function pipeOnDestroyTemplate(pipeNames:List) {
|
||||
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
|
||||
}
|
||||
|
||||
function hydrateTemplate(type:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
|
||||
return `
|
||||
${type}.prototype.setContext = function(context) {
|
||||
${type}.prototype.hydrate = function(context) {
|
||||
this.context = context;
|
||||
}
|
||||
${type}.prototype.dehydrate = function() {
|
||||
${pipeOnDestroy}
|
||||
${fieldsDefinitions}
|
||||
}
|
||||
${type}.prototype.hydrated = function() {
|
||||
return this.context !== ${UTIL}.unitialized();
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -162,7 +183,10 @@ if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
|
||||
function pipeCheckTemplate(context:string, pipe:string, pipeType:string,
|
||||
value:string, change:string, addRecord:string, notify:string):string{
|
||||
return `
|
||||
if (${pipe} === ${UTIL}.unitialized() || !${pipe}.supports(${context})) {
|
||||
if (${pipe} === ${UTIL}.unitialized()) {
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
|
||||
} else if (!${pipe}.supports(${context})) {
|
||||
${pipe}.onDestroy();
|
||||
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context});
|
||||
}
|
||||
|
||||
@ -281,25 +305,34 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
generate():Function {
|
||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genSetContext());
|
||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'ContextWithVariableBindings', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, ContextWithVariableBindings, this.records);
|
||||
}
|
||||
|
||||
genConstructor():string {
|
||||
var fields = [];
|
||||
fields = fields.concat(this.fieldNames);
|
||||
|
||||
this.records.forEach((r) => {
|
||||
if (r.mode === RECORD_TYPE_PIPE) {
|
||||
fields.push(this.pipeNames[r.selfIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
return constructorTemplate(this.typeName, fieldDefinitionsTemplate(fields));
|
||||
return constructorTemplate(this.typeName, this.genFieldDefinitions());
|
||||
}
|
||||
|
||||
genSetContext():string {
|
||||
return setContextTemplate(this.typeName);
|
||||
genHydrate():string {
|
||||
return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
|
||||
pipeOnDestroyTemplate(this.getnonNullPipeNames()));
|
||||
}
|
||||
|
||||
genFieldDefinitions() {
|
||||
var fields = [];
|
||||
fields = fields.concat(this.fieldNames);
|
||||
fields = fields.concat(this.getnonNullPipeNames());
|
||||
return fieldDefinitionsTemplate(fields);
|
||||
}
|
||||
|
||||
getnonNullPipeNames():List<String> {
|
||||
var pipes = [];
|
||||
this.records.forEach((r) => {
|
||||
if (r.mode === RECORD_TYPE_PIPE) {
|
||||
pipes.push(this.pipeNames[r.selfIndex]);
|
||||
}
|
||||
});
|
||||
return pipes;
|
||||
}
|
||||
|
||||
genDetectChanges():string {
|
||||
|
@ -43,15 +43,36 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||
|
||||
ListWrapper.fill(this.values, uninitialized);
|
||||
ListWrapper.fill(this.pipes, null);
|
||||
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
|
||||
this.protos = protoRecords;
|
||||
}
|
||||
|
||||
setContext(context:any) {
|
||||
hydrate(context:any) {
|
||||
this.values[0] = context;
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
this._destroyPipes();
|
||||
ListWrapper.fill(this.values, uninitialized);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
ListWrapper.fill(this.pipes, null);
|
||||
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||
this.values[0] = context;
|
||||
}
|
||||
|
||||
_destroyPipes() {
|
||||
for(var i = 0; i < this.pipes.length; ++i) {
|
||||
if (isPresent(this.pipes[i])) {
|
||||
this.pipes[i].onDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hydrated():boolean {
|
||||
return this.values[0] !== uninitialized;
|
||||
}
|
||||
|
||||
detectChangesInRecords(throwOnChange:boolean) {
|
||||
@ -184,11 +205,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||
var storedPipe = this._readPipe(proto);
|
||||
if (isPresent(storedPipe) && storedPipe.supports(context)) {
|
||||
return storedPipe;
|
||||
} else {
|
||||
var pipe = this.pipeRegistry.get(proto.name, context);
|
||||
this._writePipe(proto, pipe);
|
||||
return pipe;
|
||||
}
|
||||
if (isPresent(storedPipe)) {
|
||||
storedPipe.onDestroy();
|
||||
}
|
||||
var pipe = this.pipeRegistry.get(proto.name, context);
|
||||
this._writePipe(proto, pipe);
|
||||
return pipe;
|
||||
}
|
||||
|
||||
_readContext(proto:ProtoRecord) {
|
||||
|
@ -55,7 +55,8 @@ export class ChangeDetector {
|
||||
addChild(cd:ChangeDetector) {}
|
||||
removeChild(cd:ChangeDetector) {}
|
||||
remove() {}
|
||||
setContext(context:any) {}
|
||||
hydrate(context:any) {}
|
||||
dehydrate() {}
|
||||
markPathToRootAsCheckOnce() {}
|
||||
|
||||
detectChanges() {}
|
||||
|
@ -2,5 +2,6 @@ export var NO_CHANGE = new Object();
|
||||
|
||||
export class Pipe {
|
||||
supports(obj):boolean {return false;}
|
||||
onDestroy() {}
|
||||
transform(value:any):any {return null;}
|
||||
}
|
3
modules/angular2/src/core/compiler/view.js
vendored
3
modules/angular2/src/core/compiler/view.js
vendored
@ -97,7 +97,7 @@ export class View {
|
||||
// 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.setContext(this.context);
|
||||
this.changeDetector.hydrate(this.context);
|
||||
}
|
||||
|
||||
_dehydrateContext() {
|
||||
@ -105,6 +105,7 @@ export class View {
|
||||
this.contextWithLocals.clearValues();
|
||||
}
|
||||
this.context = null;
|
||||
this.changeDetector.dehydrate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user