refactor(ElementBinder): Store componentDirective and templateDirective as well.

This commit is contained in:
Tobias Bosch 2014-11-13 15:15:55 -08:00
parent 352b6406ad
commit 5e0ff2cbb7
6 changed files with 54 additions and 20 deletions

View File

@ -17,7 +17,7 @@ export class Component extends Directive {
template:TemplateConfig, template:TemplateConfig,
lightDomServices:DomServicesFunction, lightDomServices:DomServicesFunction,
shadowDomServices:DomServicesFunction, shadowDomServices:DomServicesFunction,
componentServices:ComponentServicesFunction, componentServices:Array,
implementsTypes:Array<Type> implementsTypes:Array<Type>
}={}) }={})
{ {

View File

@ -1,16 +1,20 @@
import {ProtoElementInjector} from './element_injector'; import {ProtoElementInjector} from './element_injector';
import {FIELD} from 'facade/lang'; import {FIELD} from 'facade/lang';
import {AnnotatedType} from './annotated_type';
// Comment out as dartanalyzer does not look into @FIELD // Comment out as dartanalyzer does not look into @FIELD
// import {List} from 'facade/collection'; // import {List} from 'facade/collection';
// import {ProtoView} from './view'; // import {ProtoView} from './view';
export class ElementBinder { export class ElementBinder {
@FIELD('final protoElementInjector:ProtoElementInjector') @FIELD('final protoElementInjector:ProtoElementInjector')
@FIELD('final componentDirective:AnnotatedType')
@FIELD('final templateDirective:AnnotatedType')
@FIELD('final textNodeIndices:List<int>') @FIELD('final textNodeIndices:List<int>')
@FIELD('hasElementPropertyBindings:bool') @FIELD('hasElementPropertyBindings:bool')
@FIELD('nestedProtoView:ProtoView') constructor(protoElementInjector: ProtoElementInjector, componentDirective:AnnotatedType, templateDirective:AnnotatedType) {
constructor(protoElementInjector: ProtoElementInjector) {
this.protoElementInjector = protoElementInjector; this.protoElementInjector = protoElementInjector;
this.componentDirective = componentDirective;
this.templateDirective = templateDirective;
// updated later when text nodes are bound // updated later when text nodes are bound
this.textNodeIndices = []; this.textNodeIndices = [];
// updated later when element properties are bound // updated later when element properties are bound

View File

@ -52,7 +52,8 @@ export class ElementBinderBuilder extends CompileStep {
var elementBinder; var elementBinder;
if (current.hasBindings) { if (current.hasBindings) {
var protoView = current.inheritedProtoView; var protoView = current.inheritedProtoView;
elementBinder = protoView.bindElement(current.inheritedProtoElementInjector); elementBinder = protoView.bindElement(current.inheritedProtoElementInjector,
current.componentDirective, current.templateDirective);
if (isPresent(current.textNodeBindings)) { if (isPresent(current.textNodeBindings)) {
this._bindTextNodes(protoView, current.textNodeBindings); this._bindTextNodes(protoView, current.textNodeBindings);

View File

@ -3,8 +3,10 @@ import {ListWrapper} from 'facade/collection';
import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher} from 'change_detection/watch_group'; import {ProtoWatchGroup, WatchGroup, WatchGroupDispatcher} from 'change_detection/watch_group';
import {Record} from 'change_detection/record'; import {Record} from 'change_detection/record';
import {AST} from 'change_detection/parser/ast'; import {AST} from 'change_detection/parser/ast';
import {ProtoElementInjector, ElementInjector} from './element_injector'; import {ProtoElementInjector, ElementInjector} from './element_injector';
import {ElementBinder} from './element_binder'; import {ElementBinder} from './element_binder';
import {AnnotatedType} from './annotated_type';
import {SetterFn} from 'change_detection/parser/closure_map'; import {SetterFn} from 'change_detection/parser/closure_map';
import {FIELD, IMPLEMENTS, int, isPresent, isBlank} from 'facade/lang'; import {FIELD, IMPLEMENTS, int, isPresent, isBlank} from 'facade/lang';
import {List} from 'facade/collection'; import {List} from 'facade/collection';
@ -99,8 +101,9 @@ export class ProtoView {
bindElements, this.protoWatchGroup, context); bindElements, this.protoWatchGroup, context);
} }
bindElement(protoElementInjector:ProtoElementInjector):ElementBinder { bindElement(protoElementInjector:ProtoElementInjector,
var elBinder = new ElementBinder(protoElementInjector); componentDirective:AnnotatedType = null, templateDirective:AnnotatedType = null):ElementBinder {
var elBinder = new ElementBinder(protoElementInjector, componentDirective, templateDirective);
ListWrapper.push(this.elementBinders, elBinder); ListWrapper.push(this.elementBinders, elBinder);
return elBinder; return elBinder;
} }

View File

@ -100,7 +100,7 @@ export function main() {
}); });
it('should store the current protoElementInjector', () => { it('should store the current protoElementInjector', () => {
var directives = [SomeSimpleDirective]; var directives = [SomeDecoratorDirective];
var protoElementInjector = new ProtoElementInjector(null, 0, directives); var protoElementInjector = new ProtoElementInjector(null, 0, directives);
var pipeline = createPipeline({protoElementInjector: protoElementInjector, directives: directives}); var pipeline = createPipeline({protoElementInjector: protoElementInjector, directives: directives});
@ -110,6 +110,24 @@ export function main() {
expect(pv.elementBinders[0].protoElementInjector).toBe(protoElementInjector); expect(pv.elementBinders[0].protoElementInjector).toBe(protoElementInjector);
}); });
it('should store the component directive', () => {
var directives = [SomeComponentDirective];
var pipeline = createPipeline({protoElementInjector: null, directives: directives});
var results = pipeline.process(createElement('<div viewroot directives></div>'));
var pv = results[0].inheritedProtoView;
expect(pv.elementBinders[0].componentDirective.type).toBe(SomeComponentDirective);
});
it('should store the template directive', () => {
var directives = [SomeTemplateDirective];
var pipeline = createPipeline({protoElementInjector: null, directives: directives});
var results = pipeline.process(createElement('<div viewroot directives></div>'));
var pv = results[0].inheritedProtoView;
expect(pv.elementBinders[0].templateDirective.type).toBe(SomeTemplateDirective);
});
it('should bind text nodes', () => { it('should bind text nodes', () => {
var textNodeBindings = MapWrapper.create(); var textNodeBindings = MapWrapper.create();
MapWrapper.set(textNodeBindings, 0, 'prop1'); MapWrapper.set(textNodeBindings, 0, 'prop1');
@ -155,7 +173,7 @@ export function main() {
'boundprop2': 'prop2', 'boundprop2': 'prop2',
'boundprop3': 'prop3' 'boundprop3': 'prop3'
}); });
var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; var directives = [SomeDecoratorDirectiveWithBinding, SomeTemplateDirectiveWithBinding, SomeComponentDirectiveWithBinding];
var protoElementInjector = new ProtoElementInjector(null, 0, directives); var protoElementInjector = new ProtoElementInjector(null, 0, directives);
var pipeline = createPipeline({ var pipeline = createPipeline({
propertyBindings: propertyBindings, propertyBindings: propertyBindings,
@ -171,16 +189,16 @@ export function main() {
evalContext.prop3 = 'c'; evalContext.prop3 = 'c';
changeDetector.detectChanges(); changeDetector.detectChanges();
expect(view.elementInjectors[0].get(SomeDecoratorDirective).decorProp).toBe('a'); expect(view.elementInjectors[0].get(SomeDecoratorDirectiveWithBinding).decorProp).toBe('a');
expect(view.elementInjectors[0].get(SomeTemplateDirective).templProp).toBe('b'); expect(view.elementInjectors[0].get(SomeTemplateDirectiveWithBinding).templProp).toBe('b');
expect(view.elementInjectors[0].get(SomeComponentDirective).compProp).toBe('c'); expect(view.elementInjectors[0].get(SomeComponentDirectiveWithBinding).compProp).toBe('c');
}); });
it('should bind directive properties for sibling elements', () => { it('should bind directive properties for sibling elements', () => {
var propertyBindings = MapWrapper.createFromStringMap({ var propertyBindings = MapWrapper.createFromStringMap({
'boundprop1': 'prop1' 'boundprop1': 'prop1'
}); });
var directives = [SomeDecoratorDirective]; var directives = [SomeDecoratorDirectiveWithBinding];
var protoElementInjector = new ProtoElementInjector(null, 0, directives); var protoElementInjector = new ProtoElementInjector(null, 0, directives);
var pipeline = createPipeline({ var pipeline = createPipeline({
propertyBindings: propertyBindings, propertyBindings: propertyBindings,
@ -196,16 +214,16 @@ export function main() {
evalContext.prop1 = 'a'; evalContext.prop1 = 'a';
changeDetector.detectChanges(); changeDetector.detectChanges();
expect(view.elementInjectors[1].get(SomeDecoratorDirective).decorProp).toBe('a'); expect(view.elementInjectors[1].get(SomeDecoratorDirectiveWithBinding).decorProp).toBe('a');
}); });
describe('errors', () => { describe('errors', () => {
it('should throw if there is no element property bindings for a directive property binding', () => { it('should throw if there is no element property bindings for a directive property binding', () => {
var pipeline = createPipeline({propertyBindings: MapWrapper.create(), directives: [SomeDecoratorDirective]}); var pipeline = createPipeline({propertyBindings: MapWrapper.create(), directives: [SomeDecoratorDirectiveWithBinding]});
expect( () => { expect( () => {
pipeline.process(createElement('<div viewroot prop-binding directives>')); pipeline.process(createElement('<div viewroot prop-binding directives>'));
}).toThrowError('No element binding found for property boundprop1 which is required by directive SomeDecoratorDirective'); }).toThrowError('No element binding found for property boundprop1 which is required by directive SomeDecoratorDirectiveWithBinding');
}); });
}); });
@ -215,31 +233,39 @@ export function main() {
} }
@Decorator() @Decorator()
class SomeSimpleDirective { class SomeDecoratorDirective {
} }
@Decorator({ @Decorator({
bind: {'boundprop1': 'decorProp'} bind: {'boundprop1': 'decorProp'}
}) })
class SomeDecoratorDirective { class SomeDecoratorDirectiveWithBinding {
constructor() { constructor() {
this.decorProp = null; this.decorProp = null;
} }
} }
@Template()
class SomeTemplateDirective {
}
@Template({ @Template({
bind: {'boundprop2': 'templProp'} bind: {'boundprop2': 'templProp'}
}) })
class SomeTemplateDirective { class SomeTemplateDirectiveWithBinding {
constructor() { constructor() {
this.templProp = null; this.templProp = null;
} }
} }
@Component()
class SomeComponentDirective {
}
@Component({ @Component({
bind: {'boundprop3': 'compProp'} bind: {'boundprop3': 'compProp'}
}) })
class SomeComponentDirective { class SomeComponentDirectiveWithBinding {
constructor() { constructor() {
this.compProp = null; this.compProp = null;
} }

View File

@ -15,7 +15,7 @@ export function main() {
if (isPresent(current.element.getAttribute('viewroot'))) { if (isPresent(current.element.getAttribute('viewroot'))) {
current.isViewRoot = true; current.isViewRoot = true;
} }
current.inheritedElementBinder = new ElementBinder(null); current.inheritedElementBinder = new ElementBinder(null, null, null);
}), new ProtoViewBuilder()]); }), new ProtoViewBuilder()]);
} }