feat(ivy): add support for content query (#21912)

PR Close #21912
This commit is contained in:
Kara Erickson
2018-01-30 15:37:01 -08:00
committed by Jason Aden
parent 0365592119
commit 81306c1f61
8 changed files with 138 additions and 40 deletions

View File

@ -13,7 +13,7 @@ import {ComponentRef as viewEngine_ComponentRef} from '../linker/component_facto
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef} from '../linker/view_ref';
import {assertNotNull} from './assert';
import {NG_HOST_SYMBOL, createError, createLView, createTView, directiveCreate, enterView, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
import {NG_HOST_SYMBOL, createError, createLView, createTView, directiveCreate, enterView, getDirectiveInstance, hostElement, leaveView, locateHostElement, renderComponentOrTemplate} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
@ -178,7 +178,7 @@ export function renderComponent<T>(
// Create element node at index 0 in data array
hostElement(hostNode, componentDef);
// Create directive instance with n() and store at index 1 in data array (el is 0)
component = directiveCreate(1, componentDef.n(), componentDef);
component = getDirectiveInstance(directiveCreate(1, componentDef.n(), componentDef));
} finally {
leaveView(oldView);
}

View File

@ -18,7 +18,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
import {Type} from '../type';
import {assertLessThan} from './assert';
import {assertPreviousIsParent, getPreviousOrParentNode, getRenderer, renderEmbeddedTemplate} from './instructions';
import {assertPreviousIsParent, getDirectiveInstance, getPreviousOrParentNode, getRenderer, renderEmbeddedTemplate} from './instructions';
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode} from './interfaces/node';
@ -293,7 +293,7 @@ export function getOrCreateInjectable<T>(
// and matches the given token, return the directive instance.
const directiveDef = tData[i] as DirectiveDef<any>;
if (directiveDef.diPublic && directiveDef.type == token) {
return node.view.data[i];
return getDirectiveInstance(node.view.data[i]);
}
}
}

View File

@ -1185,7 +1185,7 @@ export function componentRefresh<T>(directiveIndex: number, elementIndex: number
ngDevMode && assertNodeType(element, LNodeFlags.Element);
ngDevMode && assertNotEqual(element.data, null, 'isComponent');
ngDevMode && assertDataInRange(directiveIndex);
const directive = data[directiveIndex];
const directive = getDirectiveInstance<T>(data[directiveIndex]);
const hostView = element.data !;
ngDevMode && assertNotEqual(hostView, null, 'hostView');
const oldView = enterView(hostView, element);
@ -1858,6 +1858,12 @@ export function getRenderer(): Renderer3 {
return renderer;
}
export function getDirectiveInstance<T>(instanceOrArray: T | [T]): T {
// Directives with content queries store an array in data[directiveIndex]
// with the instance as the first index
return Array.isArray(instanceOrArray) ? instanceOrArray[0] : instanceOrArray;
}
export function assertPreviousIsParent() {
assertEqual(isParent, true, 'isParent');
}

View File

@ -60,12 +60,15 @@ export interface DirectiveDef<T> {
readonly exportAs: string|null;
/**
* factory function used to create a new directive instance.
* Factory function used to create a new directive instance.
*
* Usually returns the directive instance, but if the directive has a content query,
* it instead returns an array that contains the instance as well as content query data.
*
* NOTE: this property is short (1 char) because it is used in
* component templates which is sensitive to size.
*/
n(): T;
n(): T|[T];
/**
* Refreshes host bindings on the associated directive. Also calls lifecycle hooks
@ -108,7 +111,7 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
export interface DirectiveDefArgs<T> {
type: Type<T>;
factory: () => T;
factory: () => T | [T];
inputs?: {[P in keyof T]?: string};
outputs?: {[P in keyof T]?: string};
methods?: {[P in keyof T]?: string};

View File

@ -17,7 +17,7 @@ import {getSymbolIterator} from '../util';
import {assertEqual, assertNotNull} from './assert';
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
import {assertPreviousIsParent, getCurrentQueries} from './instructions';
import {assertPreviousIsParent, getCurrentQueries, memory} from './instructions';
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, LNodeFlags, TNode, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
@ -370,13 +370,27 @@ class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
export type QueryList<T> = viewEngine_QueryList<T>;
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
/**
* Creates and returns a QueryList.
*
* @param memoryIndex The index in memory where the QueryList should be saved. If null,
* this is is a content query and the QueryList will be saved later through directiveCreate.
* @param predicate The type for which the query will search
* @param descend Whether or not to descend into children
* @param read What to save in the query
* @returns QueryList<T>
*/
export function query<T>(
predicate: Type<any>| string[], descend?: boolean,
memoryIndex: number | null, predicate: Type<any>| string[], descend?: boolean,
read?: QueryReadType<T>| Type<T>): QueryList<T> {
ngDevMode && assertPreviousIsParent();
const queryList = new QueryList<T>();
const queries = getCurrentQueries(LQueries_);
queries.track(queryList, predicate, descend, read);
if (memoryIndex != null) {
memory(memoryIndex, queryList);
}
return queryList;
}