Skip to content

Commit 5749692

Browse files
committed
fix(di): fixed dynamic component loading of components created in child injector
1 parent 19e4ee8 commit 5749692

File tree

2 files changed

+54
-49
lines changed

2 files changed

+54
-49
lines changed

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
476476
this._host = host;
477477
this._preBuiltObjects = preBuiltObjects;
478478

479-
this._reattachInjectors(imperativelyCreatedInjector, host);
479+
this._reattachInjectors(imperativelyCreatedInjector);
480480
this._strategy.hydrate();
481481

482482
if (isPresent(host)) {
@@ -489,52 +489,37 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
489489
this.hydrated = true;
490490
}
491491

492-
private _reattachInjectors(imperativelyCreatedInjector: Injector, host: ElementInjector): void {
492+
private _reattachInjectors(imperativelyCreatedInjector: Injector): void {
493+
// Dynamically-loaded component in the template. Not a root ElementInjector.
493494
if (isPresent(this._parent)) {
494-
this._reattachInjector(this._injector, this._parent._injector, false);
495-
} else {
496-
// This injector is at the boundary.
497-
//
498-
// The injector tree we are assembling:
499-
//
500-
// host._injector (only if present)
501-
// |
502-
// |boundary
503-
// |
504-
// imperativelyCreatedInjector (only if present)
505-
// |
506-
// |boundary
507-
// |
508-
// this._injector
509-
//
510-
511-
// host._injector (only if present)
512-
// |
513-
// |boundary
514-
// |
515-
// imperativelyCreatedInjector (only if present)
516-
if (isPresent(imperativelyCreatedInjector) && isPresent(host)) {
517-
this._reattachInjector(imperativelyCreatedInjector, host._injector, true);
495+
if (isPresent(imperativelyCreatedInjector)) {
496+
// The imperative injector is similar to having an element between
497+
// the dynamic-loaded component and its parent => no boundaries.
498+
this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
499+
this._reattachInjector(imperativelyCreatedInjector, this._parent._injector, false);
500+
} else {
501+
this._reattachInjector(this._injector, this._parent._injector, false);
518502
}
519503

520-
// host._injector OR imperativelyCreatedInjector OR null
521-
// |
522-
// |boundary
523-
// |
524-
// this._injector
525-
var parent = this._closestBoundaryInjector(imperativelyCreatedInjector, host);
526-
this._reattachInjector(this._injector, parent, true);
527-
}
528-
}
504+
// Dynamically-loaded component in the template. A root ElementInjector.
505+
} else if (isPresent(this._host)) {
506+
// The imperative injector is similar to having an element between
507+
// the dynamic-loaded component and its parent => no boundary between
508+
// the component and imperativelyCreatedInjector.
509+
// But since it is a root ElementInjector, we need to create a boundary
510+
// between imperativelyCreatedInjector and _host.
511+
if (isPresent(imperativelyCreatedInjector)) {
512+
this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
513+
this._reattachInjector(imperativelyCreatedInjector, this._host._injector, true);
514+
} else {
515+
this._reattachInjector(this._injector, this._host._injector, true);
516+
}
529517

530-
private _closestBoundaryInjector(imperativelyCreatedInjector: Injector,
531-
host: ElementInjector): Injector {
532-
if (isPresent(imperativelyCreatedInjector)) {
533-
return imperativelyCreatedInjector;
534-
} else if (isPresent(host)) {
535-
return host._injector;
518+
// Bootstrap
536519
} else {
537-
return null;
520+
if (isPresent(imperativelyCreatedInjector)) {
521+
this._reattachInjector(this._injector, imperativelyCreatedInjector, true);
522+
}
538523
}
539524
}
540525

modules/angular2/test/core/compiler/element_injector_spec.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ export function main() {
247247

248248
for (var i = 0; i < 20; i++) {
249249
dynamicBindings.push(bind(i).toValue(i));
250-
}
250+
}
251251

252252
function createPei(parent, index, bindings, distance = 1, hasShadowRoot = false, dirVariableBindings = null) {
253253
var directiveBinding = ListWrapper.map(bindings, b => {
@@ -278,7 +278,7 @@ export function main() {
278278
return inj;
279279
}
280280

281-
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null) {
281+
function parentChildInjectors(parentBindings, childBindings, parentPreBuildObjects = null, imperativelyCreatedInjector = null) {
282282
if (isBlank(parentPreBuildObjects)) parentPreBuildObjects = defaultPreBuiltObjects;
283283

284284
var protoParent = createPei(null, 0, parentBindings);
@@ -288,20 +288,20 @@ export function main() {
288288

289289
var protoChild = createPei(protoParent, 1, childBindings, 1, false);
290290
var child = protoChild.instantiate(parent);
291-
child.hydrate(null, null, defaultPreBuiltObjects);
291+
child.hydrate(imperativelyCreatedInjector, null, defaultPreBuiltObjects);
292292

293293
return child;
294294
}
295295

296296
function hostShadowInjectors(hostBindings: List<any>,
297-
shadowBindings: List<any>): ElementInjector {
297+
shadowBindings: List<any>, imperativelyCreatedInjector = null): ElementInjector {
298298
var protoHost = createPei(null, 0, hostBindings, 0, true);
299299
var host = protoHost.instantiate(null);
300300
host.hydrate(null, null, defaultPreBuiltObjects);
301301

302302
var protoShadow = createPei(null, 0, shadowBindings, 0, false);
303303
var shadow = protoShadow.instantiate(null);
304-
shadow.hydrate(null, host, null);
304+
shadow.hydrate(imperativelyCreatedInjector, host, null);
305305

306306
return shadow;
307307
}
@@ -715,12 +715,32 @@ export function main() {
715715
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
716716
});
717717

718-
it("should instantiate directives that depend on imperativley created injector bindings", () => {
718+
it("should instantiate directives that depend on imperatively created injector bindings (bootstrap)", () => {
719719
var imperativelyCreatedInjector = Injector.resolveAndCreate([
720720
bind("service").toValue('appService')
721721
]);
722722
var inj = injector([NeedsService], imperativelyCreatedInjector);
723723
expect(inj.get(NeedsService).service).toEqual('appService');
724+
725+
expect(() => injector([NeedsAncestorService], imperativelyCreatedInjector)).toThrowError();
726+
});
727+
728+
it("should instantiate directives that depend on imperatively created injector bindings (root injector)", () => {
729+
var imperativelyCreatedInjector = Injector.resolveAndCreate([
730+
bind("service").toValue('appService')
731+
]);
732+
var inj = hostShadowInjectors([SimpleDirective], [NeedsService, NeedsAncestorService], imperativelyCreatedInjector);
733+
expect(inj.get(NeedsService).service).toEqual('appService');
734+
expect(inj.get(NeedsAncestorService).service).toEqual('appService');
735+
});
736+
737+
it("should instantiate directives that depend on imperatively created injector bindings (child injector)", () => {
738+
var imperativelyCreatedInjector = Injector.resolveAndCreate([
739+
bind("service").toValue('appService')
740+
]);
741+
var inj = parentChildInjectors([], [NeedsService, NeedsAncestorService], null, imperativelyCreatedInjector);
742+
expect(inj.get(NeedsService).service).toEqual('appService');
743+
expect(inj.get(NeedsAncestorService).service).toEqual('appService');
724744
});
725745

726746
it("should prioritize viewInjector over hostInjector for the same binding", () => {
@@ -1192,7 +1212,7 @@ export function main() {
11921212
});
11931213
});
11941214
});
1195-
});
1215+
});
11961216
}
11971217

11981218
class ContextWithHandler {

0 commit comments

Comments
 (0)