fix(core): `QueryList` should not fire changes if the underlying list did not change. (#40091)
Previous implementation would fire changes `QueryList.changes.subscribe` whenever the `QueryList` was recomputed. This resulted in 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 which determines how often change event should fire. This change introduces a new `emitDistinctChangesOnly` option for `ContentChildren` and `ViewChildren`. ``` export class QueryCompWithStrictChangeEmitParent { @ContentChildren('foo', { // This option will become the default in the future emitDistinctChangesOnly: true, }) foos!: QueryList<any>; } ``` PR Close #40091
This commit is contained in:
parent
cf02cf1e18
commit
e32b6256ce
|
@ -174,10 +174,12 @@ export declare type ContentChildren = Query;
|
|||
export declare interface ContentChildrenDecorator {
|
||||
(selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
|
||||
descendants?: boolean;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
read?: any;
|
||||
}): any;
|
||||
new (selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
|
||||
descendants?: boolean;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
read?: any;
|
||||
}): Query;
|
||||
}
|
||||
|
@ -734,6 +736,7 @@ export declare type Provider = TypeProvider | ValueProvider | ClassProvider | Co
|
|||
|
||||
export declare interface Query {
|
||||
descendants: boolean;
|
||||
emitDistinctChangesOnly: boolean;
|
||||
first: boolean;
|
||||
isViewQuery: boolean;
|
||||
read: any;
|
||||
|
@ -746,12 +749,12 @@ export declare abstract class Query {
|
|||
|
||||
export declare class QueryList<T> implements Iterable<T> {
|
||||
[Symbol.iterator]: () => Iterator<T>;
|
||||
readonly changes: Observable<any>;
|
||||
get changes(): Observable<any>;
|
||||
readonly dirty = true;
|
||||
readonly first: T;
|
||||
readonly last: T;
|
||||
readonly length: number;
|
||||
constructor();
|
||||
constructor(_emitDistinctChangesOnly?: boolean);
|
||||
destroy(): void;
|
||||
filter(fn: (item: T, index: number, array: T[]) => boolean): T[];
|
||||
find(fn: (item: T, index: number, array: T[]) => boolean): T | undefined;
|
||||
|
@ -760,7 +763,7 @@ export declare class QueryList<T> implements Iterable<T> {
|
|||
map<U>(fn: (item: T, index: number, array: T[]) => U): U[];
|
||||
notifyOnChanges(): void;
|
||||
reduce<U>(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U;
|
||||
reset(resultsTree: Array<T | any[]>): void;
|
||||
reset(resultsTree: Array<T | any[]>, identityAccessor?: (value: T) => unknown): void;
|
||||
setDirty(): void;
|
||||
some(fn: (value: T, index: number, array: T[]) => boolean): boolean;
|
||||
toArray(): T[];
|
||||
|
@ -1010,9 +1013,11 @@ export declare type ViewChildren = Query;
|
|||
export declare interface ViewChildrenDecorator {
|
||||
(selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
|
||||
read?: any;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
}): any;
|
||||
new (selector: Type<any> | InjectionToken<unknown> | Function | string, opts?: {
|
||||
read?: any;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
}): ViewChildren;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3033,
|
||||
"main-es2015": 447349,
|
||||
"main-es2015": 448090,
|
||||
"polyfills-es2015": 52493
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3153,
|
||||
"main-es2015": 431137,
|
||||
"main-es2015": 432078,
|
||||
"polyfills-es2015": 52493
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2285,
|
||||
"main-es2015": 240909,
|
||||
"main-es2015": 241738,
|
||||
"polyfills-es2015": 36709,
|
||||
"5-es2015": 745
|
||||
}
|
||||
|
|
|
@ -147,6 +147,8 @@ function toQueryMetadata<TExpression>(obj: AstObject<R3DeclareQueryMetadata, TEx
|
|||
first: obj.has('first') ? obj.getBoolean('first') : false,
|
||||
predicate,
|
||||
descendants: obj.has('descendants') ? obj.getBoolean('descendants') : false,
|
||||
emitDistinctChangesOnly:
|
||||
obj.has('emitDistinctChangesOnly') ? obj.getBoolean('emitDistinctChangesOnly') : true,
|
||||
read: obj.has('read') ? obj.getOpaque('read') : null,
|
||||
static: obj.has('static') ? obj.getBoolean('static') : false,
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import {compileDeclareDirectiveFromMetadata, compileDirectiveFromMetadata, ConstantPool, Expression, ExternalExpr, getSafePropertyAccessString, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveDef, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, R3ResolvedDependencyType, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler';
|
||||
import {emitDistinctChangesOnlyDefaultValue} from '@angular/compiler/src/core';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||
|
@ -436,6 +437,7 @@ export function extractQueryMetadata(
|
|||
let read: Expression|null = null;
|
||||
// The default value for descendants is true for every decorator except @ContentChildren.
|
||||
let descendants: boolean = name !== 'ContentChildren';
|
||||
let emitDistinctChangesOnly: boolean = emitDistinctChangesOnlyDefaultValue;
|
||||
if (args.length === 2) {
|
||||
const optionsExpr = unwrapExpression(args[1]);
|
||||
if (!ts.isObjectLiteralExpression(optionsExpr)) {
|
||||
|
@ -458,6 +460,17 @@ export function extractQueryMetadata(
|
|||
descendants = descendantsValue;
|
||||
}
|
||||
|
||||
if (options.has('emitDistinctChangesOnly')) {
|
||||
const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly')!;
|
||||
const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
|
||||
if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
|
||||
throw createValueHasWrongTypeError(
|
||||
emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue,
|
||||
`@${name} options.emitDistinctChangesOnlys must be a boolean`);
|
||||
}
|
||||
emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
|
||||
}
|
||||
|
||||
if (options.has('static')) {
|
||||
const staticValue = evaluator.evaluate(options.get('static')!);
|
||||
if (typeof staticValue !== 'boolean') {
|
||||
|
@ -480,6 +493,7 @@ export function extractQueryMetadata(
|
|||
descendants,
|
||||
read,
|
||||
static: isStatic,
|
||||
emitDistinctChangesOnly,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, 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, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
|
||||
<div someDir></div>
|
||||
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{
|
||||
|
@ -86,7 +86,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"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], 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"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
|
||||
<div #myRef></div>
|
||||
<div #myRef1></div>
|
||||
`, isInline: true });
|
||||
|
@ -166,7 +166,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, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], 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, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
|
||||
<div someDir></div>
|
||||
`, isInline: true, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{
|
||||
|
@ -246,7 +246,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"], 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: `
|
||||
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: `
|
||||
<div someDir></div>
|
||||
<div #myRef></div>
|
||||
<div #myRef1></div>
|
||||
|
@ -336,7 +336,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, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective }], 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, emitDistinctChangesOnly: false, descendants: true }, { propertyName: "someDirList", predicate: SomeDirective, emitDistinctChangesOnly: false }], ngImport: i0, template: `
|
||||
<div><ng-content></ng-content></div>
|
||||
`, isInline: true });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
|
||||
|
@ -413,7 +413,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"], descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"] }], 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"], emitDistinctChangesOnly: false, descendants: true }, { propertyName: "myRefs", predicate: ["myRef1, myRef2, myRef3"], emitDistinctChangesOnly: false }], ngImport: i0, template: `
|
||||
<div #myRef></div>
|
||||
<div #myRef1></div>
|
||||
`, isInline: true });
|
||||
|
@ -493,7 +493,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, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], 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, emitDistinctChangesOnly: false, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
|
||||
<div><ng-content></ng-content></div>
|
||||
`, isInline: true });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
|
||||
|
@ -596,7 +596,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"], 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: `
|
||||
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: `
|
||||
<div someDir></div>
|
||||
<div #myRef></div>
|
||||
<div #myRef1></div>
|
||||
|
@ -652,3 +652,91 @@ export declare class MyModule {
|
|||
static ɵinj: i0.ɵɵInjectorDef<MyModule>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: some.directive.js
|
||||
****************************************************************************************************/
|
||||
import { Directive } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class SomeDirective {
|
||||
}
|
||||
SomeDirective.ɵfac = function SomeDirective_Factory(t) { return new (t || SomeDirective)(); };
|
||||
SomeDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: "0.0.0-PLACEHOLDER", type: SomeDirective, selector: "[someDir]", ngImport: i0 });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SomeDirective, [{
|
||||
type: Directive,
|
||||
args: [{
|
||||
selector: '[someDir]',
|
||||
}]
|
||||
}], null, null); })();
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: some.directive.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class SomeDirective {
|
||||
static ɵfac: i0.ɵɵFactoryDef<SomeDirective, never>;
|
||||
static ɵdir: i0.ɵɵDirectiveDefWithMeta<SomeDirective, "[someDir]", never, {}, {}, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: query_with_emit_distinct_changes_only.js
|
||||
****************************************************************************************************/
|
||||
import { Component, ContentChildren, NgModule, ViewChildren } from '@angular/core';
|
||||
import { SomeDirective } from './some.directive';
|
||||
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: "myRefs", predicate: ["myRef"] }, { propertyName: "oldMyRefs", predicate: ["myRef"], emitDistinctChangesOnly: false }], viewQueries: [{ propertyName: "someDirs", predicate: SomeDirective, descendants: true }, { propertyName: "oldSomeDirs", predicate: SomeDirective, emitDistinctChangesOnly: false, descendants: true }], ngImport: i0, template: `
|
||||
<div someDir></div>
|
||||
<div #myRef></div>
|
||||
`, isInline: true });
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ContentQueryComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
selector: 'content-query-component',
|
||||
template: `
|
||||
<div someDir></div>
|
||||
<div #myRef></div>
|
||||
`
|
||||
}]
|
||||
}], null, { myRefs: [{
|
||||
type: ContentChildren,
|
||||
args: ['myRef', { emitDistinctChangesOnly: true }]
|
||||
}], oldMyRefs: [{
|
||||
type: ContentChildren,
|
||||
args: ['myRef', { emitDistinctChangesOnly: false }]
|
||||
}], someDirs: [{
|
||||
type: ViewChildren,
|
||||
args: [SomeDirective, { emitDistinctChangesOnly: true }]
|
||||
}], oldSomeDirs: [{
|
||||
type: ViewChildren,
|
||||
args: [SomeDirective, { emitDistinctChangesOnly: false }]
|
||||
}] }); })();
|
||||
export class MyModule {
|
||||
}
|
||||
MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule });
|
||||
MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } });
|
||||
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [ContentQueryComponent] }); })();
|
||||
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyModule, [{
|
||||
type: NgModule,
|
||||
args: [{ declarations: [ContentQueryComponent] }]
|
||||
}], null, null); })();
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: query_with_emit_distinct_changes_only.d.ts
|
||||
****************************************************************************************************/
|
||||
import { ElementRef, QueryList } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class ContentQueryComponent {
|
||||
myRefs: QueryList<ElementRef>;
|
||||
oldMyRefs: QueryList<ElementRef>;
|
||||
someDirs: QueryList<any>;
|
||||
oldSomeDirs: QueryList<any>;
|
||||
static ɵfac: i0.ɵɵFactoryDef<ContentQueryComponent, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDefWithMeta<ContentQueryComponent, "content-query-component", never, {}, {}, ["myRefs", "oldMyRefs"], never>;
|
||||
}
|
||||
export declare class MyModule {
|
||||
static ɵmod: i0.ɵɵNgModuleDefWithMeta<MyModule, [typeof ContentQueryComponent], never, never>;
|
||||
static ɵinj: i0.ɵɵInjectorDef<MyModule>;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,21 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should support query emitDistinctChangesOnly flag",
|
||||
"inputFiles": [
|
||||
"some.directive.ts",
|
||||
"query_with_emit_distinct_changes_only.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"failureMessage": "Invalid ContentQuery declaration",
|
||||
"files": [
|
||||
"query_with_emit_distinct_changes_only.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
selectors: [["content-query-component"]],
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -5,8 +5,8 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
// ...
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -5,10 +5,10 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
// ...
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, true, TemplateRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
|
||||
$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);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
const $e0_attrs$ = ["myRef"];
|
||||
// ...
|
||||
ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
||||
// ...
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.emitDistinctChangesOnly__);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, __QueryFlags.none__);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.myRefs = $tmp$);
|
||||
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.oldMyRefs = $tmp$);
|
||||
}
|
||||
},
|
||||
// ...
|
||||
viewQuery: function ContentQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.emitDistinctChangesOnly__|__QueryFlags.descendants__);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, __QueryFlags.descendants__);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.someDirs = $tmp$);
|
||||
$r3$.ɵɵqueryRefresh($tmp$ = $r3$.ɵɵloadQuery()) && (ctx.oldSomeDirs = $tmp$);
|
||||
}
|
||||
},
|
||||
//...
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import {Component, ContentChildren, ElementRef, NgModule, QueryList, TemplateRef, ViewChildren} from '@angular/core';
|
||||
|
||||
import {SomeDirective} from './some.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'content-query-component',
|
||||
template: `
|
||||
<div someDir></div>
|
||||
<div #myRef></div>
|
||||
`
|
||||
})
|
||||
export class ContentQueryComponent {
|
||||
@ContentChildren('myRef', {emitDistinctChangesOnly: true}) myRefs!: QueryList<ElementRef>;
|
||||
@ContentChildren('myRef', {emitDistinctChangesOnly: false}) oldMyRefs!: QueryList<ElementRef>;
|
||||
|
||||
@ViewChildren(SomeDirective, {emitDistinctChangesOnly: true}) someDirs!: QueryList<any>;
|
||||
@ViewChildren(SomeDirective, {emitDistinctChangesOnly: false}) oldSomeDirs!: QueryList<any>;
|
||||
}
|
||||
@NgModule({declarations: [ContentQueryComponent]})
|
||||
export class MyModule {
|
||||
}
|
|
@ -3,8 +3,8 @@ ContentQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
selectors: [["content-query-component"]],
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(rf, ctx, dirIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, true);
|
||||
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -5,8 +5,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
selectors: [["view-query-component"]],
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵstaticViewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery($refs$, true);
|
||||
$r3$.ɵɵstaticViewQuery(SomeDirective, 3);
|
||||
$r3$.ɵɵviewQuery($refs$, 1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -3,8 +3,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
selectors: [["view-query-component"]],
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -5,8 +5,8 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
// ...
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, true);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, true);
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, 1);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, 1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -5,10 +5,10 @@ ViewQueryComponent.ɵcmp = $r3$.ɵɵdefineComponent({
|
|||
// ...
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, true, TemplateRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true, ElementRef);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef);
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1, TemplateRef);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -1636,8 +1636,8 @@ describe('compiler compliance', () => {
|
|||
selectors: [["view-query-component"]],
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1);
|
||||
}
|
||||
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$, true);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, true);
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, 1);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, 1);
|
||||
}
|
||||
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$.ɵɵstaticViewQuery(SomeDirective, true);
|
||||
$r3$.ɵɵviewQuery($refs$, true);
|
||||
$r3$.ɵɵstaticViewQuery(SomeDirective, 3);
|
||||
$r3$.ɵɵviewQuery($refs$, 1);
|
||||
}
|
||||
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$, true, TemplateRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true, ElementRef);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, true, ElementRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, true, TemplateRef);
|
||||
$r3$.ɵɵviewQuery($e0_attrs$, 1, TemplateRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1, ElementRef);
|
||||
$r3$.ɵɵviewQuery($e1_attrs$, 1, ElementRef);
|
||||
$r3$.ɵɵviewQuery(SomeDirective, 1, 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, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 1);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, 0);
|
||||
}
|
||||
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$, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e0_attrs$, 1);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, 0);
|
||||
}
|
||||
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$.ɵɵstaticContentQuery(dirIndex, SomeDirective, true);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, true);
|
||||
$r3$.ɵɵstaticContentQuery(dirIndex, SomeDirective, 3);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $ref0$, 1);
|
||||
}
|
||||
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$, true, TemplateRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
|
||||
$r3$.ɵɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
|
||||
$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);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$;
|
||||
|
|
|
@ -22,14 +22,14 @@ const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
|
|||
|
||||
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
||||
|
||||
const viewQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
|
||||
const viewQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${descend}${maybeRef}\\)`);
|
||||
return new RegExp(`i0\\.ɵɵviewQuery\\(${predicate}, ${flags}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
|
||||
const contentQueryRegExp = (predicate: string, flags: number, ref?: string): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${descend}${maybeRef}\\)`);
|
||||
return new RegExp(`i0\\.ɵɵcontentQuery\\(dirIndex, ${predicate}, ${flags}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
const setClassMetadataRegExp = (expectedType: string): RegExp =>
|
||||
|
@ -3093,10 +3093,10 @@ runInEachFileSystem(os => {
|
|||
expect(jsContents).toMatch(varRegExp('test1'));
|
||||
expect(jsContents).toMatch(varRegExp('test2'));
|
||||
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
|
||||
// match `i0.ɵɵviewQuery(_c2, true, null)`
|
||||
expect(jsContents).toMatch(viewQueryRegExp('\\w+', true));
|
||||
// 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));
|
||||
});
|
||||
|
||||
it('should generate queries for directives', () => {
|
||||
|
@ -3125,14 +3125,14 @@ runInEachFileSystem(os => {
|
|||
expect(jsContents).toMatch(varRegExp('test1'));
|
||||
expect(jsContents).toMatch(varRegExp('test2'));
|
||||
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, _c1, 1, TemplateRef)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('\\w+', 1, 'TemplateRef'));
|
||||
|
||||
// match `i0.ɵɵviewQuery(_c2, true)`
|
||||
// match `i0.ɵɵviewQuery(_c2, 1)`
|
||||
// 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+', true));
|
||||
expect(jsContents).toMatch(viewQueryRegExp('\\w+', 1));
|
||||
});
|
||||
|
||||
it('should handle queries that use forwardRef', () => {
|
||||
|
@ -3154,13 +3154,13 @@ runInEachFileSystem(os => {
|
|||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, true, null)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, true, null)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, _c0, true, null)`
|
||||
// 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)`
|
||||
expect(jsContents).toContain('_c0 = ["parens"];');
|
||||
expect(jsContents).toMatch(contentQueryRegExp('_c0', true));
|
||||
expect(jsContents).toMatch(contentQueryRegExp('_c0', 1));
|
||||
});
|
||||
|
||||
it('should handle queries that use an InjectionToken', () => {
|
||||
|
@ -3181,10 +3181,10 @@ runInEachFileSystem(os => {
|
|||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
// match `i0.ɵɵviewQuery(TOKEN, true, null)`
|
||||
expect(jsContents).toMatch(viewQueryRegExp('TOKEN', true));
|
||||
// match `i0.ɵɵcontentQuery(dirIndex, TOKEN, true, null)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('TOKEN', true));
|
||||
// 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));
|
||||
});
|
||||
|
||||
it('should compile expressions that write keys', () => {
|
||||
|
|
|
@ -169,6 +169,7 @@ export interface CompileQueryMetadata {
|
|||
propertyName: string;
|
||||
read: CompileTokenMetadata;
|
||||
static?: boolean;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -240,6 +240,7 @@ export interface R3QueryMetadataFacade {
|
|||
first: boolean;
|
||||
predicate: any|string[];
|
||||
descendants: boolean;
|
||||
emitDistinctChangesOnly: boolean;
|
||||
read: any|null;
|
||||
static: boolean;
|
||||
}
|
||||
|
@ -251,6 +252,7 @@ export interface R3DeclareQueryMetadataFacade {
|
|||
descendants?: boolean;
|
||||
read?: OpaqueValue;
|
||||
static?: boolean;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface ParseSourceSpan {
|
||||
|
|
|
@ -27,6 +27,12 @@ export interface Attribute {
|
|||
export const createAttribute =
|
||||
makeMetadataFactory<Attribute>('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;
|
||||
|
||||
|
||||
export interface Query {
|
||||
descendants: boolean;
|
||||
first: boolean;
|
||||
|
@ -37,17 +43,27 @@ export interface Query {
|
|||
}
|
||||
|
||||
export const createContentChildren = makeMetadataFactory<Query>(
|
||||
'ContentChildren',
|
||||
(selector?: any, data: any = {}) =>
|
||||
({selector, first: false, isViewQuery: false, descendants: false, ...data}));
|
||||
'ContentChildren', (selector?: any, data: any = {}) => ({
|
||||
selector,
|
||||
first: false,
|
||||
isViewQuery: false,
|
||||
descendants: false,
|
||||
emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
|
||||
...data
|
||||
}));
|
||||
export const createContentChild = makeMetadataFactory<Query>(
|
||||
'ContentChild',
|
||||
(selector?: any, data: any = {}) =>
|
||||
({selector, first: true, isViewQuery: false, descendants: true, ...data}));
|
||||
export const createViewChildren = makeMetadataFactory<Query>(
|
||||
'ViewChildren',
|
||||
(selector?: any, data: any = {}) =>
|
||||
({selector, first: false, isViewQuery: true, descendants: true, ...data}));
|
||||
'ViewChildren', (selector?: any, data: any = {}) => ({
|
||||
selector,
|
||||
first: false,
|
||||
isViewQuery: true,
|
||||
descendants: true,
|
||||
emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
|
||||
...data
|
||||
}));
|
||||
export const createViewChild = makeMetadataFactory<Query>(
|
||||
'ViewChild',
|
||||
(selector: any, data: any) =>
|
||||
|
@ -224,6 +240,7 @@ export const enum NodeFlags {
|
|||
StaticQuery = 1 << 28,
|
||||
DynamicQuery = 1 << 29,
|
||||
TypeModuleProvider = 1 << 30,
|
||||
EmitDistinctChangesOnly = 1 << 31,
|
||||
CatQuery = TypeContentQuery | TypeViewQuery,
|
||||
|
||||
// mutually exclusive values...
|
||||
|
|
|
@ -243,7 +243,8 @@ function convertToR3QueryMetadata(facade: R3QueryMetadataFacade): R3QueryMetadat
|
|||
predicate: Array.isArray(facade.predicate) ? facade.predicate :
|
||||
new WrappedNodeExpr(facade.predicate),
|
||||
read: facade.read ? new WrappedNodeExpr(facade.read) : null,
|
||||
static: facade.static
|
||||
static: facade.static,
|
||||
emitDistinctChangesOnly: facade.emitDistinctChangesOnly,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -257,6 +258,7 @@ function convertQueryDeclarationToMetadata(declaration: R3DeclareQueryMetadataFa
|
|||
descendants: declaration.descendants ?? false,
|
||||
read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
||||
static: declaration.static ?? false,
|
||||
emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1196,6 +1196,7 @@ export class CompileMetadataResolver {
|
|||
selectors,
|
||||
first: q.first,
|
||||
descendants: q.descendants,
|
||||
emitDistinctChangesOnly: q.emitDistinctChangesOnly,
|
||||
propertyName,
|
||||
read: q.read ? this._getTokenMetadata(q.read) : null!,
|
||||
static: q.static
|
||||
|
|
|
@ -232,6 +232,14 @@ export interface R3DeclareQueryMetadata {
|
|||
*/
|
||||
descendants?: boolean;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An expression representing a type to read from each matched node, or null if the default value
|
||||
* for a given node is to be returned.
|
||||
|
|
|
@ -86,6 +86,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`.
|
||||
meta.set('emitDistinctChangesOnly', o.literal(false));
|
||||
}
|
||||
if (query.descendants) {
|
||||
meta.set('descendants', o.literal(true));
|
||||
}
|
||||
|
|
|
@ -304,6 +304,13 @@ export interface R3QueryMetadata {
|
|||
*/
|
||||
descendants: boolean;
|
||||
|
||||
/**
|
||||
* If the `QueryList` should fire change event only if actual change to query was computed (vs old
|
||||
* behavior where the change was fired whenever the query was recomputed, even if the recomputed
|
||||
* query resulted in the same list.)
|
||||
*/
|
||||
emitDistinctChangesOnly: boolean;
|
||||
|
||||
/**
|
||||
* An expression representing a type to read from each matched node, or null if the default value
|
||||
* for a given node is to be returned.
|
||||
|
|
|
@ -404,6 +404,7 @@ function queriesFromGlobalMetadata(
|
|||
predicate: selectorsFromGlobalMetadata(query.selectors, outputCtx),
|
||||
descendants: query.descendants,
|
||||
read,
|
||||
emitDistinctChangesOnly: !!query.emitDistinctChangesOnly,
|
||||
static: !!query.static
|
||||
};
|
||||
});
|
||||
|
@ -435,13 +436,55 @@ function selectorsFromGlobalMetadata(
|
|||
}
|
||||
|
||||
function prepareQueryParams(query: R3QueryMetadata, constantPool: ConstantPool): o.Expression[] {
|
||||
const parameters = [getQueryPredicate(query, constantPool), o.literal(query.descendants)];
|
||||
const parameters = [getQueryPredicate(query, constantPool), o.literal(toQueryFlags(query))];
|
||||
if (query.read) {
|
||||
parameters.push(query.read);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of flags to be used with Queries.
|
||||
*
|
||||
* NOTE: Ensure changes here are in sync with `packages/core/src/render3/interfaces/query.ts`
|
||||
*/
|
||||
export const enum QueryFlags {
|
||||
/**
|
||||
* No flags
|
||||
*/
|
||||
none = 0b0000,
|
||||
|
||||
/**
|
||||
* Whether or not the query should descend into children.
|
||||
*/
|
||||
descendants = 0b0001,
|
||||
|
||||
/**
|
||||
* The query can be computed statically and hence can be assigned eagerly.
|
||||
*
|
||||
* NOTE: Backwards compatibility with ViewEngine.
|
||||
*/
|
||||
isStatic = 0b0010,
|
||||
|
||||
/**
|
||||
* If the `QueryList` should fire change event only if actual change to query was computed (vs old
|
||||
* behavior where the change was fired whenever the query was recomputed, even if the recomputed
|
||||
* query resulted in the same list.)
|
||||
*/
|
||||
emitDistinctChangesOnly = 0b0100,
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
|
||||
* @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);
|
||||
}
|
||||
|
||||
function convertAttributesToExpressions(attributes: {[name: string]: o.Expression}):
|
||||
o.Expression[] {
|
||||
const values: o.Expression[] = [];
|
||||
|
|
|
@ -143,7 +143,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
|||
// Note: queries start with id 1 so we can use the number in a Bloom filter!
|
||||
const queryId = queryIndex + 1;
|
||||
const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All;
|
||||
const flags = NodeFlags.TypeViewQuery | calcStaticDynamicQueryFlags(query);
|
||||
const flags = NodeFlags.TypeViewQuery | calcQueryFlags(query);
|
||||
this.nodes.push(() => ({
|
||||
sourceSpan: null,
|
||||
nodeFlags: flags,
|
||||
|
@ -485,7 +485,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver {
|
|||
|
||||
dirAst.directive.queries.forEach((query, queryIndex) => {
|
||||
const queryId = dirAst.contentQueryStartId + queryIndex;
|
||||
const flags = NodeFlags.TypeContentQuery | calcStaticDynamicQueryFlags(query);
|
||||
const flags = NodeFlags.TypeContentQuery | calcQueryFlags(query);
|
||||
const bindingType = query.first ? QueryBindingType.First : QueryBindingType.All;
|
||||
this.nodes.push(() => ({
|
||||
sourceSpan: dirAst.sourceSpan,
|
||||
|
@ -1028,7 +1028,7 @@ function elementEventNameAndTarget(
|
|||
}
|
||||
}
|
||||
|
||||
function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) {
|
||||
function calcQueryFlags(query: CompileQueryMetadata) {
|
||||
let flags = NodeFlags.None;
|
||||
// Note: We only make queries static that query for a single item and the user specifically
|
||||
// set the to be static. This is because of backwards compatibility with the old view compiler...
|
||||
|
@ -1037,6 +1037,9 @@ function calcStaticDynamicQueryFlags(query: CompileQueryMetadata) {
|
|||
} else {
|
||||
flags |= NodeFlags.DynamicQuery;
|
||||
}
|
||||
if (query.emitDistinctChangesOnly) {
|
||||
flags |= NodeFlags.EmitDistinctChangesOnly;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ export interface R3QueryMetadataFacade {
|
|||
first: boolean;
|
||||
predicate: any|string[];
|
||||
descendants: boolean;
|
||||
emitDistinctChangesOnly: boolean;
|
||||
read: any|null;
|
||||
static: boolean;
|
||||
}
|
||||
|
@ -251,6 +252,7 @@ export interface R3DeclareQueryMetadataFacade {
|
|||
descendants?: boolean;
|
||||
read?: OpaqueValue;
|
||||
static?: boolean;
|
||||
emitDistinctChangesOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface ParseSourceSpan {
|
||||
|
|
|
@ -86,3 +86,14 @@ export class ElementRef<T = any> {
|
|||
*/
|
||||
static __NG_ELEMENT_ID__: () => ElementRef = SWITCH_ELEMENT_REF_FACTORY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps `ElementRef` and return the `nativeElement`.
|
||||
*
|
||||
* Conditionally unwrap the `ElementRef`.
|
||||
* @param value value to unwrap
|
||||
* @returns `nativeElement` if `ElementRef` otherwise returns value as is.
|
||||
*/
|
||||
export function unwrapElementRef<T, R>(value: T|ElementRef<R>): T|R {
|
||||
return value instanceof ElementRef ? value.nativeElement : value;
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
import {Observable} from 'rxjs';
|
||||
|
||||
import {EventEmitter} from '../event_emitter';
|
||||
import {flatten} from '../util/array_utils';
|
||||
import {arrayEquals, flatten} from '../util/array_utils';
|
||||
import {getSymbolIterator} from '../util/symbol';
|
||||
|
||||
function symbolIterator<T>(this: QueryList<T>): Iterator<T> {
|
||||
|
@ -45,15 +45,31 @@ function symbolIterator<T>(this: QueryList<T>): Iterator<T> {
|
|||
export class QueryList<T> implements Iterable<T> {
|
||||
public readonly dirty = true;
|
||||
private _results: Array<T> = [];
|
||||
public readonly changes: Observable<any> = new EventEmitter();
|
||||
private _changesDetected: boolean = false;
|
||||
private _changes: EventEmitter<QueryList<T>>|null = null;
|
||||
|
||||
readonly length: number = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly first!: T;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
readonly last!: T;
|
||||
readonly first: T = undefined!;
|
||||
readonly last: T = undefined!;
|
||||
|
||||
constructor() {
|
||||
/**
|
||||
* 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<any> {
|
||||
return this._changes || (this._changes = new EventEmitter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
constructor(private _emitDistinctChangesOnly: boolean = false) {
|
||||
// This function should be declared on the prototype, but doing so there will cause the class
|
||||
// declaration to have side-effects and become not tree-shakable. For this reason we do it in
|
||||
// the constructor.
|
||||
|
@ -135,20 +151,27 @@ export class QueryList<T> implements Iterable<T> {
|
|||
* occurs.
|
||||
*
|
||||
* @param resultsTree The query results to store
|
||||
* @param identityAccessor Optional functions for extracting stable object identity from a value
|
||||
* in the array.
|
||||
*/
|
||||
reset(resultsTree: Array<T|any[]>): void {
|
||||
this._results = flatten(resultsTree);
|
||||
(this as {dirty: boolean}).dirty = false;
|
||||
(this as {length: number}).length = this._results.length;
|
||||
(this as {last: T}).last = this._results[this.length - 1];
|
||||
(this as {first: T}).first = this._results[0];
|
||||
reset(resultsTree: Array<T|any[]>, identityAccessor?: (value: T) => unknown): void {
|
||||
const self = this as QueryListInternal<T>;
|
||||
(self as {dirty: boolean}).dirty = false;
|
||||
const newResultFlat = flatten(resultsTree);
|
||||
if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) {
|
||||
self._results = newResultFlat;
|
||||
self.length = newResultFlat.length;
|
||||
self.last = newResultFlat[this.length - 1];
|
||||
self.first = newResultFlat[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a change event by emitting on the `changes` {@link EventEmitter}.
|
||||
*/
|
||||
notifyOnChanges(): void {
|
||||
(this.changes as EventEmitter<any>).emit(this);
|
||||
if (this._changes && (this._emitDistinctChangesOnly ? this._changesDetected : true))
|
||||
this._changes.emit(this);
|
||||
}
|
||||
|
||||
/** internal */
|
||||
|
@ -169,3 +192,14 @@ export class QueryList<T> implements Iterable<T> {
|
|||
// over QueryLists to work correctly, since QueryList must be assignable to NgIterable.
|
||||
[Symbol.iterator]!: () => Iterator<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal set of APIs used by the framework. (not to be made public)
|
||||
*/
|
||||
export interface QueryListInternal<T> extends QueryList<T> {
|
||||
reset(a: any[]): void;
|
||||
notifyOnChanges(): void;
|
||||
length: number;
|
||||
last: T;
|
||||
first: T;
|
||||
}
|
|
@ -98,6 +98,7 @@ export interface Attribute {
|
|||
*/
|
||||
export interface Query {
|
||||
descendants: boolean;
|
||||
emitDistinctChangesOnly: boolean;
|
||||
first: boolean;
|
||||
read: any;
|
||||
isViewQuery: boolean;
|
||||
|
@ -105,6 +106,12 @@ export interface Query {
|
|||
static?: boolean;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for query metadata.
|
||||
*
|
||||
|
@ -140,6 +147,9 @@ 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.
|
||||
* * **read** - Used to read a different token from the queried elements.
|
||||
*
|
||||
* @usageNotes
|
||||
|
@ -157,10 +167,13 @@ export interface ContentChildrenDecorator {
|
|||
*
|
||||
* @Annotation
|
||||
*/
|
||||
(selector: Type<any>|InjectionToken<unknown>|Function|string,
|
||||
opts?: {descendants?: boolean, read?: any}): any;
|
||||
(selector: Type<any>|InjectionToken<unknown>|Function|string, opts?: {
|
||||
descendants?: boolean,
|
||||
emitDistinctChangesOnly?: boolean,
|
||||
read?: any,
|
||||
}): any;
|
||||
new(selector: Type<any>|InjectionToken<unknown>|Function|string,
|
||||
opts?: {descendants?: boolean, read?: any}): Query;
|
||||
opts?: {descendants?: boolean, emitDistinctChangesOnly?: boolean, read?: any}): Query;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -180,9 +193,14 @@ export type ContentChildren = Query;
|
|||
* @publicApi
|
||||
*/
|
||||
export const ContentChildren: ContentChildrenDecorator = makePropDecorator(
|
||||
'ContentChildren',
|
||||
(selector?: any, data: any = {}) =>
|
||||
({selector, first: false, isViewQuery: false, descendants: false, ...data}),
|
||||
'ContentChildren', (selector?: any, data: any = {}) => ({
|
||||
selector,
|
||||
first: false,
|
||||
isViewQuery: false,
|
||||
descendants: false,
|
||||
emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
|
||||
...data
|
||||
}),
|
||||
Query);
|
||||
|
||||
/**
|
||||
|
@ -268,6 +286,9 @@ 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.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
|
@ -279,9 +300,10 @@ export interface ViewChildrenDecorator {
|
|||
*
|
||||
* @Annotation
|
||||
*/
|
||||
(selector: Type<any>|InjectionToken<unknown>|Function|string, opts?: {read?: any}): any;
|
||||
(selector: Type<any>|InjectionToken<unknown>|Function|string,
|
||||
opts?: {read?: any, emitDistinctChangesOnly?: boolean}): any;
|
||||
new(selector: Type<any>|InjectionToken<unknown>|Function|string,
|
||||
opts?: {read?: any}): ViewChildren;
|
||||
opts?: {read?: any, emitDistinctChangesOnly?: boolean}): ViewChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,9 +320,14 @@ export type ViewChildren = Query;
|
|||
* @publicApi
|
||||
*/
|
||||
export const ViewChildren: ViewChildrenDecorator = makePropDecorator(
|
||||
'ViewChildren',
|
||||
(selector?: any, data: any = {}) =>
|
||||
({selector, first: false, isViewQuery: true, descendants: true, ...data}),
|
||||
'ViewChildren', (selector?: any, data: any = {}) => ({
|
||||
selector,
|
||||
first: false,
|
||||
isViewQuery: true,
|
||||
descendants: true,
|
||||
emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
|
||||
...data
|
||||
}),
|
||||
Query);
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,9 +18,39 @@ import {TView} from './view';
|
|||
*/
|
||||
export interface TQueryMetadata {
|
||||
predicate: Type<any>|InjectionToken<unknown>|string[];
|
||||
descendants: boolean;
|
||||
read: any;
|
||||
isStatic: boolean;
|
||||
flags: QueryFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of flags to be used with Queries.
|
||||
*
|
||||
* NOTE: Ensure changes here are reflected in `packages/compiler/src/render3/view/compiler.ts`
|
||||
*/
|
||||
export const enum QueryFlags {
|
||||
/**
|
||||
* No flags
|
||||
*/
|
||||
none = 0b0000,
|
||||
|
||||
/**
|
||||
* Whether or not the query should descend into children.
|
||||
*/
|
||||
descendants = 0b0001,
|
||||
|
||||
/**
|
||||
* The query can be computed statically and hence can be assigned eagerly.
|
||||
*
|
||||
* NOTE: Backwards compatibility with ViewEngine.
|
||||
*/
|
||||
isStatic = 0b0010,
|
||||
|
||||
/**
|
||||
* If the `QueryList` should fire change event only if actual change to query was computed (vs old
|
||||
* behavior where the change was fired whenever the query was recomputed, even if the recomputed
|
||||
* query resulted in the same list.)
|
||||
*/
|
||||
emitDistinctChangesOnly = 0b0100,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -286,7 +286,8 @@ export function convertToR3QueryMetadata(propertyName: string, ann: Query): R3Qu
|
|||
descendants: ann.descendants,
|
||||
first: ann.first,
|
||||
read: ann.read ? ann.read : null,
|
||||
static: !!ann.static
|
||||
static: !!ann.static,
|
||||
emitDistinctChangesOnly: !!ann.emitDistinctChangesOnly,
|
||||
};
|
||||
}
|
||||
function extractQueriesMetadata(
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
|
||||
import {InjectionToken} from '../di/injection_token';
|
||||
import {Type} from '../interface/type';
|
||||
import {createElementRef, ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
|
||||
import {createElementRef, ElementRef as ViewEngine_ElementRef, unwrapElementRef} from '../linker/element_ref';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {createTemplateRef, TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
|
||||
import {createContainerRef, ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {assertDefined, assertIndexInRange, throwError} from '../util/assert';
|
||||
import {assertDefined, assertIndexInRange, assertNumber, throwError} from '../util/assert';
|
||||
import {stringify} from '../util/stringify';
|
||||
import {assertFirstCreatePass, assertLContainer} from './assert';
|
||||
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
||||
|
@ -24,7 +24,7 @@ import {CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from './interfaces/con
|
|||
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {LQueries, LQuery, QueryFlags, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view';
|
||||
import {assertTNodeType} from './node_assert';
|
||||
import {getCurrentQueryIndex, getCurrentTNode, getLView, getTView, setCurrentQueryIndex} from './state';
|
||||
|
@ -88,8 +88,8 @@ class LQueries_ implements LQueries {
|
|||
|
||||
class TQueryMetadata_ implements TQueryMetadata {
|
||||
constructor(
|
||||
public predicate: Type<any>|InjectionToken<unknown>|string[], public descendants: boolean,
|
||||
public isStatic: boolean, public read: any = null) {}
|
||||
public predicate: Type<any>|InjectionToken<unknown>|string[], public flags: QueryFlags,
|
||||
public read: any = null) {}
|
||||
}
|
||||
|
||||
class TQueries_ implements TQueries {
|
||||
|
@ -202,7 +202,8 @@ class TQuery_ implements TQuery {
|
|||
}
|
||||
|
||||
private isApplyingToNode(tNode: TNode): boolean {
|
||||
if (this._appliesToNextNode && this.metadata.descendants === false) {
|
||||
const isDescend = (this.metadata.flags & QueryFlags.descendants) === QueryFlags.descendants;
|
||||
if (this._appliesToNextNode && !isDescend) {
|
||||
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
|
||||
|
@ -427,14 +428,15 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
|||
setCurrentQueryIndex(queryIndex + 1);
|
||||
|
||||
const tQuery = getTQuery(tView, queryIndex);
|
||||
if (queryList.dirty && (isCreationMode(lView) === tQuery.metadata.isStatic)) {
|
||||
const isStatic = (tQuery.metadata.flags & QueryFlags.isStatic) === QueryFlags.isStatic;
|
||||
if (queryList.dirty && (isCreationMode(lView) === isStatic)) {
|
||||
if (tQuery.matches === null) {
|
||||
queryList.reset([]);
|
||||
} else {
|
||||
const result = tQuery.crossesNgTemplate ?
|
||||
collectQueryResults(tView, lView, queryIndex, []) :
|
||||
materializeViewResults(tView, lView, tQuery, queryIndex);
|
||||
queryList.reset(result);
|
||||
queryList.reset(result, unwrapElementRef);
|
||||
queryList.notifyOnChanges();
|
||||
}
|
||||
return true;
|
||||
|
@ -447,40 +449,42 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
|||
* Creates new QueryList for a static view query.
|
||||
*
|
||||
* @param predicate The type for which the query will search
|
||||
* @param descend Whether or not to descend into children
|
||||
* @param flags Flags associated with the query
|
||||
* @param read What to save in the query
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstaticViewQuery<T>(
|
||||
predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, read?: any): void {
|
||||
viewQueryInternal(getTView(), getLView(), predicate, descend, read, true);
|
||||
predicate: Type<any>|InjectionToken<unknown>|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.
|
||||
*
|
||||
* @param predicate The type for which the query will search
|
||||
* @param descend Whether or not to descend into children
|
||||
* @param flags Flags associated with the query
|
||||
* @param read What to save in the query
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵviewQuery<T>(
|
||||
predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean, read?: any): void {
|
||||
viewQueryInternal(getTView(), getLView(), predicate, descend, read, false);
|
||||
predicate: Type<any>|InjectionToken<unknown>|string[], flags: QueryFlags, read?: any): void {
|
||||
ngDevMode && assertNumber(flags, 'Expecting flags');
|
||||
viewQueryInternal(getTView(), getLView(), predicate, flags, read);
|
||||
}
|
||||
|
||||
function viewQueryInternal<T>(
|
||||
tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[],
|
||||
descend: boolean, read: any, isStatic: boolean): void {
|
||||
flags: QueryFlags, read: any): void {
|
||||
if (tView.firstCreatePass) {
|
||||
createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), -1);
|
||||
if (isStatic) {
|
||||
createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1);
|
||||
if (flags & QueryFlags.isStatic) {
|
||||
tView.staticViewQueries = true;
|
||||
}
|
||||
}
|
||||
createLQuery<T>(tView, lView);
|
||||
createLQuery<T>(tView, lView, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -489,17 +493,18 @@ function viewQueryInternal<T>(
|
|||
*
|
||||
* @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 flags Flags associated with the query
|
||||
* @param read What to save in the query
|
||||
* @returns QueryList<T>
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵcontentQuery<T>(
|
||||
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean,
|
||||
read?: any): void {
|
||||
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[],
|
||||
flags: QueryFlags, read?: any): void {
|
||||
ngDevMode && assertNumber(flags, 'Expecting flags');
|
||||
contentQueryInternal(
|
||||
getTView(), getLView(), predicate, descend, read, false, getCurrentTNode()!, directiveIndex);
|
||||
getTView(), getLView(), predicate, flags, read, false, getCurrentTNode()!, directiveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,31 +513,32 @@ export function ɵɵcontentQuery<T>(
|
|||
*
|
||||
* @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 flags Flags associated with the query
|
||||
* @param read What to save in the query
|
||||
* @returns QueryList<T>
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstaticContentQuery<T>(
|
||||
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[], descend: boolean,
|
||||
read?: any): void {
|
||||
directiveIndex: number, predicate: Type<any>|InjectionToken<unknown>|string[],
|
||||
flags: QueryFlags, read?: any): void {
|
||||
ngDevMode && assertNumber(flags, 'Expecting flags');
|
||||
contentQueryInternal(
|
||||
getTView(), getLView(), predicate, descend, read, true, getCurrentTNode()!, directiveIndex);
|
||||
getTView(), getLView(), predicate, flags, read, true, getCurrentTNode()!, directiveIndex);
|
||||
}
|
||||
|
||||
function contentQueryInternal<T>(
|
||||
tView: TView, lView: LView, predicate: Type<any>|InjectionToken<unknown>|string[],
|
||||
descend: boolean, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void {
|
||||
flags: QueryFlags, read: any, isStatic: boolean, tNode: TNode, directiveIndex: number): void {
|
||||
if (tView.firstCreatePass) {
|
||||
createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), tNode.index);
|
||||
createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index);
|
||||
saveContentQueryAndDirectiveIndex(tView, directiveIndex);
|
||||
if (isStatic) {
|
||||
tView.staticContentQueries = true;
|
||||
}
|
||||
}
|
||||
|
||||
createLQuery<T>(tView, lView);
|
||||
createLQuery<T>(tView, lView, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -551,8 +557,9 @@ function loadQueryInternal<T>(lView: LView, queryIndex: number): QueryList<T> {
|
|||
return lView[QUERIES]!.queries[queryIndex].queryList;
|
||||
}
|
||||
|
||||
function createLQuery<T>(tView: TView, lView: LView) {
|
||||
const queryList = new QueryList<T>();
|
||||
function createLQuery<T>(tView: TView, lView: LView, flags: QueryFlags) {
|
||||
const queryList = new QueryList<T>(
|
||||
(flags & QueryFlags.emitDistinctChangesOnly) === QueryFlags.emitDistinctChangesOnly);
|
||||
storeCleanupWithContext(tView, lView, queryList, queryList.destroy);
|
||||
|
||||
if (lView[QUERIES] === null) lView[QUERIES] = new LQueries_();
|
||||
|
|
|
@ -20,6 +20,31 @@ export function addAllToArray(items: any[], arr: any[]) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the contents of two arrays is identical
|
||||
*
|
||||
* @param a first array
|
||||
* @param b second array
|
||||
* @param identityAccessor Optional functions for extracting stable object identity from a value in
|
||||
* the array.
|
||||
*/
|
||||
export function arrayEquals<T>(a: T[], b: T[], identityAccessor?: (value: T) => unknown): boolean {
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
let valueA = a[i];
|
||||
let valueB = b[i];
|
||||
if (identityAccessor) {
|
||||
valueA = identityAccessor(valueA) as any;
|
||||
valueB = identityAccessor(valueB) as any;
|
||||
}
|
||||
if (valueB !== valueA) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flattens an array.
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {ElementRef, unwrapElementRef} from '../linker/element_ref';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
|
||||
import {asElementData, asProviderData, asQueryList, NodeDef, NodeFlags, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, ViewData} from './types';
|
||||
|
@ -50,8 +50,8 @@ export function queryDef(
|
|||
};
|
||||
}
|
||||
|
||||
export function createQuery(): QueryList<any> {
|
||||
return new QueryList();
|
||||
export function createQuery(emitDistinctChangesOnly: boolean): QueryList<any> {
|
||||
return new QueryList(emitDistinctChangesOnly);
|
||||
}
|
||||
|
||||
export function dirtyParentQueries(view: ViewData) {
|
||||
|
@ -107,7 +107,7 @@ export function checkAndUpdateQuery(view: ViewData, nodeDef: NodeDef) {
|
|||
newValues = calcQueryValues(view, 0, view.def.nodes.length - 1, nodeDef.query!, []);
|
||||
directiveInstance = view.component;
|
||||
}
|
||||
queryList.reset(newValues);
|
||||
queryList.reset(newValues, unwrapElementRef);
|
||||
const bindings = nodeDef.query!.bindings;
|
||||
let notify = false;
|
||||
for (let i = 0; i < bindings.length; i++) {
|
||||
|
|
|
@ -209,6 +209,7 @@ export const enum NodeFlags {
|
|||
StaticQuery = 1 << 28,
|
||||
DynamicQuery = 1 << 29,
|
||||
TypeNgModule = 1 << 30,
|
||||
EmitDistinctChangesOnly = 1 << 31,
|
||||
CatQuery = TypeContentQuery | TypeViewQuery,
|
||||
|
||||
// mutually exclusive values...
|
||||
|
|
|
@ -327,7 +327,9 @@ function createViewNodes(view: ViewData) {
|
|||
break;
|
||||
case NodeFlags.TypeContentQuery:
|
||||
case NodeFlags.TypeViewQuery:
|
||||
nodeData = createQuery() as any;
|
||||
nodeData = createQuery(
|
||||
(nodeDef.flags & NodeFlags.EmitDistinctChangesOnly) ===
|
||||
NodeFlags.EmitDistinctChangesOnly) as any;
|
||||
break;
|
||||
case NodeFlags.TypeNgContent:
|
||||
appendNgContent(view, renderHost, nodeDef);
|
||||
|
|
|
@ -1123,6 +1123,46 @@ describe('query logic', () => {
|
|||
fixture.detectChanges();
|
||||
expect(changes).toBe(1);
|
||||
});
|
||||
|
||||
it('should only fire if the content of the query changes', () => {
|
||||
// When views are inserted/removed the content query need to be recomputed.
|
||||
// Recomputing the query may result in no changes to the query (the item added/removed was
|
||||
// not part of the query). This tests asserts that the query does not fire when no changes
|
||||
// occur.
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [QueryCompWithStrictChangeEmitParent, QueryCompWithNoChanges]});
|
||||
const fixture = TestBed.createComponent(QueryCompWithNoChanges);
|
||||
let changesStrict = 0;
|
||||
const componentInstance = fixture.componentInstance.queryComp;
|
||||
fixture.detectChanges();
|
||||
|
||||
componentInstance.foos.changes.subscribe((value: any) => {
|
||||
// subscribe to the changes and record when changes occur.
|
||||
changesStrict += 1;
|
||||
});
|
||||
|
||||
// First verify that the subscription is working.
|
||||
fixture.componentInstance.innerShowing = false;
|
||||
fixture.detectChanges();
|
||||
expect(changesStrict).toBe(1); // We detected a change
|
||||
expect(componentInstance.foos.toArray().length).toEqual(1);
|
||||
|
||||
|
||||
// now verify that removing a view does not needlessly fire subscription
|
||||
fixture.componentInstance.showing = false;
|
||||
fixture.detectChanges();
|
||||
expect(changesStrict).toBe(1); // We detected a change
|
||||
expect(componentInstance.foos.toArray().length).toEqual(1);
|
||||
|
||||
// now verify that adding a view does not needlessly fire subscription
|
||||
fixture.componentInstance.showing = true;
|
||||
fixture.detectChanges();
|
||||
expect(changesStrict).toBe(1); // We detected a change
|
||||
// Note: even though the `showing` is `true` and the second `<div>` is displayed, the
|
||||
// child element of that <div> is hidden because the `innerShowing` flag is still `false`,
|
||||
// so we expect only one element to be present in the `foos` array.
|
||||
expect(componentInstance.foos.toArray().length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('view boundaries', () => {
|
||||
|
@ -1219,7 +1259,7 @@ describe('query logic', () => {
|
|||
* - 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 behaviour in ivy.
|
||||
* 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',
|
||||
() => {
|
||||
|
@ -1964,6 +2004,37 @@ export class QueryCompWithChanges {
|
|||
showing = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'query-with-no-changes',
|
||||
template: `
|
||||
<query-component>
|
||||
<div *ngIf="true" #foo></div>
|
||||
<div *ngIf="showing">
|
||||
Showing me should not change the content of the query
|
||||
<div *ngIf="innerShowing" #foo></div>
|
||||
</div>
|
||||
</query-component>
|
||||
`
|
||||
})
|
||||
export class QueryCompWithNoChanges {
|
||||
showing: boolean = true;
|
||||
innerShowing: boolean = true;
|
||||
queryComp!: QueryCompWithStrictChangeEmitParent;
|
||||
}
|
||||
|
||||
@Component({selector: 'query-component', template: `<ng-content></ng-content>`})
|
||||
export class QueryCompWithStrictChangeEmitParent {
|
||||
@ContentChildren('foo', {
|
||||
descendants: true,
|
||||
emitDistinctChangesOnly: true,
|
||||
})
|
||||
foos!: QueryList<any>;
|
||||
|
||||
constructor(public queryCompWithNoChanges: QueryCompWithNoChanges) {
|
||||
queryCompWithNoChanges.queryComp = this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'query-target', template: '<ng-content></ng-content>'})
|
||||
class SuperDirectiveQueryTarget {
|
||||
}
|
||||
|
|
|
@ -1982,6 +1982,9 @@
|
|||
{
|
||||
"name": "u"
|
||||
},
|
||||
{
|
||||
"name": "unwrapElementRef"
|
||||
},
|
||||
{
|
||||
"name": "unwrapRNode"
|
||||
},
|
||||
|
|
|
@ -121,22 +121,24 @@ describe('component declaration jit compilation', () => {
|
|||
static: true,
|
||||
first: true,
|
||||
read: ElementRef,
|
||||
emitDistinctChangesOnly: false,
|
||||
}
|
||||
],
|
||||
}) as ComponentDef<TestClass>;
|
||||
|
||||
expectComponentDef(def, {
|
||||
contentQueries: functionContaining([
|
||||
// "byRef" should use `contentQuery` with `false` for descendants flag without a read token,
|
||||
// and bind to the full query result.
|
||||
// "byRef" should use `contentQuery` with `0` (`QueryFlags.none`) for descendants 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,false\)/,
|
||||
/(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/,
|
||||
'(ctx.byRef = _t)',
|
||||
|
||||
// "byToken" should use `staticContentQuery` with `true` for descendants flag and
|
||||
// `ElementRef` as read token, and bind to the first result in the query result.
|
||||
// "byToken" should use `staticContentQuery` with `3`
|
||||
// (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants 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.
|
||||
/(?:staticContentQuery|anonymous)[^(]*\(dirIndex,[^,]*String[^,]*,true,[^)]*ElementRef[^)]*\)/,
|
||||
/(?:contentQuery|anonymous)[^(]*\(dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
|
||||
'(ctx.byToken = _t.first)',
|
||||
]),
|
||||
});
|
||||
|
@ -158,22 +160,24 @@ describe('component declaration jit compilation', () => {
|
|||
static: true,
|
||||
first: true,
|
||||
read: ElementRef,
|
||||
emitDistinctChangesOnly: false,
|
||||
}
|
||||
],
|
||||
}) as ComponentDef<TestClass>;
|
||||
|
||||
expectComponentDef(def, {
|
||||
viewQuery: functionContaining([
|
||||
// "byRef" should use `viewQuery` with `false` for descendants 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,false\)/,
|
||||
// "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 `staticViewQuery` with `true` for descendants flag and
|
||||
// `ElementRef` as read token, and bind to the first result in the query result.
|
||||
// "byToken" should use `viewQuery` with `3`
|
||||
// (`QueryFlags.descendants|QueryFlags.isStatic`) for descendants 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.
|
||||
/(?:staticViewQuery|anonymous)[^(]*\([^,]*String[^,]*,true,[^)]*ElementRef[^)]*\)/,
|
||||
/(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
|
||||
'(ctx.byToken = _t.first)',
|
||||
]),
|
||||
});
|
||||
|
|
|
@ -95,22 +95,24 @@ describe('directive declaration jit compilation', () => {
|
|||
static: true,
|
||||
first: true,
|
||||
read: ElementRef,
|
||||
emitDistinctChangesOnly: false,
|
||||
}
|
||||
],
|
||||
}) as DirectiveDef<TestClass>;
|
||||
|
||||
expectDirectiveDef(def, {
|
||||
contentQueries: functionContaining([
|
||||
// "byRef" should use `contentQuery` with `false` for descendants flag without a read token,
|
||||
// and bind to the full query result.
|
||||
// "byRef" should use `contentQuery` with `0` (`QueryFlags.descendants|QueryFlags.isStatic`)
|
||||
// for descendants 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,false\)/,
|
||||
/(?:contentQuery|anonymous)[^(]*\(dirIndex,_c0,4\)/,
|
||||
'(ctx.byRef = _t)',
|
||||
|
||||
// "byToken" should use `staticContentQuery` with `true` for descendants flag and
|
||||
// `ElementRef` as read token, and bind to the first result in the query result.
|
||||
// "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
|
||||
// query result.
|
||||
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
|
||||
/(?:staticContentQuery|anonymous)[^(]*\(dirIndex,[^,]*String[^,]*,true,[^)]*ElementRef[^)]*\)/,
|
||||
/(?:contentQuery|anonymous)[^(]*\([^,]*dirIndex,[^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
|
||||
'(ctx.byToken = _t.first)',
|
||||
]),
|
||||
});
|
||||
|
@ -131,6 +133,7 @@ describe('directive declaration jit compilation', () => {
|
|||
static: true,
|
||||
first: true,
|
||||
read: ElementRef,
|
||||
emitDistinctChangesOnly: false,
|
||||
}
|
||||
],
|
||||
}) as DirectiveDef<TestClass>;
|
||||
|
@ -140,13 +143,14 @@ describe('directive declaration jit compilation', () => {
|
|||
// "byRef" should use `viewQuery` with `false` for descendants 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,false\)/,
|
||||
/(?:viewQuery|anonymous)[^(]*\(_c0,4\)/,
|
||||
'(ctx.byRef = _t)',
|
||||
|
||||
// "byToken" should use `staticViewQuery` with `true` for descendants flag and
|
||||
// `ElementRef` as read token, and bind to the first result in the query result.
|
||||
// "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
|
||||
// query result.
|
||||
// NOTE: the `anonymous` match is to support IE11, as functions don't have a name there.
|
||||
/(?:staticViewQuery|anonymous)[^(]*\([^,]*String[^,]*,true,[^)]*ElementRef[^)]*\)/,
|
||||
/(?:viewQuery|anonymous)[^(]*\([^,]*String[^,]*,3,[^)]*ElementRef[^)]*\)/,
|
||||
'(ctx.byToken = _t.first)',
|
||||
]),
|
||||
});
|
||||
|
|
|
@ -51,13 +51,15 @@ describe('jit directive helper functions', () => {
|
|||
isViewQuery: false,
|
||||
read: undefined,
|
||||
static: false,
|
||||
emitDistinctChangesOnly: false,
|
||||
})).toEqual({
|
||||
propertyName: 'propName',
|
||||
predicate: ['localRef'],
|
||||
descendants: false,
|
||||
first: false,
|
||||
read: null,
|
||||
static: false
|
||||
static: false,
|
||||
emitDistinctChangesOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -69,13 +71,15 @@ describe('jit directive helper functions', () => {
|
|||
isViewQuery: true,
|
||||
read: undefined,
|
||||
static: false,
|
||||
emitDistinctChangesOnly: false,
|
||||
})).toEqual({
|
||||
propertyName: 'propName',
|
||||
predicate: ['foo', 'bar', 'baz'],
|
||||
descendants: true,
|
||||
first: true,
|
||||
read: null,
|
||||
static: false
|
||||
static: false,
|
||||
emitDistinctChangesOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -88,7 +92,8 @@ describe('jit directive helper functions', () => {
|
|||
first: true,
|
||||
isViewQuery: true,
|
||||
read: Directive,
|
||||
static: false
|
||||
static: false,
|
||||
emitDistinctChangesOnly: false,
|
||||
});
|
||||
|
||||
expect(converted.predicate).toEqual(Directive);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
import {QueryFlags} from '@angular/core/src/render3/interfaces/query';
|
||||
import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view';
|
||||
|
||||
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵProvidersFeature} from '../../src/render3/index';
|
||||
|
@ -80,8 +81,8 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(Child, false);
|
||||
ɵɵviewQuery(Child, true);
|
||||
ɵɵviewQuery(Child, QueryFlags.none);
|
||||
ɵɵviewQuery(Child, QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -119,7 +120,7 @@ describe('query', () => {
|
|||
1, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(Child, false, ElementRef);
|
||||
ɵɵviewQuery(Child, QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -158,7 +159,7 @@ describe('query', () => {
|
|||
1, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(Child, false, OtherChild);
|
||||
ɵɵviewQuery(Child, QueryFlags.none, OtherChild);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -193,7 +194,7 @@ describe('query', () => {
|
|||
1, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(Child, false, OtherChild);
|
||||
ɵɵviewQuery(Child, QueryFlags.none, OtherChild);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -263,9 +264,9 @@ describe('query', () => {
|
|||
viewQuery:
|
||||
function(rf: RenderFlags, ctx: App) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(MyDirective, false);
|
||||
ɵɵviewQuery(Service, false);
|
||||
ɵɵviewQuery(Alias, false);
|
||||
ɵɵviewQuery(MyDirective, QueryFlags.none);
|
||||
ɵɵviewQuery(Service, QueryFlags.none);
|
||||
ɵɵviewQuery(Alias, QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -315,7 +316,7 @@ describe('query', () => {
|
|||
function(rf: RenderFlags, ctx: App) {
|
||||
let tmp: any;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(MyDirective, false, Alias);
|
||||
ɵɵviewQuery(MyDirective, QueryFlags.none, Alias);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<any>>()) &&
|
||||
|
@ -353,7 +354,7 @@ describe('query', () => {
|
|||
3, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -392,8 +393,8 @@ describe('query', () => {
|
|||
4, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false);
|
||||
ɵɵviewQuery(['bar'], false);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none);
|
||||
ɵɵviewQuery(['bar'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -441,7 +442,7 @@ describe('query', () => {
|
|||
5, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo', 'bar'], false);
|
||||
ɵɵviewQuery(['foo', 'bar'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -479,7 +480,7 @@ describe('query', () => {
|
|||
3, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -517,7 +518,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, ElementRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -554,7 +555,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -588,7 +589,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, ViewContainerRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, ViewContainerRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -621,7 +622,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, ViewContainerRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, ViewContainerRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -655,7 +656,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, ElementRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -690,7 +691,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -724,7 +725,7 @@ describe('query', () => {
|
|||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, TemplateRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, TemplateRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -763,7 +764,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -810,7 +811,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -850,7 +851,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -891,7 +892,7 @@ describe('query', () => {
|
|||
3, 0, [Child1, Child2], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo', 'bar'], true);
|
||||
ɵɵviewQuery(['foo', 'bar'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -932,8 +933,8 @@ describe('query', () => {
|
|||
3, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['bar'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
ɵɵviewQuery(['bar'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -977,7 +978,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, ElementRef);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1017,7 +1018,7 @@ describe('query', () => {
|
|||
3, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo', 'bar'], false);
|
||||
ɵɵviewQuery(['foo', 'bar'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1053,7 +1054,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, Child);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, Child);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1088,7 +1089,7 @@ describe('query', () => {
|
|||
1, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(Child, false, OtherChild);
|
||||
ɵɵviewQuery(Child, QueryFlags.none, OtherChild);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1123,7 +1124,7 @@ describe('query', () => {
|
|||
1, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(OtherChild, false, Child);
|
||||
ɵɵviewQuery(OtherChild, QueryFlags.none, Child);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1155,7 +1156,7 @@ describe('query', () => {
|
|||
1, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(TemplateRef as any, false, ElementRef);
|
||||
ɵɵviewQuery(TemplateRef as any, QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1188,7 +1189,7 @@ describe('query', () => {
|
|||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], false, Child);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.none, Child);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1223,7 +1224,7 @@ describe('query', () => {
|
|||
1, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(TemplateRef as any, false);
|
||||
ɵɵviewQuery(TemplateRef as any, QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1270,8 +1271,8 @@ describe('query', () => {
|
|||
6, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(TemplateRef as any, false);
|
||||
ɵɵviewQuery(TemplateRef as any, false, ElementRef);
|
||||
ɵɵviewQuery(TemplateRef as any, QueryFlags.none);
|
||||
ɵɵviewQuery(TemplateRef as any, QueryFlags.none, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1334,7 +1335,7 @@ describe('query', () => {
|
|||
3, 0, [SomeDir], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1376,7 +1377,7 @@ describe('query', () => {
|
|||
contentQueries:
|
||||
(rf: RenderFlags, ctx: any, dirIndex: number) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], true);
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1463,7 +1464,7 @@ describe('query', () => {
|
|||
5, 0, [WithContentDirective], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo', 'bar'], true);
|
||||
ɵɵviewQuery(['foo', 'bar'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1505,7 +1506,7 @@ describe('query', () => {
|
|||
5, 0, [WithContentDirective], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['bar'], true);
|
||||
ɵɵviewQuery(['bar'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1533,7 +1534,7 @@ describe('query', () => {
|
|||
// @ContentChildren('foo, bar, baz', {descendants: true})
|
||||
// fooBars: QueryList<ElementRef>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], true);
|
||||
ɵɵcontentQuery(dirIndex, ['foo', 'bar', 'baz'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1602,7 +1603,7 @@ describe('query', () => {
|
|||
// @ContentChildren('foo', {descendants: true})
|
||||
// fooBars: QueryList<ElementRef>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], false);
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1662,7 +1663,7 @@ describe('query', () => {
|
|||
// @ContentChildren('foo', {descendants: true})
|
||||
// fooBars: QueryList<ElementRef>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], false);
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1726,7 +1727,7 @@ describe('query', () => {
|
|||
// @ContentChildren('foo', {descendants: false})
|
||||
// foos: QueryList<ElementRef>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], false);
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.none);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1748,7 +1749,7 @@ describe('query', () => {
|
|||
// @ContentChildren('foo', {descendants: true})
|
||||
// foos: QueryList<ElementRef>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], true);
|
||||
ɵɵcontentQuery(dirIndex, ['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1824,7 +1825,7 @@ describe('query', () => {
|
|||
// @ContentChildren(TextDirective, {descendants: true})
|
||||
// texts: QueryList<TextDirective>;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵcontentQuery(dirIndex, TextDirective, true);
|
||||
ɵɵcontentQuery(dirIndex, TextDirective, QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
@ -1910,7 +1911,7 @@ describe('query', () => {
|
|||
function(rf: RenderFlags, ctx: ViewQueryComponent) {
|
||||
let tmp: any;
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(TextDirective, true);
|
||||
ɵɵviewQuery(TextDirective, QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵqueryRefresh(tmp = ɵɵloadQuery<QueryList<TextDirective>>()) &&
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {QueryFlags} from '@angular/core/src/render3/interfaces/query';
|
||||
import {HEADER_OFFSET} from '@angular/core/src/render3/interfaces/view';
|
||||
import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, QueryList, TemplateRef, ViewContainerRef, ViewRef} from '../../src/core';
|
||||
import {ViewEncapsulation} from '../../src/metadata';
|
||||
|
@ -368,7 +369,7 @@ describe('ViewContainerRef', () => {
|
|||
viewQuery:
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['foo'], true);
|
||||
ɵɵviewQuery(['foo'], QueryFlags.descendants);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
|
|
Loading…
Reference in New Issue