From 2bf0d1a56fb2edd2ead9632db7bd3284a7690d5b Mon Sep 17 00:00:00 2001 From: Alexey Zuev Date: Sun, 10 Feb 2019 11:41:00 +0400 Subject: [PATCH] fix(ivy): compile pipe in context of ternary operator (#28635) Previously, it wasn't possible to compile template that contains pipe in context of ternary operator `{{ 1 ? 2 : 0 | myPipe }}` due to the error `Error: Illegal state: Pipes should have been converted into functions. Pipe: async`. This PR fixes a typo in expression parser so that pipes are correctly converted into functions. PR Close #28635 --- .../compliance/r3_compiler_compliance_spec.ts | 11 +++--- .../compiler/src/expression_parser/ast.ts | 2 +- packages/core/test/acceptance/pipe_spec.ts | 38 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 packages/core/test/acceptance/pipe_spec.ts diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 8eca2159c4..3015e4bbac 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -1756,7 +1756,7 @@ describe('compiler compliance', () => { @Component({ selector: 'my-app', - template: '{{name | myPipe:size | myPurePipe:size }}

{{ name | myPipe:1:2:3:4:5 }}

' + template: '{{name | myPipe:size | myPurePipe:size }}

{{ name | myPipe:1:2:3:4:5 }} {{ name ? 1 : 2 | myPipe }}

' }) export class MyApp { name = 'World'; @@ -1795,8 +1795,8 @@ describe('compiler compliance', () => { type: MyApp, selectors: [["my-app"]], factory: function MyApp_Factory(t) { return new (t || MyApp)(); }, - consts: 6, - vars: 17, + consts: 7, + vars: 20, template: function MyApp_Template(rf, ctx) { if (rf & 1) { $r3$.ɵtext(0); @@ -1805,11 +1805,12 @@ describe('compiler compliance', () => { $r3$.ɵelementStart(3, "p"); $r3$.ɵtext(4); $r3$.ɵpipe(5, "myPipe"); + $r3$.ɵpipe(6, "myPipe"); $r3$.ɵelementEnd(); } if (rf & 2) { - $r3$.ɵtextBinding(0, $r3$.ɵinterpolation1("", $r3$.ɵpipeBind2(1, 2, $r3$.ɵpipeBind2(2, 5, ctx.name, ctx.size), ctx.size), "")); - $r3$.ɵtextBinding(4, $r3$.ɵinterpolation1("", $r3$.ɵpipeBindV(5, 8, $r3$.ɵpureFunction1(15, $c0$, ctx.name)), "")); + $r3$.ɵtextBinding(0, $r3$.ɵinterpolation1("", $r3$.ɵpipeBind2(1, 3, $r3$.ɵpipeBind2(2, 6, ctx.name, ctx.size), ctx.size), "")); + $r3$.ɵtextBinding(4, $r3$.ɵinterpolation2("", $r3$.ɵpipeBindV(5, 9, $r3$.ɵpureFunction1(18, $c0$, ctx.name)), " ", (ctx.name ? 1 : $r3$.ɵpipeBind1(6, 16, 2)), "")); } }, pipes: [MyPurePipe, MyPipe], diff --git a/packages/compiler/src/expression_parser/ast.ts b/packages/compiler/src/expression_parser/ast.ts index c511a4dc23..4c35710620 100644 --- a/packages/compiler/src/expression_parser/ast.ts +++ b/packages/compiler/src/expression_parser/ast.ts @@ -546,7 +546,7 @@ export class AstMemoryEfficientTransformer implements AstVisitor { const condition = ast.condition.visit(this); const trueExp = ast.trueExp.visit(this); const falseExp = ast.falseExp.visit(this); - if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== falseExp) { + if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) { return new Conditional(ast.span, condition, trueExp, falseExp); } return ast; diff --git a/packages/core/test/acceptance/pipe_spec.ts b/packages/core/test/acceptance/pipe_spec.ts new file mode 100644 index 0000000000..003473a731 --- /dev/null +++ b/packages/core/test/acceptance/pipe_spec.ts @@ -0,0 +1,38 @@ +/** + * @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 {Component, Pipe, PipeTransform} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/src/matchers'; + +describe('pipe', () => { + it('should support pipe in context of ternary operator', () => { + @Pipe({name: 'pipe'}) + class MyPipe implements PipeTransform { + transform(value: any): any { return value; } + } + + @Component({ + selector: 'my-app', + template: `{{ condition ? 'a' : 'b' | pipe }}`, + }) + class MyApp { + condition = false; + } + + TestBed.configureTestingModule({declarations: [MyApp, MyPipe]}); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); + + expect(fixture.nativeElement).toHaveText('b'); + + fixture.componentInstance.condition = true; + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('a'); + }); +});