Skip to content

Commit

Permalink
refactor(compiler): add support for interpolation in style mappings (a…
Browse files Browse the repository at this point in the history
…ngular#50489)

Add support for interpolation in style map bindings in the template
pipeline

PR Close angular#50489
  • Loading branch information
mmalerba authored and AndrewKushnir committed Jun 26, 2023
1 parent 3c1feed commit 060830e
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,27 @@
"cases": [
{
"description": "should generate style/class instructions for a host component creation definition",
"inputFiles": [
"static_and_dynamic.ts"
],
"inputFiles": ["static_and_dynamic.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
"files": [
"static_and_dynamic.js"
]
"files": ["static_and_dynamic.js"]
}
]
},
{
"description": "should generate style/class instructions for multiple host binding definitions",
"inputFiles": [
"multiple_dynamic.ts"
],
"inputFiles": ["multiple_dynamic.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
"files": [
"multiple_dynamic.js"
]
"files": ["multiple_dynamic.js"]
}
]
},
{
"description": "should generate override instructions for only single-level styling bindings when !important is present",
"inputFiles": [
"important.ts"
],
"inputFiles": ["important.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
Expand All @@ -58,45 +48,32 @@
},
{
"description": "should support class interpolation",
"inputFiles": [
"class_interpolation.ts"
],
"inputFiles": ["class_interpolation.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
"files": [
"class_interpolation.js"
]
"files": ["class_interpolation.js"]
}
],
"skipForTemplatePipeline": true
},
{
"description": "should support style interpolation",
"inputFiles": [
"style_interpolation.ts"
],
"inputFiles": ["style_interpolation.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
"files": [
"style_interpolation.js"
]
"files": ["style_interpolation.js"]
}
],
"skipForTemplatePipeline": true
]
},
{
"description": "should generate styling instructions for multiple directives that contain host binding definitions",
"inputFiles": [
"multiple_directives.ts"
],
"inputFiles": ["multiple_directives.ts"],
"expectations": [
{
"failureMessage": "Incorrect template",
"files": [
"multiple_directives.js"
]
"files": ["multiple_directives.js"]
}
]
}
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler/src/template/pipeline/ir/src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ export enum OpKind {
*/
InterpolateStyleProp,

/**
* An operation to interpolate text into a style mapping.
*/
InterpolateStyleMap,

/**
* An operation to advance the runtime's implicit slot context during the update phase of a view.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ export function transformExpressionsInOp(
break;
case OpKind.InterpolateProperty:
case OpKind.InterpolateStyleProp:
case OpKind.InterpolateStyleMap:
case OpKind.InterpolateText:
for (let i = 0; i < op.expressions.length; i++) {
op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform, flags);
Expand Down
48 changes: 46 additions & 2 deletions packages/compiler/src/template/pipeline/ir/src/ops/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import {ListEndOp, NEW_OP, StatementOp, VariableOp} from './shared';
* An operation usable on the update side of the IR.
*/
export type UpdateOp = ListEndOp<UpdateOp>|StatementOp<UpdateOp>|PropertyOp|StylePropOp|StyleMapOp|
InterpolatePropertyOp|InterpolateStylePropOp|AttributeOp|InterpolateTextOp|AdvanceOp|
VariableOp<UpdateOp>;
InterpolatePropertyOp|InterpolateStylePropOp|InterpolateStyleMapOp|AttributeOp|
InterpolateTextOp|AdvanceOp|VariableOp<UpdateOp>;

/**
* A logical operation to perform string interpolation on a text node.
Expand Down Expand Up @@ -338,6 +338,50 @@ export function createInterpolateStylePropOp(
};
}

/**
* A logical operation representing binding an interpolation to a style mapping in the update IR.
*/
export interface InterpolateStyleMapOp extends Op<UpdateOp>, ConsumesVarsTrait,
DependsOnSlotContextOpTrait {
kind: OpKind.InterpolateStyleMap;

/**
* Reference to the element on which the property is bound.
*/
target: XrefId;

/**
* All of the literal strings in the property interpolation, in order.
*
* Conceptually interwoven around the `expressions`.
*/
strings: string[];

/**
* All of the dynamic expressions in the property interpolation, in order.
*
* Conceptually interwoven in between the `strings`.
*/
expressions: o.Expression[];
}

/**
* Create a `InterpolateStyleMap`.
*/
export function createInterpolateStyleMapOp(
xref: XrefId, strings: string[], expressions: o.Expression[]): InterpolateStyleMapOp {
return {
kind: OpKind.InterpolateStyleMap,
target: xref,
strings,
expressions,
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
...TRAIT_CONSUMES_VARS,
...NEW_OP,
};
}


/**
* Logical operation to advance the runtime's internal slot pointer in the update IR.
*/
Expand Down
14 changes: 11 additions & 3 deletions packages/compiler/src/template/pipeline/src/ingest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,17 @@ function ingestPropertyBinding(
if (value instanceof e.Interpolation) {
switch (type) {
case e.BindingType.Property:
view.update.push(ir.createInterpolatePropertyOp(
xref, bindingKind, name, value.strings,
value.expressions.map(expr => convertAst(expr, view.tpl))));
if (name === 'style') {
if (bindingKind !== ir.ElementAttributeKind.Binding) {
throw Error('Unexpected style binding on ng-template');
}
view.update.push(ir.createInterpolateStyleMapOp(
xref, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
} else {
view.update.push(ir.createInterpolatePropertyOp(
xref, bindingKind, name, value.strings,
value.expressions.map(expr => convertAst(expr, view.tpl))));
}
break;
case e.BindingType.Style:
if (bindingKind !== ir.ElementAttributeKind.Binding) {
Expand Down
35 changes: 34 additions & 1 deletion packages/compiler/src/template/pipeline/src/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ export function stylePropInterpolate(
STYLE_PROP_INTERPOLATE_CONFIG, [o.literal(name)], interpolationArgs, extraArgs);
}

export function styleMapInterpolate(strings: string[], expressions: o.Expression[]): ir.UpdateOp {
const interpolationArgs = collateInterpolationArgs(strings, expressions);

return callVariadicInstruction(STYLE_MAP_INTERPOLATE_CONFIG, [], interpolationArgs);
}

export function pureFunction(
varOffset: number, fn: o.Expression, args: o.Expression[]): o.Expression {
return callVariadicInstructionExpr(
Expand Down Expand Up @@ -326,7 +332,7 @@ const PROPERTY_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
*/
const STYLE_PROP_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
constant: [
null!, // Interpolation with 0 variables is not supported.
null!, // Single argument stylePropInterpolate is converted to styleProp instruction.
Identifiers.stylePropInterpolate1,
Identifiers.stylePropInterpolate2,
Identifiers.stylePropInterpolate3,
Expand All @@ -348,6 +354,33 @@ const STYLE_PROP_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
},
};

/**
* `InterpolationConfig` for the `styleMapInterpolate` instruction.
*/
const STYLE_MAP_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
constant: [
null!, // Single argument styleMapInterpolate is converted to styleMap instruction.
Identifiers.styleMapInterpolate1,
Identifiers.styleMapInterpolate2,
Identifiers.styleMapInterpolate3,
Identifiers.styleMapInterpolate4,
Identifiers.styleMapInterpolate5,
Identifiers.styleMapInterpolate6,
Identifiers.styleMapInterpolate7,
Identifiers.styleMapInterpolate8,
],
variable: Identifiers.styleMapInterpolateV,
mapping: n => {
if (n % 2 === 0) {
throw new Error(`Expected odd number of arguments`);
}
if (n < 3) {
throw new Error(`Expected at least 3 arguments`);
}
return (n - 1) / 2;
},
};

const PURE_FUNCTION_CONFIG: VariadicInstructionConfig = {
constant: [
Identifiers.pureFunction0,
Expand Down
3 changes: 3 additions & 0 deletions packages/compiler/src/template/pipeline/src/phases/reify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ function reifyUpdateOperations(_view: ViewCompilation, ops: ir.OpList<ir.UpdateO
ir.OpList.replace(
op, ng.stylePropInterpolate(op.name, op.strings, op.expressions, op.unit));
break;
case ir.OpKind.InterpolateStyleMap:
ir.OpList.replace(op, ng.styleMapInterpolate(op.strings, op.expressions));
break;
case ir.OpKind.InterpolateText:
ir.OpList.replace(op, ng.textInterpolate(op.strings, op.expressions));
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ function varsUsedByOp(op: (ir.CreateOp|ir.UpdateOp)&ir.ConsumesVarsTrait): numbe
return op.expressions.length;
case ir.OpKind.InterpolateProperty:
case ir.OpKind.InterpolateStyleProp:
case ir.OpKind.InterpolateStyleMap:
// `ir.InterpolatePropertyOp`s use a variable slot for each dynamic expression, plus one for
// the result.
return 1 + op.expressions.length;
Expand Down

0 comments on commit 060830e

Please sign in to comment.