fix(di): fixed dynamic component loading of components created in child injector

This commit is contained in:
vsavkin 2015-07-17 15:29:05 -07:00
parent 19e4ee81b9
commit 57496926ca
2 changed files with 54 additions and 49 deletions

View File

@ -476,7 +476,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
this._host = host;
this._preBuiltObjects = preBuiltObjects;
this._reattachInjectors(imperativelyCreatedInjector, host);
this._reattachInjectors(imperativelyCreatedInjector);
this._strategy.hydrate();
if (isPresent(host)) {
@ -489,52 +489,37 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
this.hydrated = true;
}
private _reattachInjectors(imperativelyCreatedInjector: Injector, host: ElementInjector): void {
private _reattachInjectors(imperativelyCreatedInjector: Injector): void {
// Dynamically-loaded component in the template. Not a root ElementInjector.
if (isPresent(this._parent)) {
this._reattachInjector(this._injector, this._parent._injector, false);
} else {
// This injector is at the boundary.
//
// The injector tree we are assembling:
//
// host._injector (only if present)
// |
// |boundary
// |
// imperativelyCreatedInjector (only if present)
// |
// |boundary
// |
// this._injector
//
// host._injector (only if present)
// |
// |boundary
// |
// imperativelyCreatedInjector (only if present)
if (isPresent(imperativelyCreatedInjector) && isPresent(host)) {
this._reattachInjector(imperativelyCreatedInjector, host._injector, true);
if (isPresent(imperativelyCreatedInjector)) {
// The imperative injector is similar to having an element between
// the dynamic-loaded component and its parent => no boundaries.
this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
this._reattachInjector(imperativelyCreatedInjector, this._parent._injector, false);
} else {
this._reattachInjector(this._injector, this._parent._injector, false);
}
// host._injector OR imperativelyCreatedInjector OR null
// |
// |boundary
// |
// this._injector
var parent = this._closestBoundaryInjector(imperativelyCreatedInjector, host);
this._reattachInjector(this._injector, parent, true);
}
}
// Dynamically-loaded component in the template. A root ElementInjector.
} else if (isPresent(this._host)) {
// The imperative injector is similar to having an element between
// the dynamic-loaded component and its parent => no boundary between
// the component and imperativelyCreatedInjector.
// But since it is a root ElementInjector, we need to create a boundary
// between imperativelyCreatedInjector and _host.
if (isPresent(imperativelyCreatedInjector)) {
this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
this._reattachInjector(imperativelyCreatedInjector, this._host._injector, true);
} else {
this._reattachInjector(this._injector, this._host._injector, true);
}
private _closestBoundaryInjector(imperativelyCreatedInjector: Injector,
host: ElementInjector): Injector {
if (isPresent(imperativelyCreatedInjector)) {
return imperativelyCreatedInjector;
} else if (isPresent(host)) {
return host._injector;
// Bootstrap
} else {
return null;
if (isPresent(imperativelyCreatedInjector)) {
this._reattachInjector(this._injector, imperativelyCreatedInjector, true);
}
}
}

View File

@ -247,7 +247,7 @@ export function main() {
for (var i = 0; i < 20; i++) {
dynamicBindings.push(bind(i).toValue(i));
}
}
function createPei(parent, index, bindings, distance = 1, hasShadowRoot = false, dirVariableBindings = null) {
var directiveBinding = ListWrapper.map(bindings, b => {
@ -278,7 +278,7 @@ export function main() {
return inj;
}
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null) {
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null, imperativelyCreatedInjector = null) {
if (isBlank(parentPreBuildObjects)) parentPreBuildObjects = defaultPreBuiltObjects;
var protoParent = createPei(null, 0, parentBindings);
@ -288,20 +288,20 @@ export function main() {
var protoChild = createPei(protoParent, 1, childBindings, 1, false);
var child = protoChild.instantiate(parent);
child.hydrate(null, null, defaultPreBuiltObjects);
child.hydrate(imperativelyCreatedInjector, null, defaultPreBuiltObjects);
return child;
}
function hostShadowInjectors(hostBindings: List<any>,
shadowBindings: List<any>): ElementInjector {
shadowBindings: List<any>, imperativelyCreatedInjector = null): ElementInjector {
var protoHost = createPei(null, 0, hostBindings, 0, true);
var host = protoHost.instantiate(null);
host.hydrate(null, null, defaultPreBuiltObjects);
var protoShadow = createPei(null, 0, shadowBindings, 0, false);
var shadow = protoShadow.instantiate(null);
shadow.hydrate(null, host, null);
shadow.hydrate(imperativelyCreatedInjector, host, null);
return shadow;
}
@ -715,12 +715,32 @@ export function main() {
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
});
it("should instantiate directives that depend on imperativley created injector bindings", () => {
it("should instantiate directives that depend on imperatively created injector bindings (bootstrap)", () => {
var imperativelyCreatedInjector = Injector.resolveAndCreate([
bind("service").toValue('appService')
]);
var inj = injector([NeedsService], imperativelyCreatedInjector);
expect(inj.get(NeedsService).service).toEqual('appService');
expect(() => injector([NeedsAncestorService], imperativelyCreatedInjector)).toThrowError();
});
it("should instantiate directives that depend on imperatively created injector bindings (root injector)", () => {
var imperativelyCreatedInjector = Injector.resolveAndCreate([
bind("service").toValue('appService')
]);
var inj = hostShadowInjectors([SimpleDirective], [NeedsService, NeedsAncestorService], imperativelyCreatedInjector);
expect(inj.get(NeedsService).service).toEqual('appService');
expect(inj.get(NeedsAncestorService).service).toEqual('appService');
});
it("should instantiate directives that depend on imperatively created injector bindings (child injector)", () => {
var imperativelyCreatedInjector = Injector.resolveAndCreate([
bind("service").toValue('appService')
]);
var inj = parentChildInjectors([], [NeedsService, NeedsAncestorService], null, imperativelyCreatedInjector);
expect(inj.get(NeedsService).service).toEqual('appService');
expect(inj.get(NeedsAncestorService).service).toEqual('appService');
});
it("should prioritize viewInjector over hostInjector for the same binding", () => {
@ -1192,7 +1212,7 @@ export function main() {
});
});
});
});
});
}
class ContextWithHandler {