fix(ivy): avoid generating instructions for empty style and class bindings (#30024)

Fixes Ivy throwing an error because it tries to generate styling instructions for empty `style` and `class` bindings.

This PR resolves FW-1274.

PR Close #30024
This commit is contained in:
Kristiyan Kostadinov 2019-04-22 10:28:59 +02:00 committed by Ben Lesh
parent a9242c4fc2
commit 63523f7964
4 changed files with 82 additions and 3 deletions

View File

@ -649,6 +649,30 @@ describe('compiler compliance: styling', () => {
expectEmit(result.source, template, 'Incorrect template');
});
it('should not create instructions for empty style bindings', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<div [style.color]></div>\`
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expect(result.source).not.toContain('elementStyling');
});
});
describe('[class]', () => {
@ -802,6 +826,30 @@ describe('compiler compliance: styling', () => {
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should not create instructions for empty class bindings', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<div [class.is-open]></div>\`
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expect(result.source).not.toContain('elementStyling');
});
});
describe('[style] mixed with [class]', () => {

View File

@ -10,6 +10,7 @@ import {AttributeMarker} from '../../core';
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 * as t from '../r3_ast';
import {Identifiers as R3} from '../r3_identifiers';
@ -160,7 +161,10 @@ export class StylingBuilder {
registerStyleInput(
name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan,
unit?: string|null): BoundStylingEntry {
unit?: string|null): BoundStylingEntry|null {
if (isEmptyExpression(value)) {
return null;
}
const {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
const entry: BoundStylingEntry = {
name: property,
@ -180,7 +184,10 @@ export class StylingBuilder {
}
registerClassInput(name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan):
BoundStylingEntry {
BoundStylingEntry|null {
if (isEmptyExpression(value)) {
return null;
}
const {property, hasOverrideFlag} = parseProperty(name);
const entry:
BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, unit: null};

View File

@ -899,7 +899,7 @@ export function removeSummaryDuplicates<T extends{type: CompileTypeMetadata}>(it
return Array.from(map.values());
}
function isEmptyExpression(ast: AST): boolean {
export function isEmptyExpression(ast: AST): boolean {
if (ast instanceof ASTWithSource) {
ast = ast.ast;
}

View File

@ -106,4 +106,28 @@ describe('styling', () => {
const outer = element.querySelector('.outer-area');
expect(outer.textContent.trim()).toEqual('outer');
});
it('should do nothing for empty style bindings', () => {
@Component({template: '<div [style.color]></div>'})
class App {
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe('<div></div>');
});
it('should do nothing for empty class bindings', () => {
@Component({template: '<div [class.is-open]></div>'})
class App {
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toBe('<div></div>');
});
});