From 70962465b5795f0a192f745016b1c461e7c8790b Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 8 Mar 2021 11:11:21 -0800 Subject: [PATCH] fix(core): Switch `emitDistinctChangesOnlyDefaultValue` to true (#41121) BREAKING CHANGE: Switching default of `emitDistinctChangesOnlyDefaultValue` which changes the default behavior and may cause some applications which rely on the incorrect behavior to fail. `emitDistinctChangesOnly` flag has also been deprecated and will be removed in a future major release. The previous implementation would fire changes `QueryList.changes.subscribe` whenever the `QueryList` was recomputed. This resulted in an artificially high number of change notifications, as it is possible that recomputing `QueryList` results in the same list. When the `QueryList` gets recomputed is an implementation detail, and it should not be the thing that determines how often change event should fire. Unfortunately, fixing the behavior outright caused too many existing applications to fail. For this reason, Angular considers this fix a breaking fix and has introduced a flag in `@ContentChildren` and `@ViewChildren`, that controls the behavior. ``` export class QueryCompWithStrictChangeEmitParent { @ContentChildren('foo', { // This option is the new default with this change. emitDistinctChangesOnly: true, }) foos!: QueryList; } ``` For backward compatibility before v12 `emitDistinctChangesOnlyDefaultValue` was set to `false. This change changes the default to `true`. PR Close #41121 --- aio/content/guide/deprecations.md | 1 + .../ngcc/test/rendering/renderer_spec.ts | 2 +- .../src/ngtsc/annotations/src/directive.ts | 2 +- .../queries/GOLDEN_PARTIAL.js | 16 ++++---- .../queries/content_query_for_directive.js | 4 +- .../queries/content_query_for_local_ref.js | 4 +- .../queries/content_query_read_token.js | 8 ++-- .../queries/static_content_query.js | 4 +- .../queries/static_view_query.js | 4 +- .../queries/view_query_for_directive.js | 4 +- .../queries/view_query_for_local_ref.js | 4 +- .../queries/view_query_read_token.js | 8 ++-- .../r3_compiler_compliance_spec.ts | 40 +++++++++---------- .../compiler-cli/test/ngtsc/ngtsc_spec.ts | 36 ++++++++--------- packages/compiler/src/core.ts | 5 +-- packages/compiler/src/render3/partial/api.ts | 3 -- .../compiler/src/render3/partial/directive.ts | 6 ++- packages/core/src/linker/query_list.ts | 3 +- packages/core/src/metadata/di.ts | 17 ++++---- packages/core/test/acceptance/query_spec.ts | 7 +--- 20 files changed, 88 insertions(+), 90 deletions(-) diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 7c33b7d2b8..7ead16e1e5 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -95,6 +95,7 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) | | [`WrappedValue`](api/core/WrappedValue) | none | v10 | See [removing `WrappedValue`](#wrapped-value) | | [`async`](api/core/testing/async) | [`waitForAsync`](api/core/testing/waitForAsync) | v11 | The `async` function from `@angular/core/testing` has been renamed to `waitForAsync` in order to avoid confusion with the native JavaScript `async` syntax. The existing function is deprecated and will be removed in a future version. | +[ `ViewChildren.emitDistinctChangesOnly` / `ContentChildren.emitDistinctChangesOnly` | none (was part of [issue #40091](https://github.com/angular/angular/issues/40091)) ] | This is a temporary flag introduced as part of bugfix of [issue #40091](https://github.com/angular/angular/issues/40091) and will be removed. | {@a testing} ### @angular/core/testing diff --git a/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/ngcc/test/rendering/renderer_spec.ts index b80b39b3a0..5ea2e0ce07 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 = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: A, selectors: [["", UndecoratedBase.ɵfac = function UndecoratedBase_Factory(t) { return new (t || UndecoratedBase)(); }; // TRANSPILED UndecoratedBase.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: UndecoratedBase, viewQuery: function UndecoratedBase_Query(rf, ctx) { if (rf & 1) { - ɵngcc0.ɵɵviewQuery(_c0, 3); + ɵngcc0.ɵɵviewQuery(_c0, 7); } if (rf & 2) { let _t; ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.test = _t.first); diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index 52f5a8b2bb..623935e23d 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -605,7 +605,7 @@ export function extractQueryMetadata( if (typeof emitDistinctChangesOnlyValue !== 'boolean') { throw createValueHasWrongTypeError( emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, - `@${name} options.emitDistinctChangesOnlys must be a boolean`); + `@${name} options.emitDistinctChangesOnly must be a boolean`); } emitDistinctChangesOnly = emitDistinctChangesOnlyValue; } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js index 611b733f93..8f49e5621a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js @@ -32,7 +32,7 @@ import * as i0 from "@angular/core"; export class ViewQueryComponent { } ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; -ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: ` +ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, descendants: true }], ngImport: i0, template: `
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{ @@ -88,7 +88,7 @@ import * as i0 from "@angular/core"; export class ViewQueryComponent { } ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; -ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: ` +ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], descendants: true }], ngImport: i0, template: `
`, isInline: true }); @@ -170,7 +170,7 @@ import * as i0 from "@angular/core"; export class ViewQueryComponent { } ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; -ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: ` +ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], descendants: true }], ngImport: i0, template: `
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{ @@ -252,7 +252,7 @@ import * as i0 from "@angular/core"; export class ViewQueryComponent { } ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; -ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }], ngImport: i0, template: ` +ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], descendants: true, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, descendants: true, read: TemplateRef }], ngImport: i0, template: `
@@ -344,7 +344,7 @@ import * as i0 from "@angular/core"; export class ContentQueryComponent { } ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); }; -ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective, emitDistinctChangesOnly: false }], ngImport: i0, template: ` +ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective }], ngImport: i0, template: `
`, isInline: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{ @@ -423,7 +423,7 @@ import * as i0 from "@angular/core"; export class ContentQueryComponent { } ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); }; -ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false }], ngImport: i0, template: ` +ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"] }], ngImport: i0, template: `
`, isInline: true }); @@ -505,7 +505,7 @@ import * as i0 from "@angular/core"; export class ContentQueryComponent { } ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); }; -ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: ` +ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], descendants: true }], ngImport: i0, template: `
`, isInline: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{ @@ -610,7 +610,7 @@ import * as i0 from "@angular/core"; export class ContentQueryComponent { } ContentQueryComponent.ɵfac = function ContentQueryComponent_Factory(t) { return new (t || ContentQueryComponent)(); }; -ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], emitDistinctChangesOnly: false, descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, read: TemplateRef }], ngImport: i0, template: ` +ContentQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ContentQueryComponent, selector: "content-query-component", queries: [{ propertyName: "myRef", first: true, predicate: ["myRef"], descendants: true, read: TemplateRef }, { propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, read: ElementRef }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], read: ElementRef }, { propertyName: "someDirs", predicate: SomeDirective, read: TemplateRef }], ngImport: i0, template: `
diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js index a06fbaeeaf..d0c83d7933 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_directive.js @@ -3,8 +3,8 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js index e4a8fa784f..3a78c30a1b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_for_local_ref.js @@ -5,8 +5,8 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js index 654977fd1a..6de5bc94e3 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/content_query_read_token.js @@ -5,10 +5,10 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1, TemplateRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4, TemplateRef); } if (rf & 2) { let $tmp$; 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 92e99edfd6..051d73112e 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 @@ -4,8 +4,8 @@ ContentQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { $r3$.ɵɵcontentQuery( - dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__); - $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1); + dirIndex, SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); + $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 5); } 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 878e33d922..36c7118540 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,8 +5,8 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__); - $r3$.ɵɵviewQuery($refs$, 1); + $r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.isStatic__|__QueryFlags.descendants__|__QueryFlags.emitDistinctChangesOnly__); + $r3$.ɵɵviewQuery($refs$, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js index c674a30fc5..13502a9a5a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_directive.js @@ -3,8 +3,8 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, 1); - $r3$.ɵɵviewQuery(SomeDirective, 1); + $r3$.ɵɵviewQuery(SomeDirective, 5); + $r3$.ɵɵviewQuery(SomeDirective, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js index a2b233cf80..d8770b0d9b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_for_local_ref.js @@ -5,8 +5,8 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 1); - $r3$.ɵɵviewQuery($e1_attrs$, 1); + $r3$.ɵɵviewQuery($e0_attrs$, 5); + $r3$.ɵɵviewQuery($e1_attrs$, 5); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js index 2e28dd0f0e..247b2fd807 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/view_query_read_token.js @@ -5,10 +5,10 @@ ViewQueryComponent.ɵcmp = /*@__PURE__*/ $r3$.ɵɵdefineComponent({ // ... viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef); - $r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef); - $r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef); - $r3$.ɵɵviewQuery(SomeDirective, 1, TemplateRef); + $r3$.ɵɵviewQuery($e0_attrs$, 5, TemplateRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, ElementRef); + $r3$.ɵɵviewQuery($e1_attrs$, 5, ElementRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, TemplateRef); } if (rf & 2) { let $tmp$; 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 50f0b4854d..c2d68d83d3 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 @@ -1636,8 +1636,8 @@ describe('compiler compliance', () => { selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, 1); - $r3$.ɵɵviewQuery(SomeDirective, 1); + $r3$.ɵɵviewQuery(SomeDirective, 5); + $r3$.ɵɵviewQuery(SomeDirective, 5); } if (rf & 2) { let $tmp$; @@ -1695,8 +1695,8 @@ describe('compiler compliance', () => { … viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 1); - $r3$.ɵɵviewQuery($e1_attrs$, 1); + $r3$.ɵɵviewQuery($e0_attrs$, 5); + $r3$.ɵɵviewQuery($e1_attrs$, 5); } if (rf & 2) { let $tmp$; @@ -1746,8 +1746,8 @@ describe('compiler compliance', () => { selectors: [["view-query-component"]], viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery(SomeDirective, 3); - $r3$.ɵɵviewQuery($refs$, 1); + $r3$.ɵɵviewQuery(SomeDirective, 7); + $r3$.ɵɵviewQuery($refs$, 5); } if (rf & 2) { let $tmp$; @@ -1810,10 +1810,10 @@ describe('compiler compliance', () => { … viewQuery: function ViewQueryComponent_Query(rf, ctx) { if (rf & 1) { - $r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef); - $r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef); - $r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef); - $r3$.ɵɵviewQuery(SomeDirective, 1, TemplateRef); + $r3$.ɵɵviewQuery($e0_attrs$, 5, TemplateRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, ElementRef); + $r3$.ɵɵviewQuery($e1_attrs$, 5, ElementRef); + $r3$.ɵɵviewQuery(SomeDirective, 5, TemplateRef); } if (rf & 2) { let $tmp$; @@ -1873,8 +1873,8 @@ describe('compiler compliance', () => { selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4); } if (rf & 2) { let $tmp$; @@ -1933,8 +1933,8 @@ describe('compiler compliance', () => { … contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4); } if (rf & 2) { let $tmp$; @@ -1992,8 +1992,8 @@ describe('compiler compliance', () => { selectors: [["content-query-component"]], contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 3); - $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 7); + $r3$.ɵɵcontentQuery(dirIndex, $ref0$, 5); } if (rf & 2) { let $tmp$; @@ -2057,10 +2057,10 @@ describe('compiler compliance', () => { … contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { - $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1, TemplateRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0, ElementRef); - $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 5, TemplateRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 5, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 4, ElementRef); + $r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 4, TemplateRef); } if (rf & 2) { let $tmp$; diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 8ccdfd27ea..9ad05bbc82 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -3133,10 +3133,10 @@ function allTests(os: string) { expect(jsContents).toMatch(varRegExp('test1')); expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, 1, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', 1, 'TemplateRef')); - // match `i0.ɵɵviewQuery(_c2, 1, null)` - expect(jsContents).toMatch(viewQueryRegExp('\\w+', 1)); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); + // match `i0.ɵɵviewQuery(_c2, 5, null)` + expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); }); it('should generate queries for directives', () => { @@ -3165,14 +3165,14 @@ function allTests(os: string) { expect(jsContents).toMatch(varRegExp('test1')); expect(jsContents).toMatch(varRegExp('test2')); expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, 1, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', 1, 'TemplateRef')); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, 5, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', 5, 'TemplateRef')); - // match `i0.ɵɵviewQuery(_c2, 1)` + // match `i0.ɵɵviewQuery(_c2, 5)` // Note that while ViewQuery doesn't necessarily make sense on a directive, // because it doesn't have a view, we still need to handle it because a component // could extend the directive. - expect(jsContents).toMatch(viewQueryRegExp('\\w+', 1)); + expect(jsContents).toMatch(viewQueryRegExp('\\w+', 5)); }); it('should handle queries that use forwardRef', () => { @@ -3194,13 +3194,13 @@ function allTests(os: string) { env.driveMain(); const jsContents = env.getContents('test.js'); - // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 1, null)` - expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', 1)); - // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, 1, null)` - expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', 1)); - // match `i0.ɵɵcontentQuery(dirIndex, _c0, 1, null)` + // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, _c0, 5, null)` expect(jsContents).toContain('_c0 = ["parens"];'); - expect(jsContents).toMatch(contentQueryRegExp('_c0', 1)); + expect(jsContents).toMatch(contentQueryRegExp('_c0', 5)); }); it('should handle queries that use an InjectionToken', () => { @@ -3221,10 +3221,10 @@ function allTests(os: string) { env.driveMain(); const jsContents = env.getContents('test.js'); - // match `i0.ɵɵviewQuery(TOKEN, 1, null)` - expect(jsContents).toMatch(viewQueryRegExp('TOKEN', 1)); - // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, 1, null)` - expect(jsContents).toMatch(contentQueryRegExp('TOKEN', 1)); + // match `i0.ɵɵviewQuery(TOKEN, 5, null)` + expect(jsContents).toMatch(viewQueryRegExp('TOKEN', 5)); + // match `i0.ɵɵcontentQuery(dirIndex, TOKEN, 5, null)` + expect(jsContents).toMatch(contentQueryRegExp('TOKEN', 5)); }); it('should compile expressions that write keys', () => { diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts index a588b90165..a0b07718c3 100644 --- a/packages/compiler/src/core.ts +++ b/packages/compiler/src/core.ts @@ -28,9 +28,8 @@ export const createAttribute = makeMetadataFactory('Attribute', (attributeName: string) => ({attributeName})); // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not -// explicitly set. This value will be changed to `true` in v12. -// TODO(misko): switch the default in v12 to `true`. See: packages/core/src/metadata/di.ts -export const emitDistinctChangesOnlyDefaultValue = false; +// explicitly set. +export const emitDistinctChangesOnlyDefaultValue = true; export interface Query { diff --git a/packages/compiler/src/render3/partial/api.ts b/packages/compiler/src/render3/partial/api.ts index 383827a8e9..d64931985e 100644 --- a/packages/compiler/src/render3/partial/api.ts +++ b/packages/compiler/src/render3/partial/api.ts @@ -237,9 +237,6 @@ export interface R3DeclareQueryMetadata { /** * True to only fire changes if there are underlying changes to the query. */ - // TODO(misko): This will become `true` be default in v12. `QueryList.changes` would fire even if - // no changes to the query list were detected. This is not ideal, as changes should only fire if - // the `QueryList` actually materially changed. emitDistinctChangesOnly?: boolean; /** diff --git a/packages/compiler/src/render3/partial/directive.ts b/packages/compiler/src/render3/partial/directive.ts index 8b5a854585..0947ca46cd 100644 --- a/packages/compiler/src/render3/partial/directive.ts +++ b/packages/compiler/src/render3/partial/directive.ts @@ -87,9 +87,11 @@ function compileQuery(query: R3QueryMetadata): o.LiteralMapExpr { meta.set( 'predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : query.predicate); if (!query.emitDistinctChangesOnly) { - // `emitDistinctChangesOnly` is special because in future we expect it to be `true`. For this - // reason the absence should be interpreted as `true`. + // `emitDistinctChangesOnly` is special because we expect it to be `true`. + // Therefore we explicitly emit the field, and explicitly place it only when it's `false`. meta.set('emitDistinctChangesOnly', o.literal(false)); + } else { + // The linker will assume that an absent `emitDistinctChangesOnly` flag is by default `true`. } if (query.descendants) { meta.set('descendants', o.literal(true)); diff --git a/packages/core/src/linker/query_list.ts b/packages/core/src/linker/query_list.ts index c2bdada5ba..26c12e5b24 100644 --- a/packages/core/src/linker/query_list.ts +++ b/packages/core/src/linker/query_list.ts @@ -62,8 +62,7 @@ export class QueryList implements Iterable { /** * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in - * the same result) This is set to `false` for backwards compatibility but will be changed to - * true in v12. + * the same result) */ constructor(private _emitDistinctChangesOnly: boolean = false) { // This function should be declared on the prototype, but doing so there will cause the class diff --git a/packages/core/src/metadata/di.ts b/packages/core/src/metadata/di.ts index e33d78f3bf..1a793bbde4 100644 --- a/packages/core/src/metadata/di.ts +++ b/packages/core/src/metadata/di.ts @@ -107,9 +107,8 @@ export interface Query { } // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not -// explicitly set. This value will be changed to `true` in v12. -// TODO(misko): switch the default in v12 to `true`. See: packages/compiler/src/core.ts -export const emitDistinctChangesOnlyDefaultValue = false; +// explicitly set. +export const emitDistinctChangesOnlyDefaultValue = true; /** @@ -148,8 +147,10 @@ export interface ContentChildrenDecorator { * * **selector** - The directive type or the name used for querying. * * **descendants** - True to include all descendants, otherwise include only direct children. * * **emitDistinctChangesOnly** - The ` QueryList#changes` observable will emit new values only - * if the QueryList result has changed. The default value will change from `false` to `true` in - * v12. When `false` the `changes` observable might emit even if the QueryList has not changed. + * if the QueryList result has changed. When `false` the `changes` observable might emit even + * if the QueryList has not changed. + * ** Note: *** This config option is **deprecated**, it will be permanently set to `true` and + * removed in future versions of Angular. * * **read** - Used to read a different token from the queried elements. * * @usageNotes @@ -287,8 +288,10 @@ export interface ViewChildrenDecorator { * * **selector** - The directive type or the name used for querying. * * **read** - Used to read a different token from the queried elements. * * **emitDistinctChangesOnly** - The ` QueryList#changes` observable will emit new values only - * if the QueryList result has changed. The default value will change from `false` to `true` in - * v12. When `false` the `changes` observable might emit even if the QueryList has not changed. + * if the QueryList result has changed. When `false` the `changes` observable might emit even + * if the QueryList has not changed. + * ** Note: *** This config option is **deprecated**, it will be permanently set to `true` and + * removed in future versions of Angular. * * @usageNotes * diff --git a/packages/core/test/acceptance/query_spec.ts b/packages/core/test/acceptance/query_spec.ts index 82da5b20e3..97de093d80 100644 --- a/packages/core/test/acceptance/query_spec.ts +++ b/packages/core/test/acceptance/query_spec.ts @@ -1257,11 +1257,8 @@ describe('query logic', () => { * - systematically detach and insert a view - this would result in unnecessary processing * when the previous and new indexes for the move operation are the same; * - detect the situation where the indexes are the same and do no processing in such case. - * - * This tests asserts on the implementation choices done by the VE (detach and insert) so we - * can replicate the same behavior in ivy. */ - it('should notify on changes when a given view is removed and re-inserted at the same index', + it('should NOT notify on changes when a given view is removed and re-inserted at the same index', () => { @Component({ selector: 'test-comp', @@ -1298,7 +1295,7 @@ describe('query logic', () => { vc.move(viewRef, 0); fixture.detectChanges(); expect(queryList.length).toBe(1); - expect(fixture.componentInstance.queryListNotificationCounter).toBe(2); + expect(fixture.componentInstance.queryListNotificationCounter).toBe(1); }); it('should support a mix of content queries from the declaration and embedded view', () => {