refactor(ivy): move classMap interpolation logic internally (#31211)

Adds the new `classMapInterpolate1` through `classMapInterpolate8` instructions which handle interpolations inside the `class` attribute and moves the interpolation logic internally. This allows us to remove the `interpolationX` instructions in a follow-up PR.

These changes also add an error if an interpolation is encountered inside a `style` tag (e.g. `style="width: {{value}}"`). Up until now this would actually generate valid instructions, because `styleMap` goes through the same code path as `classMap` which does support interpolation. At runtime, however, `styleMap` would set invalid styles that look like `<div style="0:w;1:i;2:d;3:t;4:h;5::;7:1;">`. In `ViewEngine` interpolations inside `style` weren't supported either, however there we'd output invalid styles like `<div style="unsafe">`, even if the content was trusted.

PR Close #31211
This commit is contained in:
crisbeto 2019-06-27 18:57:09 +02:00 committed by Alex Rickabaugh
parent dca713c087
commit 02491a6ce8
15 changed files with 1469 additions and 89 deletions

View File

@ -454,7 +454,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵclassMap($r3$.ɵɵinterpolation1("foo foo-", $ctx$.fooId, ""));
$r3$.ɵɵclassMapInterpolate1("foo foo-", $ctx$.fooId, "");
$r3$.ɵɵstylingApply();
}
}
@ -468,7 +468,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵclassMap($r3$.ɵɵinterpolation2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, ""));
$r3$.ɵɵclassMapInterpolate2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, "");
$r3$.ɵɵstylingApply();
}
}
@ -1340,6 +1340,239 @@ describe('compiler compliance: styling', () => {
});
});
describe('interpolations', () => {
it('should generate the proper update instructions for interpolated classes', () => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: \`
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
<div class="a{{one}}b{{two}}c{{three}}d"></div>
<div class="a{{one}}b{{two}}c"></div>
<div class="a{{one}}b"></div>
<div class="{{one}}"></div>
\`
})
export class MyComponent {
}
`
}
};
const template = `
if (rf & 2) {
$r3$.ɵɵclassMapInterpolateV(["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(1);
$r3$.ɵɵclassMapInterpolate8("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(2);
$r3$.ɵɵclassMapInterpolate7("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(3);
$r3$.ɵɵclassMapInterpolate6("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(4);
$r3$.ɵɵclassMapInterpolate5("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(5);
$r3$.ɵɵclassMapInterpolate4("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(6);
$r3$.ɵɵclassMapInterpolate3("a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(7);
$r3$.ɵɵclassMapInterpolate2("a", ctx.one, "b", ctx.two, "c");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(8);
$r3$.ɵɵclassMapInterpolate1("a", ctx.one, "b");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(9);
$r3$.ɵɵclassMap(ctx.one);
$r3$.ɵɵstylingApply();
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect handling of interpolated classes');
});
it('should throw for interpolations inside `style`', () => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: '<div style="color:{{red}}"></div>'
})
export class MyComponent {
}
`
}
};
expect(() => compile(files, angularFiles)).toThrowError(/Unexpected interpolation/);
});
it('should throw for interpolations inside individual class bindings', () => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: '<div class.something="{{isEnabled}}"></div>'
})
export class MyComponent {
}
`
}
};
expect(() => compile(files, angularFiles)).toThrowError(/Unexpected interpolation/);
});
it('should generate the proper update instructions for interpolated style properties', () => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: \`
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
<div style.color="a{{one}}b{{two}}c{{three}}d"></div>
<div style.color="a{{one}}b{{two}}c"></div>
<div style.color="a{{one}}b"></div>
<div style.color="{{one}}"></div>
\`
})
export class MyComponent {
}
`
}
};
const template = `
if (rf & 2) {
$r3$.ɵɵstylePropInterpolateV(0, ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(1);
$r3$.ɵɵstylePropInterpolate8(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(2);
$r3$.ɵɵstylePropInterpolate7(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(3);
$r3$.ɵɵstylePropInterpolate6(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(4);
$r3$.ɵɵstylePropInterpolate5(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(5);
$r3$.ɵɵstylePropInterpolate4(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(6);
$r3$.ɵɵstylePropInterpolate3(0, "a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(7);
$r3$.ɵɵstylePropInterpolate2(0, "a", ctx.one, "b", ctx.two, "c");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(8);
$r3$.ɵɵstylePropInterpolate1(0, "a", ctx.one, "b");
$r3$.ɵɵstylingApply();
$r3$.ɵɵselect(9);
$r3$.ɵɵstyleProp(0, ctx.one);
$r3$.ɵɵstylingApply();
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
});
it('should generate update instructions for interpolated style properties with a suffix',
() => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: \`
<div style.width.px="a{{one}}b{{two}}c"></div>
\`
})
export class MyComponent {
}
`
}
};
const template = `
if (rf & 2) {
$r3$.ɵɵstylePropInterpolate2(0, "a", ctx.one, "b", ctx.two, "c", "px");
$r3$.ɵɵstylingApply();
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
});
it('should generate update instructions for interpolated style properties with !important',
() => {
const files = {
app: {
'spec.ts': `
import {Component} from '@angular/core';
@Component({
template: \`
<div style.width!important="a{{one}}b{{two}}c"></div>
\`
})
export class MyComponent {
}
`
}
};
const template = `
if (rf & 2) {
$r3$.ɵɵstylePropInterpolate2(0, "a", ctx.one, "b", ctx.two, "c", null, true);
$r3$.ɵɵstylingApply();
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect handling of interpolated style properties');
});
});
it('should count only non-style and non-class host bindings on Components', () => {
const files = {
app: {

View File

@ -72,8 +72,46 @@ export class Identifiers {
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
static classMapInterpolate1:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate1', moduleName: CORE};
static classMapInterpolate2:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate2', moduleName: CORE};
static classMapInterpolate3:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate3', moduleName: CORE};
static classMapInterpolate4:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate4', moduleName: CORE};
static classMapInterpolate5:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate5', moduleName: CORE};
static classMapInterpolate6:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate6', moduleName: CORE};
static classMapInterpolate7:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate7', moduleName: CORE};
static classMapInterpolate8:
o.ExternalReference = {name: 'ɵɵclassMapInterpolate8', moduleName: CORE};
static classMapInterpolateV:
o.ExternalReference = {name: 'ɵɵclassMapInterpolateV', moduleName: CORE};
static styleProp: o.ExternalReference = {name: 'ɵɵstyleProp', moduleName: CORE};
static stylePropInterpolate1:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate1', moduleName: CORE};
static stylePropInterpolate2:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate2', moduleName: CORE};
static stylePropInterpolate3:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate3', moduleName: CORE};
static stylePropInterpolate4:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate4', moduleName: CORE};
static stylePropInterpolate5:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate5', moduleName: CORE};
static stylePropInterpolate6:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate6', moduleName: CORE};
static stylePropInterpolate7:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate7', moduleName: CORE};
static stylePropInterpolate8:
o.ExternalReference = {name: 'ɵɵstylePropInterpolate8', moduleName: CORE};
static stylePropInterpolateV:
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
static stylingApply: o.ExternalReference = {name: 'ɵɵstylingApply', moduleName: CORE};
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};

View File

@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
import {BindingForm, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {ConstantPool, DefinitionKind} from '../../constant_pool';
import * as core from '../../core';
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
import {AST, Interpolation, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
import * as o from '../../output/output_ast';
import {ParseError, ParseSourceSpan, typeSourceSpan} from '../../parse_util';
@ -28,7 +28,7 @@ import {Render3ParseResult} from '../r3_template_transform';
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './api';
import {Instruction, StylingBuilder} from './styling_builder';
import {StylingBuilder, StylingInstruction} from './styling_builder';
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, makeBindingParser, prepareEventListenerParameters, renderFlagCheckIfStmt, resolveSanitizationFn} from './template';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, chainedInstruction, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
@ -777,8 +777,8 @@ function bindingFn(implicit: any, value: AST) {
}
function createStylingStmt(
instruction: Instruction, bindingContext: any, bindingFn: Function): o.Statement {
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);
instruction: StylingInstruction, bindingContext: any, bindingFn: Function): o.Statement {
const params = instruction.params(value => bindingFn(bindingContext, value).currValExpr);
return o.importExpr(instruction.reference, null, instruction.sourceSpan)
.callFn(params, instruction.sourceSpan)
.toStmt();

View File

@ -11,23 +11,26 @@ import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import {isEmptyExpression} from '../../template_parser/template_parser';
import {error} from '../../util';
import * as t from '../r3_ast';
import {Identifiers as R3} from '../r3_identifiers';
import {parse as parseStyle} from './style_parser';
import {compilerIsNewStylingInUse} from './styling_state';
import {ValueConverter} from './template';
import {getInterpolationArgsLength} from './util';
const IMPORTANT_FLAG = '!important';
/**
* A styling expression summary that is to be processed by the compiler
*/
export interface Instruction {
export interface StylingInstruction {
sourceSpan: ParseSourceSpan|null;
reference: o.ExternalReference;
allocateBindingSlots: number;
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
supportsInterpolation?: boolean;
params: ((convertFn: (value: any) => o.Expression | o.Expression[]) => o.Expression[]);
}
/**
@ -261,13 +264,13 @@ export class StylingBuilder {
*/
buildHostAttrsInstruction(
sourceSpan: ParseSourceSpan|null, attrs: o.Expression[],
constantPool: ConstantPool): Instruction|null {
constantPool: ConstantPool): StylingInstruction|null {
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
return {
sourceSpan,
reference: R3.elementHostAttrs,
allocateBindingSlots: 0,
buildParams: () => {
params: () => {
// params => elementHostAttrs(attrs)
this.populateInitialStylingAttrs(attrs);
const attrArray = !attrs.some(attr => attr instanceof o.WrappedNodeExpr) ?
@ -286,14 +289,14 @@ export class StylingBuilder {
* The instruction generation code below is used for producing the AOT statement code which is
* responsible for registering style/class bindings to an element.
*/
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool): Instruction
|null {
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
StylingInstruction|null {
if (this.hasBindings) {
return {
sourceSpan,
allocateBindingSlots: 0,
reference: R3.styling,
buildParams: () => {
params: () => {
// a string array of every style-based binding
const styleBindingProps =
this._singleStyleInputs ? this._singleStyleInputs.map(i => o.literal(i.name)) : [];
@ -344,7 +347,7 @@ export class StylingBuilder {
* The instruction data will contain all expressions for `classMap` to function
* which includes the `[class]` expression params.
*/
buildClassMapInstruction(valueConverter: ValueConverter): Instruction|null {
buildClassMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
if (this._classMapInput) {
return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
}
@ -357,7 +360,7 @@ export class StylingBuilder {
* The instruction data will contain all expressions for `styleMap` to function
* which includes the `[style]` expression params.
*/
buildStyleMapInstruction(valueConverter: ValueConverter): Instruction|null {
buildStyleMapInstruction(valueConverter: ValueConverter): StylingInstruction|null {
if (this._styleMapInput) {
return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
}
@ -365,7 +368,8 @@ export class StylingBuilder {
}
private _buildMapBasedInstruction(
valueConverter: ValueConverter, isClassBased: boolean, stylingInput: BoundStylingEntry) {
valueConverter: ValueConverter, isClassBased: boolean,
stylingInput: BoundStylingEntry): StylingInstruction {
let totalBindingSlotsRequired = 0;
if (compilerIsNewStylingInUse()) {
// the old implementation does not reserve slot values for
@ -377,27 +381,42 @@ export class StylingBuilder {
// be evaluated (the AST visit call) during creation time so that any
// pipes can be picked up in time before the template is built
const mapValue = stylingInput.value.visit(valueConverter);
if (mapValue instanceof Interpolation) {
let reference: o.ExternalReference;
if (mapValue instanceof Interpolation && isClassBased) {
totalBindingSlotsRequired += mapValue.expressions.length;
reference = getClassMapInterpolationExpression(mapValue);
} else {
reference = isClassBased ? R3.classMap : R3.styleMap;
}
const reference = isClassBased ? R3.classMap : R3.styleMap;
return {
sourceSpan: stylingInput.sourceSpan,
reference,
allocateBindingSlots: totalBindingSlotsRequired,
buildParams: (convertFn: (value: any) => o.Expression) => { return [convertFn(mapValue)]; }
supportsInterpolation: isClassBased,
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
const convertResult = convertFn(mapValue);
return Array.isArray(convertResult) ? convertResult : [convertResult];
}
};
}
private _buildSingleInputs(
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
allowUnits: boolean, valueConverter: ValueConverter): Instruction[] {
allowUnits: boolean, valueConverter: ValueConverter,
getInterpolationExpressionFn?: (value: Interpolation) => o.ExternalReference):
StylingInstruction[] {
let totalBindingSlotsRequired = 0;
return inputs.map(input => {
const bindingIndex: number = mapIndex.get(input.name !) !;
const value = input.value.visit(valueConverter);
totalBindingSlotsRequired += (value instanceof Interpolation) ? value.expressions.length : 0;
if (value instanceof Interpolation) {
totalBindingSlotsRequired += value.expressions.length;
if (getInterpolationExpressionFn) {
reference = getInterpolationExpressionFn(value);
}
}
if (compilerIsNewStylingInUse()) {
// the old implementation does not reserve slot values for
// binding entries. The new one does.
@ -405,14 +424,18 @@ export class StylingBuilder {
}
return {
sourceSpan: input.sourceSpan,
supportsInterpolation: !!getInterpolationExpressionFn,
allocateBindingSlots: totalBindingSlotsRequired, reference,
buildParams: (convertFn: (value: any) => o.Expression) => {
params: (convertFn: (value: any) => o.Expression | o.Expression[]) => {
// min params => stylingProp(elmIndex, bindingIndex, value)
// max params => stylingProp(elmIndex, bindingIndex, value, overrideFlag)
const params: o.Expression[] = [];
params.push(o.literal(bindingIndex));
params.push(convertFn(value));
const params: o.Expression[] = [o.literal(bindingIndex)];
const convertResult = convertFn(value);
if (Array.isArray(convertResult)) {
params.push(...convertResult);
} else {
params.push(convertResult);
}
if (allowUnits) {
if (input.unit) {
params.push(o.literal(input.unit));
@ -431,7 +454,7 @@ export class StylingBuilder {
});
}
private _buildClassInputs(valueConverter: ValueConverter): Instruction[] {
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
if (this._singleClassInputs) {
return this._buildSingleInputs(
R3.classProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
@ -439,29 +462,30 @@ export class StylingBuilder {
return [];
}
private _buildStyleInputs(valueConverter: ValueConverter): Instruction[] {
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
if (this._singleStyleInputs) {
return this._buildSingleInputs(
R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
R3.styleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter,
getStylePropInterpolationExpression);
}
return [];
}
private _buildApplyFn(): Instruction {
private _buildApplyFn(): StylingInstruction {
return {
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
reference: R3.stylingApply,
allocateBindingSlots: 0,
buildParams: () => { return []; }
params: () => { return []; }
};
}
private _buildSanitizerFn() {
private _buildSanitizerFn(): StylingInstruction {
return {
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
reference: R3.styleSanitizer,
allocateBindingSlots: 0,
buildParams: () => [o.importExpr(R3.defaultStyleSanitizer)]
params: () => [o.importExpr(R3.defaultStyleSanitizer)]
};
}
@ -470,7 +494,7 @@ export class StylingBuilder {
* into the update block of a template function or a directive hostBindings function.
*/
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
const instructions: Instruction[] = [];
const instructions: StylingInstruction[] = [];
if (this.hasBindings) {
if (compilerIsNewStylingInUse() && this._useDefaultSanitizer) {
instructions.push(this._buildSanitizerFn());
@ -548,3 +572,61 @@ export function parseProperty(name: string):
return {property, unit, hasOverrideFlag};
}
/**
* Gets the instruction to generate for an interpolated class map.
* @param interpolation An Interpolation AST
*/
function getClassMapInterpolationExpression(interpolation: Interpolation): o.ExternalReference {
switch (getInterpolationArgsLength(interpolation)) {
case 1:
return R3.classMap;
case 3:
return R3.classMapInterpolate1;
case 5:
return R3.classMapInterpolate2;
case 7:
return R3.classMapInterpolate3;
case 9:
return R3.classMapInterpolate4;
case 11:
return R3.classMapInterpolate5;
case 13:
return R3.classMapInterpolate6;
case 15:
return R3.classMapInterpolate7;
case 17:
return R3.classMapInterpolate8;
default:
return R3.classMapInterpolateV;
}
}
/**
* Gets the instruction to generate for an interpolated style prop.
* @param interpolation An Interpolation AST
*/
function getStylePropInterpolationExpression(interpolation: Interpolation) {
switch (getInterpolationArgsLength(interpolation)) {
case 1:
return R3.styleProp;
case 3:
return R3.stylePropInterpolate1;
case 5:
return R3.stylePropInterpolate2;
case 7:
return R3.stylePropInterpolate3;
case 9:
return R3.stylePropInterpolate4;
case 11:
return R3.stylePropInterpolate5;
case 13:
return R3.stylePropInterpolate6;
case 15:
return R3.stylePropInterpolate7;
case 17:
return R3.stylePropInterpolate8;
default:
return R3.stylePropInterpolateV;
}
}

View File

@ -36,8 +36,8 @@ import {I18nContext} from './i18n/context';
import {I18nMetaVisitor} from './i18n/meta';
import {getSerializedI18nContent} from './i18n/serializer';
import {I18N_ICU_MAPPING_PREFIX, TRANSLATION_PREFIX, assembleBoundTextPlaceholders, assembleI18nBoundString, formatI18nPlaceholderName, getTranslationConstPrefix, getTranslationDeclStmts, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, metaFromI18nMessage, placeholdersToParams, wrapI18nPlaceholder} from './i18n/util';
import {Instruction, StylingBuilder} from './styling_builder';
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
import {StylingBuilder, StylingInstruction} from './styling_builder';
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, chainedInstruction, getAttrsForDirectiveMatching, getInterpolationArgsLength, invalid, trimTrailingNulls, unsupported} from './util';
@ -1067,14 +1067,21 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
private processStylingInstruction(
elementIndex: number, instruction: Instruction|null, createMode: boolean) {
elementIndex: number, instruction: StylingInstruction|null, createMode: boolean) {
if (instruction) {
const paramsFn = () => instruction.buildParams(value => this.convertPropertyBinding(value));
if (createMode) {
this.creationInstruction(instruction.sourceSpan, instruction.reference, paramsFn);
this.creationInstruction(instruction.sourceSpan, instruction.reference, () => {
return instruction.params(value => this.convertPropertyBinding(value)) as o.Expression[];
});
} else {
this.updateInstruction(
elementIndex, instruction.sourceSpan, instruction.reference, paramsFn);
this.updateInstruction(elementIndex, instruction.sourceSpan, instruction.reference, () => {
return instruction
.params(value => {
return (instruction.supportsInterpolation && value instanceof Interpolation) ?
this.getUpdateInstructionArguments(value) :
this.convertPropertyBinding(value);
}) as o.Expression[];
});
}
}
}
@ -1150,12 +1157,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
private convertPropertyBinding(value: AST): o.Expression {
const interpolationFn =
value instanceof Interpolation ? interpolate : () => error('Unexpected interpolation');
const convertedPropertyBinding = convertPropertyBinding(
this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple,
interpolationFn);
() => error('Unexpected interpolation'));
const valExpr = convertedPropertyBinding.currValExpr;
this._tempVariables.push(...convertedPropertyBinding.stmts);
@ -1751,31 +1755,6 @@ function getNgProjectAsLiteral(attribute: t.TextAttribute): o.Expression[] {
return [o.literal(core.AttributeMarker.ProjectAs), asLiteral(parsedR3Selector)];
}
function interpolate(args: o.Expression[]): o.Expression {
args = args.slice(1); // Ignore the length prefix added for render2
switch (args.length) {
case 3:
return o.importExpr(R3.interpolation1).callFn(args);
case 5:
return o.importExpr(R3.interpolation2).callFn(args);
case 7:
return o.importExpr(R3.interpolation3).callFn(args);
case 9:
return o.importExpr(R3.interpolation4).callFn(args);
case 11:
return o.importExpr(R3.interpolation5).callFn(args);
case 13:
return o.importExpr(R3.interpolation6).callFn(args);
case 15:
return o.importExpr(R3.interpolation7).callFn(args);
case 17:
return o.importExpr(R3.interpolation8).callFn(args);
}
(args.length >= 19 && args.length % 2 == 1) ||
error(`Invalid interpolation argument length ${args.length}`);
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
}
/**
* Gets the instruction to generate for an interpolated property
* @param interpolation An Interpolation AST
@ -1861,22 +1840,6 @@ function getTextInterpolationExpression(interpolation: Interpolation): o.Externa
}
}
/**
* Gets the number of arguments expected to be passed to a generated instruction in the case of
* interpolation instructions.
* @param interpolation An interpolation ast
*/
function getInterpolationArgsLength(interpolation: Interpolation) {
const {expressions, strings} = interpolation;
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
// `textInterpolate`.
return 1;
} else {
return expressions.length + strings.length;
}
}
/**
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
*/

View File

@ -7,6 +7,7 @@
*/
import {ConstantPool} from '../../constant_pool';
import {Interpolation} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import {splitAtColon} from '../../util';
@ -201,3 +202,20 @@ export function chainedInstruction(
return expression;
}
/**
* Gets the number of arguments expected to be passed to a generated instruction in the case of
* interpolation instructions.
* @param interpolation An interpolation ast
*/
export function getInterpolationArgsLength(interpolation: Interpolation) {
const {expressions, strings} = interpolation;
if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
// If the interpolation has one interpolated value, but the prefix and suffix are both empty
// strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
// `textInterpolate`.
return 1;
} else {
return expressions.length + strings.length;
}
}

View File

@ -130,7 +130,25 @@ export {
ɵɵstyling,
ɵɵstyleMap,
ɵɵclassMap,
ɵɵclassMapInterpolate1,
ɵɵclassMapInterpolate2,
ɵɵclassMapInterpolate3,
ɵɵclassMapInterpolate4,
ɵɵclassMapInterpolate5,
ɵɵclassMapInterpolate6,
ɵɵclassMapInterpolate7,
ɵɵclassMapInterpolate8,
ɵɵclassMapInterpolateV,
ɵɵstyleProp,
ɵɵstylePropInterpolate1,
ɵɵstylePropInterpolate2,
ɵɵstylePropInterpolate3,
ɵɵstylePropInterpolate4,
ɵɵstylePropInterpolate5,
ɵɵstylePropInterpolate6,
ɵɵstylePropInterpolate7,
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstylingApply,
ɵɵclassProp,
ɵɵelementHostAttrs,

View File

@ -36,6 +36,16 @@ export {
ɵɵattributeInterpolateV,
ɵɵclassMap,
ɵɵclassMapInterpolate1,
ɵɵclassMapInterpolate2,
ɵɵclassMapInterpolate3,
ɵɵclassMapInterpolate4,
ɵɵclassMapInterpolate5,
ɵɵclassMapInterpolate6,
ɵɵclassMapInterpolate7,
ɵɵclassMapInterpolate8,
ɵɵclassMapInterpolateV,
ɵɵclassProp,
ɵɵcomponentHostSyntheticListener,
@ -97,7 +107,18 @@ export {
ɵɵselect,
ɵɵstyleMap,
ɵɵstyleProp,
ɵɵstylePropInterpolate1,
ɵɵstylePropInterpolate2,
ɵɵstylePropInterpolate3,
ɵɵstylePropInterpolate4,
ɵɵstylePropInterpolate5,
ɵɵstylePropInterpolate6,
ɵɵstylePropInterpolate7,
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstyleSanitizer,
ɵɵstyling,
ɵɵstylingApply,

View File

@ -48,3 +48,5 @@ export * from './styling';
export {styleSanitizer as ɵɵstyleSanitizer} from '../styling_next/instructions';
export * from './text';
export * from './text_interpolation';
export * from './class_map_interpolation';
export * from './style_prop_interpolation';

View File

@ -0,0 +1,353 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NO_CHANGE} from '../tokens';
import {ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV} from './interpolation';
import {ɵɵclassMap} from './styling';
/**
*
* Update an interpolated class on an element with single bound value surrounded by text.
*
* Used when the value passed to a property has 1 interpolated value in it:
*
* ```html
* <div class="prefix{{v0}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate1('prefix', v0, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate1(prefix: string, v0: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation1(prefix, v0, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 2 bound values surrounded by text.
*
* Used when the value passed to a property has 2 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate2('prefix', v0, '-', v1, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate2(
prefix: string, v0: any, i0: string, v1: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation2(prefix, v0, i0, v1, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 3 bound values surrounded by text.
*
* Used when the value passed to a property has 3 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate3(
* 'prefix', v0, '-', v1, '-', v2, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate3(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 4 bound values surrounded by text.
*
* Used when the value passed to a property has 4 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate4(
* 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate4(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 5 bound values surrounded by text.
*
* Used when the value passed to a property has 5 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate5(
* 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate5(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 6 bound values surrounded by text.
*
* Used when the value passed to a property has 6 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate6(
* 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate6(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 7 bound values surrounded by text.
*
* Used when the value passed to a property has 7 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate7(
* 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate7(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
*
* Update an interpolated class on an element with 8 bound values surrounded by text.
*
* Used when the value passed to a property has 8 interpolated values in it:
*
* ```html
* <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolate8(
* 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
* ```
*
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param i6 Static value used for concatenation only.
* @param v7 Value checked for change.
* @param suffix Static value used for concatenation only.
* @codeGenApi
*/
export function ɵɵclassMapInterpolate8(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}
/**
* Update an interpolated class on an element with 8 or more bound values surrounded by text.
*
* Used when the number of interpolated values exceeds 7.
*
* ```html
* <div
* class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵclassMapInterpolateV(
* ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
* 'suffix']);
* ```
*.
* @param values The a collection of values and the strings in-between those values, beginning with
* a string prefix and ending with a string suffix.
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
* @codeGenApi
*/
export function ɵɵclassMapInterpolateV(values: any[]): void {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolationV(values);
if (interpolatedValue !== NO_CHANGE) {
ɵɵclassMap(interpolatedValue);
}
}

View File

@ -0,0 +1,425 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NO_CHANGE} from '../tokens';
import {ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV} from './interpolation';
import {TsickleIssue1009} from './shared';
import {ɵɵstyleProp} from './styling';
/**
*
* Update an interpolated style property on an element with single bound value surrounded by text.
*
* Used when the value passed to a property has 1 interpolated value in it:
*
* ```html
* <div style.color="prefix{{v0}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate1(0, 'prefix', v0, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate1(
styleIndex: number, prefix: string, v0: any, suffix: string, valueSuffix?: string | null,
forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation1(prefix, v0, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate1;
}
/**
*
* Update an interpolated style property on an element with 2 bound values surrounded by text.
*
* Used when the value passed to a property has 2 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate2(0, 'prefix', v0, '-', v1, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate2(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, suffix: string,
valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation2(prefix, v0, i0, v1, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate2;
}
/**
*
* Update an interpolated style property on an element with 3 bound values surrounded by text.
*
* Used when the value passed to a property has 3 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate3(0, 'prefix', v0, '-', v1, '-', v2, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate3(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation3(prefix, v0, i0, v1, i1, v2, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate3;
}
/**
*
* Update an interpolated style property on an element with 4 bound values surrounded by text.
*
* Used when the value passed to a property has 4 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate4(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate4(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
i2: string, v3: any, suffix: string, valueSuffix?: string | null,
forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate4;
}
/**
*
* Update an interpolated style property on an element with 5 bound values surrounded by text.
*
* Used when the value passed to a property has 5 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate5(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate5(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
i2: string, v3: any, i3: string, v4: any, suffix: string, valueSuffix?: string | null,
forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue = ɵɵinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate5;
}
/**
*
* Update an interpolated style property on an element with 6 bound values surrounded by text.
*
* Used when the value passed to a property has 6 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate6(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate6(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string,
valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate6;
}
/**
*
* Update an interpolated style property on an element with 7 bound values surrounded by text.
*
* Used when the value passed to a property has 7 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate7(
* 0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate7(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any,
suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate7;
}
/**
*
* Update an interpolated style property on an element with 8 bound values surrounded by text.
*
* Used when the value passed to a property has 8 interpolated values in it:
*
* ```html
* <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolate8(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6,
* '-', v7, 'suffix');
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`.
* @param prefix Static value used for concatenation only.
* @param v0 Value checked for change.
* @param i0 Static value used for concatenation only.
* @param v1 Value checked for change.
* @param i1 Static value used for concatenation only.
* @param v2 Value checked for change.
* @param i2 Static value used for concatenation only.
* @param v3 Value checked for change.
* @param i3 Static value used for concatenation only.
* @param v4 Value checked for change.
* @param i4 Static value used for concatenation only.
* @param v5 Value checked for change.
* @param i5 Static value used for concatenation only.
* @param v6 Value checked for change.
* @param i6 Static value used for concatenation only.
* @param v7 Value checked for change.
* @param suffix Static value used for concatenation only.
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolate8(
styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string,
v7: any, suffix: string, valueSuffix?: string | null,
forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolatedValue =
ɵɵinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
if (interpolatedValue !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolatedValue as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolate8;
}
/**
* Update an interpolated style property on an element with 8 or more bound values surrounded by
* text.
*
* Used when the number of interpolated values exceeds 7.
*
* ```html
* <div
* style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix">
* </div>
* ```
*
* Its compiled representation is:
*
* ```ts
* ɵɵstylePropInterpolateV(
* 0, ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
* 'suffix']);
* ```
*
* @param styleIndex Index of style to update. This index value refers to the
* index of the style in the style bindings array that was passed into
* `styling`..
* @param values The a collection of values and the strings in-between those values, beginning with
* a string prefix and ending with a string suffix.
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
* @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
* @param forceOverride Whether or not to update the styling value immediately.
* @returns itself, so that it may be chained.
* @codeGenApi
*/
export function ɵɵstylePropInterpolateV(
styleIndex: number, values: any[], valueSuffix?: string | null,
forceOverride?: boolean): TsickleIssue1009 {
// TODO(FW-1340): Refactor to remove the use of other instructions here.
const interpolated = ɵɵinterpolationV(values);
if (interpolated !== NO_CHANGE) {
ɵɵstyleProp(styleIndex, interpolated as string, valueSuffix, forceOverride);
}
return ɵɵstylePropInterpolateV;
}

View File

@ -310,7 +310,7 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu
*
* @codeGenApi
*/
export function ɵɵclassMap(classes: {[styleName: string]: any} | NO_CHANGE | string | null): void {
export function ɵɵclassMap(classes: {[styleName: string]: any} | string | null): void {
const index = getSelectedIndex();
const lView = getLView();
const stylingContext = getStylingContext(index, lView);
@ -323,7 +323,7 @@ export function ɵɵclassMap(classes: {[styleName: string]: any} | NO_CHANGE | s
// inputs are only evaluated from a template binding into a directive, therefore,
// there should not be a situation where a directive host bindings function
// evaluates the inputs (this should only happen in the template function)
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
if (hasClassInput(tNode)) {
const initialClasses = getInitialClassNameValue(stylingContext);
const classInputVal =
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);

View File

@ -115,9 +115,27 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵreference': r3.ɵɵreference,
'ɵɵelementHostAttrs': r3.ɵɵelementHostAttrs,
'ɵɵclassMap': r3.ɵɵclassMap,
'ɵɵclassMapInterpolate1': r3.ɵɵclassMapInterpolate1,
'ɵɵclassMapInterpolate2': r3.ɵɵclassMapInterpolate2,
'ɵɵclassMapInterpolate3': r3.ɵɵclassMapInterpolate3,
'ɵɵclassMapInterpolate4': r3.ɵɵclassMapInterpolate4,
'ɵɵclassMapInterpolate5': r3.ɵɵclassMapInterpolate5,
'ɵɵclassMapInterpolate6': r3.ɵɵclassMapInterpolate6,
'ɵɵclassMapInterpolate7': r3.ɵɵclassMapInterpolate7,
'ɵɵclassMapInterpolate8': r3.ɵɵclassMapInterpolate8,
'ɵɵclassMapInterpolateV': r3.ɵɵclassMapInterpolateV,
'ɵɵstyling': r3.ɵɵstyling,
'ɵɵstyleMap': r3.ɵɵstyleMap,
'ɵɵstyleProp': r3.ɵɵstyleProp,
'ɵɵstylePropInterpolate1': r3.ɵɵstylePropInterpolate1,
'ɵɵstylePropInterpolate2': r3.ɵɵstylePropInterpolate2,
'ɵɵstylePropInterpolate3': r3.ɵɵstylePropInterpolate3,
'ɵɵstylePropInterpolate4': r3.ɵɵstylePropInterpolate4,
'ɵɵstylePropInterpolate5': r3.ɵɵstylePropInterpolate5,
'ɵɵstylePropInterpolate6': r3.ɵɵstylePropInterpolate6,
'ɵɵstylePropInterpolate7': r3.ɵɵstylePropInterpolate7,
'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8,
'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV,
'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer,
'ɵɵstylingApply': r3.ɵɵstylingApply,
'ɵɵclassProp': r3.ɵɵclassProp,

View File

@ -200,4 +200,177 @@ describe('styling', () => {
// that we use to run tests doesn't support `clip-path` in `CSSStyleDeclaration`.
expect(html).toMatch(/style=["|']clip-path:\s*url\(.*#test.*\)/);
});
it('should support interpolations inside a class binding', () => {
@Component({
template: `
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i{{nine}}j"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h{{eight}}i"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g{{seven}}h"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f{{six}}g"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e{{five}}f"></div>
<div class="a{{one}}b{{two}}c{{three}}d{{four}}e"></div>
<div class="a{{one}}b{{two}}c{{three}}d"></div>
<div class="a{{one}}b{{two}}c"></div>
<div class="a{{one}}b"></div>
<div class="{{one}}"></div>
`
})
class Cmp {
one = 'one';
two = 'two';
three = 'three';
four = 'four';
five = 'five';
six = 'six';
seven = 'seven';
eight = 'eight';
nine = 'nine';
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const instance = fixture.componentInstance;
fixture.detectChanges();
const divs = fixture.nativeElement.querySelectorAll('div');
expect(divs[0].getAttribute('class')).toBe('aonebtwocthreedfourefivefsixgsevenheightininej');
expect(divs[1].getAttribute('class')).toBe('aonebtwocthreedfourefivefsixgsevenheighti');
expect(divs[2].getAttribute('class')).toBe('aonebtwocthreedfourefivefsixgsevenh');
expect(divs[3].getAttribute('class')).toBe('aonebtwocthreedfourefivefsixg');
expect(divs[4].getAttribute('class')).toBe('aonebtwocthreedfourefivef');
expect(divs[5].getAttribute('class')).toBe('aonebtwocthreedfoure');
expect(divs[6].getAttribute('class')).toBe('aonebtwocthreed');
expect(divs[7].getAttribute('class')).toBe('aonebtwoc');
expect(divs[8].getAttribute('class')).toBe('aoneb');
expect(divs[9].getAttribute('class')).toBe('one');
instance.one = instance.two = instance.three = instance.four = instance.five = instance.six =
instance.seven = instance.eight = instance.nine = '';
fixture.detectChanges();
expect(divs[0].getAttribute('class')).toBe('abcdefghij');
expect(divs[1].getAttribute('class')).toBe('abcdefghi');
expect(divs[2].getAttribute('class')).toBe('abcdefgh');
expect(divs[3].getAttribute('class')).toBe('abcdefg');
expect(divs[4].getAttribute('class')).toBe('abcdef');
expect(divs[5].getAttribute('class')).toBe('abcde');
expect(divs[6].getAttribute('class')).toBe('abcd');
expect(divs[7].getAttribute('class')).toBe('abc');
expect(divs[8].getAttribute('class')).toBe('ab');
expect(divs[9].getAttribute('class')).toBeFalsy();
});
it('should support interpolations inside a class binding when other classes are present', () => {
@Component({template: '<div class="zero i-{{one}} {{two}} three"></div>'})
class Cmp {
one = 'one';
two = 'two';
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();
const classList = fixture.nativeElement.querySelector('div').classList;
expect(classList).toContain('zero');
expect(classList).toContain('i-one');
expect(classList).toContain('two');
expect(classList).toContain('three');
fixture.componentInstance.one = fixture.componentInstance.two = '';
fixture.detectChanges();
expect(classList).toContain('zero');
expect(classList).toContain('i-');
expect(classList).toContain('three');
expect(classList).not.toContain('i-one');
expect(classList).not.toContain('two');
});
it('should support interpolations inside a style property binding', () => {
@Component({
template: `
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}{{five}}{{six}}{{seven}}{{eight}}{{nine}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}{{five}}{{six}}{{seven}}{{eight}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}{{five}}{{six}}{{seven}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}{{five}}{{six}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}{{five}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}{{four}}"></div>
<div style.font-family="f{{one}}{{two}}{{three}}"></div>
<div style.font-family="f{{one}}{{two}}"></div>
<div style.font-family="f{{one}}"></div>
<div style.width="{{singleBinding}}"></div>
`
})
class Cmp {
singleBinding: string|null = '1337px';
one = 1;
two = 2;
three = 3;
four = 4;
five = 5;
six = 6;
seven = 7;
eight = 8;
nine = 9;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const instance = fixture.componentInstance;
fixture.detectChanges();
const divs: NodeListOf<HTMLElement> = fixture.nativeElement.querySelectorAll('div');
expect(divs[0].style.fontFamily).toBe('f123456789');
expect(divs[1].style.fontFamily).toBe('f12345678');
expect(divs[2].style.fontFamily).toBe('f1234567');
expect(divs[3].style.fontFamily).toBe('f123456');
expect(divs[4].style.fontFamily).toBe('f12345');
expect(divs[5].style.fontFamily).toBe('f1234');
expect(divs[6].style.fontFamily).toBe('f123');
expect(divs[7].style.fontFamily).toBe('f12');
expect(divs[8].style.fontFamily).toBe('f1');
expect(divs[9].style.width).toBe('1337px');
instance.singleBinding = null;
instance.one = instance.two = instance.three = instance.four = instance.five = instance.six =
instance.seven = instance.eight = instance.nine = 1;
fixture.detectChanges();
expect(divs[0].style.fontFamily).toBe('f111111111');
expect(divs[1].style.fontFamily).toBe('f11111111');
expect(divs[2].style.fontFamily).toBe('f1111111');
expect(divs[3].style.fontFamily).toBe('f111111');
expect(divs[4].style.fontFamily).toBe('f11111');
expect(divs[5].style.fontFamily).toBe('f1111');
expect(divs[6].style.fontFamily).toBe('f111');
expect(divs[7].style.fontFamily).toBe('f11');
expect(divs[8].style.fontFamily).toBe('f1');
expect(divs[9].style.width).toBeFalsy();
});
it('should support interpolations when a style property has a unit suffix', () => {
@Component({template: '<div style.width.px="{{one}}{{three}}{{three}}7"></div>'})
class Cmp {
one = 1;
three = 3;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();
const div = fixture.nativeElement.querySelector('div');
expect(div.style.width).toBe('1337px');
fixture.componentInstance.one = 2;
fixture.componentInstance.three = 6;
fixture.detectChanges();
expect(div.style.width).toBe('2667px');
});
});

View File

@ -702,7 +702,25 @@ export interface ɵɵBaseDef<T> {
export declare function ɵɵclassMap(classes: {
[styleName: string]: any;
} | NO_CHANGE | string | null): void;
} | string | null): void;
export declare function ɵɵclassMapInterpolate1(prefix: string, v0: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate2(prefix: string, v0: any, i0: string, v1: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate3(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate4(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate5(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate6(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate7(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): void;
export declare function ɵɵclassMapInterpolate8(prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string): void;
export declare function ɵɵclassMapInterpolateV(values: any[]): void;
export declare function ɵɵclassProp(classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void;
@ -1043,6 +1061,24 @@ export declare function ɵɵstyleMap(styles: {
export declare function ɵɵstyleProp(styleIndex: number, value: string | number | String | PlayerFactory | null, suffix?: string | null, forceOverride?: boolean): void;
export declare function ɵɵstylePropInterpolate1(styleIndex: number, prefix: string, v0: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate2(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate3(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate4(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate5(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate6(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate7(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolate8(styleIndex: number, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, suffix: string, valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstylePropInterpolateV(styleIndex: number, values: any[], valueSuffix?: string | null, forceOverride?: boolean): TsickleIssue1009;
export declare function ɵɵstyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): void;
export declare function ɵɵstylingApply(): void;