refactor(view): moved the logic from ProtoView to ProtoViewFactory

This commit is contained in:
vsavkin 2015-04-30 14:45:42 -07:00
parent 0f4a089c32
commit ce6a2ba836
16 changed files with 309 additions and 290 deletions

View File

@ -4,9 +4,13 @@ import {IterableChangesFactory} from './pipes/iterable_changes';
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
import {AsyncPipeFactory} from './pipes/async_pipe';
import {NullPipeFactory} from './pipes/null_pipe';
import {BindingRecord} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {DEFAULT} from './constants';
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
import {Injectable} from 'angular2/di';
import {List} from 'angular2/src/facade/collection';
/**
* Structural diffing for `Object`s and `Map`s.
@ -61,8 +65,9 @@ export class DynamicChangeDetection extends ChangeDetection {
this.registry = registry;
}
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new DynamicProtoChangeDetector(this.registry, changeControlStrategy);
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
}
}
@ -82,8 +87,9 @@ export class JitChangeDetection extends ChangeDetection {
this.registry = registry;
}
createProtoChangeDetector(name:string, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new JitProtoChangeDetector(this.registry, changeControlStrategy);
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
}
}

View File

@ -4,7 +4,7 @@ import {DEFAULT} from './constants';
import {BindingRecord} from './binding_record';
export class ProtoChangeDetector {
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List):ChangeDetector{
instantiate(dispatcher:any):ChangeDetector{
return null;
}
}
@ -33,7 +33,8 @@ export class ProtoChangeDetector {
* @exportedAs angular2/change_detection
*/
export class ChangeDetection {
createProtoChangeDetector(name:string, changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
createProtoChangeDetector(name:string, bindingRecords:List, variableBindings:List, directiveRecords:List,
changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
return null;
}
}

View File

@ -28,7 +28,7 @@ import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record';
import {DirectiveIndex} from './directive_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {coalesce} from './coalesce';
@ -50,25 +50,31 @@ import {
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>;
_bindingRecords:List<BindingRecord>;
_variableBindings:List<string>;
_directiveRecords:List<DirectiveRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry:PipeRegistry, changeControlStrategy:string) {
constructor(pipeRegistry:PipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._bindingRecords = bindingRecords;
this._variableBindings = variableBindings;
this._directiveRecords = directiveRecords;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createRecordsIfNecessary(bindingRecords, variableBindings);
instantiate(dispatcher:any) {
this._createRecordsIfNecessary();
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
this._pipeRegistry, this._records, directiveRecords);
this._pipeRegistry, this._records, this._directiveRecords);
}
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
_createRecordsIfNecessary() {
if (isBlank(this._records)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(b, variableBindings);
ListWrapper.forEach(this._bindingRecords, (b) => {
recordBuilder.addAst(b, this._variableBindings);
});
this._records = coalesce(recordBuilder.records);
}
@ -79,31 +85,37 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
export class JitProtoChangeDetector extends ProtoChangeDetector {
_factory:Function;
_pipeRegistry;
_bindingRecords:List<BindingRecord>;
_variableBindings:List<string>;
_directiveRecords:List<DirectiveRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry, changeControlStrategy:string) {
constructor(pipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._factory = null;
this._bindingRecords = bindingRecords;
this._variableBindings = variableBindings;
this._directiveRecords = directiveRecords;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveRecords);
instantiate(dispatcher:any) {
this._createFactoryIfNecessary();
return this._factory(dispatcher, this._pipeRegistry);
}
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveRecords:List) {
_createFactoryIfNecessary() {
if (isBlank(this._factory)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(b, variableBindings);
ListWrapper.forEach(this._bindingRecords, (b) => {
recordBuilder.addAst(b, this._variableBindings);
});
var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
directiveRecords).generate();
this._directiveRecords).generate();
}
}
}

View File

@ -94,7 +94,7 @@ export class Compiler {
var directiveMetadata = Compiler.buildRenderDirective(componentBinding);
return this._renderer.createHostProtoView(directiveMetadata).then( (hostRenderPv) => {
return this._compileNestedProtoViews(null, hostRenderPv, [componentBinding], true);
return this._compileNestedProtoViews(null, null, hostRenderPv, [componentBinding], true);
}).then( (appProtoView) => {
return new ProtoViewRef(appProtoView);
});
@ -135,7 +135,7 @@ export class Compiler {
if (isPresent(template.renderer)) {
var directives = [];
pvPromise = this._renderer.createImperativeComponentProtoView(template.renderer).then( (renderPv) => {
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
});
} else {
var directives = ListWrapper.map(
@ -144,7 +144,7 @@ export class Compiler {
);
var renderTemplate = this._buildRenderTemplate(component, template, directives);
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
return this._compileNestedProtoViews(null, componentBinding, renderPv, directives, true);
});
}
@ -153,9 +153,9 @@ export class Compiler {
}
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
_compileNestedProtoViews(componentBinding, renderPv, directives, isComponentRootView) {
_compileNestedProtoViews(parentProtoView, componentBinding, renderPv, directives, isComponentRootView) {
var nestedPVPromises = [];
var protoView = this._protoViewFactory.createProtoView(componentBinding, renderPv, directives);
var protoView = this._protoViewFactory.createProtoView(parentProtoView, componentBinding, renderPv, directives);
if (isComponentRootView && isPresent(componentBinding)) {
// Populate the cache before compiling the nested components,
// so that components can reference themselves in their template.
@ -170,15 +170,12 @@ export class Compiler {
var nestedRenderProtoView = renderPv.elementBinders[binderIndex].nestedProtoView;
var elementBinderDone = (nestedPv) => {
elementBinder.nestedProtoView = nestedPv;
// Can't set the parentProtoView for components,
// as their AppProtoView might be used in multiple other components.
nestedPv.parentProtoView = isPresent(nestedComponent) ? null : protoView;
};
var nestedCall = null;
if (isPresent(nestedComponent)) {
nestedCall = this._compile(nestedComponent);
} else if (isPresent(nestedRenderProtoView)) {
nestedCall = this._compileNestedProtoViews(componentBinding, nestedRenderProtoView, directives, false);
nestedCall = this._compileNestedProtoViews(protoView, componentBinding, nestedRenderProtoView, directives, false);
}
if (PromiseWrapper.isPromise(nestedCall)) {
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));

View File

@ -3,13 +3,103 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {ChangeDetection, DirectiveIndex} from 'angular2/change_detection';
import {ChangeDetection, DirectiveIndex, BindingRecord, DirectiveRecord, ProtoChangeDetector} from 'angular2/change_detection';
import {Component} from '../annotations_impl/annotations';
import * as renderApi from 'angular2/src/render/api';
import {AppProtoView} from './view';
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
class BindingRecordsCreator {
_directiveRecordsMap;
_textNodeIndex:number;
constructor() {
this._directiveRecordsMap = MapWrapper.create();
this._textNodeIndex = 0;
}
getBindingRecords(elementBinders:List<renderApi.ElementBinder>, sortedDirectives:List<SortedDirectives>):List<BindingRecord> {
var bindings = [];
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; boundElementIndex++) {
var renderElementBinder = elementBinders[boundElementIndex];
bindings = ListWrapper.concat(bindings, this._createTextNodeRecords(renderElementBinder));
bindings = ListWrapper.concat(bindings, this._createElementPropertyRecords(boundElementIndex, renderElementBinder));
bindings = ListWrapper.concat(bindings, this._createDirectiveRecords(boundElementIndex, sortedDirectives[boundElementIndex]));
}
return bindings;
}
getDirectiveRecords(sortedDirectives:List<SortedDirectives>): List {
var directiveRecords = [];
for (var elementIndex = 0; elementIndex < sortedDirectives.length; ++elementIndex) {
var dirs = sortedDirectives[elementIndex].directives;
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
ListWrapper.push(directiveRecords, this._getDirectiveRecord(elementIndex, dirIndex, dirs[dirIndex]));
}
}
return directiveRecords;
}
_createTextNodeRecords(renderElementBinder:renderApi.ElementBinder) {
if (isBlank(renderElementBinder.textBindings)) return [];
return ListWrapper.map(renderElementBinder.textBindings, b => BindingRecord.createForTextNode(b, this._textNodeIndex++));
}
_createElementPropertyRecords(boundElementIndex:number, renderElementBinder:renderApi.ElementBinder) {
var res = [];
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
ListWrapper.push(res, BindingRecord.createForElement(astWithSource, boundElementIndex, propertyName));
});
return res;
}
_createDirectiveRecords(boundElementIndex:number, sortedDirectives:SortedDirectives) {
var res = [];
for (var i = 0; i < sortedDirectives.renderDirectives.length; i++) {
var directiveBinder = sortedDirectives.renderDirectives[i];
// directive properties
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
// TODO: these setters should eventually be created by change detection, to make
// it monomorphic!
var setter = reflector.setter(propertyName);
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, sortedDirectives.directives[i]);
var b = BindingRecord.createForDirective(astWithSource, propertyName, setter, directiveRecord);
ListWrapper.push(res, b);
});
// host properties
MapWrapper.forEach(directiveBinder.hostPropertyBindings, (astWithSource, propertyName) => {
var dirIndex = new DirectiveIndex(boundElementIndex, i);
var b = BindingRecord.createForHostProperty(dirIndex, astWithSource, propertyName);
ListWrapper.push(res, b);
});
}
return res;
}
_getDirectiveRecord(boundElementIndex:number, directiveIndex:number, binding:DirectiveBinding): DirectiveRecord {
var id = boundElementIndex * 100 + directiveIndex;
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
var changeDetection = binding.changeDetection;
MapWrapper.set(this._directiveRecordsMap, id,
new DirectiveRecord(new DirectiveIndex(boundElementIndex, directiveIndex),
binding.callOnAllChangesDone, binding.callOnChange, changeDetection));
}
return MapWrapper.get(this._directiveRecordsMap, id);
}
}
@Injectable()
export class ProtoViewFactory {
_changeDetection:ChangeDetection;
@ -18,40 +108,85 @@ export class ProtoViewFactory {
this._changeDetection = changeDetection;
}
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
directives:List<DirectiveBinding>):AppProtoView {
var protoChangeDetector;
if (isBlank(componentBinding)) {
protoChangeDetector = this._changeDetection.createProtoChangeDetector('root', null);
} else {
var componentAnnotation:Component = componentBinding.annotation;
protoChangeDetector = this._changeDetection.createProtoChangeDetector(
'dummy', componentAnnotation.changeDetection
);
}
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector);
createProtoView(parentProtoView:AppProtoView, componentBinding:DirectiveBinding,
renderProtoView: renderApi.ProtoViewDto, directives:List<DirectiveBinding>):AppProtoView {
var elementBinders = renderProtoView.elementBinders;
var sortedDirectives = ListWrapper.map(elementBinders, b => new SortedDirectives(b.directives, directives));
var variableBindings = this._createVariableBindings(renderProtoView);
var protoLocals = this._createProtoLocals(renderProtoView);
var variableNames = this._createVariableNames(parentProtoView, protoLocals);
var protoChangeDetector = this._createProtoChangeDetector(elementBinders, sortedDirectives, componentBinding, variableNames);
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings, protoLocals, variableNames);
// TODO: vsavkin refactor to pass element binders into proto view
this._createElementBinders(protoView, elementBinders, sortedDirectives)
this._bindDirectiveEvents(protoView, sortedDirectives);
for (var i=0; i<renderProtoView.elementBinders.length; i++) {
var renderElementBinder = renderProtoView.elementBinders[i];
var sortedDirectives = new SortedDirectives(renderElementBinder.directives, directives);
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
i, protoView.elementBinders, renderProtoView.elementBinders
);
var protoElementInjector = this._createProtoElementInjector(
i, parentPeiWithDistance,
sortedDirectives, renderElementBinder
);
this._createElementBinder(
protoView, renderElementBinder, protoElementInjector, sortedDirectives
);
this._createDirectiveBinders(protoView, i, sortedDirectives);
}
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
protoView.bindVariable(varName, mappedName);
});
return protoView;
}
_createProtoLocals(renderProtoView):Map {
var protoLocals = MapWrapper.create();
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
MapWrapper.set(protoLocals, mappedName, null);
});
return protoLocals;
}
_createVariableBindings(renderProtoView):Map {
var variableBindings = MapWrapper.create();
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
MapWrapper.set(variableBindings, varName, mappedName);
});
return variableBindings;
}
_createVariableNames(parentProtoView, protoLocals):List {
var variableNames = isPresent(parentProtoView) ? ListWrapper.clone(parentProtoView.variableNames) : [];
MapWrapper.forEach(protoLocals, (v, local) => {
ListWrapper.push(variableNames, local);
});
return variableNames;
}
_createProtoChangeDetector(elementBinders, sortedDirectives, componentBinding, variableNames):ProtoChangeDetector {
var bindingRecordsCreator = new BindingRecordsCreator();
var bindingRecords = bindingRecordsCreator.getBindingRecords(elementBinders, sortedDirectives);
var directiveRecords = bindingRecordsCreator.getDirectiveRecords(sortedDirectives);
var changeDetection = null;
var name = 'root';
if (isPresent(componentBinding)) {
var componentAnnotation:Component = componentBinding.annotation;
changeDetection = componentAnnotation.changeDetection;
name = 'dummy';
}
return this._changeDetection.createProtoChangeDetector(
name,
bindingRecords,
variableNames,
directiveRecords,
changeDetection
);
}
_createElementBinders(protoView, elementBinders, sortedDirectives) {
for (var i=0; i<elementBinders.length; i++) {
var renderElementBinder = elementBinders[i];
var dirs = sortedDirectives[i];
var parentPeiWithDistance = this._findParentProtoElementInjectorWithDistance(
i, protoView.elementBinders, elementBinders);
var protoElementInjector = this._createProtoElementInjector(
i, parentPeiWithDistance, dirs, renderElementBinder);
this._createElementBinder(protoView, i, renderElementBinder, protoElementInjector, dirs);
}
}
_findParentProtoElementInjectorWithDistance(binderIndex, elementBinders, renderElementBinders) {
var distance = 0;
do {
@ -95,7 +230,7 @@ export class ProtoViewFactory {
return protoElementInjector;
}
_createElementBinder(protoView, renderElementBinder, protoElementInjector, sortedDirectives) {
_createElementBinder(protoView, boundElementIndex, renderElementBinder, protoElementInjector, sortedDirectives) {
var parent = null;
if (renderElementBinder.parentIndex !== -1) {
parent = protoView.elementBinders[renderElementBinder.parentIndex];
@ -106,16 +241,7 @@ export class ProtoViewFactory {
protoElementInjector,
sortedDirectives.componentDirective
);
// text nodes
for (var i=0; i<renderElementBinder.textBindings.length; i++) {
protoView.bindTextNode(renderElementBinder.textBindings[i]);
}
// element properties
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
protoView.bindElementProperty(astWithSource, propertyName);
});
// events
protoView.bindEvent(renderElementBinder.eventBindings, -1);
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
// variables
// The view's locals needs to have a full set of variable names at construction time
// in order to prevent new variables from being set later in the lifecycle. Since we don't want
@ -127,26 +253,15 @@ export class ProtoViewFactory {
return elBinder;
}
_createDirectiveBinders(protoView, boundElementIndex, sortedDirectives) {
for (var i = 0; i < sortedDirectives.renderDirectives.length; i++) {
var directiveBinder = sortedDirectives.renderDirectives[i];
_bindDirectiveEvents(protoView, sortedDirectives:List<SortedDirectives>) {
for (var boundElementIndex = 0; boundElementIndex < sortedDirectives.length; ++boundElementIndex) {
var dirs = sortedDirectives[boundElementIndex].renderDirectives;
for (var i = 0; i < dirs.length; i++) {
var directiveBinder = dirs[i];
// directive properties
MapWrapper.forEach(directiveBinder.propertyBindings, (astWithSource, propertyName) => {
// TODO: these setters should eventually be created by change detection, to make
// it monomorphic!
var setter = reflector.setter(propertyName);
protoView.bindDirectiveProperty(i, astWithSource, propertyName, setter);
});
// host properties
MapWrapper.forEach(directiveBinder.hostPropertyBindings, (astWithSource, propertyName) => {
var directiveIndex = new DirectiveIndex(boundElementIndex, i);
protoView.bindHostElementProperty(astWithSource, propertyName, directiveIndex);
});
// directive events
protoView.bindEvent(directiveBinder.eventBindings, i);
// directive events
protoView.bindEvent(directiveBinder.eventBindings, boundElementIndex, i);
}
}
}
}

View File

@ -4,7 +4,6 @@ import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector';
import {ElementBinder} from './element_binder';
import {SetterFn} from 'angular2/src/reflection/types';
import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import * as renderApi from 'angular2/src/render/api';
@ -164,74 +163,22 @@ export class AppProtoView {
protoChangeDetector:ProtoChangeDetector;
variableBindings: Map;
protoLocals:Map;
textNodesWithBindingCount:int;
bindings:List;
parentProtoView:AppProtoView;
_variableBindings:List;
_directiveRecordsMap:Map;
_directiveRecords:List;
variableNames:List;
render:renderApi.RenderProtoViewRef;
constructor(
render:renderApi.RenderProtoViewRef,
protoChangeDetector:ProtoChangeDetector) {
protoChangeDetector:ProtoChangeDetector,
variableBindings:Map,
protoLocals:Map,
variableNames:List) {
this.render = render;
this.elementBinders = [];
this.variableBindings = MapWrapper.create();
this.protoLocals = MapWrapper.create();
this.variableBindings = variableBindings;
this.protoLocals = protoLocals;
this.variableNames = variableNames;
this.protoChangeDetector = protoChangeDetector;
this.parentProtoView = null;
this.textNodesWithBindingCount = 0;
this.bindings = [];
this._directiveRecordsMap = MapWrapper.create();
this._variableBindings = null;
this._directiveRecords = null;
}
//TODO: Tobias or Victor. Moving it into the constructor.
// this work should be done the constructor of AppProtoView once we separate
// AppProtoView and ProtoViewBuilder
getVariableBindings(): List {
if (isPresent(this._variableBindings)) {
return this._variableBindings;
}
this._variableBindings = isPresent(this.parentProtoView) ?
ListWrapper.clone(this.parentProtoView.getVariableBindings()) : [];
MapWrapper.forEach(this.protoLocals, (v, local) => {
ListWrapper.push(this._variableBindings, local);
});
return this._variableBindings;
}
//TODO: Tobias or Victor. Moving it into the constructor.
// this work should be done the constructor of ProtoView once we separate
// AppProtoView and ProtoViewBuilder
getdirectiveRecords(): List {
if (isPresent(this._directiveRecords)) {
return this._directiveRecords;
}
this._directiveRecords = [];
for (var injectorIndex = 0; injectorIndex < this.elementBinders.length; ++injectorIndex) {
var pei = this.elementBinders[injectorIndex].protoElementInjector;
if (isPresent(pei)) {
for (var directiveIndex = 0; directiveIndex < pei.numberOfDirectives; ++directiveIndex) {
ListWrapper.push(this._directiveRecords, this._getDirectiveRecord(injectorIndex, directiveIndex));
}
}
}
return this._directiveRecords;
}
bindVariable(contextName:string, templateName:string): void {
MapWrapper.set(this.variableBindings, contextName, templateName);
MapWrapper.set(this.protoLocals, templateName, null);
}
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
@ -242,32 +189,6 @@ export class AppProtoView {
return elBinder;
}
/**
* Adds a text node binding for the last created ElementBinder via bindElement
*/
bindTextNode(expression:AST):void {
var textNodeIndex = this.textNodesWithBindingCount++;
var b = BindingRecord.createForTextNode(expression, textNodeIndex);
ListWrapper.push(this.bindings, b);
}
/**
* Adds an element property binding for the last created ElementBinder via bindElement
*/
bindElementProperty(expression:AST, setterName:string):void {
var elementIndex = this.elementBinders.length-1;
var b = BindingRecord.createForElement(expression, elementIndex, setterName);
ListWrapper.push(this.bindings, b);
}
/**
* Adds an host property binding for the last created ElementBinder via bindElement
*/
bindHostElementProperty(expression:AST, setterName:string, directiveIndex:DirectiveIndex):void {
var b = BindingRecord.createForHostProperty(directiveIndex, expression, setterName);
ListWrapper.push(this.bindings, b);
}
/**
* Adds an event binding for the last created ElementBinder via bindElement.
*
@ -281,8 +202,8 @@ export class AppProtoView {
* @param {int} directiveIndex The directive index in the binder or -1 when the event is not bound
* to a directive
*/
bindEvent(eventBindings: List<renderApi.EventBinding>, directiveIndex: int = -1): void {
var elBinder = this.elementBinders[this.elementBinders.length - 1];
bindEvent(eventBindings: List<renderApi.EventBinding>, boundElementIndex:number, directiveIndex: int = -1): void {
var elBinder = this.elementBinders[boundElementIndex];
var events = elBinder.hostListeners;
if (isBlank(events)) {
events = StringMapWrapper.create();
@ -299,35 +220,4 @@ export class AppProtoView {
MapWrapper.set(event, directiveIndex, eventBinding.source);
}
}
/**
* Adds a directive property binding for the last created ElementBinder via bindElement
*/
bindDirectiveProperty(
directiveIndex:number,
expression:AST,
setterName:string,
setter:SetterFn): void {
var elementIndex = this.elementBinders.length-1;
var directiveRecord = this._getDirectiveRecord(elementIndex, directiveIndex);
var b = BindingRecord.createForDirective(expression, setterName, setter, directiveRecord);
ListWrapper.push(this.bindings, b);
}
_getDirectiveRecord(elementInjectorIndex:number, directiveIndex:number): DirectiveRecord {
var id = elementInjectorIndex * 100 + directiveIndex;
var protoElementInjector = this.elementBinders[elementInjectorIndex].protoElementInjector;
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
var binding = protoElementInjector.getDirectiveBindingAtIndex(directiveIndex);
var changeDetection = binding.changeDetection;
MapWrapper.set(this._directiveRecordsMap, id,
new DirectiveRecord(new DirectiveIndex(elementInjectorIndex, directiveIndex),
binding.callOnAllChangesDone, binding.callOnChange, changeDetection));
}
return MapWrapper.get(this._directiveRecordsMap, id);
}
}

View File

@ -28,8 +28,7 @@ export class AppViewManagerUtils {
createView(protoView:viewModule.AppProtoView, viewManager:avmModule.AppViewManager, renderer:Renderer): viewModule.AppView {
var view = new viewModule.AppView(renderer, protoView, protoView.protoLocals);
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
protoView.getVariableBindings(), protoView.getdirectiveRecords());
var changeDetector = protoView.protoChangeDetector.instantiate(view);
var binders = protoView.elementBinders;
var elementInjectors = ListWrapper.createFixedSize(binders.length);

View File

@ -16,8 +16,22 @@ import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/c
export function main() {
describe("change detection", () => {
StringMapWrapper.forEach(
{ "dynamic": (registry = null, strategy = null) => new DynamicProtoChangeDetector(registry, strategy),
"JIT": (registry = null, strategy = null) => new JitProtoChangeDetector(registry, strategy)
{ "dynamic": (bindingRecords, variableBindings = null, directiveRecords = null, registry = null, strategy = null) =>
new DynamicProtoChangeDetector(
registry,
isBlank(bindingRecords) ? [] : bindingRecords,
isBlank(variableBindings) ? [] : variableBindings,
isBlank(directiveRecords) ? [] : directiveRecords,
strategy),
"JIT": (bindingRecords, variableBindings = null, directiveRecords = null, registry = null, strategy = null) =>
new JitProtoChangeDetector(
registry,
isBlank(bindingRecords) ? [] : bindingRecords,
isBlank(variableBindings) ? [] : variableBindings,
isBlank(directiveRecords) ? [] : directiveRecords,
strategy)
}, (createProtoChangeDetector, name) => {
if (name == "JIT" && IS_DARTIUM) return;
@ -43,13 +57,13 @@ export function main() {
}
function createChangeDetector(propName:string, exp:string, context = null, locals = null, registry = null) {
var pcd = createProtoChangeDetector(registry);
var dispatcher = new TestDispatcher();
var variableBindings = convertLocalsToVariableBindings(locals);
var records = [BindingRecord.createForElement(ast(exp), 0, propName)];
var cd = pcd.instantiate(dispatcher, records, variableBindings, []);
var pcd = createProtoChangeDetector(records, variableBindings, [], registry);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(context, locals, null);
return {"changeDetector" : cd, "dispatcher" : dispatcher};
@ -61,11 +75,6 @@ export function main() {
return res["dispatcher"].log;
}
function instantiate(protoChangeDetector, dispatcher, bindings, directiveRecords = null) {
if (isBlank(directiveRecords)) directiveRecords = [];
return protoChangeDetector.instantiate(dispatcher, bindings, null, directiveRecords);
}
describe(`${name} change detection`, () => {
var dispatcher;
@ -205,10 +214,10 @@ export function main() {
});
it("should support interpolation", () => {
var pcd = createProtoChangeDetector();
var ast = parser.parseInterpolation("B{{a}}A", "location");
var pcd = createProtoChangeDetector([BindingRecord.createForElement(ast, 0, "memo")]);
var cd = instantiate(pcd, dispatcher, [BindingRecord.createForElement(ast, 0, "memo")]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(new TestData("value"), null, null);
cd.detectChanges();
@ -267,10 +276,9 @@ export function main() {
});
it("should happen directly, without invoking the dispatcher", () => {
var pcd = createProtoChangeDetector();
var pcd = createProtoChangeDetector([updateA("42", dirRecord1)], [], [dirRecord1]);
var cd = instantiate(pcd, dispatcher, [updateA("42", dirRecord1)],
[dirRecord1]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive1]));
@ -282,13 +290,13 @@ export function main() {
describe("onChange", () => {
it("should notify the directive when a group of records changes", () => {
var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [
var pcd = createProtoChangeDetector([
updateA("1", dirRecord1),
updateB("2", dirRecord1),
updateA("3", dirRecord2)
], [dirRecord1, dirRecord2]);
], [], [dirRecord1, dirRecord2]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive1, directive2]));
@ -299,11 +307,11 @@ export function main() {
});
it("should not call onChange when callOnChange is false", () => {
var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [
var pcd = createProtoChangeDetector([
updateA("1", dirRecordNoCallbacks)
], [dirRecordNoCallbacks]);
], [], [dirRecordNoCallbacks]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive1]));
@ -315,9 +323,9 @@ export function main() {
describe("onAllChangesDone", () => {
it("should be called after processing all the children", () => {
var pcd = createProtoChangeDetector();
var pcd = createProtoChangeDetector([], [], [dirRecord1, dirRecord2]);
var cd = instantiate(pcd, dispatcher, [], [dirRecord1, dirRecord2]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive1, directive2]));
cd.detectChanges();
@ -328,11 +336,11 @@ export function main() {
it("should not be called when onAllChangesDone is false", () => {
var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [
var pcd = createProtoChangeDetector([
updateA("1", dirRecordNoCallbacks)
], [dirRecordNoCallbacks]);
], [], [dirRecordNoCallbacks]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive1]));
@ -342,8 +350,8 @@ export function main() {
});
it("should be called in reverse order so the child is always notified before the parent", () => {
var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [], [dirRecord1, dirRecord2]);
var pcd = createProtoChangeDetector([], [], [dirRecord1, dirRecord2]);
var cd = pcd.instantiate(dispatcher);
var onChangesDoneCalls = [];
var td1;
@ -358,13 +366,12 @@ export function main() {
});
it("should be called before processing shadow dom children", () => {
var pcd = createProtoChangeDetector();
var shadowDomChildPCD = createProtoChangeDetector();
var pcd = createProtoChangeDetector([], null, [dirRecord1]);
var shadowDomChildPCD = createProtoChangeDetector([updateA("1", dirRecord1)], null, [dirRecord1]);
var parent = pcd.instantiate(dispatcher, [], null, [dirRecord1]);
var parent = pcd.instantiate(dispatcher);
var child = shadowDomChildPCD.instantiate(dispatcher,
[updateA("1", dirRecord1)], null, [dirRecord1]);
var child = shadowDomChildPCD.instantiate(dispatcher);
parent.addShadowDomChild(child);
var directiveInShadowDom = new TestDirective();
@ -389,8 +396,8 @@ export function main() {
var directive = new TestDirective();
directive.a = "aaa";
var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [BindingRecord.createForHostProperty(index, ast("a"), "prop")], [dirRecord]);
var pcd = createProtoChangeDetector([BindingRecord.createForHostProperty(index, ast("a"), "prop")], null, [dirRecord]);
var cd = pcd.instantiate(dispatcher);
cd.hydrate(null, null, dirs([directive]));
cd.detectChanges();
@ -401,12 +408,12 @@ export function main() {
describe("enforce no new changes", () => {
it("should throw when a record gets changed after it has been checked", () => {
var pcd = createProtoChangeDetector();
var dispatcher = new TestDispatcher();
var cd = instantiate(pcd, dispatcher, [
var pcd = createProtoChangeDetector([
BindingRecord.createForElement(ast("a"), 0, "a")
]);
var dispatcher = new TestDispatcher();
var cd = pcd.instantiate(dispatcher);
cd.hydrate(new TestData('value'), null, null);
expect(() => {
@ -487,11 +494,8 @@ export function main() {
var parent, child;
beforeEach(() => {
var protoParent = createProtoChangeDetector();
parent = instantiate(protoParent, null, []);
var protoChild = createProtoChangeDetector();
child = instantiate(protoChild, null, []);
parent = createProtoChangeDetector([]).instantiate(null);
child = createProtoChangeDetector([]).instantiate(null);
});
it("should add light dom children", () => {
@ -526,8 +530,8 @@ export function main() {
describe("mode", () => {
it("should set the mode to CHECK_ALWAYS when the default change detection is used", () => {
var proto = createProtoChangeDetector(null, DEFAULT);
var cd = proto.instantiate(null, [], [], []);
var proto = createProtoChangeDetector([], [], [], null, DEFAULT);
var cd = proto.instantiate(null);
expect(cd.mode).toEqual(null);
@ -537,8 +541,8 @@ export function main() {
});
it("should set the mode to CHECK_ONCE when the push change detection is used", () => {
var proto = createProtoChangeDetector(null, ON_PUSH);
var cd = proto.instantiate(null, [], [], []);
var proto = createProtoChangeDetector([], [], [], null, ON_PUSH);
var cd = proto.instantiate(null);
cd.hydrate(null, null, null);
expect(cd.mode).toEqual(CHECK_ONCE);
@ -567,7 +571,7 @@ export function main() {
});
it("should change CHECK_ONCE to CHECKED", () => {
var cd = instantiate(createProtoChangeDetector(), null, []);
var cd = createProtoChangeDetector([]).instantiate(null);
cd.mode = CHECK_ONCE;
cd.detectChanges();
@ -576,7 +580,7 @@ export function main() {
});
it("should not change the CHECK_ALWAYS", () => {
var cd = instantiate(createProtoChangeDetector(), null, []);
var cd = createProtoChangeDetector([]).instantiate(null);
cd.mode = CHECK_ALWAYS;
cd.detectChanges();
@ -591,8 +595,8 @@ export function main() {
var directives;
beforeEach(() => {
var proto = createProtoChangeDetector(null, ON_PUSH);
checkedDetector = instantiate(proto, null, [], []);
var proto = createProtoChangeDetector([], [], [], null, ON_PUSH);
checkedDetector = proto.instantiate(null);
checkedDetector.hydrate(null, null, null);
checkedDetector.mode = CHECKED;
@ -608,9 +612,9 @@ export function main() {
});
it("should set the mode to CHECK_ONCE when a binding is updated", () => {
var proto = createProtoChangeDetector(null);
var proto = createProtoChangeDetector([updateDirWithOnPushRecord], [], [dirRecordWithOnPush]);
var cd = instantiate(proto, null, [updateDirWithOnPushRecord], [dirRecordWithOnPush]);
var cd = proto.instantiate(null);
cd.hydrate(null, null, directives);
expect(checkedDetector.mode).toEqual(CHECKED);
@ -625,7 +629,7 @@ export function main() {
describe("markPathToRootAsCheckOnce", () => {
function changeDetector(mode, parent) {
var cd = instantiate(createProtoChangeDetector(), null, []);
var cd = createProtoChangeDetector([]).instantiate(null);
cd.mode = mode;
if (isPresent(parent)) parent.addChild(cd);
return cd;

View File

@ -283,9 +283,6 @@ export function main() {
compiler.compile(MainComponent).then( (protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
// parentProtoView of nested components has to be null as components can
// be used by multiple other components.
expect(nestedProtoView.parentProtoView).toBe(null);
async.done();
});
}));
@ -316,11 +313,7 @@ export function main() {
compiler.compile(MainComponent).then( (protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(viewportProtoView);
expect(viewportProtoView.parentProtoView).toBe(mainProtoView);
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
// parentProtoView of nested components has to be null as components can
// be used by multiple other components.
expect(nestedProtoView.parentProtoView).toBe(null);
async.done();
});
@ -432,7 +425,7 @@ function createDirectiveBinding(reader, type) {
}
function createProtoView(elementBinders = null) {
var pv = new AppProtoView(null, null);
var pv = new AppProtoView(null, null, null, null, null);
if (isBlank(elementBinders)) {
elementBinders = [];
}
@ -575,7 +568,7 @@ class FakeProtoViewFactory extends ProtoViewFactory {
this._results = results;
}
createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
createProtoView(parentProtoView, componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
directives:List<DirectiveBinding>):AppProtoView {
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
return ListWrapper.removeAt(this._results, 0);

View File

@ -453,7 +453,7 @@ export function main() {
});
it("should instantiate directives that depend on pre built objects", function () {
var protoView = new AppProtoView(null, null);
var protoView = new AppProtoView(null, null, null, null, null);
var inj = injector([NeedsProtoViewRef], null, null, new PreBuiltObjects(null, null, protoView));
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
@ -700,7 +700,7 @@ export function main() {
});
it("should inject ProtoViewRef", function () {
var protoView = new AppProtoView(null, null);
var protoView = new AppProtoView(null, null, null, null, null);
var inj = injector([NeedsProtoViewRef], null, null, new PreBuiltObjects(null, null, protoView));
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));

View File

@ -14,6 +14,7 @@ import {
xit
} from 'angular2/test_lib';
import {TestBed} from 'angular2/src/test_lib/test_bed';
import {DOM} from 'angular2/src/dom/dom_adapter';
@ -46,6 +47,7 @@ export function main() {
ctx = new MyComp();
});
describe('react to record changes', function() {
it('should consume text node changes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideView(MyComp, new View({template: '<div>{{ctxProp}}</div>'}));

View File

@ -37,7 +37,7 @@ export function main() {
}
function createProtoView() {
return new AppProtoView(null, null);
return new AppProtoView(null, null, null, null, null);
}
function createView() {

View File

@ -80,7 +80,7 @@ export function main() {
staticChildComponentCount++;
}
}
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null);
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null, null, null);
res.elementBinders = binders;
return res;
}

View File

@ -60,7 +60,7 @@ export function main() {
if (isBlank(binders)) {
binders = [];
}
var res = new AppProtoView(null, null);
var res = new AppProtoView(null, null, null, null, null);
res.elementBinders = binders;
return res;
}

View File

@ -26,7 +26,7 @@ export function main() {
}
function createProtoView() {
return new AppProtoView(null, null);
return new AppProtoView(null, null, null, null, null);
}
function createView(pv) {

View File

@ -187,10 +187,8 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations, objec
var dispatcher = new DummyDispatcher();
var parser = new Parser(new Lexer());
var parentProto = changeDetection.createProtoChangeDetector('parent');
var parentCd = parentProto.instantiate(dispatcher, [], [], []);
var proto = changeDetection.createProtoChangeDetector("proto");
var parentProto = changeDetection.createProtoChangeDetector('parent', [], [], []);
var parentCd = parentProto.instantiate(dispatcher);
var directiveRecord = new DirectiveRecord(new DirectiveIndex(0, 0), false, false, DEFAULT);
var bindings = [
@ -206,9 +204,11 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations, objec
BindingRecord.createForDirective(parser.parseBinding('field9', null), "field9", reflector.setter("field9"), directiveRecord)
];
var proto = changeDetection.createProtoChangeDetector("proto", bindings, [], [directiveRecord]);
var targetObj = new Obj();
for (var i = 0; i < iterations; ++i) {
var cd = proto.instantiate(dispatcher, bindings, [], [directiveRecord]);
var cd = proto.instantiate(dispatcher);
cd.hydrate(object, null, new FakeDirectives(targetObj));
parentCd.addChild(cd);
}