diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index 6b89ea51fe..fc72d7ea21 100644 --- a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts @@ -570,7 +570,7 @@ A.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", "a", ""]] });` UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); }; // TRANSPILED UndecoratedBase.ɵdir = ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { - ɵngcc0.ɵɵstaticViewQuery(_c0, true); + ɵngcc0.ɵɵviewQuery(_c0, 3); } if (rf & 2) { let _t; ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js index e7053157b8..c919901e24 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_content_query.js @@ -3,8 +3,9 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3); - $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1); + $r3$.ɵɵcontentQuery( + dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__); + $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js index 1ece02dd91..46c3646ff2 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/static_view_query.js @@ -5,7 +5,7 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵstaticViewQuery(SomeDirective, 3); + $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__); $r3$.ɵɵviewQuery($refs$, 1); } if (rf & 2) { diff --git a/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts index 0d90b52fac..78234c85e3 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {AttributeMarker, SelectorFlags} from '@angular/compiler/src/core'; +import {QueryFlags} from '@angular/compiler/src/render3/view/compiler'; import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder} from './i18n_helpers'; const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ @@ -35,6 +36,9 @@ const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ // E.g. `__SelectorFlags.ELEMENT__` flagUnion(/__SelectorFlags\.([^_]+)__/, (_match, member) => getSelectorFlag(member)), + + // E.g. `__QueryFlags.ELEMENT__` + flagUnion(/__QueryFlags\.([^_]+)__/, (_match, member) => getQueryFlag(member)), ]; /** @@ -107,6 +111,21 @@ function getSelectorFlag(member: string): number { return marker; } +const QueryFlagsMap: Record = { + none: QueryFlags.none, + descendants: QueryFlags.descendants, + isStatic: QueryFlags.isStatic, + emitDistinctChangesOnly: QueryFlags.emitDistinctChangesOnly, +}; + +function getQueryFlag(member: string): number { + const marker = QueryFlagsMap[member]; + if (typeof marker !== 'number') { + throw new Error('Unknown SelectorFlag: ' + member); + } + return marker; +} + function stringParam() { return /'([^']*?[^\\])'/; } diff --git a/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts index 80989aaa55..d5a15fda76 100644 --- a/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_compiler_compliance_spec.ts @@ -1746,7 +1746,7 @@ describe('compiler compliance', () => { selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵstaticViewQuery(SomeDirective, 3); + $r3$.ɵɵviewQuery(SomeDirective, 3); $r3$.ɵɵviewQuery($refs$, 1); } if (rf & 2) { @@ -1992,7 +1992,7 @@ describe('compiler compliance', () => { selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 3); $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1); } if (rf & 2) { diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index 8ac2250a30..a588b90165 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -40,6 +40,7 @@ export interface Query { isViewQuery: boolean; selector: any; static?: boolean; + emitDistinctChangesOnly: boolean; } export const createContentChildren = makeMetadataFactory( diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index c4cb1d1771..d3163693a8 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -297,8 +297,6 @@ export class Identifiers { static queryRefresh: o.ExternalReference = {name: 'ɵɵqueryRefresh', moduleName: CORE}; static viewQuery: o.ExternalReference = {name: 'ɵɵviewQuery', moduleName: CORE}; - static staticViewQuery: o.ExternalReference = {name: 'ɵɵstaticViewQuery', moduleName: CORE}; - static staticContentQuery: o.ExternalReference = {name: 'ɵɵstaticContentQuery', moduleName: CORE}; static loadQuery: o.ExternalReference = {name: 'ɵɵloadQuery', moduleName: CORE}; static contentQuery: o.ExternalReference = {name: 'ɵɵcontentQuery', moduleName: CORE}; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 9c7ddce8c5..20390fd70b 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -479,10 +479,9 @@ export const enum QueryFlags { * @param query */ function toQueryFlags(query: R3QueryMetadata): number { - // NOTE: Verify that changes here match - return (query.descendants ? 1 /* TQueryFlags.descendants */ : 0) | - (query.static ? 2 /* TQueryFlags.isStatic */ : 0) | - (query.emitDistinctChangesOnly ? 4 /* TQueryFlags.emitDistinctChangesOnly */ : 0); + return (query.descendants ? QueryFlags.descendants : QueryFlags.none) | + (query.static ? QueryFlags.isStatic : QueryFlags.none) | + (query.emitDistinctChangesOnly ? QueryFlags.emitDistinctChangesOnly : QueryFlags.none); } function convertAttributesToExpressions(attributes: {[name: string]: o.Expression}): @@ -503,11 +502,9 @@ function createContentQueriesFunction( const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME); for (const query of queries) { - const queryInstruction = query.static ? R3.staticContentQuery : R3.contentQuery; - // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null); createStatements.push( - o.importExpr(queryInstruction) + o.importExpr(R3.contentQuery) .callFn([o.variable('dirIndex'), ...prepareQueryParams(query, constantPool) as any]) .toStmt()); @@ -587,11 +584,9 @@ function createViewQueriesFunction( const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME); viewQueries.forEach((query: R3QueryMetadata) => { - const queryInstruction = query.static ? R3.staticViewQuery : R3.viewQuery; - // creation, e.g. r3.viewQuery(somePredicate, true); const queryDefinition = - o.importExpr(queryInstruction).callFn(prepareQueryParams(query, constantPool)); + o.importExpr(R3.viewQuery).callFn(prepareQueryParams(query, constantPool)); createStatements.push(queryDefinition.toStmt()); // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp)); diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 856b98fc49..70975ee65c 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -206,8 +206,6 @@ export { ɵɵsetComponentScope, ɵɵsetNgModuleScope, - ɵɵstaticContentQuery, - ɵɵstaticViewQuery, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, diff --git a/packages/core/src/linker/element_ref.ts b/packages/core/src/linker/element_ref.ts index 8a9ff8133e..c66ca02265 100644 --- a/packages/core/src/linker/element_ref.ts +++ b/packages/core/src/linker/element_ref.ts @@ -90,7 +90,6 @@ export class ElementRef { /** * Unwraps `ElementRef` and return the `nativeElement`. * - * Conditionally unwrap the `ElementRef`. * @param value value to unwrap * @returns `nativeElement` if `ElementRef` otherwise returns value as is. */ diff --git a/packages/core/src/linker/query_list.ts b/packages/core/src/linker/query_list.ts index 71f6e5fb4f..c2bdada5ba 100644 --- a/packages/core/src/linker/query_list.ts +++ b/packages/core/src/linker/query_list.ts @@ -54,10 +54,6 @@ export class QueryList implements Iterable { /** * Returns `Observable` of `QueryList` notifying the subscriber of changes. - * - * NOTE: This currently points to `changesDeprecated` which incorrectly notifies of changes even - * if no changes to `QueryList` have occurred. (It fires more often than it needs to.) - * The implementation will change to point `changesStrict` starting with v12. */ get changes(): Observable { return this._changes || (this._changes = new EventEmitter()); @@ -151,10 +147,15 @@ export class QueryList implements Iterable { * occurs. * * @param resultsTree The query results to store - * @param identityAccessor Optional functions for extracting stable object identity from a value - * in the array. + * @param identityAccessor Optional function for extracting stable object identity from a value + * in the array. This function is executed for each element of the query result list while + * comparing current query list with the new one (provided as a first argument of the `reset` + * function) to detect if the lists are different. If the function is not provided, elements + * are compared as is (without any pre-processing). */ reset(resultsTree: Array, identityAccessor?: (value: T) => unknown): void { + // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of + // QueryList (but not for QueryList itself.) const self = this as QueryListInternal; (self as {dirty: boolean}).dirty = false; const newResultFlat = flatten(resultsTree); @@ -170,7 +171,7 @@ export class QueryList implements Iterable { * Triggers a change event by emitting on the `changes` {@link EventEmitter}. */ notifyOnChanges(): void { - if (this._changes && (this._emitDistinctChangesOnly ? this._changesDetected : true)) + if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly)) this._changes.emit(this); } @@ -196,7 +197,7 @@ export class QueryList implements Iterable { /** * Internal set of APIs used by the framework. (not to be made public) */ -export interface QueryListInternal extends QueryList { +interface QueryListInternal extends QueryList { reset(a: any[]): void; notifyOnChanges(): void; length: number; diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 24d1d92309..182961bac6 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -162,9 +162,6 @@ export { ɵɵcontentQuery, ɵɵloadQuery, ɵɵqueryRefresh, - ɵɵstaticContentQuery -, - ɵɵstaticViewQuery, ɵɵviewQuery} from './query'; export { ɵɵdisableBindings, diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index 97ec1d822a..289f1f6e1e 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -98,8 +98,6 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵpipe': r3.ɵɵpipe, 'ɵɵqueryRefresh': r3.ɵɵqueryRefresh, 'ɵɵviewQuery': r3.ɵɵviewQuery, - 'ɵɵstaticViewQuery': r3.ɵɵstaticViewQuery, - 'ɵɵstaticContentQuery': r3.ɵɵstaticContentQuery, 'ɵɵloadQuery': r3.ɵɵloadQuery, 'ɵɵcontentQuery': r3.ɵɵcontentQuery, 'ɵɵreference': r3.ɵɵreference, diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index eaa599d255..3cd7b47b2e 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -202,8 +202,8 @@ class TQuery_ implements TQuery { } private isApplyingToNode(tNode: TNode): boolean { - const isDescend = (this.metadata.flags & QueryFlags.descendants) === QueryFlags.descendants; - if (this._appliesToNextNode && !isDescend) { + if (this._appliesToNextNode && + (this.metadata.flags & QueryFlags.descendants) !== QueryFlags.descendants) { const declarationNodeIdx = this._declarationNodeIndex; let parent = tNode.parent; // Determine if a given TNode is a "direct" child of a node on which a content query was @@ -428,8 +428,9 @@ export function ɵɵqueryRefresh(queryList: QueryList): boolean { setCurrentQueryIndex(queryIndex + 1); const tQuery = getTQuery(tView, queryIndex); - const isStatic = (tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic; - if (queryList.dirty && (isCreationMode(lView) === isStatic)) { + if (queryList.dirty && + (isCreationMode(lView) === + ((tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic))) { if (tQuery.matches === null) { queryList.reset([]); } else { @@ -445,21 +446,6 @@ export function ɵɵqueryRefresh(queryList: QueryList): boolean { return false; } -/** - * Creates new QueryList for a static view query. - * - * @param predicate The type for which the query will search - * @param flags Flags associated with the query - * @param read What to save in the query - * - * @codeGenApi - */ -export function ɵɵstaticViewQuery( - predicate: Type|InjectionToken|string[], flags: QueryFlags, read?: any): void { - ngDevMode && assertNumber(flags, 'Expecting flags'); - viewQueryInternal(getTView(), getLView(), predicate, flags | QueryFlags.isStatic, read); -} - /** * Creates new QueryList, stores the reference in LView and returns QueryList. * @@ -472,19 +458,14 @@ export function ɵɵstaticViewQuery( export function ɵɵviewQuery( predicate: Type|InjectionToken|string[], flags: QueryFlags, read?: any): void { ngDevMode && assertNumber(flags, 'Expecting flags'); - viewQueryInternal(getTView(), getLView(), predicate, flags, read); -} - -function viewQueryInternal( - tView: TView, lView: LView, predicate: Type|InjectionToken|string[], - flags: QueryFlags, read: any): void { + const tView = getTView(); if (tView.firstCreatePass) { createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1); - if (flags & QueryFlags.isStatic) { + if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) { tView.staticViewQueries = true; } } - createLQuery(tView, lView, flags); + createLQuery(tView, getLView(), flags); } /** @@ -503,42 +484,17 @@ export function ɵɵcontentQuery( directiveIndex: number, predicate: Type|InjectionToken|string[], flags: QueryFlags, read?: any): void { ngDevMode && assertNumber(flags, 'Expecting flags'); - contentQueryInternal( - getTView(), getLView(), predicate, flags, read, false, getCurrentTNode()!, directiveIndex); -} - -/** - * Registers a QueryList, associated with a static 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 flags Flags associated with the query - * @param read What to save in the query - * @returns QueryList - * - * @codeGenApi - */ -export function ɵɵstaticContentQuery( - directiveIndex: number, predicate: Type|InjectionToken|string[], - flags: QueryFlags, read?: any): void { - ngDevMode && assertNumber(flags, 'Expecting flags'); - contentQueryInternal( - getTView(), getLView(), predicate, flags, read, true, getCurrentTNode()!, directiveIndex); -} - -function contentQueryInternal( - tView: TView, lView: LView, predicate: Type|InjectionToken|string[], - flags: QueryFlags, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void { + const tView = getTView(); if (tView.firstCreatePass) { + const tNode = getCurrentTNode()!; createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index); saveContentQueryAndDirectiveIndex(tView, directiveIndex); - if (isStatic) { + if ((flags & QueryFlags.isStatic) === QueryFlags.isStatic) { tView.staticContentQueries = true; } } - createLQuery(tView, lView, flags); + createLQuery(tView, getLView(), flags); } /** diff --git a/packages/core/src/util/array_utils.ts b/packages/core/src/util/array_utils.ts index 90e5ffe8cf..747f476977 100644 --- a/packages/core/src/util/array_utils.ts +++ b/packages/core/src/util/array_utils.ts @@ -25,7 +25,7 @@ export function addAllToArray(items: any[], arr: any[]) { * * @param a first array * @param b second array - * @param identityAccessor Optional functions for extracting stable object identity from a value in + * @param identityAccessor Optional function for extracting stable object identity from a value in * the array. */ export function arrayEquals(a: T[], b: T[], identityAccessor?: (value: T) => unknown): boolean { diff --git a/packages/core/test/render3/jit/declare_component_spec.ts b/packages/core/test/render3/jit/declare_component_spec.ts index e395b1f1e1..85cc90b219 100644 --- a/packages/core/test/render3/jit/declare_component_spec.ts +++ b/packages/core/test/render3/jit/declare_component_spec.ts @@ -128,14 +128,14 @@ describe('component declaration jit compilation', () => { expectComponentDef(def, { contentQueries: functionContaining([ - // "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for descendants flag + // "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for query flag // without a read token, and bind to the full query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/, '(ctx.byRef = _t)', // "byToken" should use `staticContentQuery` with `3` - // (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants flag and `ElementRef` as + // (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as // read token, and bind to the first result in the query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:contentQuery|anonymous)[^(]*\(dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, @@ -174,7 +174,7 @@ describe('component declaration jit compilation', () => { '(ctx.byRef = _t)', // "byToken" should use `viewQuery` with `3` - // (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants flag and `ElementRef` as + // (`QueryFlags.descendants|QueryFlags.isStatic`) for query flag and `ElementRef` as // read token, and bind to the first result in the query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, diff --git a/packages/core/test/render3/jit/declare_directive_spec.ts b/packages/core/test/render3/jit/declare_directive_spec.ts index 08ef808b83..9b85febbcb 100644 --- a/packages/core/test/render3/jit/declare_directive_spec.ts +++ b/packages/core/test/render3/jit/declare_directive_spec.ts @@ -103,13 +103,13 @@ describe('directive declaration jit compilation', () => { expectDirectiveDef(def, { contentQueries: functionContaining([ // "byRef" should use `contentQuery` with `0` (`QueryFlags.descendants|QueryFlags.isStatic`) - // for descendants flag without a read token, and bind to the full query result. + // for query flag without a read token, and bind to the full query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/, '(ctx.byRef = _t)', // "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`) - // for descendants flag and `ElementRef` as read token, and bind to the first result in the + // for query flag and `ElementRef` as read token, and bind to the first result in the // query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:contentQuery|anonymous)[^(]*\([^,]*dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/, @@ -140,14 +140,14 @@ describe('directive declaration jit compilation', () => { expectDirectiveDef(def, { viewQuery: functionContaining([ - // "byRef" should use `viewQuery` with `false` for descendants flag without a read token, - // and bind to the full query result. + // "byRef" should use `viewQuery` with`0` (`QueryFlags.none`) for query flag without a read + // token, and bind to the full query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:viewQuery|anonymous)[^(]*\(_c0,4\)/, '(ctx.byRef = _t)', // "byToken" should use `viewQuery` with `3` (`QueryFlags.static|QueryFlags.descendants`) - // for descendants flag and `ElementRef` as read token, and bind to the first result in the + // for query flag and `ElementRef` as read token, and bind to the first result in the // query result. // NOTE: the `anonymous` match is to support IE11, as functions don't have a name there. /(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,