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:
parent
a9242c4fc2
commit
63523f7964
|
@ -649,6 +649,30 @@ describe('compiler compliance: styling', () => {
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
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]', () => {
|
describe('[class]', () => {
|
||||||
|
@ -802,6 +826,30 @@ describe('compiler compliance: styling', () => {
|
||||||
const result = compile(files, angularFiles);
|
const result = compile(files, angularFiles);
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
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]', () => {
|
describe('[style] mixed with [class]', () => {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {AttributeMarker} from '../../core';
|
||||||
import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
|
import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../parse_util';
|
import {ParseSourceSpan} from '../../parse_util';
|
||||||
|
import {isEmptyExpression} from '../../template_parser/template_parser';
|
||||||
import * as t from '../r3_ast';
|
import * as t from '../r3_ast';
|
||||||
import {Identifiers as R3} from '../r3_identifiers';
|
import {Identifiers as R3} from '../r3_identifiers';
|
||||||
|
|
||||||
|
@ -160,7 +161,10 @@ export class StylingBuilder {
|
||||||
|
|
||||||
registerStyleInput(
|
registerStyleInput(
|
||||||
name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan,
|
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 {property, hasOverrideFlag, unit: bindingUnit} = parseProperty(name);
|
||||||
const entry: BoundStylingEntry = {
|
const entry: BoundStylingEntry = {
|
||||||
name: property,
|
name: property,
|
||||||
|
@ -180,7 +184,10 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
registerClassInput(name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan):
|
registerClassInput(name: string, isMapBased: boolean, value: AST, sourceSpan: ParseSourceSpan):
|
||||||
BoundStylingEntry {
|
BoundStylingEntry|null {
|
||||||
|
if (isEmptyExpression(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const {property, hasOverrideFlag} = parseProperty(name);
|
const {property, hasOverrideFlag} = parseProperty(name);
|
||||||
const entry:
|
const entry:
|
||||||
BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, unit: null};
|
BoundStylingEntry = {name: property, value, sourceSpan, hasOverrideFlag, unit: null};
|
||||||
|
|
|
@ -899,7 +899,7 @@ export function removeSummaryDuplicates<T extends{type: CompileTypeMetadata}>(it
|
||||||
return Array.from(map.values());
|
return Array.from(map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmptyExpression(ast: AST): boolean {
|
export function isEmptyExpression(ast: AST): boolean {
|
||||||
if (ast instanceof ASTWithSource) {
|
if (ast instanceof ASTWithSource) {
|
||||||
ast = ast.ast;
|
ast = ast.ast;
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,4 +106,28 @@ describe('styling', () => {
|
||||||
const outer = element.querySelector('.outer-area');
|
const outer = element.querySelector('.outer-area');
|
||||||
expect(outer.textContent.trim()).toEqual('outer');
|
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>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue