From 0399c6972aed9e3d6198aa997c0a934be1fc9262 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Tue, 10 Jul 2018 10:43:07 +0200 Subject: [PATCH] refactor(ivy): remove content query creation from directive factories (#24811) PR Close #24811 --- .../core/src/core_render3_private_export.ts | 2 + packages/core/src/render3/definition.ts | 20 ++- packages/core/src/render3/di.ts | 4 +- packages/core/src/render3/index.ts | 5 + packages/core/src/render3/instructions.ts | 71 ++++++++--- .../core/src/render3/interfaces/definition.ts | 8 ++ packages/core/src/render3/interfaces/view.ts | 20 ++- .../hello_world/bundle.golden_symbols.json | 6 +- .../bundling/todo/bundle.golden_symbols.json | 6 +- .../render3/compiler_canonical/query_spec.ts | 21 ++-- packages/core/test/render3/query_spec.ts | 117 ++++++++++++------ 11 files changed, 205 insertions(+), 75 deletions(-) diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 85485fbb7c..a6853410d9 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -39,6 +39,7 @@ export { T as ɵT, V as ɵV, Q as ɵQ, + Qr as ɵQr, d as ɵd, P as ɵP, b as ɵb, @@ -69,6 +70,7 @@ export { cR as ɵcR, cr as ɵcr, qR as ɵqR, + ql as ɵql, e as ɵe, p as ɵp, pD as ɵpD, diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index bcdce3f532..0de1e46439 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -48,7 +48,7 @@ export function defineComponent(componentDefinition: { /** * Factory method used to create an instance of directive. */ - factory: () => T | ({0: T} & any[]); /* trying to say T | [T, ...any] */ + factory: () => T; /** * Static attributes to set on host element. @@ -120,6 +120,14 @@ export function defineComponent(componentDefinition: { */ hostBindings?: (directiveIndex: number, elementIndex: number) => void; + /** + * Function to create instances of content queries associated with a given directive. + */ + contentQueries?: (() => void); + + /** Refreshes content queries associated with directives in a given view */ + contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void); + /** * Defines the name that can be used in the template to assign this directive to a variable. * @@ -216,6 +224,8 @@ export function defineComponent(componentDefinition: { factory: componentDefinition.factory, template: componentDefinition.template || null !, hostBindings: componentDefinition.hostBindings || null, + contentQueries: componentDefinition.contentQueries || null, + contentQueriesRefresh: componentDefinition.contentQueriesRefresh || null, attributes: componentDefinition.attributes || null, inputs: invertObject(componentDefinition.inputs, declaredInputs), declaredInputs: declaredInputs, @@ -448,6 +458,14 @@ export const defineDirective = defineComponent as any as(directiveDefinition: */ hostBindings?: (directiveIndex: number, elementIndex: number) => void; + /** + * Function to create instances of content queries associated with a given directive. + */ + contentQueries?: (() => void); + + /** Refreshes content queries associated with directives in a given view */ + contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void); + /** * Defines the name that can be used in the template to assign this directive to a variable. * diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index 63a28cddd8..e6eb323f8b 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -19,7 +19,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie import {Type} from '../type'; import {assertDefined, assertGreaterThan, assertLessThan} from './assert'; -import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; +import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions'; import {VIEWS} from './interfaces/container'; import {ComponentTemplate, DirectiveDefInternal, RenderFlags} from './interfaces/definition'; import {LInjector} from './interfaces/injector'; @@ -378,7 +378,7 @@ export function getOrCreateInjectable( // and matches the given token, return the directive instance. const directiveDef = defs[i] as DirectiveDefInternal; if (directiveDef.type === token && directiveDef.diPublic) { - return getDirectiveInstance(node.view[DIRECTIVES] ![i]); + return node.view[DIRECTIVES] ![i]; } } } diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index b503e7df4f..5734e884ed 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -116,6 +116,11 @@ export { query as Q, queryRefresh as qR, } from './query'; +export { + registerContentQuery as Qr, + loadQueryList as ql, +} from './instructions'; + export { pureFunction0 as f0, pureFunction1 as f1, diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index ff42452007..d5f0e2c4f4 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -8,6 +8,7 @@ import './ng_dev_mode'; +import {QueryList} from '../linker'; import {Sanitizer} from '../sanitization/security'; import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert'; @@ -20,7 +21,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {LQueries} from './interfaces/query'; import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; -import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTEXT, CurrentMatchesList, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; +import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; @@ -29,6 +30,7 @@ import {isDifferent, stringify} from './util'; import {ViewRef} from './view_ref'; + /** * Directive (D) sets a property on all component instances using this constant as a key and the * component's host node (LElement) as the value. This is used in methods like detectChanges to @@ -266,9 +268,11 @@ function refreshView() { tView.firstTemplatePass = firstTemplatePass = false; setHostBindings(tView.hostBindings); + refreshContentQueries(tView); refreshChildComponents(tView.components); } + /** Sets the host bindings for the current view. */ export function setHostBindings(bindings: number[] | null): void { if (bindings != null) { @@ -281,6 +285,18 @@ export function setHostBindings(bindings: number[] | null): void { } } +/** Refreshes content queries for all directives in the given view. */ +function refreshContentQueries(tView: TView): void { + if (tView.contentQueries != null) { + for (let i = 0; i < tView.contentQueries.length; i += 2) { + const directiveDefIdx = tView.contentQueries[i]; + const directiveDef = tView.directives ![directiveDefIdx]; + + directiveDef.contentQueriesRefresh !(directiveDefIdx, tView.contentQueries[i + 1]); + } + } +} + /** Refreshes child components in the current view. */ function refreshChildComponents(components: number[] | null): void { if (components != null) { @@ -315,7 +331,8 @@ export function createLViewData( renderer, // renderer sanitizer || null, // sanitizer null, // tail - -1 // containerIndex + -1, // containerIndex + null, // contentQueries ]; } @@ -882,6 +899,7 @@ export function createTView( pipeDestroyHooks: null, cleanup: null, hostBindings: null, + contentQueries: null, components: null, directiveRegistry: typeof directives === 'function' ? directives() : directives, pipeRegistry: typeof pipes === 'function' ? pipes() : pipes, @@ -1472,7 +1490,7 @@ export function textBinding(index: number, value: T | NO_CHANGE): void { ////////////////////////// /** - * Create a directive. + * Create a directive and their associated content queries. * * NOTE: directives can be created in order other than the index order. They can also * be retrieved before they are created in which case the value will be null. @@ -1481,28 +1499,32 @@ export function textBinding(index: number, value: T | NO_CHANGE): void { * @param directiveDef DirectiveDef object which contains information about the template. */ export function directiveCreate( - index: number, directive: T, + directiveDefIdx: number, directive: T, directiveDef: DirectiveDefInternal| ComponentDefInternal): T { - const instance = baseDirectiveCreate(index, directive, directiveDef); + const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef); ngDevMode && assertDefined(previousOrParentNode.tNode, 'previousOrParentNode.tNode'); const tNode = previousOrParentNode.tNode; const isComponent = (directiveDef as ComponentDefInternal).template; if (isComponent) { - addComponentLogic(index, directive, directiveDef as ComponentDefInternal); + addComponentLogic(directiveDefIdx, directive, directiveDef as ComponentDefInternal); } if (firstTemplatePass) { // Init hooks are queued now so ngOnInit is called in host components before // any projected components. - queueInitHooks(index, directiveDef.onInit, directiveDef.doCheck, tView); + queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView); - if (directiveDef.hostBindings) queueHostBindingForCheck(index); + if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx); } if (tNode && tNode.attrs) { - setInputsFromAttrs(index, instance, directiveDef.inputs, tNode); + setInputsFromAttrs(directiveDefIdx, instance, directiveDef.inputs, tNode); + } + + if (directiveDef.contentQueries) { + directiveDef.contentQueries(); } return instance; @@ -1918,7 +1940,7 @@ export function componentRefresh(directiveIndex: number, adjustedElementIndex // Only attached CheckAlways components or attached, dirty OnPush components should be checked if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) { ngDevMode && assertDataInRange(directiveIndex, directives !); - detectChangesInternal(hostView, element, getDirectiveInstance(directives ![directiveIndex])); + detectChangesInternal(hostView, element, directives ![directiveIndex]); } } @@ -2537,6 +2559,15 @@ export function loadDirective(index: number): T { return directives ![index]; } +export function loadQueryList(queryListIdx: number): QueryList { + ngDevMode && assertDefined( + viewData[CONTENT_QUERIES], + 'Content QueryList array should be defined if reading a query.'); + ngDevMode && assertDataInRange(queryListIdx, viewData[CONTENT_QUERIES] !); + + return viewData[CONTENT_QUERIES] ![queryListIdx]; +} + /** Gets the current binding value and increments the binding index. */ export function consumeBinding(): any { ngDevMode && assertDataInRange(viewData[BINDING_INDEX]); @@ -2586,10 +2617,22 @@ export function getTView(): TView { return tView; } -export function getDirectiveInstance(instanceOrArray: T | [T]): T { - // Directives with content queries store an array in directives[directiveIndex] - // with the instance as the first index - return Array.isArray(instanceOrArray) ? instanceOrArray[0] : instanceOrArray; +/** + * Registers a QueryList, associated with a content query, for later refresh (part of a view + * refresh). + */ +export function registerContentQuery(queryList: QueryList): void { + const savedContentQueriesLength = + (viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList); + if (firstTemplatePass) { + const currentDirectiveIndex = directives !.length - 1; + const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []); + const lastSavedDirectiveIndex = + tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 2] : -1; + if (currentDirectiveIndex !== lastSavedDirectiveIndex) { + tViewContentQueries.push(currentDirectiveIndex, savedContentQueriesLength - 1); + } + } } export function assertPreviousIsParent() { diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 460d33bad4..20ddafad12 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -122,6 +122,14 @@ export interface DirectiveDef { */ factory(): T|[T]; + /** + * Function to create instances of content queries associated with a given directive. + */ + contentQueries: (() => void)|null; + + /** Refreshes content queries associated with directives in a given view */ + contentQueriesRefresh: ((directiveIndex: number, queryIndex: number) => void)|null; + /** Refreshes host bindings on the associated directive. */ hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null; diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 1548b16dd8..3ee8b338b9 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -7,6 +7,7 @@ */ import {Injector} from '../../di/injector'; +import {QueryList} from '../../linker'; import {Sanitizer} from '../../sanitization/security'; import {LContainer} from './container'; @@ -16,7 +17,7 @@ import {LQueries} from './query'; import {Renderer3} from './renderer'; /** Size of LViewData's header. Necessary to adjust for it when setting slots. */ -export const HEADER_OFFSET = 15; +export const HEADER_OFFSET = 16; // Below are constants for LViewData indices to help us look up LViewData members // without having to remember the specific indices. @@ -36,6 +37,7 @@ export const RENDERER = 11; export const SANITIZER = 12; export const TAIL = 13; export const CONTAINER_INDEX = 14; +export const CONTENT_QUERIES = 15; /** * `LViewData` stores all of the information needed to process the instructions as @@ -153,6 +155,13 @@ export interface LViewData extends Array { * in multiple containers, so the parent cannot be shared between view instances). */ [CONTAINER_INDEX]: number; + + /** + * Stores QueryLists associated with content queries of a directive. This data structure is + * filled-in as part of a directive creation process and is later used to retrieve a QueryList to + * be refreshed. + */ + [CONTENT_QUERIES]: QueryList[]|null; } /** Flags associated with an LView (saved in LViewData[FLAGS]) */ @@ -413,6 +422,15 @@ export interface TView { * they will be fed into instructions that expect the raw index (e.g. elementProperty) */ hostBindings: number[]|null; + + + /** + * A list of indices for child directives that have content queries. + * + * Even indices: Directive indices + * Odd indices: Starting index of content queries (stored in CONTENT_QUERIES) for this directive + */ + contentQueries: number[]|null; } /** diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 23c9cce6c6..f2c007b7b3 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -158,9 +158,6 @@ { "name": "getChildLNode" }, - { - "name": "getDirectiveInstance" - }, { "name": "getLViewChild" }, @@ -197,6 +194,9 @@ { "name": "refreshChildComponents" }, + { + "name": "refreshContentQueries" + }, { "name": "refreshDynamicEmbeddedViews" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 07a77163ed..ba013de043 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -446,9 +446,6 @@ { "name": "getCurrentSanitizer" }, - { - "name": "getDirectiveInstance" - }, { "name": "getLViewChild" }, @@ -632,6 +629,9 @@ { "name": "refreshChildComponents" }, + { + "name": "refreshContentQueries" + }, { "name": "refreshDynamicEmbeddedViews" }, diff --git a/packages/core/test/render3/compiler_canonical/query_spec.ts b/packages/core/test/render3/compiler_canonical/query_spec.ts index d8fafb9b72..a3e85d4459 100644 --- a/packages/core/test/render3/compiler_canonical/query_spec.ts +++ b/packages/core/test/render3/compiler_canonical/query_spec.ts @@ -108,18 +108,17 @@ describe('queries', () => { static ngComponentDef = $r3$.ɵdefineComponent({ type: ContentQueryComponent, selectors: [['content-query-component']], - factory: function ContentQueryComponent_Factory() { - return [ - new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false), - $r3$.ɵQ(null, SomeDirective, false) - ]; + factory: function ContentQueryComponent_Factory() { return new ContentQueryComponent(); }, + contentQueries: function ContentQueryComponent_ContentQueries() { + $r3$.ɵQr($r3$.ɵQ(null, SomeDirective, false)); + $r3$.ɵQr($r3$.ɵQ(null, SomeDirective, false)); }, - hostBindings: function ContentQueryComponent_HostBindings( - dirIndex: $number$, elIndex: $number$) { + contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh( + dirIndex: $number$, queryStartIndex: $number$) { let $tmp$: any; - const $instance$ = $r3$.ɵd(dirIndex)[0]; - $r3$.ɵqR($tmp$ = $r3$.ɵd(dirIndex)[1]) && ($instance$.someDir = $tmp$.first); - $r3$.ɵqR($tmp$ = $r3$.ɵd(dirIndex)[2]) && ($instance$.someDirList = $tmp$); + const $instance$ = $r3$.ɵd(dirIndex); + $r3$.ɵqR($tmp$ = $r3$.ɵql(queryStartIndex)) && ($instance$.someDir = $tmp$.first); + $r3$.ɵqR($tmp$ = $r3$.ɵql(queryStartIndex + 1)) && ($instance$.someDirList = $tmp$); }, template: function ContentQueryComponent_Template( rf: $number$, ctx: $ContentQueryComponent$) { @@ -153,7 +152,7 @@ describe('queries', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵE(0, 'content-query-component'); - contentQueryComp = $r3$.ɵd(0)[0]; + contentQueryComp = $r3$.ɵd(0); $r3$.ɵEe(1, 'div', $e2_attrs$); $r3$.ɵe(); } diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index 1a98c2f9a4..e86ce71409 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -12,7 +12,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; import {EventEmitter} from '../..'; import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectViewContainerRef} from '../../src/render3/index'; -import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective} from '../../src/render3/instructions'; +import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadQueryList, registerContentQuery} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {query, queryRefresh} from '../../src/render3/query'; @@ -1684,6 +1684,48 @@ describe('query', () => { describe('content', () => { + it('should support content queries for directives', () => { + let withContentInstance: WithContentDirective; + + class WithContentDirective { + // @ContentChildren('foo') foos; + // TODO(issue/24571): remove '!'. + foos !: QueryList; + + static ngComponentDef = defineDirective({ + type: WithContentDirective, + selectors: [['', 'with-content', '']], + factory: () => new WithContentDirective(), + contentQueries: + () => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); }, + contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { + let tmp: any; + withContentInstance = loadDirective(dirIndex); + queryRefresh(tmp = loadQueryList(queryStartIdx)) && + (withContentInstance.foos = tmp); + } + }); + } + + /** + *
+ * + *
+ * class Cmpt { + * } + */ + const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + elementStart(0, 'div', [AttributeMarker.SelectOnly, 'with-content']); + { element(1, 'span', null, ['foo', '']); } + elementEnd(); + } + }, [WithContentDirective]); + + const fixture = new ComponentFixture(AppComponent); + expect(withContentInstance !.foos.length).toBe(1); + }); + // https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts it('should support view and content queries matching the same element', () => { let withContentComponentInstance: WithContentComponent; @@ -1696,18 +1738,17 @@ describe('query', () => { static ngComponentDef = defineComponent({ type: WithContentComponent, selectors: [['with-content']], - factory: () => { - return [new WithContentComponent(), query(null, ['foo'], true, QUERY_READ_FROM_NODE)]; - }, + factory: () => new WithContentComponent(), + contentQueries: + () => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); }, template: (rf: RenderFlags, ctx: WithContentComponent) => { // intentionally left empty, don't need anything for this test }, - hostBindings: function ContentQueryComponent_HostBindings( - dirIndex: number, elIndex: number) { + contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; - withContentComponentInstance = loadDirective(dirIndex)[0]; - queryRefresh(tmp = loadDirective(dirIndex)[1]) && - (withContentComponentInstance.foos = tmp as QueryList); + withContentComponentInstance = loadDirective(dirIndex); + queryRefresh(tmp = loadQueryList(queryStartIdx)) && + (withContentComponentInstance.foos = tmp); }, }); } @@ -1760,19 +1801,17 @@ describe('query', () => { type: QueryDirective, selectors: [['', 'query', '']], exportAs: 'query', - factory: () => { + factory: () => new QueryDirective(), + contentQueries: () => { // @ContentChildren('foo, bar, baz', {descendants: true}) fooBars: // QueryList; - return [ - new QueryDirective(), query(null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE) - ]; + registerContentQuery(query(null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE)); }, - hostBindings: function ContentQueryComponent_HostBindings( - dirIndex: number, elIndex: number) { + contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; - const instance = loadDirective(dirIndex)[0]; - queryRefresh(tmp = loadDirective(dirIndex)[1]) && - (instance.fooBars = tmp as QueryList); + const instance = loadDirective(dirIndex); + queryRefresh(tmp = loadQueryList(queryStartIdx)) && + (instance.fooBars = tmp); }, }); } @@ -1804,8 +1843,8 @@ describe('query', () => { elementEnd(); } if (rf & RenderFlags.Update) { - outInstance = (load(1))[0] as QueryDirective; - inInstance = (load(5))[0] as QueryDirective; + outInstance = load(1); + inInstance = load(5); } }, [QueryDirective]); @@ -1823,18 +1862,16 @@ describe('query', () => { type: ShallowQueryDirective, selectors: [['', 'shallow-query', '']], exportAs: 'shallow-query', - factory: () => { - // @ContentChildren('foo', {descendants: false}) fooBars: QueryList; - return [ - new ShallowQueryDirective(), query(null, ['foo'], false, QUERY_READ_FROM_NODE) - ]; + factory: () => new ShallowQueryDirective(), + contentQueries: () => { + // @ContentChildren('foo', {descendants: false}) foos: QueryList; + registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE)); }, - hostBindings: function ContentQueryComponent_HostBindings( - dirIndex: number, elIndex: number) { + contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; - const instance = loadDirective(dirIndex)[0]; - queryRefresh(tmp = loadDirective(dirIndex)[1]) && - (instance.foos = tmp as QueryList); + const instance = loadDirective(dirIndex); + queryRefresh(tmp = loadQueryList(queryStartIdx)) && + (instance.foos = tmp); }, }); } @@ -1845,16 +1882,16 @@ describe('query', () => { type: DeepQueryDirective, selectors: [['', 'deep-query', '']], exportAs: 'deep-query', - factory: () => { - // @ContentChildren('foo', {descendants: true}) fooBars: QueryList; - return [new DeepQueryDirective(), query(null, ['foo'], true, QUERY_READ_FROM_NODE)]; + factory: () => new DeepQueryDirective(), + contentQueries: () => { + // @ContentChildren('foo', {descendants: false}) foos: QueryList; + registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); }, - hostBindings: function ContentQueryComponent_HostBindings( - dirIndex: number, elIndex: number) { + contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { let tmp: any; - const instance = loadDirective(dirIndex)[0]; - queryRefresh(tmp = loadDirective(dirIndex)[1]) && - (instance.foos = tmp as QueryList); + const instance = loadDirective(dirIndex); + queryRefresh(tmp = loadQueryList(queryStartIdx)) && + (instance.foos = tmp); }, }); } @@ -1878,8 +1915,8 @@ describe('query', () => { elementEnd(); } if (rf & RenderFlags.Update) { - shallowInstance = (load(1))[0] as ShallowQueryDirective; - deepInstance = (load(2))[0] as DeepQueryDirective; + shallowInstance = load(1); + deepInstance = load(2); } }, [ShallowQueryDirective, DeepQueryDirective]);