fix(ivy): update ViewContainerRef to get the correct parentInjector (#35013)
PR Close #35013
This commit is contained in:
parent
519e9e1ae8
commit
b8ffcf973c
|
@ -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
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
Loading…
Reference in New Issue