fix(ivy): host-styling throws assert exception inside *ngFor (#35133)

Inside `*ngFor` the second run of the styling instructions can get into situation where it tries to read a value from a binding which has not yet executed. As a result the read is `NO_CHANGE` value and subsequent property read cause an exception as it is of wrong type.

Fix #35118

PR Close #35133
This commit is contained in:
Miško Hevery
2020-02-03 12:23:00 -08:00
parent a8609ba0ad
commit ab931cf872
3 changed files with 63 additions and 2 deletions

View File

@ -3273,6 +3273,52 @@ describe('styling', () => {
expect(logs).toEqual([]);
});
describe('regression', () => {
onlyInIvy('styling priority resolution is Ivy only feature.')
.it('should allow lookahead binding on second pass #35118', () => {
@Component({
selector: 'my-cmp',
template: ``,
host: {
'[class.foo]': 'hostClass',
}
})
class MyCmp {
hostClass = true;
}
@Directive({
selector: '[host-styling]',
host: {
'[class]': 'hostClass',
}
})
class HostStylingsDir {
hostClass = {'bar': true};
}
@Component({template: `<my-cmp *ngFor="let i of [1,2]" host-styling></my-cmp>`})
class MyApp {
// When the first view in the list gets CD-ed, everything works.
// When the second view gets CD-ed, the styling has already created the data structures
// in the `TView`. As a result when
// `[class.foo]` runs it already knows that `[class]` is a duplicate and hence it
// should check with it. While the resolution is happening it reads the value of the
// `[class]`, however `[class]` has not yet executed and therefore it does not have
// normalized value in its `LView`. The result is that the assertions fails as it
// expects an
// `KeyValueArray`.
}
TestBed.configureTestingModule({declarations: [MyApp, MyCmp, HostStylingsDir]});
const fixture = TestBed.createComponent(MyApp);
expect(() => fixture.detectChanges()).not.toThrow();
const [cmp1, cmp2] = fixture.nativeElement.querySelectorAll('my-cmp');
expectClass(cmp1).toEqual({foo: true, bar: true});
expectClass(cmp2).toEqual({foo: true, bar: true});
});
});
});
function assertStyleCounters(countForSet: number, countForRemove: number) {