fix(ivy): host injection flag should not throw for embedded views (#26795)
PR Close #26795
This commit is contained in:
parent
2a869271f6
commit
1130e48541
|
@ -21,7 +21,7 @@ import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TN
|
||||||
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, TData, TVIEW, TView} from './interfaces/view';
|
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, TData, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {getPreviousOrParentTNode, getViewData, setTNodeAndViewData} from './state';
|
import {getPreviousOrParentTNode, getViewData, setTNodeAndViewData} from './state';
|
||||||
import {getParentInjectorIndex, getParentInjectorView, getParentInjectorViewOffset, hasParentInjector, isComponent, stringify} from './util';
|
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector, isComponent, stringify} from './util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines if the call to `inject` should include `viewProviders` in its resolution.
|
* Defines if the call to `inject` should include `viewProviders` in its resolution.
|
||||||
|
@ -196,7 +196,7 @@ export function getInjectorIndex(tNode: TNode, hostView: LViewData): number {
|
||||||
*/
|
*/
|
||||||
export function getParentInjectorLocation(tNode: TNode, view: LViewData): RelativeInjectorLocation {
|
export function getParentInjectorLocation(tNode: TNode, view: LViewData): RelativeInjectorLocation {
|
||||||
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
||||||
return tNode.parent.injectorIndex as any; // view offset is 0
|
return tNode.parent.injectorIndex as any; // ViewOffset is 0, AcrossHostBoundary is 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// For most cases, the parent injector index can be found on the host node (e.g. for component
|
// For most cases, the parent injector index can be found on the host node (e.g. for component
|
||||||
|
@ -209,8 +209,13 @@ export function getParentInjectorLocation(tNode: TNode, view: LViewData): Relati
|
||||||
hostTNode = view[HOST_NODE] !;
|
hostTNode = view[HOST_NODE] !;
|
||||||
viewOffset++;
|
viewOffset++;
|
||||||
}
|
}
|
||||||
|
const acrossHostBoundary = hostTNode && hostTNode.type === TNodeType.Element ?
|
||||||
|
RelativeInjectorLocationFlags.AcrossHostBoundary :
|
||||||
|
0;
|
||||||
|
|
||||||
return hostTNode ?
|
return hostTNode ?
|
||||||
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) :
|
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) |
|
||||||
|
acrossHostBoundary :
|
||||||
-1 as any;
|
-1 as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +512,8 @@ function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjector
|
||||||
number {
|
number {
|
||||||
return !(
|
return !(
|
||||||
flags & InjectFlags.Self ||
|
flags & InjectFlags.Self ||
|
||||||
(flags & InjectFlags.Host && getParentInjectorViewOffset(parentLocation) > 0));
|
(flags & InjectFlags.Host &&
|
||||||
|
((parentLocation as any as number) & RelativeInjectorLocationFlags.AcrossHostBoundary)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function injectInjector() {
|
export function injectInjector() {
|
||||||
|
|
|
@ -26,7 +26,8 @@ export interface RelativeInjectorLocation { __brand__: 'RelativeInjectorLocation
|
||||||
|
|
||||||
export const enum RelativeInjectorLocationFlags {
|
export const enum RelativeInjectorLocationFlags {
|
||||||
InjectorIndexMask = 0b111111111111111,
|
InjectorIndexMask = 0b111111111111111,
|
||||||
ViewOffsetShift = 15,
|
AcrossHostBoundary = 0b1000000000000000,
|
||||||
|
ViewOffsetShift = 16,
|
||||||
NO_PARENT = -1,
|
NO_PARENT = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1010,8 +1010,10 @@ describe('di', () => {
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not pass component boundary with @Host', () => {
|
describe('@Host', () => {
|
||||||
let dirA: DirA;
|
let dirA: DirA|null = null;
|
||||||
|
|
||||||
|
beforeEach(() => dirA = null);
|
||||||
|
|
||||||
class DirA {
|
class DirA {
|
||||||
constructor(@Host() public dirB: DirB) {}
|
constructor(@Host() public dirB: DirB) {}
|
||||||
|
@ -1023,6 +1025,7 @@ describe('di', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('should not find providers across component boundaries', () => {
|
||||||
/** <div dirA></div> */
|
/** <div dirA></div> */
|
||||||
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
|
@ -1040,11 +1043,88 @@ describe('di', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new ComponentFixture(App);
|
new ComponentFixture(App);
|
||||||
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not find providers across component boundaries if in inline view', () => {
|
||||||
|
let comp !: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* % if (showing) {
|
||||||
|
* <div dirA></div>
|
||||||
|
* % }
|
||||||
|
*/
|
||||||
|
const Comp = createComponent('comp', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
containerRefreshStart(0);
|
||||||
|
{
|
||||||
|
if (ctx.showing) {
|
||||||
|
let rf1 = embeddedViewStart(0, 1, 0);
|
||||||
|
if (rf1 & RenderFlags.Create) {
|
||||||
|
element(0, 'div', ['dirA', '']);
|
||||||
|
}
|
||||||
|
embeddedViewEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerRefreshEnd();
|
||||||
|
}
|
||||||
|
}, 1, 0, [DirA, DirB]);
|
||||||
|
|
||||||
|
/* <comp dirB></comp> */
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'comp', ['dirB', '']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
comp = getDirectiveOnNode(0);
|
||||||
|
}
|
||||||
|
}, 1, 0, [Comp, DirB]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
expect(() => {
|
||||||
|
comp.showing = true;
|
||||||
|
fixture.update();
|
||||||
|
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should find providers across embedded views if not passing component boundary', () => {
|
||||||
|
let dirB !: DirB;
|
||||||
|
|
||||||
|
function IfTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'div', ['dirA', '']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div dirB>
|
||||||
|
* <div *ngIf="showing" dirA></div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', ['dirB', '']);
|
||||||
|
{ template(1, IfTemplate, 1, 0, '', ['ngIf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(1, 'ngIf', bind(ctx.showing));
|
||||||
|
|
||||||
|
// testing only
|
||||||
|
dirB = getDirectiveOnNode(0);
|
||||||
|
}
|
||||||
|
}, 2, 1, [NgIf, DirA, DirB]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
fixture.component.showing = true;
|
||||||
|
fixture.update();
|
||||||
|
|
||||||
|
expect(dirA !.dirB).toEqual(dirB);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Special tokens', () => {
|
describe('Special tokens', () => {
|
||||||
|
|
Loading…
Reference in New Issue