fix(element_injector): changed visibility rules to expose hostInjector of the component to its shadow dom

This commit is contained in:
vsavkin 2015-06-11 13:54:17 -07:00
parent bbfb4e1dcc
commit c51aef9f7d
3 changed files with 47 additions and 51 deletions

View File

@ -398,41 +398,45 @@ export class ProtoElementInjector {
var bd = []; var bd = [];
ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent); ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent);
ProtoElementInjector._createHostInjectorBindingData(bindings, bd);
if (firstBindingIsComponent) { if (firstBindingIsComponent) {
ProtoElementInjector._createViewInjectorBindingData(bindings, bd); ProtoElementInjector._createViewInjectorBindingData(bindings, bd);
} }
ProtoElementInjector._createHostInjectorBindingData(bindings, bd, firstBindingIsComponent);
return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent); return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent);
} }
private static _createDirectiveBindingData(bindings: List<ResolvedBinding>, bd: List<BindingData>, private static _createDirectiveBindingData(dirBindings: List<ResolvedBinding>,
bd: List<BindingData>,
firstBindingIsComponent: boolean) { firstBindingIsComponent: boolean) {
if (firstBindingIsComponent) { ListWrapper.forEach(dirBindings, dirBinding => {
ListWrapper.push(bd, new BindingData(bindings[0], LIGHT_DOM_AND_SHADOW_DOM)); ListWrapper.push(bd, ProtoElementInjector._createBindingData(
for (var i = 1; i < bindings.length; ++i) { firstBindingIsComponent, dirBinding, dirBindings, dirBinding));
ListWrapper.push(bd, new BindingData(bindings[i], LIGHT_DOM)); });
}
} else {
ListWrapper.forEach(bindings, b => {ListWrapper.push(bd, new BindingData(b, LIGHT_DOM))});
}
} }
private static _createHostInjectorBindingData(bindings: List<ResolvedBinding>, private static _createHostInjectorBindingData(dirBindings: List<ResolvedBinding>,
bd: List<BindingData>) { bd: List<BindingData>,
firstBindingIsComponent: boolean) {
var visitedIds: Map<number, boolean> = MapWrapper.create(); var visitedIds: Map<number, boolean> = MapWrapper.create();
ListWrapper.forEach(bindings, b => { ListWrapper.forEach(dirBindings, dirBinding => {
ListWrapper.forEach(b.resolvedHostInjectables, b => { ListWrapper.forEach(dirBinding.resolvedHostInjectables, b => {
if (MapWrapper.contains(visitedIds, b.key.id)) { if (MapWrapper.contains(visitedIds, b.key.id)) {
throw new BaseException( throw new BaseException(
`Multiple directives defined the same host injectable: "${stringify(b.key.token)}"`); `Multiple directives defined the same host injectable: "${stringify(b.key.token)}"`);
} }
MapWrapper.set(visitedIds, b.key.id, true); MapWrapper.set(visitedIds, b.key.id, true);
ListWrapper.push(bd, new BindingData(ProtoElementInjector._createBinding(b), LIGHT_DOM)); ListWrapper.push(bd, ProtoElementInjector._createBindingData(
firstBindingIsComponent, dirBinding, dirBindings,
ProtoElementInjector._createBinding(b)));
}); });
}); });
} }
private static _createBindingData(firstBindingIsComponent, dirBinding, dirBindings, binding) {
var isComponent = firstBindingIsComponent && dirBindings[0] === dirBinding;
return new BindingData(binding, isComponent ? LIGHT_DOM_AND_SHADOW_DOM : LIGHT_DOM);
}
private static _createViewInjectorBindingData(bindings: List<ResolvedBinding>, private static _createViewInjectorBindingData(bindings: List<ResolvedBinding>,
bd: List<BindingData>) { bd: List<BindingData>) {
var db = <DirectiveBinding>bindings[0]; var db = <DirectiveBinding>bindings[0];

View File

@ -625,15 +625,38 @@ export function main() {
expect(inj.get(NeedsService).service).toEqual('service'); expect(inj.get(NeedsService).service).toEqual('service');
}); });
it("should prioritize hostInjector over viewInjector for the same binding", () => { it("should prioritize viewInjector over hostInjector for the same binding", () => {
var inj = injector( var inj = injector(
ListWrapper.concat([DirectiveBinding.createFromType(NeedsService, new dirAnn.Component({ ListWrapper.concat([DirectiveBinding.createFromType(NeedsService, new dirAnn.Component({
hostInjector: [bind('service').toValue('hostService')], hostInjector: [bind('service').toValue('hostService')],
viewInjector: [bind('service').toValue('viewService')]}) viewInjector: [bind('service').toValue('viewService')]})
)], extraBindings), null, true); )], extraBindings), null, true);
expect(inj.get(NeedsService).service).toEqual('hostService'); expect(inj.get(NeedsService).service).toEqual('viewService');
}); });
it("should instantiate a directive in a view that depends on hostInjector bindings of the component", () => {
var shadowInj = hostShadowInjectors(
ListWrapper.concat([DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({
hostInjector: [bind('service').toValue('hostService')]})
)], extraBindings),
ListWrapper.concat([NeedsService], extraBindings)
);
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
});
it("should not instantiate a directive in a view that depends on hostInjector bindings of a decorator directive", () => {
expect(() => {
hostShadowInjectors(
ListWrapper.concat([
SimpleDirective,
DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Directive({
hostInjector: [bind('service').toValue('hostService')]})
)], extraBindings),
ListWrapper.concat([NeedsService], extraBindings)
);
}).toThrowError(new RegExp("No provider for service!"));
});
it("should instantiate directives that depend on app services", () => { it("should instantiate directives that depend on app services", () => {
var appInjector = Injector.resolveAndCreate( var appInjector = Injector.resolveAndCreate(

View File

@ -973,28 +973,6 @@ export function main() {
}); });
})); }));
it('should prioritze hostInjector over viewInjector for the same binding',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(MyComp, new viewAnn.View({
template: `
<directive-providing-injectable>
<directive-consuming-injectable #consuming>
</directive-consuming-injectable>
</directive-providing-injectable>
`,
directives:
[DirectiveProvidingInjectableInHostAndView, DirectiveConsumingInjectable]
}));
tb.createView(MyComp, {context: ctx})
.then((view) => {
var comp = view.rawView.locals.get("consuming");
expect(comp.injectable).toEqual("host");
async.done();
});
}));
it("should support viewInjector", it("should support viewInjector",
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => { inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(DirectiveProvidingInjectableInView, new viewAnn.View({ tb.overrideView(DirectiveProvidingInjectableInView, new viewAnn.View({
@ -1688,23 +1666,15 @@ class GrandParentProvidingEventBus {
constructor(bus: EventBus) { this.bus = bus; } constructor(bus: EventBus) { this.bus = bus; }
} }
function createParentBusHost(peb) { function createParentBus(peb) {
return new EventBus(peb, "parent"); return new EventBus(peb, "parent");
} }
function createParentBusView(p) {
return p.bus;
}
@Component({ @Component({
selector: 'parent-providing-event-bus', selector: 'parent-providing-event-bus',
hostInjector: [ hostInjector: [
new Binding(EventBus, new Binding(EventBus,
{toFactory: createParentBusHost, deps: [[EventBus, new visAnn.Unbounded()]]}) {toFactory: createParentBus, deps: [[EventBus, new visAnn.Unbounded()]]})
],
viewInjector: [
new Binding(
EventBus,
{toFactory: createParentBusView, deps: [[forwardRef(() => ParentProvidingEventBus)]]})
] ]
}) })
@View({ @View({
@ -1718,7 +1688,6 @@ class ParentProvidingEventBus {
grandParentBus: EventBus; grandParentBus: EventBus;
constructor(bus: EventBus, @Unbounded() grandParentBus: EventBus) { constructor(bus: EventBus, @Unbounded() grandParentBus: EventBus) {
// constructor(bus: EventBus) {
this.bus = bus; this.bus = bus;
this.grandParentBus = grandParentBus; this.grandParentBus = grandParentBus;
} }