fix(ivy): consider providers for view/content queries (#27156)
In ViewEngine it is possible to query for a token that is provided by a directive that is in scope. See StackBlitz for example: https://stackblitz.com/edit/ng-viewengine-viewchild-providers Material uses this pattern with its `MatFormFieldControl` setup, see https://bit.ly/2zgCUxD for documentation. PR Close #27156
This commit is contained in:
@ -396,9 +396,6 @@ function searchTokensOnInjector<T>(
|
||||
previousTView: TView | null) {
|
||||
const currentTView = injectorView[TVIEW];
|
||||
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
|
||||
const nodeFlags = tNode.flags;
|
||||
const nodeProviderIndexes = tNode.providerIndexes;
|
||||
const tInjectables = currentTView.data;
|
||||
// First, we step through providers
|
||||
let canAccessViewProviders = false;
|
||||
// We need to determine if view providers can be accessed by the starting element.
|
||||
@ -412,9 +409,35 @@ function searchTokensOnInjector<T>(
|
||||
// and check the host node of the current view to identify component views.
|
||||
if (previousTView == null && isComponent(tNode) && includeViewProviders ||
|
||||
previousTView != null && previousTView != currentTView &&
|
||||
(currentTView.node == null || currentTView.node !.type === TNodeType.Element)) {
|
||||
(currentTView.node == null || currentTView.node.type === TNodeType.Element)) {
|
||||
canAccessViewProviders = true;
|
||||
}
|
||||
const injectableIdx =
|
||||
locateDirectiveOrProvider(tNode, injectorView, token, canAccessViewProviders);
|
||||
if (injectableIdx !== null) {
|
||||
return getNodeInjectable(currentTView.data, injectorView, injectableIdx, tNode as TElementNode);
|
||||
} else {
|
||||
return NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the given token among the node's directives and providers.
|
||||
*
|
||||
* @param tNode TNode on which directives are present.
|
||||
* @param view The view we are currently processing
|
||||
* @param token Provider token or type of a directive to look for.
|
||||
* @param canAccessViewProviders Whether view providers should be considered.
|
||||
* @returns Index of a found directive or provider, or null when none found.
|
||||
*/
|
||||
export function locateDirectiveOrProvider<T>(
|
||||
tNode: TNode, view: LViewData, token: Type<T>| InjectionToken<T>,
|
||||
canAccessViewProviders: boolean): number|null {
|
||||
const tView = view[TVIEW];
|
||||
const nodeFlags = tNode.flags;
|
||||
const nodeProviderIndexes = tNode.providerIndexes;
|
||||
const tInjectables = tView.data;
|
||||
|
||||
const startInjectables = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
|
||||
const startDirectives = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const cptViewProvidersCount =
|
||||
@ -426,10 +449,10 @@ function searchTokensOnInjector<T>(
|
||||
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
|
||||
if (i < startDirectives && token === providerTokenOrDef ||
|
||||
i >= startDirectives && (providerTokenOrDef as DirectiveDef<any>).type === token) {
|
||||
return getNodeInjectable(tInjectables, injectorView, i, tNode as TElementNode);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NOT_FOUND;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,11 +18,12 @@ import {Type} from '../type';
|
||||
import {getSymbolIterator} from '../util';
|
||||
|
||||
import {assertDefined, assertEqual} from './assert';
|
||||
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
||||
import {NG_ELEMENT_ID} from './fields';
|
||||
import {store, storeCleanupWithContext} from './instructions';
|
||||
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {LViewData, TVIEW} from './interfaces/view';
|
||||
import {assertPreviousIsParent, getOrCreateCurrentQueries, getViewData} from './state';
|
||||
@ -242,31 +243,6 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all the directives for a node and returns index of a directive for a given type.
|
||||
*
|
||||
* @param tNode TNode on which directives are present.
|
||||
* @param currentView The view we are currently processing
|
||||
* @param type Type of a directive to look for.
|
||||
* @returns Index of a found directive or null when none found.
|
||||
*/
|
||||
function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: Type<any>): number|
|
||||
null {
|
||||
const defs = currentView[TVIEW].data;
|
||||
if (defs) {
|
||||
const flags = tNode.flags;
|
||||
const count = flags & TNodeFlags.DirectiveCountMask;
|
||||
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
for (let i = start; i < end; i++) {
|
||||
const def = defs[i] as DirectiveDef<any>;
|
||||
if (def.type === type) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: "read" should be an AbstractType (FW-486)
|
||||
function queryByReadToken(read: any, tNode: TNode, currentView: LViewData): any {
|
||||
@ -274,9 +250,10 @@ function queryByReadToken(read: any, tNode: TNode, currentView: LViewData): any
|
||||
if (typeof factoryFn === 'function') {
|
||||
return factoryFn();
|
||||
} else {
|
||||
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, read as Type<any>);
|
||||
const matchingIdx = locateDirectiveOrProvider(tNode, currentView, read as Type<any>, false);
|
||||
if (matchingIdx !== null) {
|
||||
return currentView[matchingIdx];
|
||||
return getNodeInjectable(
|
||||
currentView[TVIEW].data, currentView, matchingIdx, tNode as TElementNode);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -307,7 +284,8 @@ function queryRead(tNode: TNode, currentView: LViewData, read: any, matchingIdx:
|
||||
return queryByReadToken(read, tNode, currentView);
|
||||
}
|
||||
if (matchingIdx > -1) {
|
||||
return currentView[matchingIdx];
|
||||
return getNodeInjectable(
|
||||
currentView[TVIEW].data, currentView, matchingIdx, tNode as TElementNode);
|
||||
}
|
||||
// if read token and / or strategy is not specified,
|
||||
// detect it using appropriate tNode type
|
||||
@ -326,7 +304,7 @@ function add(
|
||||
if (type === ViewEngine_TemplateRef) {
|
||||
result = queryByTemplateRef(type, tNode, currentView, predicate.read);
|
||||
} else {
|
||||
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, type);
|
||||
const matchingIdx = locateDirectiveOrProvider(tNode, currentView, type, false);
|
||||
if (matchingIdx !== null) {
|
||||
result = queryRead(tNode, currentView, predicate.read, matchingIdx);
|
||||
}
|
||||
|
Reference in New Issue
Block a user