diff --git a/packages/core/src/render3/instructions/styling.ts b/packages/core/src/render3/instructions/styling.ts
index c8d9e0ed67..4e042aa96c 100644
--- a/packages/core/src/render3/instructions/styling.ts
+++ b/packages/core/src/render3/instructions/styling.ts
@@ -261,8 +261,9 @@ export function checkStylingMap(
ngDevMode && isClassBased === false && staticPrefix !== null &&
assertEqual(
staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\'');
- if (typeof value === 'string') {
- value = concatStringsWithSpace(staticPrefix, value as string);
+ if (staticPrefix !== null) {
+ // We want to make sure that falsy values of `value` become empty strings.
+ value = concatStringsWithSpace(staticPrefix, value ? value : '');
}
// Given `
` such that `my-dir` has `@Input('style')`.
// This takes over the `[style]` binding. (Same for `[class]`)
diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts
index cee91e5718..9a9f1bef06 100644
--- a/packages/core/test/acceptance/styling_spec.ts
+++ b/packages/core/test/acceptance/styling_spec.ts
@@ -316,6 +316,52 @@ describe('styling', () => {
expect(div2.getAttribute('shadow-class')).toEqual('s2 d2');
});
+ onlyInIvy('shadow bindings include static portion')
+ .it('should bind [class] as input to directive when both static and falsy dynamic values are present',
+ () => {
+ @Component({
+ template: `
+
+ `
+ })
+ class Cmp {
+ classBinding: any = undefined;
+ }
+
+ @Directive({selector: '[dir-shadows-class-input]'})
+ class DirectiveShadowsClassInput {
+ constructor(private elementRef: ElementRef) {}
+ @Input('class')
+ set klass(value: string) {
+ this.elementRef.nativeElement.setAttribute('shadow-class', value);
+ }
+ }
+
+ TestBed.configureTestingModule({declarations: [Cmp, DirectiveShadowsClassInput]});
+ const fixture = TestBed.createComponent(Cmp);
+ fixture.detectChanges();
+
+ const div = fixture.nativeElement.querySelector('div');
+ expect(div.className).toEqual('s1');
+ expect(div.getAttribute('shadow-class')).toEqual('s1');
+
+ fixture.componentInstance.classBinding = null;
+ fixture.detectChanges();
+ expect(div.className).toEqual('s1');
+ expect(div.getAttribute('shadow-class')).toEqual('s1');
+
+ fixture.componentInstance.classBinding = false;
+ fixture.detectChanges();
+ expect(div.className).toEqual('s1');
+ expect(div.getAttribute('shadow-class')).toEqual('s1');
+
+
+ fixture.componentInstance.classBinding = {toString: () => 'd1'};
+ fixture.detectChanges();
+ expect(div.className).toEqual('s1');
+ expect(div.getAttribute('shadow-class')).toEqual('s1 d1');
+ });
+
modifiedInIvy('shadow bindings include static portion')
.it('should bind [style] as input to directive', () => {
@@ -1113,7 +1159,7 @@ describe('styling', () => {
// instruction. We don't think this is worth it, and we are just going to live
// with this.
);
- expect(capturedClassBindingValue).toEqual(null);
+ expect(capturedClassBindingValue !).toEqual('static-val');
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');