diff --git a/packages/core/src/render3/node_util.ts b/packages/core/src/render3/node_util.ts
index 1640aade65..8f750ea399 100644
--- a/packages/core/src/render3/node_util.ts
+++ b/packages/core/src/render3/node_util.ts
@@ -12,8 +12,10 @@ import {DECLARATION_VIEW, LView, T_HOST} from './interfaces/view';
import {getParentInjectorViewOffset} from './util/injector_utils';
/**
- * Unwraps a parent injector location number to find the view offset from the current injector,
- * then walks up the declaration view tree until the TNode of the parent injector is found.
+ * If `startTNode.parent` exists and has an injector, returns TNode for that injector.
+ * Otherwise, unwraps a parent injector location number to find the view offset from the current
+ * injector, then walks up the declaration view tree until the TNode of the parent injector is
+ * found.
*
* @param location The location of the parent injector, which contains the view offset
* @param startView The LView instance from which to start walking up the view tree
@@ -23,14 +25,17 @@ import {getParentInjectorViewOffset} from './util/injector_utils';
export function getParentInjectorTNode(
location: RelativeInjectorLocation, startView: LView, startTNode: TNode): TElementNode|
TContainerNode|null {
+ // If there is an injector on the parent TNode, retrieve the TNode for that injector.
if (startTNode.parent && startTNode.parent.injectorIndex !== -1) {
// view offset is 0
const injectorIndex = startTNode.parent.injectorIndex;
- let parentTNode = startTNode.parent;
- while (parentTNode.parent != null && injectorIndex == parentTNode.injectorIndex) {
- parentTNode = parentTNode.parent;
+ let tNode = startTNode.parent;
+ // If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent
+ // injector.
+ while (tNode.parent != null && injectorIndex == tNode.parent.injectorIndex) {
+ tNode = tNode.parent;
}
- return parentTNode;
+ return tNode;
}
let viewOffset = getParentInjectorViewOffset(location);
// view offset is 1
diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts
index 83faaac385..fa8ed2783c 100644
--- a/packages/core/test/acceptance/di_spec.ts
+++ b/packages/core/test/acceptance/di_spec.ts
@@ -470,6 +470,64 @@ describe('di', () => {
fixture.detectChanges();
expect(divElement.id).toBe('bar');
});
+
+ it('dynamic components should find dependencies when parent is projected', () => {
+ @Directive({selector: '[dirA]'})
+ class DirA {
+ }
+ @Directive({selector: '[dirB]'})
+ class DirB {
+ }
+ @Component({selector: 'child', template: ''})
+ class Child {
+ constructor(@Optional() readonly dirA: DirA, @Optional() readonly dirB: DirB) {}
+ }
+ @Component({
+ selector: 'projector',
+ template: '',
+ })
+ class Projector {
+ }
+
+ @Component({
+ template: `
+
+
+
+
+
+ `,
+ entryComponents: [Child]
+ })
+ class MyApp {
+ @ViewChild('childOrigin', {read: ViewContainerRef, static: true})
+ childOrigin !: ViewContainerRef;
+ @ViewChild('childOriginWithDirB', {read: ViewContainerRef, static: true})
+ childOriginWithDirB !: ViewContainerRef;
+ childFactory = this.resolver.resolveComponentFactory(Child);
+
+ constructor(readonly resolver: ComponentFactoryResolver, readonly injector: Injector) {}
+
+ addChild() { return this.childOrigin.createComponent(this.childFactory); }
+ addChildWithDirB() { return this.childOriginWithDirB.createComponent(this.childFactory); }
+ }
+
+ const fixture =
+ TestBed.configureTestingModule({declarations: [Child, DirA, DirB, Projector, MyApp]})
+ .createComponent(MyApp);
+ const child = fixture.componentInstance.addChild();
+ expect(child).toBeDefined();
+ expect(child.instance.dirA)
+ .not.toBeNull('dirA should be found. It is on the parent of the viewContainerRef.');
+ const child2 = fixture.componentInstance.addChildWithDirB();
+ expect(child2).toBeDefined();
+ expect(child2.instance.dirA)
+ .not.toBeNull('dirA should be found. It is on the parent of the viewContainerRef.');
+ expect(child2.instance.dirB)
+ .toBeNull(
+ 'dirB appears on the ng-container and should not be found because the ' +
+ 'viewContainerRef.createComponent node is inserted next to the container.');
+ });
});
it('should throw if directive is not found anywhere', () => {