feat(ivy): support generating view and content queries (#22330)
PR Close #22330
This commit is contained in:
parent
49f074f61d
commit
0451fd93df
|
@ -82,8 +82,11 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
ctx.print(stmt, `var ${stmt.name} = `);
|
ctx.print(stmt, `var ${stmt.name}`);
|
||||||
stmt.value.visitExpression(this, ctx);
|
if (stmt.value) {
|
||||||
|
ctx.print(stmt, ' = ');
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
ctx.println(stmt, `;`);
|
ctx.println(stmt, `;`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -756,14 +756,14 @@ export abstract class Statement {
|
||||||
export class DeclareVarStmt extends Statement {
|
export class DeclareVarStmt extends Statement {
|
||||||
public type: Type|null;
|
public type: Type|null;
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public value: Expression, type?: Type|null,
|
public name: string, public value?: Expression, type?: Type|null,
|
||||||
modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
|
modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
|
||||||
super(modifiers, sourceSpan);
|
super(modifiers, sourceSpan);
|
||||||
this.type = type || value.type;
|
this.type = type || (value && value.type) || null;
|
||||||
}
|
}
|
||||||
isEquivalent(stmt: Statement): boolean {
|
isEquivalent(stmt: Statement): boolean {
|
||||||
return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
|
return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
|
||||||
this.value.isEquivalent(stmt.value);
|
(this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
|
||||||
}
|
}
|
||||||
visitStatement(visitor: StatementVisitor, context: any): any {
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
return visitor.visitDeclareVarStmt(this, context);
|
return visitor.visitDeclareVarStmt(this, context);
|
||||||
|
@ -1087,11 +1087,9 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
||||||
|
const value = stmt.value && stmt.value.visitExpression(this, context);
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
new DeclareVarStmt(
|
new DeclareVarStmt(stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan), context);
|
||||||
stmt.name, stmt.value.visitExpression(this, context), stmt.type, stmt.modifiers,
|
|
||||||
stmt.sourceSpan),
|
|
||||||
context);
|
|
||||||
}
|
}
|
||||||
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
||||||
return this.transformStmt(
|
return this.transformStmt(
|
||||||
|
@ -1275,7 +1273,9 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
||||||
stmt.value.visitExpression(this, context);
|
if (stmt.value) {
|
||||||
|
stmt.value.visitExpression(this, context);
|
||||||
|
}
|
||||||
if (stmt.type) {
|
if (stmt.type) {
|
||||||
stmt.type.visitType(this, context);
|
stmt.type.visitType(this, context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,8 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
|
||||||
debugAst(ast: o.Expression|o.Statement|o.Type): string { return debugOutputAstAsTypeScript(ast); }
|
debugAst(ast: o.Expression|o.Statement|o.Type): string { return debugOutputAstAsTypeScript(ast); }
|
||||||
|
|
||||||
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: _ExecutionContext): any {
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: _ExecutionContext): any {
|
||||||
ctx.vars.set(stmt.name, stmt.value.visitExpression(this, ctx));
|
const initialValue = stmt.value ? stmt.value.visitExpression(this, ctx) : undefined;
|
||||||
|
ctx.vars.set(stmt.name, initialValue);
|
||||||
if (stmt.hasModifier(o.StmtModifier.Exported)) {
|
if (stmt.hasModifier(o.StmtModifier.Exported)) {
|
||||||
ctx.exports.push(stmt.name);
|
ctx.exports.push(stmt.name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,8 +161,10 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
|
||||||
}
|
}
|
||||||
ctx.print(stmt, ` ${stmt.name}`);
|
ctx.print(stmt, ` ${stmt.name}`);
|
||||||
this._printColonType(stmt.type, ctx);
|
this._printColonType(stmt.type, ctx);
|
||||||
ctx.print(stmt, ` = `);
|
if (stmt.value) {
|
||||||
stmt.value.visitExpression(this, ctx);
|
ctx.print(stmt, ` = `);
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
ctx.println(stmt, `;`);
|
ctx.println(stmt, `;`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,5 +110,8 @@ export class Identifiers {
|
||||||
|
|
||||||
static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE};
|
static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE};
|
||||||
|
|
||||||
|
static query: o.ExternalReference = {name: 'ɵQ', moduleName: CORE};
|
||||||
|
static queryRefresh: o.ExternalReference = {name: 'ɵqR', moduleName: CORE};
|
||||||
|
|
||||||
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};
|
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ export function compilePipe(
|
||||||
{key: 'type', value: outputCtx.importExpr(pipe.type.reference), quoted: false});
|
{key: 'type', value: outputCtx.importExpr(pipe.type.reference), quoted: false});
|
||||||
|
|
||||||
// e.g. factory: function MyPipe_Factory() { return new MyPipe(); },
|
// e.g. factory: function MyPipe_Factory() { return new MyPipe(); },
|
||||||
const templateFactory = createFactory(pipe.type, outputCtx, reflector);
|
const templateFactory = createFactory(pipe.type, outputCtx, reflector, []);
|
||||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||||
|
|
||||||
// e.g. pure: true
|
// e.g. pure: true
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompilePipeSummary, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompilePipeSummary, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, tokenReference, viewClassName} from '../compile_metadata';
|
||||||
import {CompileReflector} from '../compile_reflector';
|
import {CompileReflector} from '../compile_reflector';
|
||||||
import {BindingForm, BuiltinConverter, BuiltinFunctionCall, ConvertPropertyBindingResult, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
import {BindingForm, BuiltinConverter, BuiltinFunctionCall, ConvertPropertyBindingResult, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||||
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
||||||
|
@ -47,7 +47,7 @@ export function compileDirective(
|
||||||
{key: 'type', value: outputCtx.importExpr(directive.type.reference), quoted: false});
|
{key: 'type', value: outputCtx.importExpr(directive.type.reference), quoted: false});
|
||||||
|
|
||||||
// e.g. `factory: () => new MyApp(injectElementRef())`
|
// e.g. `factory: () => new MyApp(injectElementRef())`
|
||||||
const templateFactory = createFactory(directive.type, outputCtx, reflector);
|
const templateFactory = createFactory(directive.type, outputCtx, reflector, directive.queries);
|
||||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||||
|
|
||||||
// e.g 'inputs: {a: 'a'}`
|
// e.g 'inputs: {a: 'a'}`
|
||||||
|
@ -108,9 +108,15 @@ export function compileComponent(
|
||||||
}
|
}
|
||||||
|
|
||||||
// e.g. `factory: function MyApp_Factory() { return new MyApp(injectElementRef()); }`
|
// e.g. `factory: function MyApp_Factory() { return new MyApp(injectElementRef()); }`
|
||||||
const templateFactory = createFactory(component.type, outputCtx, reflector);
|
const templateFactory = createFactory(component.type, outputCtx, reflector, component.queries);
|
||||||
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
definitionMapValues.push({key: 'factory', value: templateFactory, quoted: false});
|
||||||
|
|
||||||
|
// e.g `hostBindings: function MyApp_HostBindings { ... }
|
||||||
|
const hostBindings = createHostBindingsFunction(component.type, outputCtx, component.queries);
|
||||||
|
if (hostBindings) {
|
||||||
|
definitionMapValues.push({key: 'hostBindings', value: hostBindings, quoted: false});
|
||||||
|
}
|
||||||
|
|
||||||
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
||||||
const templateTypeName = component.type.reference.name;
|
const templateTypeName = component.type.reference.name;
|
||||||
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
||||||
|
@ -118,7 +124,8 @@ export function compileComponent(
|
||||||
const templateFunctionExpression =
|
const templateFunctionExpression =
|
||||||
new TemplateDefinitionBuilder(
|
new TemplateDefinitionBuilder(
|
||||||
outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
|
outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
|
||||||
component.template !.ngContentSelectors, templateTypeName, templateName, pipeMap)
|
component.template !.ngContentSelectors, templateTypeName, templateName, pipeMap,
|
||||||
|
component.viewQueries)
|
||||||
.buildTemplateFunction(template, []);
|
.buildTemplateFunction(template, []);
|
||||||
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
definitionMapValues.push({key: 'template', value: templateFunctionExpression, quoted: false});
|
||||||
|
|
||||||
|
@ -296,7 +303,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
private reflector: CompileReflector, private contextParameter: string,
|
private reflector: CompileReflector, private contextParameter: string,
|
||||||
private bindingScope: BindingScope, private level = 0, private ngContentSelectors: string[],
|
private bindingScope: BindingScope, private level = 0, private ngContentSelectors: string[],
|
||||||
private contextName: string|null, private templateName: string|null,
|
private contextName: string|null, private templateName: string|null,
|
||||||
private pipes: Map<string, CompilePipeSummary>) {
|
private pipes: Map<string, CompilePipeSummary>, private viewQueries: CompileQueryMetadata[]) {
|
||||||
this._valueConverter = new ValueConverter(
|
this._valueConverter = new ValueConverter(
|
||||||
outputCtx, () => this.allocateDataSlot(), (name, localName, slot, value) => {
|
outputCtx, () => this.allocateDataSlot(), (name, localName, slot, value) => {
|
||||||
bindingScope.set(localName, value);
|
bindingScope.set(localName, value);
|
||||||
|
@ -353,6 +360,32 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define and update any view queries
|
||||||
|
for (let query of this.viewQueries) {
|
||||||
|
// e.g. r3.Q(0, SomeDirective, true);
|
||||||
|
const querySlot = this.allocateDataSlot();
|
||||||
|
const predicate = getQueryPredicate(query, this.outputCtx);
|
||||||
|
const args = [
|
||||||
|
/* memoryIndex */ o.literal(querySlot, o.INFERRED_TYPE),
|
||||||
|
/* predicate */ predicate,
|
||||||
|
/* descend */ o.literal(query.descendants, o.INFERRED_TYPE)
|
||||||
|
];
|
||||||
|
|
||||||
|
if (query.read) {
|
||||||
|
args.push(this.outputCtx.importExpr(query.read.identifier !.reference));
|
||||||
|
}
|
||||||
|
this.instruction(this._creationMode, null, R3.query, ...args);
|
||||||
|
|
||||||
|
// (r3.qR(tmp = r3.ɵld(0)) && (ctx.someDir = tmp));
|
||||||
|
const temporary = this.temp();
|
||||||
|
const getQueryList = o.importExpr(R3.load).callFn([o.literal(querySlot)]);
|
||||||
|
const refresh = o.importExpr(R3.queryRefresh).callFn([temporary.set(getQueryList)]);
|
||||||
|
const updateDirective = o.variable(CONTEXT_NAME)
|
||||||
|
.prop(query.propertyName)
|
||||||
|
.set(query.first ? temporary.prop('first') : temporary);
|
||||||
|
this._bindingMode.push(refresh.and(updateDirective).toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
templateVisitAll(this, asts);
|
templateVisitAll(this, asts);
|
||||||
|
|
||||||
const creationMode = this._creationMode.length > 0 ?
|
const creationMode = this._creationMode.length > 0 ?
|
||||||
|
@ -589,7 +622,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
const templateVisitor = new TemplateDefinitionBuilder(
|
const templateVisitor = new TemplateDefinitionBuilder(
|
||||||
this.outputCtx, this.constantPool, this.reflector, templateContext,
|
this.outputCtx, this.constantPool, this.reflector, templateContext,
|
||||||
this.bindingScope.nestedScope(), this.level + 1, this.ngContentSelectors, contextName,
|
this.bindingScope.nestedScope(), this.level + 1, this.ngContentSelectors, contextName,
|
||||||
templateName, this.pipes);
|
templateName, this.pipes, []);
|
||||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children, ast.variables);
|
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children, ast.variables);
|
||||||
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||||
}
|
}
|
||||||
|
@ -643,9 +676,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
|
|
||||||
private temp(): o.ReadVarExpr {
|
private temp(): o.ReadVarExpr {
|
||||||
if (!this._temporaryAllocated) {
|
if (!this._temporaryAllocated) {
|
||||||
this._prefix.push(o.variable(TEMPORARY_NAME, o.DYNAMIC_TYPE, null)
|
this._prefix.push(new o.DeclareVarStmt(TEMPORARY_NAME, undefined, o.DYNAMIC_TYPE));
|
||||||
.set(o.literal(undefined))
|
|
||||||
.toDeclStmt(o.DYNAMIC_TYPE));
|
|
||||||
this._temporaryAllocated = true;
|
this._temporaryAllocated = true;
|
||||||
}
|
}
|
||||||
return o.variable(TEMPORARY_NAME);
|
return o.variable(TEMPORARY_NAME);
|
||||||
|
@ -665,9 +696,31 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getQueryPredicate(query: CompileQueryMetadata, outputCtx: OutputContext): o.Expression {
|
||||||
|
let predicate: o.Expression;
|
||||||
|
if (query.selectors.length > 1 || (query.selectors.length == 1 && query.selectors[0].value)) {
|
||||||
|
const selectors = query.selectors.map(value => value.value as string);
|
||||||
|
selectors.some(value => !value) && error('Found a type among the string selectors expected');
|
||||||
|
predicate = outputCtx.constantPool.getConstLiteral(
|
||||||
|
o.literalArr(selectors.map(value => o.literal(value))));
|
||||||
|
} else if (query.selectors.length == 1) {
|
||||||
|
const first = query.selectors[0];
|
||||||
|
if (first.identifier) {
|
||||||
|
predicate = outputCtx.importExpr(first.identifier.reference);
|
||||||
|
} else {
|
||||||
|
error('Unexpected query form');
|
||||||
|
predicate = o.literal(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('Unexpected query form');
|
||||||
|
predicate = o.literal(null);
|
||||||
|
}
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
export function createFactory(
|
export function createFactory(
|
||||||
type: CompileTypeMetadata, outputCtx: OutputContext,
|
type: CompileTypeMetadata, outputCtx: OutputContext, reflector: CompileReflector,
|
||||||
reflector: CompileReflector): o.FunctionExpr {
|
queries: CompileQueryMetadata[]): o.Expression {
|
||||||
let args: o.Expression[] = [];
|
let args: o.Expression[] = [];
|
||||||
|
|
||||||
const elementRef = reflector.resolveExternalReference(Identifiers.ElementRef);
|
const elementRef = reflector.resolveExternalReference(Identifiers.ElementRef);
|
||||||
|
@ -700,10 +753,74 @@ export function createFactory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryDefinitions: o.Expression[] = [];
|
||||||
|
for (let query of queries) {
|
||||||
|
const predicate = getQueryPredicate(query, outputCtx);
|
||||||
|
|
||||||
|
// e.g. r3.Q(null, SomeDirective, false) or r3.Q(null, ['div'], false)
|
||||||
|
const parameters = [
|
||||||
|
/* memoryIndex */ o.literal(null, o.INFERRED_TYPE),
|
||||||
|
/* predicate */ predicate,
|
||||||
|
/* descend */ o.literal(query.descendants)
|
||||||
|
];
|
||||||
|
|
||||||
|
if (query.read) {
|
||||||
|
parameters.push(outputCtx.importExpr(query.read.identifier !.reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
queryDefinitions.push(o.importExpr(R3.query).callFn(parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
const createInstance = new o.InstantiateExpr(outputCtx.importExpr(type.reference), args);
|
||||||
|
const result = queryDefinitions.length > 0 ? o.literalArr([createInstance, ...queryDefinitions]) :
|
||||||
|
createInstance;
|
||||||
|
|
||||||
return o.fn(
|
return o.fn(
|
||||||
[],
|
[], [new o.ReturnStatement(result)], o.INFERRED_TYPE, null,
|
||||||
[new o.ReturnStatement(new o.InstantiateExpr(outputCtx.importExpr(type.reference), args))],
|
type.reference.name ? `${type.reference.name}_Factory` : null);
|
||||||
o.INFERRED_TYPE, null, type.reference.name ? `${type.reference.name}_Factory` : null);
|
}
|
||||||
|
|
||||||
|
// Return a host binding function or null if one is not necessary.
|
||||||
|
export function createHostBindingsFunction(
|
||||||
|
type: CompileTypeMetadata, outputCtx: OutputContext,
|
||||||
|
queries: CompileQueryMetadata[]): o.Expression|null {
|
||||||
|
const statements: o.Statement[] = [];
|
||||||
|
|
||||||
|
const temporary = function() {
|
||||||
|
let declared = false;
|
||||||
|
return () => {
|
||||||
|
if (!declared) {
|
||||||
|
statements.push(new o.DeclareVarStmt(TEMPORARY_NAME, undefined, o.DYNAMIC_TYPE));
|
||||||
|
declared = true;
|
||||||
|
}
|
||||||
|
return o.variable(TEMPORARY_NAME);
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
for (let index = 0; index < queries.length; index++) {
|
||||||
|
const query = queries[index];
|
||||||
|
|
||||||
|
// e.g. r3.qR(tmp = r3.ld(dirIndex)[1]) && (r3.ld(dirIndex)[0].someDir = tmp);
|
||||||
|
const getDirectiveMemory = o.importExpr(R3.load).callFn([o.variable('dirIndex')]);
|
||||||
|
// The query list is at the query index + 1 because the directive itself is in slot 0.
|
||||||
|
const getQueryList = getDirectiveMemory.key(o.literal(index + 1));
|
||||||
|
const assignToTemporary = temporary().set(getQueryList);
|
||||||
|
const callQueryRefresh = o.importExpr(R3.queryRefresh).callFn([assignToTemporary]);
|
||||||
|
const updateDirective = getDirectiveMemory.key(o.literal(0, o.INFERRED_TYPE))
|
||||||
|
.prop(query.propertyName)
|
||||||
|
.set(query.first ? temporary().key(o.literal(0)) : temporary());
|
||||||
|
const andExpression = callQueryRefresh.and(updateDirective);
|
||||||
|
statements.push(andExpression.toStmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statements.length > 0) {
|
||||||
|
return o.fn(
|
||||||
|
[new o.FnParam('dirIndex', o.NUMBER_TYPE), new o.FnParam('elIndex', o.NUMBER_TYPE)],
|
||||||
|
statements, o.INFERRED_TYPE, null,
|
||||||
|
type.reference.name ? `${type.reference.name}_HostBindings` : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ValueConverter extends AstMemoryEfficientTransformer {
|
class ValueConverter extends AstMemoryEfficientTransformer {
|
||||||
|
|
|
@ -551,6 +551,133 @@ describe('compiler compliance', () => {
|
||||||
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
result.source, ComplexComponentDefinition, 'Incorrect ComplexComponent definition');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('queries', () => {
|
||||||
|
const directive = {
|
||||||
|
'some.directive.ts': `
|
||||||
|
import {Directive} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[someDir]',
|
||||||
|
})
|
||||||
|
export class SomeDirective { }
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should support view queries', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
...directive,
|
||||||
|
'view_query.component.ts': `
|
||||||
|
import {Component, NgModule, ViewChild} from '@angular/core';
|
||||||
|
import {SomeDirective} from './some.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'view-query-component',
|
||||||
|
template: \`
|
||||||
|
<div someDir></div>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class ViewQueryComponent {
|
||||||
|
@ViewChild(SomeDirective) someDir: SomeDirective;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [SomeDirective, ViewQueryComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ViewQueryComponentDefinition = `
|
||||||
|
const $e0_attrs$ = ['someDir',''];
|
||||||
|
const $e1_dirs$ = [SomeDirective];
|
||||||
|
…
|
||||||
|
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: ViewQueryComponent,
|
||||||
|
tag: 'view-query-component',
|
||||||
|
factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
|
||||||
|
template: function ViewQueryComponent_Template(ctx: $ViewQueryComponent$, cm: $boolean$) {
|
||||||
|
var $tmp$: $any$;
|
||||||
|
if (cm) {
|
||||||
|
$r3$.ɵQ(0, SomeDirective, true);
|
||||||
|
$r3$.ɵE(1, 'div', $e0_attrs$, $e1_dirs$);
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
($r3$.ɵqR(($tmp$ = $r3$.ɵld(0))) && (ctx.someDir = $tmp$.first));
|
||||||
|
SomeDirective.ngDirectiveDef.h(2, 1);
|
||||||
|
$r3$.ɵr(2, 1);
|
||||||
|
}
|
||||||
|
});`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const source = result.source;
|
||||||
|
|
||||||
|
expectEmit(source, ViewQueryComponentDefinition, 'Invalid ViewQuery declaration');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support content queries', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
...directive,
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, ContentChild, NgModule} from '@angular/core';
|
||||||
|
import {SomeDirective} from './some.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'content-query-component',
|
||||||
|
template: \`
|
||||||
|
<div><ng-content></ng-content></div>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class ContentQueryComponent {
|
||||||
|
@ContentChild(SomeDirective) someDir: SomeDirective;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: \`
|
||||||
|
<content-query-component>
|
||||||
|
<div someDir></div>
|
||||||
|
</content-query-component>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class MyApp { }
|
||||||
|
|
||||||
|
@NgModule({declarations: [SomeDirective, ContentQueryComponent, MyApp]})
|
||||||
|
export class MyModule { }
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContentQueryComponentDefinition = `
|
||||||
|
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: ContentQueryComponent,
|
||||||
|
tag: 'content-query-component',
|
||||||
|
factory: function ContentQueryComponent_Factory() {
|
||||||
|
return [new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, true)];
|
||||||
|
},
|
||||||
|
hostBindings: function ContentQueryComponent_HostBindings(
|
||||||
|
dirIndex: $number$, elIndex: $number$) {
|
||||||
|
var $tmp$: $any$;
|
||||||
|
($r3$.ɵqR(($tmp$ = $r3$.ɵld(dirIndex)[1])) && ($r3$.ɵld(dirIndex)[0].someDir = $tmp$[0]));
|
||||||
|
},
|
||||||
|
template: function ContentQueryComponent_Template(
|
||||||
|
ctx: $ContentQueryComponent$, cm: $boolean$) {
|
||||||
|
if (cm) {
|
||||||
|
$r3$.ɵpD(0);
|
||||||
|
$r3$.ɵE(1, 'div');
|
||||||
|
$r3$.ɵP(2, 0);
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
const source = result.source;
|
||||||
|
expectEmit(source, ContentQueryComponentDefinition, 'Invalid ContentQuery declaration');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('pipes', () => {
|
describe('pipes', () => {
|
||||||
|
|
||||||
const files = {
|
const files = {
|
||||||
|
|
Loading…
Reference in New Issue