fix(ivy): ensure select(n) instructions are always generated around style/class bindings (#30311)

Prior to this patch, the `select(n)` instruction would only be generated
when property bindings are encountered which meant that styling-related
bindings were skipped. This patch ensures that all styling-related bindings
(i.e. class and style bindings) are always prepended with a `select()`
instruction prior to being generated in AOT.

PR Close #30311
This commit is contained in:
Matias Niemelä
2019-05-07 13:18:27 -07:00
committed by Kara Erickson
parent bd37622050
commit 345a3cd9aa
3 changed files with 78 additions and 9 deletions

View File

@ -506,6 +506,7 @@ describe('compiler compliance', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleProp(0, 0, ctx.color);
$r3$.ɵɵelementClassProp(0, 0, ctx.error);
$r3$.ɵɵelementStylingApply(0);

View File

@ -389,6 +389,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementStylingApply(0);
}
@ -454,6 +455,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementClassMap(0, $r3$.ɵɵinterpolation1("foo foo-", $ctx$.fooId, ""));
$r3$.ɵɵelementStylingApply(0);
}
@ -468,6 +470,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementClassMap(0, $r3$.ɵɵinterpolation2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, ""));
$r3$.ɵɵelementStylingApply(0);
}
@ -482,6 +485,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementClassMap(0, $ctx$.exp);
$r3$.ɵɵelementStylingApply(0);
}
@ -538,11 +542,11 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementStyleProp(0, 0, $ctx$.myWidth);
$r3$.ɵɵelementStyleProp(0, 1, $ctx$.myHeight);
$r3$.ɵɵelementStylingApply(0);
$r3$.ɵɵselect(0);
$r3$.ɵɵelementAttribute(0, "style", $r3$.ɵɵbind("border-width: 10px"), $r3$.ɵɵsanitizeStyle);
}
},
@ -598,6 +602,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleProp(0, 0, ctx.myImage);
$r3$.ɵɵelementStylingApply(0);
}
@ -639,6 +644,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleProp(0, 0, 12, "px");
$r3$.ɵɵelementStylingApply(0);
}
@ -704,6 +710,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementClassMap(0,$ctx$.myClassExp);
$r3$.ɵɵelementStylingApply(0);
}
@ -760,11 +767,11 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementClassMap(0, $ctx$.myClassExp);
$r3$.ɵɵelementClassProp(0, 0, $ctx$.yesToApple);
$r3$.ɵɵelementClassProp(0, 1, $ctx$.yesToOrange);
$r3$.ɵɵelementStylingApply(0);
$r3$.ɵɵselect(0);
$r3$.ɵɵelementAttribute(0, "class", $r3$.ɵɵbind("banana"));
}
},
@ -853,7 +860,7 @@ describe('compiler compliance: styling', () => {
});
describe('[style] mixed with [class]', () => {
it('should combine [style] and [class] bindings into a single instruction', () => {
it('should split [style] and [class] bindings into a separate instructions', () => {
const files = {
app: {
'spec.ts': `
@ -882,6 +889,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementClassMap(0, $ctx$.myClassExp);
$r3$.ɵɵelementStylingApply(0);
@ -925,6 +933,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, $r3$.ɵɵpipeBind1(1, 0, $ctx$.myStyleExp));
$r3$.ɵɵelementClassMap(0, $r3$.ɵɵpipeBind1(2, 2, $ctx$.myClassExp));
$r3$.ɵɵelementStylingApply(0);
@ -982,6 +991,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, $r3$.ɵɵpipeBind2(1, 1, $ctx$.myStyleExp, 1000));
$r3$.ɵɵelementClassMap(0, $e2_styling$);
$r3$.ɵɵelementStyleProp(0, 0, $r3$.ɵɵpipeBind2(2, 4, $ctx$.barExp, 3000));
@ -997,6 +1007,59 @@ describe('compiler compliance: styling', () => {
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
it('should always generate select() statements before any styling instructions', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`
<div [style.width]="w1"></div>
<div [style.height]="h1"></div>
<div [class.active]="a1"></div>
<div [class.removed]="r1"></div>
\`
})
export class MyComponent {
w1 = '100px';
h1 = '100px';
a1 = true;
r1 = true;
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const template = `
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleProp(0, 0, $ctx$.w1);
$r3$.ɵɵelementStylingApply(0);
$r3$.ɵɵselect(1);
$r3$.ɵɵelementStyleProp(1, 0, $ctx$.h1);
$r3$.ɵɵelementStylingApply(1);
$r3$.ɵɵselect(2);
$r3$.ɵɵelementClassProp(2, 0, $ctx$.a1);
$r3$.ɵɵelementStylingApply(2);
$r3$.ɵɵselect(3);
$r3$.ɵɵelementClassProp(3, 0, $ctx$.r1);
$r3$.ɵɵelementStylingApply(3);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
});
describe('@Component host styles/classes', () => {
@ -1171,6 +1234,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
$r3$.ɵɵselect(0);
$r3$.ɵɵelementStyleMap(0, ctx.myStyleExp);
$r3$.ɵɵelementClassMap(0, ctx.myClassExp);
$r3$.ɵɵelementStyleProp(0, 0, ctx.myHeightExp, null, true);