fix(ivy): View Queries inheritance fix (#28309)
Prior to this change `viewQuery` functions that represent @ViewQuery list were not composable, which caused problems in case one Component/Directive inherits another one and both of them contain View Queries. Due to the fact that we used indices to reference queries, resulting query set was corrupted (child component queries were overridden by super class ones). In order to avoid that we no longer use indices assigned at compile time and instead maintain current view query index while iterating through them. This allows us to compose `viewQuery` functions and make inheritance feature work with View Queries. PR Close #28309
This commit is contained in:
parent
9f9024b7a1
commit
9098225ff0
|
@ -1376,20 +1376,20 @@ describe('compiler compliance', () => {
|
||||||
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
|
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
|
||||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵquery(0, SomeDirective, true);
|
$r3$.ɵviewQuery(SomeDirective, true);
|
||||||
$r3$.ɵquery(1, SomeDirective, true);
|
$r3$.ɵviewQuery(SomeDirective, true);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
var $tmp$;
|
var $tmp$;
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.someDir = $tmp$.first));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDir = $tmp$.first));
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.someDirs = $tmp$));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDirs = $tmp$));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
consts: 3,
|
consts: 1,
|
||||||
vars: 0,
|
vars: 0,
|
||||||
template: function ViewQueryComponent_Template(rf, ctx) {
|
template: function ViewQueryComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵelement(2, "div", $e0_attrs$);
|
$r3$.ɵelement(0, "div", $e0_attrs$);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: function () { return [SomeDirective]; },
|
directives: function () { return [SomeDirective]; },
|
||||||
|
@ -1434,13 +1434,13 @@ describe('compiler compliance', () => {
|
||||||
…
|
…
|
||||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵquery(0, $e0_attrs$, true);
|
$r3$.ɵviewQuery($e0_attrs$, true);
|
||||||
$r3$.ɵquery(1, $e1_attrs$, true);
|
$r3$.ɵviewQuery($e1_attrs$, true);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
var $tmp$;
|
var $tmp$;
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.myRef = $tmp$.first));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRef = $tmp$.first));
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.myRefs = $tmp$));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRefs = $tmp$));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
…
|
…
|
||||||
|
@ -1489,17 +1489,17 @@ describe('compiler compliance', () => {
|
||||||
…
|
…
|
||||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵquery(0, $e0_attrs$, true, TemplateRef);
|
$r3$.ɵviewQuery($e0_attrs$, true, TemplateRef);
|
||||||
$r3$.ɵquery(1, SomeDirective, true, ElementRef);
|
$r3$.ɵviewQuery(SomeDirective, true, ElementRef);
|
||||||
$r3$.ɵquery(2, $e1_attrs$, true, ElementRef);
|
$r3$.ɵviewQuery($e1_attrs$, true, ElementRef);
|
||||||
$r3$.ɵquery(3, SomeDirective, true, TemplateRef);
|
$r3$.ɵviewQuery(SomeDirective, true, TemplateRef);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
var $tmp$;
|
var $tmp$;
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.myRef = $tmp$.first));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRef = $tmp$.first));
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.someDir = $tmp$.first));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDir = $tmp$.first));
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(2))) && (ctx.myRefs = $tmp$));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRefs = $tmp$));
|
||||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(3))) && (ctx.someDirs = $tmp$));
|
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDirs = $tmp$));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
…
|
…
|
||||||
|
@ -1554,8 +1554,8 @@ describe('compiler compliance', () => {
|
||||||
return new (t || ContentQueryComponent)();
|
return new (t || ContentQueryComponent)();
|
||||||
},
|
},
|
||||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true), dirIndex);
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false), dirIndex);
|
||||||
},
|
},
|
||||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||||
const instance = $r3$.ɵload(dirIndex);
|
const instance = $r3$.ɵload(dirIndex);
|
||||||
|
@ -1613,8 +1613,8 @@ describe('compiler compliance', () => {
|
||||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
…
|
…
|
||||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$, true), dirIndex);
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false), dirIndex);
|
||||||
},
|
},
|
||||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||||
const instance = $r3$.ɵload(dirIndex);
|
const instance = $r3$.ɵload(dirIndex);
|
||||||
|
@ -1666,10 +1666,10 @@ describe('compiler compliance', () => {
|
||||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
…
|
…
|
||||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$ , true, TemplateRef), dirIndex);
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true, ElementRef), dirIndex);
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false, ElementRef), dirIndex);
|
||||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef), dirIndex);
|
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false, TemplateRef), dirIndex);
|
||||||
},
|
},
|
||||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||||
const instance = $r3$.ɵload(dirIndex);
|
const instance = $r3$.ɵload(dirIndex);
|
||||||
|
|
|
@ -709,9 +709,9 @@ describe('ngtsc behavioral tests', () => {
|
||||||
|
|
||||||
// Helper functions to construct RegExps for output validation
|
// Helper functions to construct RegExps for output validation
|
||||||
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
||||||
const queryRegExp = (id: number | null, descend: boolean, ref?: string): RegExp => {
|
const queryRegExp = (fnName: string, descend: boolean, ref?: string | null): RegExp => {
|
||||||
const maybeRef = ref ? `, ${ref}` : ``;
|
const maybeRef = ref ? `, ${ref}` : ``;
|
||||||
return new RegExp(`i0\\.ɵquery\\(${id}, \\w+, ${descend}${maybeRef}\\)`);
|
return new RegExp(`i0\\.ɵ${fnName}\\(\\w+, ${descend}${maybeRef}\\)`);
|
||||||
};
|
};
|
||||||
|
|
||||||
env.tsconfig();
|
env.tsconfig();
|
||||||
|
@ -740,13 +740,23 @@ describe('ngtsc behavioral tests', () => {
|
||||||
expect(jsContents).toMatch(varRegExp('test1'));
|
expect(jsContents).toMatch(varRegExp('test1'));
|
||||||
expect(jsContents).toMatch(varRegExp('test2'));
|
expect(jsContents).toMatch(varRegExp('test2'));
|
||||||
expect(jsContents).toMatch(varRegExp('accessor'));
|
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||||
expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, false)`);
|
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, false)`);
|
||||||
expect(jsContents)
|
expect(jsContents)
|
||||||
.toMatch(queryRegExp(
|
.toMatch(
|
||||||
null, true, 'TemplateRef')); // match `i0.ɵquery(null, _c0, true, TemplateRef)`
|
// match `i0.ɵquery(_c0, true, TemplateRef)`
|
||||||
expect(jsContents).toMatch(queryRegExp(null, true)); // match `i0.ɵquery(null, _c0, true)`
|
queryRegExp('query', true, 'TemplateRef'));
|
||||||
expect(jsContents).toMatch(queryRegExp(0, true)); // match `i0.ɵquery(0, _c0, true)`
|
expect(jsContents)
|
||||||
expect(jsContents).toMatch(queryRegExp(1, true)); // match `i0.ɵquery(1, _c0, true)`
|
.toMatch(
|
||||||
|
// match `i0.ɵquery(_c0, true)`
|
||||||
|
queryRegExp('query', true));
|
||||||
|
expect(jsContents)
|
||||||
|
.toMatch(
|
||||||
|
// match `i0.ɵviewQuery(_c0, true)`
|
||||||
|
queryRegExp('viewQuery', true));
|
||||||
|
expect(jsContents)
|
||||||
|
.toMatch(
|
||||||
|
// match `i0.ɵviewQuery(_c0, true)`
|
||||||
|
queryRegExp('viewQuery', true));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle queries that use forwardRef', () => {
|
it('should handle queries that use forwardRef', () => {
|
||||||
|
@ -767,8 +777,8 @@ describe('ngtsc behavioral tests', () => {
|
||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, true)`);
|
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, true)`);
|
||||||
expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`);
|
expect(jsContents).toContain(`i0.ɵquery(ViewContainerRef, true)`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate host listeners for components', () => {
|
it('should generate host listeners for components', () => {
|
||||||
|
|
|
@ -185,6 +185,8 @@ export class Identifiers {
|
||||||
|
|
||||||
static query: o.ExternalReference = {name: 'ɵquery', moduleName: CORE};
|
static query: o.ExternalReference = {name: 'ɵquery', moduleName: CORE};
|
||||||
static queryRefresh: o.ExternalReference = {name: 'ɵqueryRefresh', moduleName: CORE};
|
static queryRefresh: o.ExternalReference = {name: 'ɵqueryRefresh', moduleName: CORE};
|
||||||
|
static viewQuery: o.ExternalReference = {name: 'ɵviewQuery', moduleName: CORE};
|
||||||
|
static loadViewQuery: o.ExternalReference = {name: 'ɵloadViewQuery', moduleName: CORE};
|
||||||
static registerContentQuery:
|
static registerContentQuery:
|
||||||
o.ExternalReference = {name: 'ɵregisterContentQuery', moduleName: CORE};
|
o.ExternalReference = {name: 'ɵregisterContentQuery', moduleName: CORE};
|
||||||
|
|
||||||
|
|
|
@ -254,7 +254,7 @@ export function compileComponentFromMetadata(
|
||||||
const template = meta.template;
|
const template = meta.template;
|
||||||
const templateBuilder = new TemplateDefinitionBuilder(
|
const templateBuilder = new TemplateDefinitionBuilder(
|
||||||
constantPool, BindingScope.ROOT_SCOPE, 0, templateTypeName, null, null, templateName,
|
constantPool, BindingScope.ROOT_SCOPE, 0, templateTypeName, null, null, templateName,
|
||||||
meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML,
|
directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML,
|
||||||
meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
||||||
|
|
||||||
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
||||||
|
@ -485,22 +485,19 @@ function selectorsFromGlobalMetadata(
|
||||||
return o.NULL_EXPR;
|
return o.NULL_EXPR;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createQueryDefinition(
|
function prepareQueryParams(query: R3QueryMetadata, constantPool: ConstantPool): o.Expression[] {
|
||||||
query: R3QueryMetadata, constantPool: ConstantPool, idx: number | null): o.Expression {
|
|
||||||
const predicate = getQueryPredicate(query, constantPool);
|
|
||||||
|
|
||||||
// e.g. r3.query(null, somePredicate, false) or r3.query(0, ['div'], false)
|
|
||||||
const parameters = [
|
const parameters = [
|
||||||
o.literal(idx, o.INFERRED_TYPE),
|
getQueryPredicate(query, constantPool),
|
||||||
predicate,
|
|
||||||
o.literal(query.descendants),
|
o.literal(query.descendants),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (query.read) {
|
if (query.read) {
|
||||||
parameters.push(query.read);
|
parameters.push(query.read);
|
||||||
}
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
return o.importExpr(R3.query).callFn(parameters);
|
function createQueryDefinition(query: R3QueryMetadata, constantPool: ConstantPool): o.Expression {
|
||||||
|
return o.importExpr(R3.query).callFn(prepareQueryParams(query, constantPool));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn a directive selector into an R3-compatible selector for directive def
|
// Turn a directive selector into an R3-compatible selector for directive def
|
||||||
|
@ -522,7 +519,7 @@ function createContentQueriesFunction(
|
||||||
meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression|null {
|
meta: R3DirectiveMetadata, constantPool: ConstantPool): o.Expression|null {
|
||||||
if (meta.queries.length) {
|
if (meta.queries.length) {
|
||||||
const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => {
|
const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => {
|
||||||
const queryDefinition = createQueryDefinition(query, constantPool, null);
|
const queryDefinition = createQueryDefinition(query, constantPool);
|
||||||
return o.importExpr(R3.registerContentQuery)
|
return o.importExpr(R3.registerContentQuery)
|
||||||
.callFn([queryDefinition, o.variable('dirIndex')])
|
.callFn([queryDefinition, o.variable('dirIndex')])
|
||||||
.toStmt();
|
.toStmt();
|
||||||
|
@ -620,22 +617,21 @@ function createViewQueriesFunction(
|
||||||
const updateStatements: o.Statement[] = [];
|
const updateStatements: o.Statement[] = [];
|
||||||
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
||||||
|
|
||||||
for (let i = 0; i < meta.viewQueries.length; i++) {
|
meta.viewQueries.forEach((query: R3QueryMetadata) => {
|
||||||
const query = meta.viewQueries[i];
|
// creation, e.g. r3.viewQuery(somePredicate, true);
|
||||||
|
const queryDefinition =
|
||||||
// creation, e.g. r3.Q(0, somePredicate, true);
|
o.importExpr(R3.viewQuery).callFn(prepareQueryParams(query, constantPool));
|
||||||
const queryDefinition = createQueryDefinition(query, constantPool, i);
|
|
||||||
createStatements.push(queryDefinition.toStmt());
|
createStatements.push(queryDefinition.toStmt());
|
||||||
|
|
||||||
// update, e.g. (r3.qR(tmp = r3.ɵload(0)) && (ctx.someDir = tmp));
|
// update, e.g. (r3.queryRefresh(tmp = r3.loadViewQuery()) && (ctx.someDir = tmp));
|
||||||
const temporary = tempAllocator();
|
const temporary = tempAllocator();
|
||||||
const getQueryList = o.importExpr(R3.load).callFn([o.literal(i)]);
|
const getQueryList = o.importExpr(R3.loadViewQuery).callFn([]);
|
||||||
const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]);
|
const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]);
|
||||||
const updateDirective = o.variable(CONTEXT_NAME)
|
const updateDirective = o.variable(CONTEXT_NAME)
|
||||||
.prop(query.propertyName)
|
.prop(query.propertyName)
|
||||||
.set(query.first ? temporary.prop('first') : temporary);
|
.set(query.first ? temporary.prop('first') : temporary);
|
||||||
updateStatements.push(refresh.and(updateDirective).toStmt());
|
updateStatements.push(refresh.and(updateDirective).toStmt());
|
||||||
}
|
});
|
||||||
|
|
||||||
const viewQueryFnName = meta.name ? `${meta.name}_Query` : null;
|
const viewQueryFnName = meta.name ? `${meta.name}_Query` : null;
|
||||||
return o.fn(
|
return o.fn(
|
||||||
|
|
|
@ -31,7 +31,6 @@ import {Identifiers as R3} from '../r3_identifiers';
|
||||||
import {htmlAstToRender3Ast} from '../r3_template_transform';
|
import {htmlAstToRender3Ast} from '../r3_template_transform';
|
||||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName} from '../util';
|
import {prepareSyntheticListenerFunctionName, prepareSyntheticListenerName, prepareSyntheticPropertyName} from '../util';
|
||||||
|
|
||||||
import {R3QueryMetadata} from './api';
|
|
||||||
import {I18nContext} from './i18n/context';
|
import {I18nContext} from './i18n/context';
|
||||||
import {I18nMetaVisitor} from './i18n/meta';
|
import {I18nMetaVisitor} from './i18n/meta';
|
||||||
import {getSerializedI18nContent} from './i18n/serializer';
|
import {getSerializedI18nContent} from './i18n/serializer';
|
||||||
|
@ -161,14 +160,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
||||||
private contextName: string|null, private i18nContext: I18nContext|null,
|
private contextName: string|null, private i18nContext: I18nContext|null,
|
||||||
private templateIndex: number|null, private templateName: string|null,
|
private templateIndex: number|null, private templateName: string|null,
|
||||||
private viewQueries: R3QueryMetadata[], private directiveMatcher: SelectorMatcher|null,
|
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
|
||||||
private directives: Set<o.Expression>, private pipeTypeByName: Map<string, o.Expression>,
|
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>,
|
||||||
private pipes: Set<o.Expression>, private _namespace: o.ExternalReference,
|
private _namespace: o.ExternalReference, private relativeContextFilePath: string,
|
||||||
private relativeContextFilePath: string, private i18nUseExternalIds: boolean) {
|
private i18nUseExternalIds: boolean) {
|
||||||
// view queries can take up space in data and allocation happens earlier (in the "viewQuery"
|
|
||||||
// function)
|
|
||||||
this._dataIndex = viewQueries.length;
|
|
||||||
|
|
||||||
this._bindingScope = parentBindingScope.nestedScope(level);
|
this._bindingScope = parentBindingScope.nestedScope(level);
|
||||||
|
|
||||||
// Turn the relative context file path into an identifier by replacing non-alphanumeric
|
// Turn the relative context file path into an identifier by replacing non-alphanumeric
|
||||||
|
@ -821,9 +816,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
// Create the template function
|
// Create the template function
|
||||||
const templateVisitor = new TemplateDefinitionBuilder(
|
const templateVisitor = new TemplateDefinitionBuilder(
|
||||||
this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n,
|
this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n,
|
||||||
templateIndex, templateName, [], this.directiveMatcher, this.directives,
|
templateIndex, templateName, this.directiveMatcher, this.directives, this.pipeTypeByName,
|
||||||
this.pipeTypeByName, this.pipes, this._namespace, this.fileBasedI18nSuffix,
|
this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds);
|
||||||
this.i18nUseExternalIds);
|
|
||||||
|
|
||||||
// Nested templates must not be visited until after their parent templates have completed
|
// Nested templates must not be visited until after their parent templates have completed
|
||||||
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
||||||
|
|
|
@ -81,6 +81,8 @@ export {
|
||||||
containerRefreshStart as ɵcontainerRefreshStart,
|
containerRefreshStart as ɵcontainerRefreshStart,
|
||||||
containerRefreshEnd as ɵcontainerRefreshEnd,
|
containerRefreshEnd as ɵcontainerRefreshEnd,
|
||||||
queryRefresh as ɵqueryRefresh,
|
queryRefresh as ɵqueryRefresh,
|
||||||
|
viewQuery as ɵviewQuery,
|
||||||
|
loadViewQuery as ɵloadViewQuery,
|
||||||
loadQueryList as ɵloadQueryList,
|
loadQueryList as ɵloadQueryList,
|
||||||
elementEnd as ɵelementEnd,
|
elementEnd as ɵelementEnd,
|
||||||
elementProperty as ɵelementProperty,
|
elementProperty as ɵelementProperty,
|
||||||
|
|
|
@ -124,6 +124,8 @@ export {
|
||||||
export {
|
export {
|
||||||
query,
|
query,
|
||||||
queryRefresh,
|
queryRefresh,
|
||||||
|
viewQuery,
|
||||||
|
loadViewQuery,
|
||||||
} from './query';
|
} from './query';
|
||||||
export {
|
export {
|
||||||
registerContentQuery,
|
registerContentQuery,
|
||||||
|
|
|
@ -36,7 +36,7 @@ import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLA
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getCurrentViewQueryIndex, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentViewQueryIndex, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
||||||
import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
import {getInitialClassNameValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialStylesAndClasses, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||||
import {BoundPlayerFactory} from './styling/player_factory';
|
import {BoundPlayerFactory} from './styling/player_factory';
|
||||||
import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util';
|
import {createEmptyStylingContext, getStylingContext, hasClassInput, hasStyling, isAnimationProp} from './styling/util';
|
||||||
|
@ -722,6 +722,7 @@ export function createTView(
|
||||||
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LView
|
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LView
|
||||||
childIndex: -1, // Children set in addToViewTree(), if any
|
childIndex: -1, // Children set in addToViewTree(), if any
|
||||||
bindingStartIndex: bindingStartIndex,
|
bindingStartIndex: bindingStartIndex,
|
||||||
|
viewQueryStartIndex: initialViewLength,
|
||||||
expandoStartIndex: initialViewLength,
|
expandoStartIndex: initialViewLength,
|
||||||
expandoInstructions: null,
|
expandoInstructions: null,
|
||||||
firstTemplatePass: true,
|
firstTemplatePass: true,
|
||||||
|
@ -2690,28 +2691,24 @@ export function checkView<T>(hostView: LView, component: T) {
|
||||||
const hostTView = hostView[TVIEW];
|
const hostTView = hostView[TVIEW];
|
||||||
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
const oldView = enterView(hostView, hostView[HOST_NODE]);
|
||||||
const templateFn = hostTView.template !;
|
const templateFn = hostTView.template !;
|
||||||
const viewQuery = hostTView.viewQuery;
|
const creationMode = isCreationMode(hostView);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
namespaceHTML();
|
namespaceHTML();
|
||||||
createViewQuery(viewQuery, hostView, component);
|
creationMode && executeViewQueryFn(hostView, hostTView, component);
|
||||||
templateFn(getRenderFlags(hostView), component);
|
templateFn(getRenderFlags(hostView), component);
|
||||||
refreshDescendantViews(hostView);
|
refreshDescendantViews(hostView);
|
||||||
updateViewQuery(viewQuery, hostView, component);
|
!creationMode && executeViewQueryFn(hostView, hostTView, component);
|
||||||
} finally {
|
} finally {
|
||||||
leaveView(oldView);
|
leaveView(oldView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
|
function executeViewQueryFn<T>(lView: LView, tView: TView, component: T): void {
|
||||||
if (viewQuery && isCreationMode(view)) {
|
const viewQuery = tView.viewQuery;
|
||||||
viewQuery(RenderFlags.Create, component);
|
if (viewQuery) {
|
||||||
}
|
setCurrentViewQueryIndex(tView.viewQueryStartIndex);
|
||||||
}
|
viewQuery(getRenderFlags(lView), component);
|
||||||
|
|
||||||
function updateViewQuery<T>(viewQuery: ComponentQuery<{}>| null, view: LView, component: T): void {
|
|
||||||
if (viewQuery && !isCreationMode(view)) {
|
|
||||||
viewQuery(RenderFlags.Update, component);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,6 +335,17 @@ export interface TView {
|
||||||
*/
|
*/
|
||||||
expandoStartIndex: number;
|
expandoStartIndex: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index where the viewQueries section of `LView` begins. This section contains
|
||||||
|
* view queries defined for a component/directive.
|
||||||
|
*
|
||||||
|
* We store this start index so we know where the list of view queries starts.
|
||||||
|
* This is required when we invoke view queries at runtime. We invoke queries one by one and
|
||||||
|
* increment query index after each iteration. This information helps us to reset index back to
|
||||||
|
* the beginning of view query list before we invoke view queries again.
|
||||||
|
*/
|
||||||
|
viewQueryStartIndex: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index of the host node of the first LView or LContainer beneath this LView in
|
* Index of the host node of the first LView or LContainer beneath this LView in
|
||||||
* the hierarchy.
|
* the hierarchy.
|
||||||
|
|
|
@ -89,6 +89,8 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||||
'ɵpipe': r3.pipe,
|
'ɵpipe': r3.pipe,
|
||||||
'ɵquery': r3.query,
|
'ɵquery': r3.query,
|
||||||
'ɵqueryRefresh': r3.queryRefresh,
|
'ɵqueryRefresh': r3.queryRefresh,
|
||||||
|
'ɵviewQuery': r3.viewQuery,
|
||||||
|
'ɵloadViewQuery': r3.loadViewQuery,
|
||||||
'ɵregisterContentQuery': r3.registerContentQuery,
|
'ɵregisterContentQuery': r3.registerContentQuery,
|
||||||
'ɵreference': r3.reference,
|
'ɵreference': r3.reference,
|
||||||
'ɵelementStyling': r3.elementStyling,
|
'ɵelementStyling': r3.elementStyling,
|
||||||
|
|
|
@ -18,13 +18,13 @@ import {assertDefined, assertEqual} from '../util/assert';
|
||||||
import {assertPreviousIsParent} from './assert';
|
import {assertPreviousIsParent} from './assert';
|
||||||
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
||||||
import {NG_ELEMENT_ID} from './fields';
|
import {NG_ELEMENT_ID} from './fields';
|
||||||
import {store, storeCleanupWithContext} from './instructions';
|
import {load, store, storeCleanupWithContext} from './instructions';
|
||||||
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||||
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||||
import {LView, TVIEW} from './interfaces/view';
|
import {LView, TVIEW} from './interfaces/view';
|
||||||
import {getIsParent, getLView, getOrCreateCurrentQueries} from './state';
|
import {getCurrentViewQueryIndex, getIsParent, getLView, getOrCreateCurrentQueries, setCurrentViewQueryIndex} from './state';
|
||||||
import {isContentQueryHost} from './util';
|
import {isContentQueryHost} from './util';
|
||||||
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
|
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
|
||||||
|
|
||||||
|
@ -357,26 +357,20 @@ type QueryList_<T> = QueryList<T>& {_valuesTree: any[]};
|
||||||
/**
|
/**
|
||||||
* Creates and returns a QueryList.
|
* Creates and returns a QueryList.
|
||||||
*
|
*
|
||||||
* @param memoryIndex The index in memory where the QueryList should be saved. If null,
|
|
||||||
* this is is a content query and the QueryList will be saved later through directiveCreate.
|
|
||||||
* @param predicate The type for which the query will search
|
* @param predicate The type for which the query will search
|
||||||
* @param descend Whether or not to descend into children
|
* @param descend Whether or not to descend into children
|
||||||
* @param read What to save in the query
|
* @param read What to save in the query
|
||||||
* @returns QueryList<T>
|
* @returns QueryList<T>
|
||||||
*/
|
*/
|
||||||
export function query<T>(
|
export function query<T>(
|
||||||
memoryIndex: number | null, predicate: Type<any>| string[], descend?: boolean,
|
|
||||||
// TODO: "read" should be an AbstractType (FW-486)
|
// TODO: "read" should be an AbstractType (FW-486)
|
||||||
read?: any): QueryList<T> {
|
predicate: Type<any>| string[], descend?: boolean, read?: any): QueryList<T> {
|
||||||
ngDevMode && assertPreviousIsParent(getIsParent());
|
ngDevMode && assertPreviousIsParent(getIsParent());
|
||||||
const queryList = new QueryList<T>();
|
const queryList = new QueryList<T>();
|
||||||
const queries = getOrCreateCurrentQueries(LQueries_);
|
const queries = getOrCreateCurrentQueries(LQueries_);
|
||||||
(queryList as QueryList_<T>)._valuesTree = [];
|
(queryList as QueryList_<T>)._valuesTree = [];
|
||||||
queries.track(queryList, predicate, descend, read);
|
queries.track(queryList, predicate, descend, read);
|
||||||
storeCleanupWithContext(getLView(), queryList, queryList.destroy);
|
storeCleanupWithContext(getLView(), queryList, queryList.destroy);
|
||||||
if (memoryIndex != null) {
|
|
||||||
store(memoryIndex, queryList);
|
|
||||||
}
|
|
||||||
return queryList;
|
return queryList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,3 +388,35 @@ export function queryRefresh(queryList: QueryList<any>): boolean {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 read What to save in the query
|
||||||
|
* @returns QueryList<T>
|
||||||
|
*/
|
||||||
|
export function viewQuery<T>(
|
||||||
|
// TODO: "read" should be an AbstractType (FW-486)
|
||||||
|
predicate: Type<any>| string[], descend?: boolean, read?: any): QueryList<T> {
|
||||||
|
const lView = getLView();
|
||||||
|
const tView = lView[TVIEW];
|
||||||
|
if (tView.firstTemplatePass) {
|
||||||
|
tView.expandoStartIndex++;
|
||||||
|
}
|
||||||
|
const index = getCurrentViewQueryIndex();
|
||||||
|
const viewQuery: QueryList<T> = query<T>(predicate, descend, read);
|
||||||
|
store(index, viewQuery);
|
||||||
|
setCurrentViewQueryIndex(index + 1);
|
||||||
|
return viewQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads current View Query and moves the pointer/index to the next View Query in LView.
|
||||||
|
*/
|
||||||
|
export function loadViewQuery<T>(): T {
|
||||||
|
const index = getCurrentViewQueryIndex();
|
||||||
|
setCurrentViewQueryIndex(index + 1);
|
||||||
|
return load<T>(index);
|
||||||
|
}
|
|
@ -256,6 +256,21 @@ export function setBindingRoot(value: number) {
|
||||||
bindingRootIndex = value;
|
bindingRootIndex = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current index of a View Query which needs to be processed next.
|
||||||
|
* We iterate over the list of View Queries stored in LView and increment current query index.
|
||||||
|
*/
|
||||||
|
let viewQueryIndex: number = 0;
|
||||||
|
|
||||||
|
export function getCurrentViewQueryIndex(): number {
|
||||||
|
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
||||||
|
return viewQueryIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCurrentViewQueryIndex(value: number): void {
|
||||||
|
viewQueryIndex = value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swap the current state with a new state.
|
* Swap the current state with a new state.
|
||||||
*
|
*
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
|
||||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||||
import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector';
|
import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector';
|
||||||
import {TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
import {TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||||
import {GlobalTargetName, GlobalTargetResolver, RComment, RElement, RText} from './interfaces/renderer';
|
import {RComment, RElement, RText} from './interfaces/renderer';
|
||||||
import {StylingContext} from './interfaces/styling';
|
import {StylingContext} from './interfaces/styling';
|
||||||
import {CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, PARENT, RootContext, TData, TVIEW, TView} from './interfaces/view';
|
import {CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LView, LViewFlags, PARENT, RootContext, TData, TVIEW, TView} from './interfaces/view';
|
||||||
|
|
||||||
|
|
|
@ -179,9 +179,6 @@
|
||||||
{
|
{
|
||||||
"name": "createViewBlueprint"
|
"name": "createViewBlueprint"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "createViewQuery"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "defaultScheduler"
|
"name": "defaultScheduler"
|
||||||
},
|
},
|
||||||
|
@ -203,6 +200,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeInitHooks"
|
"name": "executeInitHooks"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeViewQueryFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "extractDirectiveDef"
|
"name": "extractDirectiveDef"
|
||||||
},
|
},
|
||||||
|
@ -407,6 +407,9 @@
|
||||||
{
|
{
|
||||||
"name": "setCurrentDirectiveDef"
|
"name": "setCurrentDirectiveDef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setCurrentViewQueryIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setFirstTemplatePass"
|
"name": "setFirstTemplatePass"
|
||||||
},
|
},
|
||||||
|
@ -437,9 +440,6 @@
|
||||||
{
|
{
|
||||||
"name": "tickRootContext"
|
"name": "tickRootContext"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "updateViewQuery"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "viewAttached"
|
"name": "viewAttached"
|
||||||
}
|
}
|
||||||
|
|
|
@ -482,9 +482,6 @@
|
||||||
{
|
{
|
||||||
"name": "createViewNode"
|
"name": "createViewNode"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "createViewQuery"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "decreaseElementDepthCount"
|
"name": "decreaseElementDepthCount"
|
||||||
},
|
},
|
||||||
|
@ -566,6 +563,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeOnDestroys"
|
"name": "executeOnDestroys"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeViewQueryFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "extendStatics"
|
"name": "extendStatics"
|
||||||
},
|
},
|
||||||
|
@ -1127,6 +1127,9 @@
|
||||||
{
|
{
|
||||||
"name": "setCurrentDirectiveDef"
|
"name": "setCurrentDirectiveDef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setCurrentViewQueryIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setDirectiveDirty"
|
"name": "setDirectiveDirty"
|
||||||
},
|
},
|
||||||
|
@ -1220,9 +1223,6 @@
|
||||||
{
|
{
|
||||||
"name": "updateContextWithBindings"
|
"name": "updateContextWithBindings"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "updateViewQuery"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "valueExists"
|
"name": "valueExists"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {SelectorFlags} from '@angular/core/src/render3/interfaces/projection';
|
import {SelectorFlags} from '@angular/core/src/render3/interfaces/projection';
|
||||||
|
|
||||||
import {AttributeMarker, defineDirective, detectChanges, directiveInject, load, query, queryRefresh, reference, templateRefExtractor} from '../../src/render3/index';
|
import {AttributeMarker, defineDirective, detectChanges, directiveInject, loadViewQuery, queryRefresh, reference, templateRefExtractor, viewQuery} from '../../src/render3/index';
|
||||||
|
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, projection, projectionDef, template, text, textBinding, interpolation1} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, projection, projectionDef, template, text, textBinding, interpolation1} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
@ -1026,11 +1026,11 @@ describe('content projection', () => {
|
||||||
function(rf: RenderFlags, ctx: any) {
|
function(rf: RenderFlags, ctx: any) {
|
||||||
/** @ViewChild(TemplateRef) template: TemplateRef<any> */
|
/** @ViewChild(TemplateRef) template: TemplateRef<any> */
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
query(0, TemplateRef as any, true);
|
viewQuery(TemplateRef as any, true);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.template = tmp.first);
|
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) && (ctx.template = tmp.first);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1009,7 +1009,7 @@ describe('host bindings', () => {
|
||||||
elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true);
|
elementProperty(elIndex, 'id', bind(ctx.foos.length), null, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
contentQueries: (dirIndex) => { registerContentQuery(query(null, ['foo']), dirIndex); },
|
contentQueries: (dirIndex) => { registerContentQuery(query(['foo']), dirIndex); },
|
||||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
const instance = load<HostBindingWithContentChildren>(dirIndex);
|
const instance = load<HostBindingWithContentChildren>(dirIndex);
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, InjectionToken} from '../../src/core';
|
import {Inject, InjectionToken, QueryList} from '../../src/core';
|
||||||
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, load} from '../../src/render3/index';
|
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, loadViewQuery, queryRefresh, viewQuery} from '../../src/render3/index';
|
||||||
|
|
||||||
import {ComponentFixture, createComponent} from './render_util';
|
import {ComponentFixture, createComponent} from './render_util';
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ describe('InheritDefinitionFeature', () => {
|
||||||
expect(divEl.title).toEqual('new-title');
|
expect(divEl.title).toEqual('new-title');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compose viewQuery', () => {
|
it('should compose viewQuery (basic mechanics check)', () => {
|
||||||
const log: Array<[string, RenderFlags, any]> = [];
|
const log: Array<[string, RenderFlags, any]> = [];
|
||||||
|
|
||||||
class SuperComponent {
|
class SuperComponent {
|
||||||
|
@ -404,6 +404,82 @@ describe('InheritDefinitionFeature', () => {
|
||||||
expect(log).toEqual([['super', 1, context], ['sub', 1, context]]);
|
expect(log).toEqual([['super', 1, context], ['sub', 1, context]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compose viewQuery (query logic check)', () => {
|
||||||
|
/*
|
||||||
|
* class SuperComponent {
|
||||||
|
* @ViewChildren('super') superQuery;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class SuperComponent {
|
||||||
|
superQuery?: QueryList<any>;
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: SuperComponent,
|
||||||
|
template: () => {},
|
||||||
|
consts: 0,
|
||||||
|
vars: 0,
|
||||||
|
selectors: [['', 'superDir', '']],
|
||||||
|
viewQuery: <T>(rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
viewQuery(['super'], false);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
let tmp: any;
|
||||||
|
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||||
|
(ctx.superQuery = tmp as QueryList<any>);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
factory: () => new SuperComponent(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div id="sub" #sub></div>
|
||||||
|
* <div id="super" #super></div>
|
||||||
|
* class SubComponent extends SuperComponent {
|
||||||
|
* @ViewChildren('sub') subQuery;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class SubComponent extends SuperComponent {
|
||||||
|
subQuery?: QueryList<any>;
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: SubComponent,
|
||||||
|
template: (rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'div', ['id', 'sub'], ['sub', '']);
|
||||||
|
element(2, 'div', ['id', 'super'], ['super', '']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
consts: 4,
|
||||||
|
vars: 0,
|
||||||
|
selectors: [['', 'subDir', '']],
|
||||||
|
viewQuery: (rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
viewQuery(['sub'], false);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
let tmp: any;
|
||||||
|
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||||
|
(ctx.subQuery = tmp as QueryList<any>);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
factory: () => new SubComponent(),
|
||||||
|
features: [InheritDefinitionFeature]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(SubComponent);
|
||||||
|
|
||||||
|
const check = (key: string): void => {
|
||||||
|
const qList = (fixture.component as any)[`${key}Query`] as QueryList<any>;
|
||||||
|
expect(qList.length).toBe(1);
|
||||||
|
expect(qList.first.nativeElement).toEqual(fixture.hostElement.querySelector(`#${key}`));
|
||||||
|
expect(qList.first.nativeElement.id).toEqual(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
check('sub');
|
||||||
|
check('super');
|
||||||
|
});
|
||||||
|
|
||||||
it('should compose contentQueries', () => {
|
it('should compose contentQueries', () => {
|
||||||
const log: string[] = [];
|
const log: string[] = [];
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,12 +8,12 @@
|
||||||
import {QueryList} from '@angular/core';
|
import {QueryList} from '@angular/core';
|
||||||
import {RenderFlags} from '@angular/core/src/render3';
|
import {RenderFlags} from '@angular/core/src/render3';
|
||||||
|
|
||||||
import {defineComponent, getHostElement} from '../../../src/render3/index';
|
import {defineComponent, getHostElement, loadViewQuery, viewQuery} from '../../../src/render3/index';
|
||||||
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, load, markDirty} from '../../../src/render3/instructions';
|
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, markDirty} from '../../../src/render3/instructions';
|
||||||
import {PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player';
|
import {PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player';
|
||||||
import {RElement} from '../../../src/render3/interfaces/renderer';
|
import {RElement} from '../../../src/render3/interfaces/renderer';
|
||||||
import {addPlayer, getPlayers} from '../../../src/render3/players';
|
import {addPlayer, getPlayers} from '../../../src/render3/players';
|
||||||
import {query, queryRefresh} from '../../../src/render3/query';
|
import {queryRefresh} from '../../../src/render3/query';
|
||||||
import {getOrCreatePlayerContext} from '../../../src/render3/styling/util';
|
import {getOrCreatePlayerContext} from '../../../src/render3/styling/util';
|
||||||
import {ComponentFixture} from '../render_util';
|
import {ComponentFixture} from '../render_util';
|
||||||
|
|
||||||
|
@ -286,11 +286,11 @@ class SuperComp {
|
||||||
},
|
},
|
||||||
viewQuery: function(rf: RenderFlags, ctx: SuperComp) {
|
viewQuery: function(rf: RenderFlags, ctx: SuperComp) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
query(0, ['child'], true);
|
viewQuery(['child'], true);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) && (ctx.query = tmp as QueryList<any>);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: [Comp]
|
directives: [Comp]
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
import {ChangeDetectorRef, Component as _Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
||||||
import {ViewEncapsulation} from '../../src/metadata';
|
import {ViewEncapsulation} from '../../src/metadata';
|
||||||
import {AttributeMarker, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load, query, queryRefresh} from '../../src/render3/index';
|
import {AttributeMarker, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, loadViewQuery, queryRefresh, viewQuery} from '../../src/render3/index';
|
||||||
|
|
||||||
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding, elementHostAttrs} from '../../src/render3/instructions';
|
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding, elementHostAttrs} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
@ -2067,11 +2067,12 @@ describe('ViewContainerRef', () => {
|
||||||
},
|
},
|
||||||
viewQuery: function(rf: RenderFlags, ctx: any) {
|
viewQuery: function(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
query(0, ['foo'], true);
|
viewQuery(['foo'], true);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.foo = tmp as QueryList<any>);
|
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||||
|
(ctx.foo = tmp as QueryList<any>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue