diff --git a/modules/@angular/compiler/src/compiler_util/expression_converter.ts b/modules/@angular/compiler/src/compiler_util/expression_converter.ts index ad56299dd2ac0..fd9dfc0206256 100644 --- a/modules/@angular/compiler/src/compiler_util/expression_converter.ts +++ b/modules/@angular/compiler/src/compiler_util/expression_converter.ts @@ -275,7 +275,12 @@ class _AstToIrVisitor implements cdAst.AstVisitor { args.push(this.visit(ast.expressions[i], _Mode.Expression)); } args.push(o.literal(ast.strings[ast.strings.length - 1])); - return o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn(args); + + return ast.expressions.length <= 9 ? + o.importExpr(resolveIdentifier(Identifiers.inlineInterpolate)).callFn(args) : + o.importExpr(resolveIdentifier(Identifiers.interpolate)).callFn([ + args[0], o.literalArr(args.slice(1)) + ]); } visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any { diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index 8254caea99bf4..655b6da340e5a 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -174,6 +174,11 @@ export class Identifiers { }; static devModeEqual: IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual}; + static inlineInterpolate: IdentifierSpec = { + name: 'inlineInterpolate', + moduleUrl: VIEW_UTILS_MODULE_URL, + runtime: view_utils.inlineInterpolate + }; static interpolate: IdentifierSpec = { name: 'interpolate', moduleUrl: VIEW_UTILS_MODULE_URL, diff --git a/modules/@angular/compiler/src/template_parser/binding_parser.ts b/modules/@angular/compiler/src/template_parser/binding_parser.ts index 1f4b7535c19e1..a2f0f63aa08d8 100644 --- a/modules/@angular/compiler/src/template_parser/binding_parser.ts +++ b/modules/@angular/compiler/src/template_parser/binding_parser.ts @@ -104,11 +104,6 @@ export class BindingParser { const ast = this._exprParser.parseInterpolation(value, sourceInfo, this._interpolationConfig); if (ast) this._reportExpressionParserErrors(ast.errors, sourceSpan); this._checkPipes(ast, sourceSpan); - if (ast && - (ast.ast).expressions.length > view_utils.MAX_INTERPOLATION_VALUES) { - throw new Error( - `Only support at most ${view_utils.MAX_INTERPOLATION_VALUES} interpolation values!`); - } return ast; } catch (e) { this._reportError(`${e}`, sourceSpan); diff --git a/modules/@angular/core/src/linker/view_utils.ts b/modules/@angular/core/src/linker/view_utils.ts index 1782916386d5b..da295153d0438 100644 --- a/modules/@angular/core/src/linker/view_utils.ts +++ b/modules/@angular/core/src/linker/view_utils.ts @@ -45,9 +45,15 @@ export function addToArray(e: any, array: any[]) { array.push(e); } -export const MAX_INTERPOLATION_VALUES = 9; +export function interpolate(valueCount: number, constAndInterp: string[]): string { + let result = ''; + for (let i = 0; i < valueCount * 2; i = i + 2) { + result = result + constAndInterp[i] + _toStringWithNull(constAndInterp[i + 1]); + } + return result + constAndInterp[valueCount * 2]; +} -export function interpolate( +export function inlineInterpolate( valueCount: number, c0: string, a1: any, c1: string, a2?: any, c2?: string, a3?: any, c3?: string, a4?: any, c4?: string, a5?: any, c5?: string, a6?: any, c6?: string, a7?: any, c7?: string, a8?: any, c8?: string, a9?: any, c9?: string): string { diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts index fb9cd116368cc..a247cbf15e225 100644 --- a/modules/@angular/core/test/linker/integration_spec.ts +++ b/modules/@angular/core/test/linker/integration_spec.ts @@ -70,6 +70,29 @@ function declareTests({useJit}: {useJit: boolean}) { expect(fixture.nativeElement).toHaveText('true|false'); }); + it('should support an arbitrary number of interpolations in an element', () => { + TestBed.configureTestingModule({declarations: [MyComp]}); + const template = + `
before{{'0'}}a{{'1'}}b{{'2'}}c{{'3'}}d{{'4'}}e{{'5'}}f{{'6'}}g{{'7'}}h{{'8'}}i{{'9'}}j{{'10'}}after
`; + const fixture = + TestBed.overrideComponent(MyComp, {set: {template}}).createComponent(MyComp); + + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('before0a1b2c3d4e5f6g7h8i9j10after'); + }); + + it('should use a blank string when interpolation evaluates to null or undefined with an arbitrary number of interpolations', + () => { + TestBed.configureTestingModule({declarations: [MyComp]}); + const template = + `
0{{null}}a{{undefined}}b{{null}}c{{undefined}}d{{null}}e{{undefined}}f{{null}}g{{undefined}}h{{null}}i{{undefined}}j{{null}}1
`; + const fixture = + TestBed.overrideComponent(MyComp, {set: {template}}).createComponent(MyComp); + + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('0abcdefghij1'); + }); + it('should consume element binding changes', () => { TestBed.configureTestingModule({declarations: [MyComp]}); const template = '
'; diff --git a/modules/benchmarks/src/tree/ng2_ftl/tree.ngfactory.ts b/modules/benchmarks/src/tree/ng2_ftl/tree.ngfactory.ts index 6aee1488f289d..12b1a31e6f7ab 100644 --- a/modules/benchmarks/src/tree/ng2_ftl/tree.ngfactory.ts +++ b/modules/benchmarks/src/tree/ng2_ftl/tree.ngfactory.ts @@ -64,7 +64,7 @@ export class _View_TreeComponent0 implements FtlView { this._el_0.style.backgroundColor = currVal_0; this._expr_0 = currVal_0; } - const currVal_1: any = import4.interpolate(1, ' ', this.context.data.value, ' '); + const currVal_1: any = import4.inlineInterpolate(1, ' ', this.context.data.value, ' '); if (import4.checkBinding(throwOnChange, this._expr_1, currVal_1)) { this._text_1.nodeValue = currVal_1; this._expr_1 = currVal_1; diff --git a/modules/benchmarks/src/tree/ng2_static_ftl/tree_branch.ngfactory.ts b/modules/benchmarks/src/tree/ng2_static_ftl/tree_branch.ngfactory.ts index a8c360317f192..c04d9b70f484f 100644 --- a/modules/benchmarks/src/tree/ng2_static_ftl/tree_branch.ngfactory.ts +++ b/modules/benchmarks/src/tree/ng2_static_ftl/tree_branch.ngfactory.ts @@ -65,7 +65,7 @@ export class View_TreeTreeComponent { this._el_0.style.backgroundColor = currVal_0; this._expr_0 = currVal_0; } - const currVal_1: any = import4.interpolate(1, ' ', this.context.data.value, ' '); + const currVal_1: any = import4.inlineInterpolate(1, ' ', this.context.data.value, ' '); if (import4.checkBinding(throwOnChange, this._expr_1, currVal_1)) { this._text_1.nodeValue = currVal_1; this._expr_1 = currVal_1; diff --git a/modules/benchmarks/src/tree/ng2_static_ftl/tree_leaf.ngfactory.ts b/modules/benchmarks/src/tree/ng2_static_ftl/tree_leaf.ngfactory.ts index 8da4f95c50e40..812e935828b1e 100644 --- a/modules/benchmarks/src/tree/ng2_static_ftl/tree_leaf.ngfactory.ts +++ b/modules/benchmarks/src/tree/ng2_static_ftl/tree_leaf.ngfactory.ts @@ -47,7 +47,7 @@ export class View_TreeLeafComponent { this._el_0.style.backgroundColor = currVal_0; this._expr_0 = currVal_0; } - const currVal_1: any = import4.interpolate(1, ' ', this.context.data.value, ' '); + const currVal_1: any = import4.inlineInterpolate(1, ' ', this.context.data.value, ' '); if (import4.checkBinding(throwOnChange, this._expr_1, currVal_1)) { this._text_1.nodeValue = currVal_1; this._expr_1 = currVal_1;