fix(ivy): Content Queries inheritance fix (#28324)

Prior to this change contentQueriesRefresh functions that represent refresh logic for @ContentQuery list were not composable, which caused problems in case one Directive inherits another one and both of them contain Content Queries. Due to the fact that we used indices to reference queries in refresh function, results were placed into wrong Queries. In order to avoid that we no longer use indices to reference queries and instead maintain current content query index while iterating through them. This allows us to compose contentQueriesRefresh functions and make inheritance feature work with Content Queries.

PR Close #28324
This commit is contained in:
Andrew Kushnir 2019-01-23 11:54:43 -08:00 committed by Jason Aden
parent ebac5dba38
commit bb94434d85
19 changed files with 224 additions and 190 deletions

View File

@ -1554,14 +1554,14 @@ describe('compiler compliance', () => {
return new (t || ContentQueryComponent)(); return new (t || ContentQueryComponent)();
}, },
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) { contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true), dirIndex); $r3$.ɵcontentQuery(dirIndex, SomeDirective, true);
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false), dirIndex); $r3$.ɵcontentQuery(dirIndex, SomeDirective, false);
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
const instance = $r3$.ɵload(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && ($instance$.someDir = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && ($instance$.someDirList = $tmp$));
}, },
ngContentSelectors: _c0, ngContentSelectors: _c0,
consts: 2, consts: 2,
@ -1613,14 +1613,14 @@ describe('compiler compliance', () => {
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({ ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) { contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$, true), dirIndex); $r3$.ɵcontentQuery(dirIndex, $e0_attrs$, true);
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false), dirIndex); $r3$.ɵcontentQuery(dirIndex, $e1_attrs$, false);
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
const instance = $r3$.ɵload(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRef = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRefs = $tmp$));
}, },
});`; });`;
@ -1666,18 +1666,18 @@ describe('compiler compliance', () => {
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({ ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) { contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$ , true, TemplateRef), dirIndex); $r3$.ɵcontentQuery(dirIndex, $e0_attrs$ , true, TemplateRef);
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true, ElementRef), dirIndex); $r3$.ɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false, ElementRef), dirIndex); $r3$.ɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false, TemplateRef), dirIndex); $r3$.ɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
}, },
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) { contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
const instance = $r3$.ɵload(dirIndex); const instance = $r3$.ɵload(dirIndex);
var $tmp$; var $tmp$;
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRef = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.someDir = $tmp$.first)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.someDir = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 2)))) && (instance.myRefs = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRefs = $tmp$));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 3)))) && (instance.someDirs = $tmp$)); ($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.someDirs = $tmp$));
}, },
});`; });`;

View File

@ -12,6 +12,18 @@ import {NgtscTestEnvironment} from './env';
const trim = (input: string): string => input.replace(/\s+/g, ' ').trim(); const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
const viewQueryRegExp = (descend: boolean, ref?: string): RegExp => {
const maybeRef = ref ? `, ${ref}` : ``;
return new RegExp(`i0\\.ɵviewQuery\\(\\w+, ${descend}${maybeRef}\\)`);
};
const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
const maybeRef = ref ? `, ${ref}` : ``;
return new RegExp(`i0\\.ɵcontentQuery\\(dirIndex, ${predicate}, ${descend}${maybeRef}\\)`);
};
describe('ngtsc behavioral tests', () => { describe('ngtsc behavioral tests', () => {
if (!NgtscTestEnvironment.supported) { if (!NgtscTestEnvironment.supported) {
// These tests should be excluded from the non-Bazel build. // These tests should be excluded from the non-Bazel build.
@ -706,14 +718,6 @@ describe('ngtsc behavioral tests', () => {
}); });
it('should generate queries for components', () => { it('should generate queries for components', () => {
// Helper functions to construct RegExps for output validation
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
const queryRegExp = (fnName: string, descend: boolean, ref?: string | null): RegExp => {
const maybeRef = ref ? `, ${ref}` : ``;
return new RegExp(`i0\\${fnName}\\(\\w+, ${descend}${maybeRef}\\)`);
};
env.tsconfig(); env.tsconfig();
env.write(`test.ts`, ` env.write(`test.ts`, `
import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core';
@ -740,23 +744,10 @@ describe('ngtsc behavioral tests', () => {
expect(jsContents).toMatch(varRegExp('test1')); expect(jsContents).toMatch(varRegExp('test1'));
expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('test2'));
expect(jsContents).toMatch(varRegExp('accessor')); expect(jsContents).toMatch(varRegExp('accessor'));
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, false)`); // match `i0.ɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
expect(jsContents) expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
.toMatch( // match `i0.ɵviewQuery(_c2, true)`
// match `i0.ɵquery(_c0, true, TemplateRef)` expect(jsContents).toMatch(viewQueryRegExp(true));
queryRegExp('query', true, 'TemplateRef'));
expect(jsContents)
.toMatch(
// match `i0.ɵquery(_c0, true)`
queryRegExp('query', true));
expect(jsContents)
.toMatch(
// match `i0.ɵviewQuery(_c0, true)`
queryRegExp('viewQuery', true));
expect(jsContents)
.toMatch(
// match `i0.ɵviewQuery(_c0, true)`
queryRegExp('viewQuery', true));
}); });
it('should handle queries that use forwardRef', () => { it('should handle queries that use forwardRef', () => {
@ -777,8 +768,10 @@ describe('ngtsc behavioral tests', () => {
env.driveMain(); env.driveMain();
const jsContents = env.getContents('test.js'); const jsContents = env.getContents('test.js');
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, true)`); // match `i0.ɵcontentQuery(dirIndex, TemplateRef, true)`
expect(jsContents).toContain(`i0.ɵquery(ViewContainerRef, true)`); expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true));
// match `i0.ɵcontentQuery(dirIndex, ViewContainerRef, true)`
expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true));
}); });
it('should generate host listeners for components', () => { it('should generate host listeners for components', () => {

View File

@ -116,7 +116,6 @@ export class Identifiers {
static i18nPostprocess: o.ExternalReference = {name: 'ɵi18nPostprocess', moduleName: CORE}; static i18nPostprocess: o.ExternalReference = {name: 'ɵi18nPostprocess', moduleName: CORE};
static load: o.ExternalReference = {name: 'ɵload', moduleName: CORE}; static load: o.ExternalReference = {name: 'ɵload', moduleName: CORE};
static loadQueryList: o.ExternalReference = {name: 'ɵloadQueryList', moduleName: CORE};
static pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE}; static pipe: o.ExternalReference = {name: 'ɵpipe', moduleName: CORE};
@ -185,12 +184,11 @@ export class Identifiers {
static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE}; static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE};
static query: o.ExternalReference = {name: 'ɵquery', moduleName: CORE};
static queryRefresh: o.ExternalReference = {name: 'ɵqueryRefresh', moduleName: CORE}; static queryRefresh: o.ExternalReference = {name: 'ɵqueryRefresh', moduleName: CORE};
static viewQuery: o.ExternalReference = {name: 'ɵviewQuery', moduleName: CORE}; static viewQuery: o.ExternalReference = {name: 'ɵviewQuery', moduleName: CORE};
static loadViewQuery: o.ExternalReference = {name: 'ɵloadViewQuery', moduleName: CORE}; static loadViewQuery: o.ExternalReference = {name: 'ɵloadViewQuery', moduleName: CORE};
static registerContentQuery: static contentQuery: o.ExternalReference = {name: 'ɵcontentQuery', moduleName: CORE};
o.ExternalReference = {name: 'ɵregisterContentQuery', moduleName: CORE}; static loadContentQuery: o.ExternalReference = {name: 'ɵloadContentQuery', moduleName: CORE};
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE}; static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};

View File

@ -496,10 +496,6 @@ function prepareQueryParams(query: R3QueryMetadata, constantPool: ConstantPool):
return parameters; return parameters;
} }
function createQueryDefinition(query: R3QueryMetadata, constantPool: ConstantPool): o.Expression {
return o.importExpr(R3.query).callFn(prepareQueryParams(query, constantPool));
}
// Turn a directive selector into an R3-compatible selector for directive def // Turn a directive selector into an R3-compatible selector for directive def
function createDirectiveSelector(selector: string | null): o.Expression { function createDirectiveSelector(selector: string | null): o.Expression {
return asLiteral(core.parseSelectorToR3Selector(selector)); return asLiteral(core.parseSelectorToR3Selector(selector));
@ -519,10 +515,8 @@ function createContentQueriesFunction(
meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression|null { meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression|null {
if (meta.queries.length) { if (meta.queries.length) {
const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => { const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => {
const queryDefinition = createQueryDefinition(query, constantPool); const args = [o.variable('dirIndex'), ...prepareQueryParams(query, constantPool) as any];
return o.importExpr(R3.registerContentQuery) return o.importExpr(R3.contentQuery).callFn(args).toStmt();
.callFn([queryDefinition, o.variable('dirIndex')])
.toStmt();
}); });
const typeName = meta.name; const typeName = meta.name;
const parameters = [new o.FnParam('dirIndex', o.NUMBER_TYPE)]; const parameters = [new o.FnParam('dirIndex', o.NUMBER_TYPE)];
@ -539,10 +533,7 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre
if (meta.queries.length > 0) { if (meta.queries.length > 0) {
const statements: o.Statement[] = []; const statements: o.Statement[] = [];
const typeName = meta.name; const typeName = meta.name;
const parameters = [ const parameters = [new o.FnParam('dirIndex', o.NUMBER_TYPE)];
new o.FnParam('dirIndex', o.NUMBER_TYPE),
new o.FnParam('queryStartIndex', o.NUMBER_TYPE),
];
const directiveInstanceVar = o.variable('instance'); const directiveInstanceVar = o.variable('instance');
// var $tmp$: any; // var $tmp$: any;
const temporary = temporaryAllocator(statements, TEMPORARY_NAME); const temporary = temporaryAllocator(statements, TEMPORARY_NAME);
@ -551,11 +542,8 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre
statements.push(directiveInstanceVar.set(o.importExpr(R3.load).callFn([o.variable('dirIndex')])) statements.push(directiveInstanceVar.set(o.importExpr(R3.load).callFn([o.variable('dirIndex')]))
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final])); .toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
meta.queries.forEach((query: R3QueryMetadata, idx: number) => { meta.queries.forEach((query: R3QueryMetadata) => {
const loadQLArg = o.variable('queryStartIndex'); const getQueryList = o.importExpr(R3.loadContentQuery).callFn([]);
const getQueryList = o.importExpr(R3.loadQueryList).callFn([
idx > 0 ? loadQLArg.plus(o.literal(idx)) : loadQLArg
]);
const assignToTemporary = temporary().set(getQueryList); const assignToTemporary = temporary().set(getQueryList);
const callQueryRefresh = o.importExpr(R3.queryRefresh).callFn([assignToTemporary]); const callQueryRefresh = o.importExpr(R3.queryRefresh).callFn([assignToTemporary]);

View File

@ -47,8 +47,6 @@ export {
listener as ɵlistener, listener as ɵlistener,
text as ɵtext, text as ɵtext,
embeddedViewStart as ɵembeddedViewStart, embeddedViewStart as ɵembeddedViewStart,
query as ɵquery,
registerContentQuery as ɵregisterContentQuery,
projection as ɵprojection, projection as ɵprojection,
bind as ɵbind, bind as ɵbind,
interpolation1 as ɵinterpolation1, interpolation1 as ɵinterpolation1,
@ -84,7 +82,8 @@ export {
queryRefresh as ɵqueryRefresh, queryRefresh as ɵqueryRefresh,
viewQuery as ɵviewQuery, viewQuery as ɵviewQuery,
loadViewQuery as ɵloadViewQuery, loadViewQuery as ɵloadViewQuery,
loadQueryList as ɵloadQueryList, contentQuery as ɵcontentQuery,
loadContentQuery as ɵloadContentQuery,
elementEnd as ɵelementEnd, elementEnd as ɵelementEnd,
elementProperty as ɵelementProperty, elementProperty as ɵelementProperty,
componentHostSyntheticProperty as ɵcomponentHostSyntheticProperty, componentHostSyntheticProperty as ɵcomponentHostSyntheticProperty,

View File

@ -136,7 +136,7 @@ export function defineComponent<T>(componentDefinition: {
contentQueries?: ((dirIndex: number) => void); contentQueries?: ((dirIndex: number) => void);
/** Refreshes content queries associated with directives in a given view */ /** Refreshes content queries associated with directives in a given view */
contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void); contentQueriesRefresh?: ((directiveIndex: number) => void);
/** /**
* Defines the name that can be used in the template to assign this directive to a variable. * Defines the name that can be used in the template to assign this directive to a variable.

View File

@ -103,9 +103,9 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
const superContentQueries = superDef.contentQueries; const superContentQueries = superDef.contentQueries;
if (superContentQueries) { if (superContentQueries) {
if (prevContentQueries) { if (prevContentQueries) {
definition.contentQueries = (dirIndex: number) => { definition.contentQueries = (directiveIndex: number) => {
superContentQueries(dirIndex); superContentQueries(directiveIndex);
prevContentQueries(dirIndex); prevContentQueries(directiveIndex);
}; };
} else { } else {
definition.contentQueries = superContentQueries; definition.contentQueries = superContentQueries;
@ -117,9 +117,9 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
const superContentQueriesRefresh = superDef.contentQueriesRefresh; const superContentQueriesRefresh = superDef.contentQueriesRefresh;
if (superContentQueriesRefresh) { if (superContentQueriesRefresh) {
if (prevContentQueriesRefresh) { if (prevContentQueriesRefresh) {
definition.contentQueriesRefresh = (directiveIndex: number, queryIndex: number) => { definition.contentQueriesRefresh = (directiveIndex: number) => {
superContentQueriesRefresh(directiveIndex, queryIndex); superContentQueriesRefresh(directiveIndex);
prevContentQueriesRefresh(directiveIndex, queryIndex); prevContentQueriesRefresh(directiveIndex);
}; };
} else { } else {
definition.contentQueriesRefresh = superContentQueriesRefresh; definition.contentQueriesRefresh = superContentQueriesRefresh;

View File

@ -122,15 +122,12 @@ export {
} from './pipe'; } from './pipe';
export { export {
query,
queryRefresh, queryRefresh,
viewQuery, viewQuery,
loadViewQuery, loadViewQuery,
contentQuery,
loadContentQuery,
} from './query'; } from './query';
export {
registerContentQuery,
loadQueryList,
} from './instructions';
export { export {
pureFunction0, pureFunction0,

View File

@ -36,7 +36,7 @@ import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLA
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation'; import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getCurrentViewQueryIndex, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentViewQueryIndex, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state'; import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getCurrentQueryIndex, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings'; import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {BoundPlayerFactory} from './styling/player_factory'; import {BoundPlayerFactory} from './styling/player_factory';
import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util'; import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util';
@ -137,12 +137,11 @@ export function setHostBindings(tView: TView, viewData: LView): void {
/** Refreshes content queries for all directives in the given view. */ /** Refreshes content queries for all directives in the given view. */
function refreshContentQueries(tView: TView): void { function refreshContentQueries(tView: TView): void {
if (tView.contentQueries != null) { if (tView.contentQueries != null) {
for (let i = 0; i < tView.contentQueries.length; i += 2) { setCurrentQueryIndex(0);
for (let i = 0; i < tView.contentQueries.length; i++) {
const directiveDefIdx = tView.contentQueries[i]; const directiveDefIdx = tView.contentQueries[i];
const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>; const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>;
directiveDef.contentQueriesRefresh !(directiveDefIdx - HEADER_OFFSET);
directiveDef.contentQueriesRefresh !(
directiveDefIdx - HEADER_OFFSET, tView.contentQueries[i + 1]);
} }
} }
} }
@ -2762,7 +2761,7 @@ export function checkView<T>(hostView: LView, component: T) {
function executeViewQueryFn<T>(lView: LView, tView: TView, component: T): void { function executeViewQueryFn<T>(lView: LView, tView: TView, component: T): void {
const viewQuery = tView.viewQuery; const viewQuery = tView.viewQuery;
if (viewQuery) { if (viewQuery) {
setCurrentViewQueryIndex(tView.viewQueryStartIndex); setCurrentQueryIndex(tView.viewQueryStartIndex);
viewQuery(getRenderFlags(lView), component); viewQuery(getRenderFlags(lView), component);
} }
} }
@ -3111,16 +3110,6 @@ export function reference<T>(index: number) {
return loadInternal<T>(contextLView, index); return loadInternal<T>(contextLView, index);
} }
export function loadQueryList<T>(queryListIdx: number): QueryList<T> {
const lView = getLView();
ngDevMode &&
assertDefined(
lView[CONTENT_QUERIES], 'Content QueryList array should be defined if reading a query.');
ngDevMode && assertDataInRange(lView[CONTENT_QUERIES] !, queryListIdx);
return lView[CONTENT_QUERIES] ![queryListIdx];
}
/** Retrieves a value from current `viewData`. */ /** Retrieves a value from current `viewData`. */
export function load<T>(index: number): T { export function load<T>(index: number): T {
return loadInternal<T>(getLView(), index); return loadInternal<T>(getLView(), index);
@ -3170,26 +3159,6 @@ export function injectAttribute(attrNameToInject: string): string|null {
return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject); return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject);
} }
/**
* Registers a QueryList, associated with a content query, for later refresh (part of a view
* refresh).
*/
export function registerContentQuery<Q>(
queryList: QueryList<Q>, currentDirectiveIndex: number): void {
const viewData = getLView();
const tView = viewData[TVIEW];
const savedContentQueriesLength =
(viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList);
if (getFirstTemplatePass()) {
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 const CLEAN_PROMISE = _CLEAN_PROMISE; export const CLEAN_PROMISE = _CLEAN_PROMISE;
function initializeTNodeInputs(tNode: TNode | null): PropertyAliases|null { function initializeTNodeInputs(tNode: TNode | null): PropertyAliases|null {

View File

@ -134,7 +134,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
contentQueries: ((directiveIndex: number) => void)|null; contentQueries: ((directiveIndex: number) => void)|null;
/** Refreshes content queries associated with directives in a given view */ /** Refreshes content queries associated with directives in a given view */
contentQueriesRefresh: ((directiveIndex: number, queryIndex: number) => void)|null; contentQueriesRefresh: ((directiveIndex: number) => void)|null;
/** Refreshes host bindings on the associated directive. */ /** Refreshes host bindings on the associated directive. */
hostBindings: HostBindingsFunction<T>|null; hostBindings: HostBindingsFunction<T>|null;

View File

@ -509,9 +509,6 @@ export interface TView {
/** /**
* A list of indices for child directives that have content queries. * 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; contentQueries: number[]|null;
} }

View File

@ -40,7 +40,6 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵnextContext': r3.nextContext, 'ɵnextContext': r3.nextContext,
'ɵcontainerRefreshStart': r3.containerRefreshStart, 'ɵcontainerRefreshStart': r3.containerRefreshStart,
'ɵcontainerRefreshEnd': r3.containerRefreshEnd, 'ɵcontainerRefreshEnd': r3.containerRefreshEnd,
'ɵloadQueryList': r3.loadQueryList,
'ɵnamespaceHTML': r3.namespaceHTML, 'ɵnamespaceHTML': r3.namespaceHTML,
'ɵnamespaceMathML': r3.namespaceMathML, 'ɵnamespaceMathML': r3.namespaceMathML,
'ɵnamespaceSVG': r3.namespaceSVG, 'ɵnamespaceSVG': r3.namespaceSVG,
@ -87,11 +86,11 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵpipeBindV': r3.pipeBindV, 'ɵpipeBindV': r3.pipeBindV,
'ɵprojectionDef': r3.projectionDef, 'ɵprojectionDef': r3.projectionDef,
'ɵpipe': r3.pipe, 'ɵpipe': r3.pipe,
'ɵquery': r3.query,
'ɵqueryRefresh': r3.queryRefresh, 'ɵqueryRefresh': r3.queryRefresh,
'ɵviewQuery': r3.viewQuery, 'ɵviewQuery': r3.viewQuery,
'ɵloadViewQuery': r3.loadViewQuery, 'ɵloadViewQuery': r3.loadViewQuery,
'ɵregisterContentQuery': r3.registerContentQuery, 'ɵcontentQuery': r3.contentQuery,
'ɵloadContentQuery': r3.loadContentQuery,
'ɵreference': r3.reference, 'ɵreference': r3.reference,
'ɵelementStyling': r3.elementStyling, 'ɵelementStyling': r3.elementStyling,
'ɵelementHostAttrs': r3.elementHostAttrs, 'ɵelementHostAttrs': r3.elementHostAttrs,

View File

@ -13,7 +13,7 @@ import {Type} from '../interface/type';
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
import {QueryList} from '../linker/query_list'; import {QueryList} from '../linker/query_list';
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
import {assertDefined, assertEqual} from '../util/assert'; import {assertDataInRange, assertDefined, assertEqual} from '../util/assert';
import {assertPreviousIsParent} from './assert'; import {assertPreviousIsParent} from './assert';
import {getNodeInjectable, locateDirectiveOrProvider} from './di'; import {getNodeInjectable, locateDirectiveOrProvider} from './di';
@ -23,8 +23,8 @@ import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, 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 {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view'; import {CONTENT_QUERIES, HEADER_OFFSET, LView, TVIEW} from './interfaces/view';
import {getCurrentViewQueryIndex, getIsParent, getLView, getOrCreateCurrentQueries, setCurrentViewQueryIndex} from './state'; import {getCurrentQueryIndex, getFirstTemplatePass, getIsParent, getLView, getOrCreateCurrentQueries, setCurrentQueryIndex} from './state';
import {isContentQueryHost} from './util'; import {isContentQueryHost} from './util';
import {createElementRef, createTemplateRef} from './view_engine_compatibility'; import {createElementRef, createTemplateRef} from './view_engine_compatibility';
@ -405,10 +405,10 @@ export function viewQuery<T>(
if (tView.firstTemplatePass) { if (tView.firstTemplatePass) {
tView.expandoStartIndex++; tView.expandoStartIndex++;
} }
const index = getCurrentViewQueryIndex(); const index = getCurrentQueryIndex();
const viewQuery: QueryList<T> = query<T>(predicate, descend, read); const viewQuery: QueryList<T> = query<T>(predicate, descend, read);
store(index - HEADER_OFFSET, viewQuery); store(index - HEADER_OFFSET, viewQuery);
setCurrentViewQueryIndex(index + 1); setCurrentQueryIndex(index + 1);
return viewQuery; return viewQuery;
} }
@ -416,7 +416,49 @@ export function viewQuery<T>(
* Loads current View Query and moves the pointer/index to the next View Query in LView. * Loads current View Query and moves the pointer/index to the next View Query in LView.
*/ */
export function loadViewQuery<T>(): T { export function loadViewQuery<T>(): T {
const index = getCurrentViewQueryIndex(); const index = getCurrentQueryIndex();
setCurrentViewQueryIndex(index + 1); setCurrentQueryIndex(index + 1);
return load<T>(index - HEADER_OFFSET); return load<T>(index - HEADER_OFFSET);
} }
/**
* Registers a QueryList, associated with a content query, for later refresh (part of a view
* refresh).
*
* @param directiveIndex Current directive index
* @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 contentQuery<T>(
directiveIndex: number, predicate: Type<any>| string[], descend?: boolean,
// TODO: "read" should be an AbstractType (FW-486)
read?: any): QueryList<T> {
const lView = getLView();
const tView = lView[TVIEW];
const contentQuery: QueryList<T> = query<T>(predicate, descend, read);
(lView[CONTENT_QUERIES] || (lView[CONTENT_QUERIES] = [])).push(contentQuery);
if (getFirstTemplatePass()) {
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
const lastSavedDirectiveIndex =
tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 1] : -1;
if (directiveIndex !== lastSavedDirectiveIndex) {
tViewContentQueries.push(directiveIndex);
}
}
return contentQuery;
}
export function loadContentQuery<T>(): QueryList<T> {
const lView = getLView();
ngDevMode &&
assertDefined(
lView[CONTENT_QUERIES], 'Content QueryList array should be defined if reading a query.');
const index = getCurrentQueryIndex();
ngDevMode && assertDataInRange(lView[CONTENT_QUERIES] !, index);
setCurrentQueryIndex(index + 1);
return lView[CONTENT_QUERIES] ![index];
}

View File

@ -257,18 +257,18 @@ export function setBindingRoot(value: number) {
} }
/** /**
* Current index of a View Query which needs to be processed next. * Current index of a View or Content Query which needs to be processed next.
* We iterate over the list of View Queries stored in LView and increment current query index. * We iterate over the list of Queries and increment current query index at every step.
*/ */
let viewQueryIndex: number = 0; let currentQueryIndex: number = 0;
export function getCurrentViewQueryIndex(): number { export function getCurrentQueryIndex(): number {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) // top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewQueryIndex; return currentQueryIndex;
} }
export function setCurrentViewQueryIndex(value: number): void { export function setCurrentQueryIndex(value: number): void {
viewQueryIndex = value; currentQueryIndex = value;
} }
/** /**

View File

@ -408,7 +408,7 @@
"name": "setCurrentDirectiveDef" "name": "setCurrentDirectiveDef"
}, },
{ {
"name": "setCurrentViewQueryIndex" "name": "setCurrentQueryIndex"
}, },
{ {
"name": "setFirstTemplatePass" "name": "setFirstTemplatePass"

View File

@ -1134,7 +1134,7 @@
"name": "setCurrentDirectiveDef" "name": "setCurrentDirectiveDef"
}, },
{ {
"name": "setCurrentViewQueryIndex" "name": "setCurrentQueryIndex"
}, },
{ {
"name": "setDirectiveDirty" "name": "setDirectiveDirty"

View File

@ -9,8 +9,8 @@
import {ElementRef, QueryList, ViewContainerRef} from '@angular/core'; import {ElementRef, QueryList, ViewContainerRef} from '@angular/core';
import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index'; import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index';
import {allocHostVars, bind, directiveInject, element, elementAttribute, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, loadQueryList, registerContentQuery, elementHostAttrs} from '../../src/render3/instructions'; import {allocHostVars, bind, directiveInject, element, elementAttribute, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, elementHostAttrs} from '../../src/render3/instructions';
import {query, queryRefresh} from '../../src/render3/query'; import {loadContentQuery, contentQuery, queryRefresh} from '../../src/render3/query';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function'; import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
import {sanitizeUrl, sanitizeUrlOrResourceUrl, sanitizeHtml} from '../../src/sanitization/sanitization'; import {sanitizeUrl, sanitizeUrlOrResourceUrl, sanitizeHtml} from '../../src/sanitization/sanitization';
@ -1009,11 +1009,11 @@ describe('host bindings', () => {
elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true); elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true);
} }
}, },
contentQueries: (dirIndex) => { registerContentQuery(query(['foo']), dirIndex); }, contentQueries: (dirIndex: number) => { contentQuery(dirIndex, ['foo']); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<HostBindingWithContentChildren>(dirIndex); const instance = load<HostBindingWithContentChildren>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && (instance.foos = tmp); queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.foos = tmp);
}, },
template: (rf: RenderFlags, cmp: HostBindingWithContentChildren) => {} template: (rf: RenderFlags, cmp: HostBindingWithContentChildren) => {}
}); });

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Inject, InjectionToken, QueryList} from '../../src/core'; import {ElementRef, Inject, InjectionToken, QueryList, ɵAttributeMarker as AttributeMarker} from '../../src/core';
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, loadViewQuery, queryRefresh, viewQuery} from '../../src/render3/index'; import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, contentQuery, defineBase, defineComponent, defineDirective, directiveInject, element, elementEnd, elementProperty, elementStart, load, loadContentQuery, loadViewQuery, queryRefresh, viewQuery} from '../../src/render3/index';
import {ComponentFixture, createComponent, getDirectiveOnNode} from './render_util'; import {ComponentFixture, createComponent, getDirectiveOnNode} from './render_util';
@ -549,14 +549,14 @@ describe('InheritDefinitionFeature', () => {
}); });
it('should compose contentQueriesRefresh', () => { it('should compose contentQueriesRefresh', () => {
const log: Array<[string, number, number]> = []; const log: Array<[string, number]> = [];
class SuperDirective { class SuperDirective {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SuperDirective, type: SuperDirective,
selectors: [['', 'superDir', '']], selectors: [['', 'superDir', '']],
contentQueriesRefresh: (directiveIndex: number, queryIndex: number) => { contentQueriesRefresh: (directiveIndex: number) => {
log.push(['super', directiveIndex, queryIndex]); log.push(['super', directiveIndex]);
}, },
factory: () => new SuperDirective(), factory: () => new SuperDirective(),
}); });
@ -566,8 +566,8 @@ describe('InheritDefinitionFeature', () => {
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
type: SubDirective, type: SubDirective,
selectors: [['', 'subDir', '']], selectors: [['', 'subDir', '']],
contentQueriesRefresh: (directiveIndex: number, queryIndex: number) => { contentQueriesRefresh: (directiveIndex: number) => {
log.push(['sub', directiveIndex, queryIndex]); log.push(['sub', directiveIndex]);
}, },
factory: () => new SubDirective(), factory: () => new SubDirective(),
features: [InheritDefinitionFeature] features: [InheritDefinitionFeature]
@ -576,9 +576,68 @@ describe('InheritDefinitionFeature', () => {
const subDef = SubDirective.ngDirectiveDef as DirectiveDef<any>; const subDef = SubDirective.ngDirectiveDef as DirectiveDef<any>;
subDef.contentQueriesRefresh !(1, 2); subDef.contentQueriesRefresh !(1);
expect(log).toEqual([['super', 1, 2], ['sub', 1, 2]]); expect(log).toEqual([['super', 1], ['sub', 1]]);
});
it('should compose contentQueries and contentQueriesRefresh', () => {
let dirInstance: SubDirective;
class SuperDirective {
// @ContentChildren('foo')
foos !: QueryList<ElementRef>;
static ngDirectiveDef = defineDirective({
type: SuperDirective,
selectors: [['', 'super-dir', '']],
factory: () => new SuperDirective(),
contentQueries: (dirIndex: number) => { contentQuery(dirIndex, ['foo'], true); },
contentQueriesRefresh: (dirIndex: number) => {
let tmp: any;
const instance = load<SuperDirective>(dirIndex);
queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.foos = tmp);
}
});
}
class SubDirective extends SuperDirective {
// @ContentChildren('bar')
bars !: QueryList<ElementRef>;
static ngDirectiveDef = defineDirective({
type: SubDirective,
selectors: [['', 'sub-dir', '']],
factory: () => new SubDirective(),
contentQueries: (dirIndex: number) => { contentQuery(dirIndex, ['bar'], true); },
contentQueriesRefresh: (dirIndex: number) => {
let tmp: any;
dirInstance = load<SubDirective>(dirIndex);
queryRefresh(tmp = loadContentQuery<ElementRef>()) && (dirInstance.bars = tmp);
},
features: [InheritDefinitionFeature]
});
}
/**
* <div sub-dir>
* <span #foo></span>
* <span #bar></span>
* </div>
*/
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div', [AttributeMarker.SelectOnly, 'sub-dir']);
{
element(1, 'span', null, ['foo', '']);
element(3, 'span', null, ['bar', '']);
}
elementEnd();
}
}, 5, 0, [SubDirective]);
const fixture = new ComponentFixture(AppComponent);
expect(dirInstance !.foos.length).toBe(1);
expect(dirInstance !.bars.length).toBe(1);
}); });
it('should throw if inheriting a component from a directive', () => { it('should throw if inheriting a component from a directive', () => {

View File

@ -13,9 +13,9 @@ import {EventEmitter} from '../..';
import {AttributeMarker, ProvidersFeature, defineComponent, defineDirective, detectChanges} from '../../src/render3/index'; import {AttributeMarker, ProvidersFeature, defineComponent, defineDirective, detectChanges} from '../../src/render3/index';
import {getNativeByIndex} from '../../src/render3/util'; import {getNativeByIndex} from '../../src/render3/util';
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template, text} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, reference, template, text} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {query, queryRefresh, viewQuery, loadViewQuery} from '../../src/render3/query'; import {queryRefresh, viewQuery, loadViewQuery, contentQuery, loadContentQuery} from '../../src/render3/query';
import {getLView} from '../../src/render3/state'; import {getLView} from '../../src/render3/state';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound'; import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
@ -2282,12 +2282,11 @@ describe('query', () => {
type: WithContentDirective, type: WithContentDirective,
selectors: [['', 'with-content', '']], selectors: [['', 'with-content', '']],
factory: () => new WithContentDirective(), factory: () => new WithContentDirective(),
contentQueries: (dirIndex) => { registerContentQuery(query(['foo'], true), dirIndex); }, contentQueries: (dirIndex: number) => { contentQuery(dirIndex, ['foo'], true); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
withContentInstance = load<WithContentDirective>(dirIndex); withContentInstance = load<WithContentDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (withContentInstance.foos = tmp);
(withContentInstance.foos = tmp);
} }
}); });
} }
@ -2303,12 +2302,11 @@ describe('query', () => {
template: function(rf: RenderFlags, ctx: any) {}, template: function(rf: RenderFlags, ctx: any) {},
consts: 0, consts: 0,
vars: 0, vars: 0,
contentQueries: (dirIndex) => { registerContentQuery(query(['foo'], false), dirIndex); }, contentQueries: (dirIndex: number) => { contentQuery(dirIndex, ['foo'], false); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
shallowCompInstance = load<ShallowComp>(dirIndex); shallowCompInstance = load<ShallowComp>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (shallowCompInstance.foos = tmp);
(shallowCompInstance.foos = tmp);
} }
}); });
} }
@ -2527,16 +2525,15 @@ describe('query', () => {
selectors: [['', 'query', '']], selectors: [['', 'query', '']],
exportAs: ['query'], exportAs: ['query'],
factory: () => new QueryDirective(), factory: () => new QueryDirective(),
contentQueries: (dirIndex) => { contentQueries: (dirIndex: number) => {
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars: // @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
// QueryList<ElementRef>; // QueryList<ElementRef>;
registerContentQuery(query(['foo', 'bar', 'baz'], true), dirIndex); contentQuery(dirIndex, ['foo', 'bar', 'baz'], true);
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<QueryDirective>(dirIndex); const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.fooBars = tmp);
(instance.fooBars = tmp);
}, },
}); });
} }
@ -2591,16 +2588,15 @@ describe('query', () => {
selectors: [['', 'query', '']], selectors: [['', 'query', '']],
exportAs: ['query'], exportAs: ['query'],
factory: () => new QueryDirective(), factory: () => new QueryDirective(),
contentQueries: (dirIndex) => { contentQueries: (dirIndex: number) => {
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars: // @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
// QueryList<ElementRef>; // QueryList<ElementRef>;
registerContentQuery(query(['foo'], false), dirIndex); contentQuery(dirIndex, ['foo'], false);
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<QueryDirective>(dirIndex); const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.fooBars = tmp);
(instance.fooBars = tmp);
}, },
}); });
} }
@ -2647,16 +2643,15 @@ describe('query', () => {
selectors: [['', 'query', '']], selectors: [['', 'query', '']],
exportAs: ['query'], exportAs: ['query'],
factory: () => new QueryDirective(), factory: () => new QueryDirective(),
contentQueries: (dirIndex) => { contentQueries: (dirIndex: number) => {
// @ContentChildren('foo', {descendants: true}) fooBars: // @ContentChildren('foo', {descendants: true}) fooBars:
// QueryList<ElementRef>; // QueryList<ElementRef>;
registerContentQuery(query(['foo'], false), dirIndex); contentQuery(dirIndex, ['foo'], false);
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<QueryDirective>(dirIndex); const instance = load<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.fooBars = tmp);
(instance.fooBars = tmp);
}, },
}); });
} }
@ -2703,15 +2698,14 @@ describe('query', () => {
selectors: [['', 'shallow-query', '']], selectors: [['', 'shallow-query', '']],
exportAs: ['shallow-query'], exportAs: ['shallow-query'],
factory: () => new ShallowQueryDirective(), factory: () => new ShallowQueryDirective(),
contentQueries: (dirIndex) => { contentQueries: (dirIndex: number) => {
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>; // @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
registerContentQuery(query(['foo'], false), dirIndex); contentQuery(dirIndex, ['foo'], false);
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<ShallowQueryDirective>(dirIndex); const instance = load<ShallowQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.foos = tmp);
(instance.foos = tmp);
}, },
}); });
} }
@ -2723,15 +2717,14 @@ describe('query', () => {
selectors: [['', 'deep-query', '']], selectors: [['', 'deep-query', '']],
exportAs: ['deep-query'], exportAs: ['deep-query'],
factory: () => new DeepQueryDirective(), factory: () => new DeepQueryDirective(),
contentQueries: (dirIndex) => { contentQueries: (dirIndex: number) => {
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>; // @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
registerContentQuery(query(['foo'], true), dirIndex); contentQuery(dirIndex, ['foo'], true);
}, },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => { contentQueriesRefresh: (dirIndex: number) => {
let tmp: any; let tmp: any;
const instance = load<DeepQueryDirective>(dirIndex); const instance = load<DeepQueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) && queryRefresh(tmp = loadContentQuery<ElementRef>()) && (instance.foos = tmp);
(instance.foos = tmp);
}, },
}); });
} }