From 7308a3acc746d7de5e27710cf7cef67b9498b27a Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 12 Nov 2014 11:17:46 -0800 Subject: [PATCH] refactor(ElementInjector): support components - Allow to access containing component directive instance from the shadow DOM. - Allow to access app services of the app level injector of the component when the component is instantiated. --- .../element_injector/instantiate_benchmark.js | 4 +- .../instantiate_benchmark_codegen.js | 6 +- .../instantiate_directive_benchmark.js | 4 +- modules/core/src/compiler/element_injector.js | 114 ++++++++++++------ .../proto_element_injector_builder.js | 28 +++-- modules/core/src/compiler/view.js | 4 +- .../test/compiler/element_injector_spec.js | 87 ++++++++++--- .../proto_element_injector_builder_spec.js | 55 +++++---- 8 files changed, 207 insertions(+), 95 deletions(-) diff --git a/modules/benchmarks/src/element_injector/instantiate_benchmark.js b/modules/benchmarks/src/element_injector/instantiate_benchmark.js index ef27f15180..ea5f212262 100644 --- a/modules/benchmarks/src/element_injector/instantiate_benchmark.js +++ b/modules/benchmarks/src/element_injector/instantiate_benchmark.js @@ -10,8 +10,8 @@ export function run () { var bindings = [A, B, C]; var proto = new ProtoElementInjector(null, 0, bindings); for (var i = 0; i < ITERATIONS; ++i) { - var ei = proto.instantiate(null,null); - ei.instantiateDirectives(appInjector); + var ei = proto.instantiate(null,null,null); + ei.instantiateDirectives(appInjector, null); } } diff --git a/modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js b/modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js index 065ddb8f59..4f50a2af8b 100644 --- a/modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js +++ b/modules/benchmarks/src/element_injector/instantiate_benchmark_codegen.js @@ -6,7 +6,7 @@ var count = 0; export function run () { var appInjector = new Injector([]); - + var bindings = [ new Binding(Key.get(A), () => new A(), [], false), new Binding(Key.get(B), () => new B(), [], false), @@ -18,8 +18,8 @@ export function run () { var proto = new ProtoElementInjector(null, 0, bindings); for (var i = 0; i < ITERATIONS; ++i) { - var ei = proto.instantiate(null,null); - ei.instantiateDirectives(appInjector); + var ei = proto.instantiate(null,null,null); + ei.instantiateDirectives(appInjector, null); } } diff --git a/modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js b/modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js index 4a0e814df1..99057ba2a5 100644 --- a/modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js +++ b/modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js @@ -9,11 +9,11 @@ export function run () { var bindings = [A, B, C]; var proto = new ProtoElementInjector(null, 0, bindings); - var ei = proto.instantiate(null,null); + var ei = proto.instantiate(null,null,null); for (var i = 0; i < ITERATIONS; ++i) { ei.clearDirectives(); - ei.instantiateDirectives(appInjector); + ei.instantiateDirectives(appInjector, null); } } diff --git a/modules/core/src/compiler/element_injector.js b/modules/core/src/compiler/element_injector.js index 5448a83394..d10f28275b 100644 --- a/modules/core/src/compiler/element_injector.js +++ b/modules/core/src/compiler/element_injector.js @@ -1,4 +1,4 @@ -import {FIELD, isPresent, isBlank, Type, int} from 'facade/lang'; +import {FIELD, isPresent, isBlank, Type, int, BaseException} from 'facade/lang'; import {Math} from 'facade/math'; import {List, ListWrapper} from 'facade/collection'; import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError, CyclicDependencyError} from 'di/di'; @@ -106,6 +106,7 @@ export class ProtoElementInjector { @FIELD('_binding7:Binding') @FIELD('_binding8:Binding') @FIELD('_binding9:Binding') + @FIELD('_binding0IsComponent:int') @FIELD('_key0:int') @FIELD('_key1:int') @FIELD('_key2:int') @@ -118,10 +119,11 @@ export class ProtoElementInjector { @FIELD('_key9:int') @FIELD('final parent:ProtoElementInjector') @FIELD('final index:int') - constructor(parent:ProtoElementInjector, index:int, bindings:List) { + constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) { this.parent = parent; this.index = index; + this._binding0IsComponent = firstBindingIsComponent; this._binding0 = null; this._keyId0 = null; this._binding1 = null; this._keyId1 = null; this._binding2 = null; this._keyId2 = null; @@ -150,8 +152,8 @@ export class ProtoElementInjector { } } - instantiate(parent:ElementInjector, view):ElementInjector { - return new ElementInjector(this, parent, view); + instantiate(parent:ElementInjector, host:ElementInjector, view):ElementInjector { + return new ElementInjector(this, parent, host, view); } _createBinding(bindingOrType) { @@ -212,7 +214,9 @@ export class ElementInjector extends TreeNode { */ @FIELD('_proto:ProtoElementInjector') - @FIELD('_appInjector:Injector') + @FIELD('_lightDomAppInjector:Injector') + @FIELD('_shadowDomAppInjector:Injector') + @FIELD('_host:ElementInjector') @FIELD('_obj0:Object') @FIELD('_obj1:Object') @FIELD('_obj2:Object') @@ -224,13 +228,24 @@ export class ElementInjector extends TreeNode { @FIELD('_obj8:Object') @FIELD('_obj9:Object') @FIELD('_view:View') - constructor(proto:ProtoElementInjector, parent:ElementInjector, view) { + constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector, view) { super(parent); + if (isPresent(parent) && isPresent(host)) { + throw new BaseException('Only either parent or host is allowed'); + } + this._host = null; // needed to satisfy Dart + if (isPresent(parent)) { + this._host = parent._host; + } else { + this._host = host; + } + this._proto = proto; this._view = view; //we cannot call clearDirectives because fields won't be detected - this._appInjector = null; + this._lightDomAppInjector = null; + this._shadowDomAppInjector = null; this._obj0 = null; this._obj1 = null; this._obj2 = null; @@ -245,7 +260,9 @@ export class ElementInjector extends TreeNode { } clearDirectives() { - this._appInjector = null; + this._lightDomAppInjector = null; + this._shadowDomAppInjector = null; + this._obj0 = null; this._obj1 = null; this._obj2 = null; @@ -259,24 +276,42 @@ export class ElementInjector extends TreeNode { this._constructionCounter = 0; } - instantiateDirectives(appInjector:Injector) { - this._appInjector = appInjector; - + instantiateDirectives(lightDomAppInjector:Injector, shadowDomAppInjector:Injector) { var p = this._proto; + if (this._proto._binding0IsComponent && isBlank(shadowDomAppInjector)) { + throw new BaseException('A shadowDomAppInjector is required as this ElementInjector contains a component'); + } else if (!this._proto._binding0IsComponent && isPresent(shadowDomAppInjector)) { + throw new BaseException('No shadowDomAppInjector allowed as there is not component stored in this ElementInjector'); + } + this._lightDomAppInjector = lightDomAppInjector; + this._shadowDomAppInjector = shadowDomAppInjector; + if (isPresent(p._keyId0)) this._getDirectiveByKeyId(p._keyId0); if (isPresent(p._keyId1)) this._getDirectiveByKeyId(p._keyId1); - if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2);; - if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3);; - if (isPresent(p._keyId4)) this._getDirectiveByKeyId(p._keyId4);; - if (isPresent(p._keyId5)) this._getDirectiveByKeyId(p._keyId5);; - if (isPresent(p._keyId6)) this._getDirectiveByKeyId(p._keyId6);; - if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);; - if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);; - if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);; + if (isPresent(p._keyId2)) this._getDirectiveByKeyId(p._keyId2); + if (isPresent(p._keyId3)) this._getDirectiveByKeyId(p._keyId3); + if (isPresent(p._keyId4)) this._getDirectiveByKeyId(p._keyId4); + if (isPresent(p._keyId5)) this._getDirectiveByKeyId(p._keyId5); + if (isPresent(p._keyId6)) this._getDirectiveByKeyId(p._keyId6); + if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7); + if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8); + if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9); } get(token) { - return this._getByKey(Key.get(token), 0); + return this._getByKey(Key.get(token), 0, null); + } + + getComponent() { + if (this._proto._binding0IsComponent) { + return this._obj0; + } else { + throw new BaseException('There is not component stored in this ElementInjector'); + } + } + + _isComponentKey(key:Key) { + return this._proto._binding0IsComponent && key.id === this._proto._keyId0; } _new(binding:Binding) { @@ -290,16 +325,16 @@ export class ElementInjector extends TreeNode { var d0,d1,d2,d3,d4,d5,d6,d7,d8,d9; try { - d0 = length > 0 ? this._getByDependency(deps[0]) : null; - d1 = length > 1 ? this._getByDependency(deps[1]) : null; - d2 = length > 2 ? this._getByDependency(deps[2]) : null; - d3 = length > 3 ? this._getByDependency(deps[3]) : null; - d4 = length > 4 ? this._getByDependency(deps[4]) : null; - d5 = length > 5 ? this._getByDependency(deps[5]) : null; - d6 = length > 6 ? this._getByDependency(deps[6]) : null; - d7 = length > 7 ? this._getByDependency(deps[7]) : null; - d8 = length > 8 ? this._getByDependency(deps[8]) : null; - d9 = length > 9 ? this._getByDependency(deps[9]) : null; + d0 = length > 0 ? this._getByDependency(deps[0], binding.key) : null; + d1 = length > 1 ? this._getByDependency(deps[1], binding.key) : null; + d2 = length > 2 ? this._getByDependency(deps[2], binding.key) : null; + d3 = length > 3 ? this._getByDependency(deps[3], binding.key) : null; + d4 = length > 4 ? this._getByDependency(deps[4], binding.key) : null; + d5 = length > 5 ? this._getByDependency(deps[5], binding.key) : null; + d6 = length > 6 ? this._getByDependency(deps[6], binding.key) : null; + d7 = length > 7 ? this._getByDependency(deps[7], binding.key) : null; + d8 = length > 8 ? this._getByDependency(deps[8], binding.key) : null; + d9 = length > 9 ? this._getByDependency(deps[9], binding.key) : null; } catch(e) { if (e instanceof ProviderError) e.addKey(binding.key); throw e; @@ -324,8 +359,8 @@ export class ElementInjector extends TreeNode { return obj; } - _getByDependency(dep:DirectiveDependency) { - return this._getByKey(dep.key, dep.depth); + _getByDependency(dep:DirectiveDependency, requestor:Key) { + return this._getByKey(dep.key, dep.depth, requestor); } /* @@ -340,7 +375,7 @@ export class ElementInjector extends TreeNode { * * Write benchmarks before doing this optimization. */ - _getByKey(key:Key, depth:int) { + _getByKey(key:Key, depth:int, requestor:Key) { var ei = this; while (ei != null && depth >= 0) { var specObj = ei._getSpecialObjectByKeyId(key.id); @@ -352,7 +387,17 @@ export class ElementInjector extends TreeNode { ei = ei._parent; depth -= 1; } - return this._appInjector.get(key); + if (isPresent(this._host) && this._host._isComponentKey(key)) { + return this._host.getComponent(); + } else { + var appInjector; + if (isPresent(requestor) && this._isComponentKey(requestor)) { + appInjector = this._shadowDomAppInjector; + } else { + appInjector = this._lightDomAppInjector; + } + return appInjector.get(key); + } } _getSpecialObjectByKeyId(keyId:int) { @@ -364,6 +409,7 @@ export class ElementInjector extends TreeNode { _getDirectiveByKeyId(keyId:int) { var p = this._proto; + if (p._keyId0 === keyId) {if (isBlank(this._obj0)){this._obj0 = this._new(p._binding0);} return this._obj0;} if (p._keyId1 === keyId) {if (isBlank(this._obj1)){this._obj1 = this._new(p._binding1);} return this._obj1;} if (p._keyId2 === keyId) {if (isBlank(this._obj2)){this._obj2 = this._new(p._binding2);} return this._obj2;} diff --git a/modules/core/src/compiler/pipeline/proto_element_injector_builder.js b/modules/core/src/compiler/pipeline/proto_element_injector_builder.js index 82fcb8c2a4..5f8e7c6753 100644 --- a/modules/core/src/compiler/pipeline/proto_element_injector_builder.js +++ b/modules/core/src/compiler/pipeline/proto_element_injector_builder.js @@ -1,7 +1,8 @@ -import {isPresent,} from 'facade/lang'; +import {isPresent, isBlank} from 'facade/lang'; import {ListWrapper} from 'facade/collection'; -import {ProtoElementInjector} from '../element_injector'; +import {Key} from 'di/di'; +import {ProtoElementInjector, ComponentKeyMetaData} from '../element_injector'; import {CompileStep} from './compile_step'; import {CompileElement} from './compile_element'; @@ -23,22 +24,23 @@ import {CompileControl} from './compile_control'; */ export class ProtoElementInjectorBuilder extends CompileStep { // public so that we can overwrite it in tests - internalCreateProtoElementInjector(parent, index, directives) { - return new ProtoElementInjector(parent, index, directives); + internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent) { + return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent); } process(parent:CompileElement, current:CompileElement, control:CompileControl) { var inheritedProtoElementInjector = null; var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current); - var injectorBindings = this._collectDirectiveTypes(current); + var injectorBindings = this._collectDirectiveBindings(current); // TODO: add lightDomServices as well, // but after the directives as we rely on that order // in the element_binder_builder. if (injectorBindings.length > 0) { var protoView = current.inheritedProtoView; + var hasComponent = isPresent(current.componentDirective); inheritedProtoElementInjector = this.internalCreateProtoElementInjector( - parentProtoElementInjector, protoView.elementBinders.length, injectorBindings + parentProtoElementInjector, protoView.elementBinders.length, injectorBindings, hasComponent ); } else { inheritedProtoElementInjector = parentProtoElementInjector; @@ -56,19 +58,19 @@ export class ProtoElementInjectorBuilder extends CompileStep { return parentProtoElementInjector; } - _collectDirectiveTypes(pipelineElement) { + _collectDirectiveBindings(pipelineElement) { var directiveTypes = []; + if (isPresent(pipelineElement.componentDirective)) { + ListWrapper.push(directiveTypes, pipelineElement.componentDirective.type); + } + if (isPresent(pipelineElement.templateDirective)) { + ListWrapper.push(directiveTypes, pipelineElement.templateDirective.type); + } if (isPresent(pipelineElement.decoratorDirectives)) { for (var i=0; i, appInjector:Injector) { for (var i = 0; i < injectors.length; ++i) { - if (injectors[i] != null) injectors[i].instantiateDirectives(appInjector); + if (injectors[i] != null) injectors[i].instantiateDirectives(appInjector, null); } } static _createElementInjector(element, parent:ElementInjector, proto:ProtoElementInjector) { //TODO: vsavkin: pass element to `proto.instantiate()` once https://github.com/angular/angular/pull/98 is merged - return proto.instantiate(parent, null); + return proto.instantiate(parent, null, null); } static _rootElementInjectors(injectors) { diff --git a/modules/core/test/compiler/element_injector_spec.js b/modules/core/test/compiler/element_injector_spec.js index 0814e49f7f..a3f52ef4f6 100644 --- a/modules/core/test/compiler/element_injector_spec.js +++ b/modules/core/test/compiler/element_injector_spec.js @@ -1,5 +1,5 @@ import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib'; -import {isBlank, FIELD, IMPLEMENTS} from 'facade/lang'; +import {isBlank, isPresent, FIELD, IMPLEMENTS} from 'facade/lang'; import {ListWrapper, MapWrapper, List} from 'facade/collection'; import {ProtoElementInjector, VIEW_KEY} from 'core/compiler/element_injector'; import {Parent, Ancestor} from 'core/annotations/visibility'; @@ -12,6 +12,10 @@ class DummyView {} class Directive { } + +class SomeOtherDirective { +} + class NeedsDirective { @FIELD("dependency:Directive") constructor(dependency:Directive){ @@ -66,13 +70,13 @@ export function main() { return [lookupName(tree), children]; } - function injector(bindings, appInjector = null, props = null) { - if (isBlank(appInjector)) appInjector = new Injector([]); - if (isBlank(props)) props = {"view" : null}; + function injector(bindings, lightDomAppInjector = null, shadowDomAppInjector = null, props = null) { + if (isBlank(lightDomAppInjector)) lightDomAppInjector = new Injector([]); + if (isBlank(props)) props = {"view": null}; - var proto = new ProtoElementInjector(null, 0, bindings); - var inj = proto.instantiate(null, props["view"]); - inj.instantiateDirectives(appInjector); + var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector)); + var inj = proto.instantiate(null, null, props["view"]); + inj.instantiateDirectives(lightDomAppInjector, shadowDomAppInjector); return inj; } @@ -80,16 +84,31 @@ export function main() { var inj = new Injector([]); var protoParent = new ProtoElementInjector(null, 0, parentBindings); - var parent = protoParent.instantiate(null, null); - parent.instantiateDirectives(inj); + var parent = protoParent.instantiate(null, null, null); + parent.instantiateDirectives(inj, null); var protoChild = new ProtoElementInjector(protoParent, 1, childBindings); - var child = protoChild.instantiate(parent, null); - child.instantiateDirectives(inj); + var child = protoChild.instantiate(parent, null, null); + child.instantiateDirectives(inj, null); return child; } + function hostShadowInjectors(hostBindings, shadowBindings) { + var inj = new Injector([]); + var shadowInj = inj.createChild([]); + + var protoParent = new ProtoElementInjector(null, 0, hostBindings, true); + var host = protoParent.instantiate(null, null, null); + host.instantiateDirectives(inj, shadowInj); + + var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false); + var shadow = protoChild.instantiate(null, host, null); + shadow.instantiateDirectives(shadowInj, null); + + return shadow; + } + describe("ElementInjector", function () { describe("instantiate", function () { it("should create an element injector", function () { @@ -97,9 +116,9 @@ export function main() { var protoChild1 = new ProtoElementInjector(protoParent, 1, []); var protoChild2 = new ProtoElementInjector(protoParent, 2, []); - var p = protoParent.instantiate(null, null); - var c1 = protoChild1.instantiate(p, null); - var c2 = protoChild2.instantiate(p, null); + var p = protoParent.instantiate(null, null, null); + var c1 = protoChild1.instantiate(p, null, null); + var c2 = protoChild2.instantiate(p, null, null); expect(humanize(p, [ [p, 'parent'], @@ -147,13 +166,47 @@ export function main() { expect(d.service).toEqual("service"); }); - it("should instantiate directives that depend on speical objects", function () { + it("should instantiate directives that depend on special objects", function () { var view = new DummyView(); - var inj = injector([NeedsView], null, {"view" : view}); + var inj = injector([NeedsView], null, null, {'view':view}); expect(inj.get(NeedsView).view).toBe(view); }); + it("should instantiate directives that depend on the containing component", function () { + var shadow = hostShadowInjectors([Directive], [NeedsDirective]); + + var d = shadow.get(NeedsDirective); + expect(d).toBeAnInstanceOf(NeedsDirective); + expect(d.dependency).toBeAnInstanceOf(Directive); + }); + + it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", () => { + expect( () => { + hostShadowInjectors([SomeOtherDirective, Directive], [NeedsDirective]); + }).toThrowError('No provider for Directive! (NeedsDirective -> Directive)') + }); + + it("should instantiate component directives that depend on app services in the shadow app injector", () => { + var shadowAppInjector = new Injector([ + bind("service").toValue("service") + ]); + var inj = injector([NeedsService], null, shadowAppInjector); + + var d = inj.get(NeedsService); + expect(d).toBeAnInstanceOf(NeedsService); + expect(d.service).toEqual("service"); + }); + + it("should not instantiate other directives that depend on app services in the shadow app injector", () => { + var shadowAppInjector = new Injector([ + bind("service").toValue("service") + ]); + expect( () => { + injector([SomeOtherDirective, NeedsService], null, shadowAppInjector); + }).toThrowError('No provider for service! (NeedsService -> service)'); + }); + it("should return app services", function () { var appInjector = new Injector([ bind("service").toValue("service") @@ -204,7 +257,7 @@ export function main() { describe("special objects", function () { it("should return view", function () { var view = new DummyView(); - var inj = injector([], null, {"view" : view}); + var inj = injector([], null, null, {"view" : view}); expect(inj.get(View)).toEqual(view); }); diff --git a/modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js b/modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js index d08656b046..9b609436b6 100644 --- a/modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js +++ b/modules/core/test/compiler/pipeline/proto_element_injector_builder_spec.js @@ -41,9 +41,8 @@ export function main() { }), protoElementInjectorBuilder]); } - function assertProtoElementInjector(protoElementInjector, parent, index, bindings) { - var args = protoElementInjectorBuilder.findArgsFor(protoElementInjector); - expect(args).toEqual([parent, index, bindings]); + function getCreationArgs(protoElementInjector) { + return protoElementInjectorBuilder.findArgsFor(protoElementInjector); } it('should not create a ProtoElementInjector for elements without directives', () => { @@ -51,10 +50,19 @@ export function main() { expect(results[0].inheritedProtoElementInjector).toBe(null); }); - it('should create a ProtoElementInjector for elements with directives', () => { - var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; + it('should create a ProtoElementInjector for elements directives', () => { + var directives = [SomeComponentDirective, SomeTemplateDirective, SomeDecoratorDirective]; var results = createPipeline(directives).process(createElement('
')); - assertProtoElementInjector(results[0].inheritedProtoElementInjector, null, 0, directives); + var creationArgs = getCreationArgs(results[0].inheritedProtoElementInjector); + expect(creationArgs['bindings']).toEqual(directives); + }); + + it('should mark ProtoElementInjector for elements with component directives and use the ComponentDirective as first binding', () => { + var directives = [SomeDecoratorDirective, SomeComponentDirective]; + var results = createPipeline(directives).process(createElement('
')); + var creationArgs = getCreationArgs(results[0].inheritedProtoElementInjector); + expect(creationArgs['firstBindingIsComponent']).toBe(true); + expect(creationArgs['bindings']).toEqual([SomeComponentDirective, SomeDecoratorDirective]); }); it('should use the next ElementBinder index as index of the ProtoElementInjector', () => { @@ -63,37 +71,36 @@ export function main() { ListWrapper.push(protoView.elementBinders, null); var directives = [SomeDecoratorDirective]; var results = createPipeline(directives).process(createElement('
')); - assertProtoElementInjector( - results[0].inheritedProtoElementInjector, null, protoView.elementBinders.length, directives); + var creationArgs = getCreationArgs(results[0].inheritedProtoElementInjector); + expect(creationArgs['index']).toBe(protoView.elementBinders.length); }); it('should inherit the ProtoElementInjector down to children without directives', () => { - var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; + var directives = [SomeDecoratorDirective]; var results = createPipeline(directives).process(createElement('
')); - assertProtoElementInjector(results[0].inheritedProtoElementInjector, null, 0, directives); - assertProtoElementInjector(results[1].inheritedProtoElementInjector, null, 0, directives); + expect(results[1].inheritedProtoElementInjector).toBe(results[0].inheritedProtoElementInjector); }); it('should use the ProtoElementInjector of the parent element as parent', () => { var el = createElement('
'); - var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; + var directives = [SomeDecoratorDirective]; var results = createPipeline(directives).process(el); - assertProtoElementInjector(results[2].inheritedProtoElementInjector, - results[0].inheritedProtoElementInjector, 0, directives); + expect(results[2].inheritedProtoElementInjector.parent).toBe( + results[0].inheritedProtoElementInjector); }); it('should use a null parent for viewRoots', () => { var el = createElement('
'); - var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; + var directives = [SomeDecoratorDirective]; var results = createPipeline(directives).process(el); - assertProtoElementInjector(results[1].inheritedProtoElementInjector, null, 0, directives); + expect(results[1].inheritedProtoElementInjector.parent).toBe(null); }); it('should use a null parent if there is an intermediate viewRoot', () => { var el = createElement('
'); - var directives = [SomeDecoratorDirective, SomeTemplateDirective, SomeComponentDirective]; + var directives = [SomeDecoratorDirective]; var results = createPipeline(directives).process(el); - assertProtoElementInjector(results[2].inheritedProtoElementInjector, null, 0, directives); + expect(results[2].inheritedProtoElementInjector.parent).toBe(null); }); }); } @@ -111,10 +118,10 @@ class TestableProtoElementInjectorBuilder extends ProtoElementInjectorBuilder { } return null; } - internalCreateProtoElementInjector(parent, index, directives) { - var result = new ProtoElementInjector(parent, index, directives); + internalCreateProtoElementInjector(parent, index, bindings, firstBindingIsComponent) { + var result = new ProtoElementInjector(parent, index, bindings, firstBindingIsComponent); ListWrapper.push(this.debugObjects, result); - ListWrapper.push(this.debugObjects, [parent, index, directives]); + ListWrapper.push(this.debugObjects, {'parent': parent, 'index': index, 'bindings': bindings, 'firstBindingIsComponent': firstBindingIsComponent}); return result; } } @@ -128,10 +135,14 @@ class MockStep extends CompileStep { } } +class SomeComponentService {} + @Template() class SomeTemplateDirective {} -@Component() +@Component({ + componentServices: [SomeComponentService] +}) class SomeComponentDirective {} @Decorator()