diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
index 5d44e679e3..d4bc7e0eb7 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
@@ -117,7 +117,7 @@ describe('compiler compliance: bindings', () => {
}
if (rf & 2) {
$i0$.ɵɵselect(0);
- $i0$.ɵɵelementProperty(0, "title", $i0$.ɵɵinterpolation1("Hello ", $ctx$.name, ""));
+ $i0$.ɵɵpropertyInterpolate1("title", "Hello ", $ctx$.name, "");
}
}`;
const result = compile(files, angularFiles);
@@ -452,6 +452,50 @@ describe('compiler compliance: bindings', () => {
}
});
+ it('should generate the proper update instructions for interpolated properties', () => {
+ const files: MockDirectory = getAppFiles(`
+
+
+
+
+
+
+
+
+
+
+ `);
+
+ const template = `
+ …
+ if (rf & 2) {
+ i0.ɵɵselect(0);
+ i0.ɵɵpropertyInterpolateV("title", "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");
+ i0.ɵɵselect(1);
+ i0.ɵɵpropertyInterpolate8("title", "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");
+ i0.ɵɵselect(2);
+ i0.ɵɵpropertyInterpolate7("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
+ i0.ɵɵselect(3);
+ i0.ɵɵpropertyInterpolate6("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
+ i0.ɵɵselect(4);
+ i0.ɵɵpropertyInterpolate5("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
+ i0.ɵɵselect(5);
+ i0.ɵɵpropertyInterpolate4("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
+ i0.ɵɵselect(6);
+ i0.ɵɵpropertyInterpolate3("title", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
+ i0.ɵɵselect(7);
+ i0.ɵɵpropertyInterpolate2("title", "a", ctx.one, "b", ctx.two, "c");
+ i0.ɵɵselect(8);
+ i0.ɵɵpropertyInterpolate1("title", "a", ctx.one, "b");
+ i0.ɵɵselect(9);
+ i0.ɵɵpropertyInterpolate("title", ctx.one);
+ }
+ …
+ `;
+ const result = compile(files, angularFiles);
+ expectEmit(result.source, template, 'Incorrect handling of interpolated properties');
+ });
+
it('should keep local ref for host element', () => {
const files: MockDirectory = getAppFiles(`
diff --git a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts
index 1e24656b78..ed1d6cee85 100644
--- a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts
+++ b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts
@@ -74,7 +74,7 @@ describe('template source-mapping', () => {
});
expect(mappings).toContain({
source: 'id="{{name}}"',
- generated: 'i0.ɵɵelementProperty(0, "id", i0.ɵɵinterpolation1("", ctx.name, ""))',
+ generated: 'i0.ɵɵpropertyInterpolate("id", ctx.name)',
sourceUrl: '../test.ts'
});
});
diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts
index e79dbef840..9f4e612e14 100644
--- a/packages/compiler/src/render3/r3_identifiers.ts
+++ b/packages/compiler/src/render3/r3_identifiers.ts
@@ -125,6 +125,27 @@ export class Identifiers {
static property: o.ExternalReference = {name: 'ɵɵproperty', moduleName: CORE};
+ static propertyInterpolate:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate', moduleName: CORE};
+ static propertyInterpolate1:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate1', moduleName: CORE};
+ static propertyInterpolate2:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate2', moduleName: CORE};
+ static propertyInterpolate3:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate3', moduleName: CORE};
+ static propertyInterpolate4:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate4', moduleName: CORE};
+ static propertyInterpolate5:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate5', moduleName: CORE};
+ static propertyInterpolate6:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate6', moduleName: CORE};
+ static propertyInterpolate7:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate7', moduleName: CORE};
+ static propertyInterpolate8:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolate8', moduleName: CORE};
+ static propertyInterpolateV:
+ o.ExternalReference = {name: 'ɵɵpropertyInterpolateV', moduleName: CORE};
+
static i18n: o.ExternalReference = {name: 'ɵɵi18n', moduleName: CORE};
static i18nAttributes: o.ExternalReference = {name: 'ɵɵi18nAttributes', moduleName: CORE};
static i18nExp: o.ExternalReference = {name: 'ɵɵi18nExp', moduleName: CORE};
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index 6329fb2565..aa8a4a63f3 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -746,20 +746,37 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
}
this.allocateBindingSlots(value);
- if (inputType === BindingType.Property && !(value instanceof Interpolation)) {
- // Bound, un-interpolated properties
- this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
- return [
- o.literal(attrName), this.convertPropertyBinding(implicit, value, true), ...params
- ];
- });
+ if (inputType === BindingType.Property) {
+ if (value instanceof Interpolation) {
+ // Interpolated properties
+ const {currValExpr} = convertPropertyBinding(
+ this, implicit, value, this.bindingContext(), BindingForm.TrySimple);
+
+ let args: o.Expression[] = (currValExpr as any).args;
+ args.shift(); // ViewEngine required a count, we don't need that.
+
+ // For interpolations like attr="{{foo}}", we don't need ["", foo, ""], just [foo].
+ if (args.length === 3 && isEmptyStringExpression(args[0]) &&
+ isEmptyStringExpression(args[2])) {
+ args = [args[1]];
+ }
+
+ this.updateInstruction(
+ elementIndex, input.sourceSpan, propertyInterpolate(args.length), () => {
+ return [o.literal(attrName), ...args, ...params];
+ });
+ } else {
+ // Bound, un-interpolated properties
+ this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
+ return [
+ o.literal(attrName), this.convertPropertyBinding(implicit, value, true), ...params
+ ];
+ });
+ }
} else {
let instruction: any;
- if (inputType === BindingType.Property) {
- // Interpolated properties
- instruction = R3.elementProperty;
- } else if (inputType === BindingType.Class) {
+ if (inputType === BindingType.Class) {
instruction = R3.elementClassProp;
} else {
instruction = R3.elementAttribute;
@@ -1627,6 +1644,39 @@ function interpolate(args: o.Expression[]): o.Expression {
return o.importExpr(R3.interpolationV).callFn([o.literalArr(args)]);
}
+function isEmptyStringExpression(exp: o.Expression) {
+ return exp instanceof o.LiteralExpr && exp.value === '';
+}
+
+function propertyInterpolate(argsLength: number) {
+ if (argsLength % 2 !== 1) {
+ error(`Invalid propertyInterpolate argument length ${argsLength}`);
+ }
+
+ switch (argsLength) {
+ case 1:
+ return R3.propertyInterpolate;
+ case 3:
+ return R3.propertyInterpolate1;
+ case 5:
+ return R3.propertyInterpolate2;
+ case 7:
+ return R3.propertyInterpolate3;
+ case 9:
+ return R3.propertyInterpolate4;
+ case 11:
+ return R3.propertyInterpolate5;
+ case 13:
+ return R3.propertyInterpolate6;
+ case 15:
+ return R3.propertyInterpolate7;
+ case 17:
+ return R3.propertyInterpolate8;
+ default:
+ return R3.propertyInterpolateV;
+ }
+}
+
/**
* Options that can be used to modify how a template is parsed by `parseTemplate()`.
*/
diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts
index 634120cfa0..fad9dd161f 100644
--- a/packages/core/src/core_render3_private_export.ts
+++ b/packages/core/src/core_render3_private_export.ts
@@ -90,6 +90,16 @@ export {
ɵɵelementEnd,
ɵɵelementProperty,
ɵɵproperty,
+ ɵɵpropertyInterpolate,
+ ɵɵpropertyInterpolate1,
+ ɵɵpropertyInterpolate2,
+ ɵɵpropertyInterpolate3,
+ ɵɵpropertyInterpolate4,
+ ɵɵpropertyInterpolate5,
+ ɵɵpropertyInterpolate6,
+ ɵɵpropertyInterpolate7,
+ ɵɵpropertyInterpolate8,
+ ɵɵpropertyInterpolateV,
ɵɵcomponentHostSyntheticProperty,
ɵɵcomponentHostSyntheticListener,
ɵɵprojectionDef,
diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts
index 408f786824..8b7ca59962 100644
--- a/packages/core/src/render3/index.ts
+++ b/packages/core/src/render3/index.ts
@@ -81,6 +81,16 @@ export {
ɵɵprojection,
ɵɵprojectionDef,
ɵɵproperty,
+ ɵɵpropertyInterpolate,
+ ɵɵpropertyInterpolate1,
+ ɵɵpropertyInterpolate2,
+ ɵɵpropertyInterpolate3,
+ ɵɵpropertyInterpolate4,
+ ɵɵpropertyInterpolate5,
+ ɵɵpropertyInterpolate6,
+ ɵɵpropertyInterpolate7,
+ ɵɵpropertyInterpolate8,
+ ɵɵpropertyInterpolateV,
ɵɵreference,
diff --git a/packages/core/src/render3/instructions/property_interpolation.ts b/packages/core/src/render3/instructions/property_interpolation.ts
index 959245925a..8204a9c8b4 100644
--- a/packages/core/src/render3/instructions/property_interpolation.ts
+++ b/packages/core/src/render3/instructions/property_interpolation.ts
@@ -322,6 +322,7 @@ const EMPTY_STRING = '';
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009 {
ɵɵpropertyInterpolate1(propName, EMPTY_STRING, v0, EMPTY_STRING);
@@ -354,6 +355,7 @@ export function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue
* @param v0 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate1(
propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009 {
@@ -389,6 +391,7 @@ export function ɵɵpropertyInterpolate1(
* @param v1 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate2(
propName: string, prefix: string, v0: any, i0: string, v1: any,
@@ -428,6 +431,7 @@ export function ɵɵpropertyInterpolate2(
* @param v2 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate3(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
@@ -469,6 +473,7 @@ export function ɵɵpropertyInterpolate3(
* @param v3 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate4(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
@@ -513,6 +518,7 @@ export function ɵɵpropertyInterpolate4(
* @param v4 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate5(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
@@ -559,6 +565,7 @@ export function ɵɵpropertyInterpolate5(
* @param v5 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate6(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
@@ -608,6 +615,7 @@ export function ɵɵpropertyInterpolate6(
* @param v6 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate7(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
@@ -660,6 +668,7 @@ export function ɵɵpropertyInterpolate7(
* @param v7 Value checked for change.
* @param suffix Static value used for concatenation only.
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolate8(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
@@ -699,6 +708,7 @@ export function ɵɵpropertyInterpolate8(
* string prefix and ending with a string suffix.
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
* @returns itself, so that it may be chained.
+ * @codeGenApi
*/
export function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009 {
const index = getSelectedIndex();
diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts
index 2488ef1991..86454a0514 100644
--- a/packages/core/src/render3/jit/environment.ts
+++ b/packages/core/src/render3/jit/environment.ts
@@ -86,6 +86,16 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵɵpipeBindV': r3.ɵɵpipeBindV,
'ɵɵprojectionDef': r3.ɵɵprojectionDef,
'ɵɵproperty': r3.ɵɵproperty,
+ 'ɵɵpropertyInterpolate': r3.ɵɵpropertyInterpolate,
+ 'ɵɵpropertyInterpolate1': r3.ɵɵpropertyInterpolate1,
+ 'ɵɵpropertyInterpolate2': r3.ɵɵpropertyInterpolate2,
+ 'ɵɵpropertyInterpolate3': r3.ɵɵpropertyInterpolate3,
+ 'ɵɵpropertyInterpolate4': r3.ɵɵpropertyInterpolate4,
+ 'ɵɵpropertyInterpolate5': r3.ɵɵpropertyInterpolate5,
+ 'ɵɵpropertyInterpolate6': r3.ɵɵpropertyInterpolate6,
+ 'ɵɵpropertyInterpolate7': r3.ɵɵpropertyInterpolate7,
+ 'ɵɵpropertyInterpolate8': r3.ɵɵpropertyInterpolate8,
+ 'ɵɵpropertyInterpolateV': r3.ɵɵpropertyInterpolateV,
'ɵɵpipe': r3.ɵɵpipe,
'ɵɵqueryRefresh': r3.ɵɵqueryRefresh,
'ɵɵviewQuery': r3.ɵɵviewQuery,
diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts
index ea05de210d..843a68282d 100644
--- a/tools/public_api_guard/core/core.d.ts
+++ b/tools/public_api_guard/core/core.d.ts
@@ -939,6 +939,26 @@ export declare function ɵɵprojectionDef(selectors?: CssSelectorList[]): void;
export declare function ɵɵproperty(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
+export declare function ɵɵpropertyInterpolate(propName: string, v0: any): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate1(propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate2(propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate3(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate4(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate5(propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate6(propName: string, 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): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate7(propName: string, 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): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolate8(propName: string, 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): TsickleIssue1009;
+
+export declare function ɵɵpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009;
+
export declare function ɵɵProvidersFeature(providers: Provider[], viewProviders?: Provider[]): (definition: DirectiveDef) => void;
export declare function ɵɵpureFunction0(slotOffset: number, pureFn: () => T, thisArg?: any): T;