fix(ivy): should not throw when getting VCRef.parentInjector on the root view (#27909)
PR Close #27909
This commit is contained in:

committed by
Kara Erickson

parent
fb7816fed4
commit
929334b0bf
@ -208,7 +208,7 @@ export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeIn
|
||||
let viewOffset = 1;
|
||||
while (hostTNode && hostTNode.injectorIndex === -1) {
|
||||
view = view[DECLARATION_VIEW] !;
|
||||
hostTNode = view[HOST_NODE] !;
|
||||
hostTNode = view ? view[HOST_NODE] : null;
|
||||
viewOffset++;
|
||||
}
|
||||
|
||||
@ -292,82 +292,86 @@ export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): str
|
||||
* @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
|
||||
*/
|
||||
export function getOrCreateInjectable<T>(
|
||||
tNode: TElementNode | TContainerNode | TElementContainerNode, lView: LView,
|
||||
tNode: TElementNode | TContainerNode | TElementContainerNode | null, lView: LView,
|
||||
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default,
|
||||
notFoundValue?: any): T|null {
|
||||
const bloomHash = bloomHashBitOrFactory(token);
|
||||
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
||||
// so just call the factory function to create it.
|
||||
if (typeof bloomHash === 'function') {
|
||||
const savePreviousOrParentTNode = getPreviousOrParentTNode();
|
||||
const saveLView = getLView();
|
||||
setTNodeAndViewData(tNode, lView);
|
||||
try {
|
||||
const value = bloomHash();
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
} else {
|
||||
return value;
|
||||
if (tNode) {
|
||||
const bloomHash = bloomHashBitOrFactory(token);
|
||||
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
||||
// so just call the factory function to create it.
|
||||
if (typeof bloomHash === 'function') {
|
||||
const savePreviousOrParentTNode = getPreviousOrParentTNode();
|
||||
const saveLView = getLView();
|
||||
setTNodeAndViewData(tNode, lView);
|
||||
try {
|
||||
const value = bloomHash();
|
||||
if (value == null && !(flags & InjectFlags.Optional)) {
|
||||
throw new Error(`No provider for ${stringify(token)}!`);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} finally {
|
||||
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
|
||||
}
|
||||
} finally {
|
||||
setTNodeAndViewData(savePreviousOrParentTNode, saveLView);
|
||||
}
|
||||
} else if (typeof bloomHash == 'number') {
|
||||
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
|
||||
} else if (typeof bloomHash == 'number') {
|
||||
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
|
||||
|
||||
// A reference to the previous injector TView that was found while climbing the element injector
|
||||
// tree. This is used to know if viewProviders can be accessed on the current injector.
|
||||
let previousTView: TView|null = null;
|
||||
let injectorIndex = getInjectorIndex(tNode, lView);
|
||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||
let hostTElementNode: TNode|null =
|
||||
flags & InjectFlags.Host ? findComponentView(lView)[HOST_NODE] : null;
|
||||
// A reference to the previous injector TView that was found while climbing the element
|
||||
// injector tree. This is used to know if viewProviders can be accessed on the current
|
||||
// injector.
|
||||
let previousTView: TView|null = null;
|
||||
let injectorIndex = getInjectorIndex(tNode, lView);
|
||||
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
|
||||
let hostTElementNode: TNode|null =
|
||||
flags & InjectFlags.Host ? findComponentView(lView)[HOST_NODE] : null;
|
||||
|
||||
// If we should skip this injector, or if there is no injector on this node, start by searching
|
||||
// the parent injector.
|
||||
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
||||
lView[injectorIndex + PARENT_INJECTOR];
|
||||
// If we should skip this injector, or if there is no injector on this node, start by
|
||||
// searching
|
||||
// the parent injector.
|
||||
if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
||||
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
||||
lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
if (!shouldSearchParent(flags, false)) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
previousTView = lView[TVIEW];
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse up the injector tree until we find a potential match or until we know there
|
||||
// *isn't* a match.
|
||||
while (injectorIndex !== -1) {
|
||||
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
// Check the current injector. If it matches, see if it contains token.
|
||||
const tView = lView[TVIEW];
|
||||
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
||||
// At this point, we have an injector which *may* contain the token, so we step through
|
||||
// the providers and directives associated with the injector's corresponding node to get
|
||||
// the instance.
|
||||
const instance: T|null = searchTokensOnInjector<T>(
|
||||
injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
||||
if (instance !== NOT_FOUND) {
|
||||
return instance;
|
||||
if (!shouldSearchParent(flags, false)) {
|
||||
injectorIndex = -1;
|
||||
} else {
|
||||
previousTView = lView[TVIEW];
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
}
|
||||
}
|
||||
if (shouldSearchParent(
|
||||
flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) &&
|
||||
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||
// Traverse up the tree and continue searching.
|
||||
previousTView = tView;
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
} else {
|
||||
// If we should not search parent OR If the ancestor bloom filter value does not have the
|
||||
// bit corresponding to the directive we can give up on traversing up to find the specific
|
||||
// injector.
|
||||
injectorIndex = -1;
|
||||
|
||||
// Traverse up the injector tree until we find a potential match or until we know there
|
||||
// *isn't* a match.
|
||||
while (injectorIndex !== -1) {
|
||||
parentLocation = lView[injectorIndex + PARENT_INJECTOR];
|
||||
|
||||
// Check the current injector. If it matches, see if it contains token.
|
||||
const tView = lView[TVIEW];
|
||||
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
||||
// At this point, we have an injector which *may* contain the token, so we step through
|
||||
// the providers and directives associated with the injector's corresponding node to get
|
||||
// the instance.
|
||||
const instance: T|null = searchTokensOnInjector<T>(
|
||||
injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
||||
if (instance !== NOT_FOUND) {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
if (shouldSearchParent(
|
||||
flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) &&
|
||||
bloomHasToken(bloomHash, injectorIndex, lView)) {
|
||||
// The def wasn't found anywhere on this node, so it was a false positive.
|
||||
// Traverse up the tree and continue searching.
|
||||
previousTView = tView;
|
||||
injectorIndex = getParentInjectorIndex(parentLocation);
|
||||
lView = getParentInjectorView(parentLocation, lView);
|
||||
} else {
|
||||
// If we should not search parent OR If the ancestor bloom filter value does not have the
|
||||
// bit corresponding to the directive we can give up on traversing up to find the specific
|
||||
// injector.
|
||||
injectorIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -570,7 +574,8 @@ export function injectInjector() {
|
||||
|
||||
export class NodeInjector implements Injector {
|
||||
constructor(
|
||||
private _tNode: TElementNode|TContainerNode|TElementContainerNode, private _lView: LView) {}
|
||||
private _tNode: TElementNode|TContainerNode|TElementContainerNode|null,
|
||||
private _lView: LView) {}
|
||||
|
||||
get(token: any, notFoundValue?: any): any {
|
||||
return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);
|
||||
|
Reference in New Issue
Block a user