fix(ivy): update ViewContainerRef to get the correct parentInjector (#35013)

PR Close #35013
This commit is contained in:
Andrew Scott 2020-01-28 10:22:17 -08:00 committed by Andrew Kushnir
parent 519e9e1ae8
commit b8ffcf973c
2 changed files with 69 additions and 6 deletions

View File

@ -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

View File

@ -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: '<ng-content></ng-content>',
})
class Projector {
}
@Component({
template: `
<projector>
<div dirA>
<ng-container #childOrigin></ng-container>
<ng-container #childOriginWithDirB dirB></ng-container>
</div>
</projector>`,
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', () => {