Skip to content

Commit 0210259

Browse files
mmalerbathePunderWoman
authored andcommitted
refactor(compiler): add support for class map interpolation bindings (angular#50805)
Adds support for bindings of the form `class="static {{dynamic}}"` PR Close angular#50805
1 parent 7e5e37a commit 0210259

File tree

8 files changed

+95
-3
lines changed

8 files changed

+95
-3
lines changed

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
"failureMessage": "Incorrect template",
2020
"files": ["binding_slots.js"]
2121
}
22-
],
23-
"skipForTemplatePipeline": true
22+
]
2423
},
2524
{
2625
"description": "should place initial, multi, singular and application followed by attribute style instructions in the template code in that order",

packages/compiler/src/template/pipeline/ir/src/enums.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ export enum OpKind {
113113
*/
114114
InterpolateStyleMap,
115115

116+
/**
117+
* An operation to interpolate text into a class mapping.
118+
*/
119+
InterpolateClassMap,
120+
116121
/**
117122
* An operation to advance the runtime's implicit slot context during the update phase of a view.
118123
*/

packages/compiler/src/template/pipeline/ir/src/expression.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ export function transformExpressionsInOp(
743743
case OpKind.InterpolateProperty:
744744
case OpKind.InterpolateStyleProp:
745745
case OpKind.InterpolateStyleMap:
746+
case OpKind.InterpolateClassMap:
746747
case OpKind.InterpolateText:
747748
for (let i = 0; i < op.expressions.length; i++) {
748749
op.expressions[i] = transformExpressionsInExpression(op.expressions[i], transform, flags);

packages/compiler/src/template/pipeline/ir/src/ops/update.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {ListEndOp, NEW_OP, StatementOp, VariableOp} from './shared';
1919
*/
2020
export type UpdateOp = ListEndOp<UpdateOp>|StatementOp<UpdateOp>|PropertyOp|AttributeOp|StylePropOp|
2121
StyleMapOp|ClassMapOp|InterpolatePropertyOp|InterpolateAttributeOp|InterpolateStylePropOp|
22-
InterpolateStyleMapOp|InterpolateTextOp|AdvanceOp|VariableOp<UpdateOp>;
22+
InterpolateStyleMapOp|InterpolateClassMapOp|InterpolateTextOp|AdvanceOp|VariableOp<UpdateOp>;
2323

2424
/**
2525
* A logical operation to perform string interpolation on a text node.
@@ -463,6 +463,48 @@ export function createInterpolateStyleMapOp(
463463
};
464464
}
465465

466+
/**
467+
* A logical operation representing binding an interpolation to a class mapping in the update IR.
468+
*/
469+
export interface InterpolateClassMapOp extends Op<UpdateOp>, ConsumesVarsTrait,
470+
DependsOnSlotContextOpTrait {
471+
kind: OpKind.InterpolateClassMap;
472+
473+
/**
474+
* Reference to the element on which the property is bound.
475+
*/
476+
target: XrefId;
477+
478+
/**
479+
* All of the literal strings in the property interpolation, in order.
480+
*
481+
* Conceptually interwoven around the `expressions`.
482+
*/
483+
strings: string[];
484+
485+
/**
486+
* All of the dynamic expressions in the property interpolation, in order.
487+
*
488+
* Conceptually interwoven in between the `strings`.
489+
*/
490+
expressions: o.Expression[];
491+
}
492+
493+
/**
494+
* Create a `InterpolateStyleMap`.
495+
*/
496+
export function createInterpolateClassMapOp(
497+
xref: XrefId, strings: string[], expressions: o.Expression[]): InterpolateClassMapOp {
498+
return {
499+
kind: OpKind.InterpolateClassMap,
500+
target: xref,
501+
strings,
502+
expressions,
503+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
504+
...TRAIT_CONSUMES_VARS,
505+
...NEW_OP,
506+
};
507+
}
466508

467509
/**
468510
* Logical operation to advance the runtime's internal slot pointer in the update IR.

packages/compiler/src/template/pipeline/src/ingest.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ function ingestPropertyBinding(
271271
}
272272
view.update.push(ir.createInterpolateStyleMapOp(
273273
xref, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
274+
} else if (name === 'class') {
275+
if (bindingKind !== ir.ElementAttributeKind.Binding) {
276+
throw Error('Unexpected class binding on ng-template');
277+
}
278+
view.update.push(ir.createInterpolateClassMapOp(
279+
xref, value.strings, value.expressions.map(expr => convertAst(expr, view.tpl))));
274280
} else {
275281
view.update.push(ir.createInterpolatePropertyOp(
276282
xref, bindingKind, name, value.strings,

packages/compiler/src/template/pipeline/src/instruction.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ export function styleMapInterpolate(strings: string[], expressions: o.Expression
243243
return callVariadicInstruction(STYLE_MAP_INTERPOLATE_CONFIG, [], interpolationArgs);
244244
}
245245

246+
export function classMapInterpolate(strings: string[], expressions: o.Expression[]): ir.UpdateOp {
247+
const interpolationArgs = collateInterpolationArgs(strings, expressions);
248+
249+
return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs);
250+
}
251+
246252
export function pureFunction(
247253
varOffset: number, fn: o.Expression, args: o.Expression[]): o.Expression {
248254
return callVariadicInstructionExpr(
@@ -421,6 +427,33 @@ const STYLE_MAP_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
421427
},
422428
};
423429

430+
/**
431+
* `InterpolationConfig` for the `classMapInterpolate` instruction.
432+
*/
433+
const CLASS_MAP_INTERPOLATE_CONFIG: VariadicInstructionConfig = {
434+
constant: [
435+
null!, // Single argument classMapInterpolate is converted to classMap instruction.
436+
Identifiers.classMapInterpolate1,
437+
Identifiers.classMapInterpolate2,
438+
Identifiers.classMapInterpolate3,
439+
Identifiers.classMapInterpolate4,
440+
Identifiers.classMapInterpolate5,
441+
Identifiers.classMapInterpolate6,
442+
Identifiers.classMapInterpolate7,
443+
Identifiers.classMapInterpolate8,
444+
],
445+
variable: Identifiers.classMapInterpolateV,
446+
mapping: n => {
447+
if (n % 2 === 0) {
448+
throw new Error(`Expected odd number of arguments`);
449+
}
450+
if (n < 3) {
451+
throw new Error(`Expected at least 3 arguments`);
452+
}
453+
return (n - 1) / 2;
454+
},
455+
};
456+
424457
const PURE_FUNCTION_CONFIG: VariadicInstructionConfig = {
425458
constant: [
426459
Identifiers.pureFunction0,

packages/compiler/src/template/pipeline/src/phases/reify.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ function reifyUpdateOperations(_view: ViewCompilation, ops: ir.OpList<ir.UpdateO
140140
case ir.OpKind.InterpolateStyleMap:
141141
ir.OpList.replace(op, ng.styleMapInterpolate(op.strings, op.expressions));
142142
break;
143+
case ir.OpKind.InterpolateClassMap:
144+
ir.OpList.replace(op, ng.classMapInterpolate(op.strings, op.expressions));
145+
break;
143146
case ir.OpKind.InterpolateText:
144147
ir.OpList.replace(op, ng.textInterpolate(op.strings, op.expressions));
145148
break;

packages/compiler/src/template/pipeline/src/phases/var_counting.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ function varsUsedByOp(op: (ir.CreateOp|ir.UpdateOp)&ir.ConsumesVarsTrait): numbe
8484
case ir.OpKind.InterpolateAttribute:
8585
// One variable slot for each dynamic expression, plus one for the result.
8686
return 1 + op.expressions.length;
87+
case ir.OpKind.InterpolateClassMap:
88+
// TODO: explain why 2+n.
89+
return 2 + op.expressions.length;
8790
default:
8891
throw new Error(`Unhandled op: ${ir.OpKind[op.kind]}`);
8992
}

0 commit comments

Comments
 (0)