* Returns a value from the closest embedded or node injector. * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to re
( tNode: TDirectiveHostNode, lView: LView, token: ProviderToken<T>, flags: InternalInjectFlags, notFoundValue?: any, )
| 969 | * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided |
| 970 | */ |
| 971 | function lookupTokenUsingEmbeddedInjector<T>( |
| 972 | tNode: TDirectiveHostNode, |
| 973 | lView: LView, |
| 974 | token: ProviderToken<T>, |
| 975 | flags: InternalInjectFlags, |
| 976 | notFoundValue?: any, |
| 977 | ) { |
| 978 | let currentTNode: TDirectiveHostNode | null = tNode; |
| 979 | let currentLView: LView | null = lView; |
| 980 | |
| 981 | // When an LView with an embedded view injector is inserted, it'll likely be interlaced with |
| 982 | // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector). |
| 983 | // Since the bloom filters for the node injectors have already been constructed and we don't |
| 984 | // have a way of extracting the records from an injector, the only way to maintain the correct |
| 985 | // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve |
| 986 | // the token at each level. |
| 987 | while ( |
| 988 | currentTNode !== null && |
| 989 | currentLView !== null && |
| 990 | currentLView[FLAGS] & LViewFlags.HasEmbeddedViewInjector && |
| 991 | !isRootView(currentLView) |
| 992 | ) { |
| 993 | ngDevMode && assertTNodeForLView(currentTNode, currentLView); |
| 994 | |
| 995 | // Note that this lookup on the node injector is using the `Self` flag, because |
| 996 | // we don't want the node injector to look at any parent injectors since we |
| 997 | // may hit the embedded view injector first. |
| 998 | const nodeInjectorValue = lookupTokenUsingNodeInjector( |
| 999 | currentTNode, |
| 1000 | currentLView, |
| 1001 | token, |
| 1002 | flags | InternalInjectFlags.Self, |
| 1003 | NOT_FOUND, |
| 1004 | ); |
| 1005 | if (nodeInjectorValue !== NOT_FOUND) { |
| 1006 | return nodeInjectorValue; |
| 1007 | } |
| 1008 | |
| 1009 | // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191 |
| 1010 | let parentTNode: TElementNode | TContainerNode | null = currentTNode.parent; |
| 1011 | |
| 1012 | // `TNode.parent` includes the parent within the current view only. If it doesn't exist, |
| 1013 | // it means that we've hit the view boundary and we need to go up to the next view. |
| 1014 | if (!parentTNode) { |
| 1015 | // Before we go to the next LView, check if the token exists on the current embedded injector. |
| 1016 | const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR]; |
| 1017 | if (embeddedViewInjector) { |
| 1018 | const embeddedViewInjectorValue = (embeddedViewInjector as BackwardsCompatibleInjector).get( |
| 1019 | token, |
| 1020 | NOT_FOUND as T | {}, |
| 1021 | // The `SkipSelf` flag is intended for the current injection context (the child component). |
| 1022 | // When we delegate to the embedded view injector, we are effectively traversing to a |
| 1023 | // parent/fallback scope, so the "Self" has already been skipped. We must strip the |
| 1024 | // flag to ensure the embedded view injector can resolve tokens from itself. |
| 1025 | flags & ~InternalInjectFlags.SkipSelf, |
| 1026 | ); |
| 1027 | if (embeddedViewInjectorValue !== NOT_FOUND) { |
| 1028 | return embeddedViewInjectorValue; |
no test coverage detected
searching dependent graphs…