diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts
index 46252453c8..2ac2d6114e 100644
--- a/packages/compiler/src/render3/view/styling_builder.ts
+++ b/packages/compiler/src/render3/view/styling_builder.ts
@@ -202,8 +202,7 @@ export class StylingBuilder {
let binding: BoundStylingEntry|null = null;
const prefix = name.substring(0, 6);
const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
- const isClass = !isStyle &&
- (name === 'class' || name === 'className' || prefix === 'class.' || prefix === 'class!');
+ const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
if (isStyle || isClass) {
const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
diff --git a/packages/core/src/render3/instructions/property.ts b/packages/core/src/render3/instructions/property.ts
index dda96ec053..e02a8808a4 100644
--- a/packages/core/src/render3/instructions/property.ts
+++ b/packages/core/src/render3/instructions/property.ts
@@ -53,6 +53,5 @@ export function setDirectiveInputsWhichShadowsStyling(
const inputs = tNode.inputs !;
const property = isClassBased ? 'class' : 'style';
// We support both 'class' and `className` hence the fallback.
- const stylingInputs = inputs[property] || (isClassBased && inputs['className']);
- setInputsForProperty(tView, lView, stylingInputs, property, value);
+ setInputsForProperty(tView, lView, inputs[property], property, value);
}
diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts
index ff00a5679c..8c0fa14d48 100644
--- a/packages/core/src/render3/instructions/shared.ts
+++ b/packages/core/src/render3/instructions/shared.ts
@@ -907,7 +907,7 @@ function initializeInputAndOutputAliases(tView: TView, tNode: TNode): void {
}
if (inputsStore !== null) {
- if (inputsStore.hasOwnProperty('class') || inputsStore.hasOwnProperty('className')) {
+ if (inputsStore.hasOwnProperty('class')) {
tNode.flags |= TNodeFlags.hasClassInput;
}
if (inputsStore.hasOwnProperty('style')) {
diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts
index a73e8e2fa5..02397afb41 100644
--- a/packages/core/test/acceptance/styling_spec.ts
+++ b/packages/core/test/acceptance/styling_spec.ts
@@ -1129,17 +1129,17 @@ describe('styling', () => {
});
onlyInIvy('only ivy combines static and dynamic class-related attr values')
- .it('should write to a `className` input binding, when static `class` is present', () => {
+ .it('should write combined class attribute and class binding to the class input', () => {
@Component({
selector: 'comp',
template: `{{className}}`,
})
class Comp {
- @Input() className: string = '';
+ @Input('class') className: string = '';
}
@Component({
- template: ``,
+ template: ``,
})
class App {
}
@@ -1150,32 +1150,6 @@ describe('styling', () => {
expect(fixture.debugElement.nativeElement.firstChild.innerHTML).toBe('static my-className');
});
- onlyInIvy('in Ivy [class] and [className] bindings on the same element are not allowed')
- .it('should throw an error in case [class] and [className] bindings are used on the same element',
- () => {
- @Component({
- selector: 'comp',
- template: `{{class}} - {{className}}`,
- })
- class Comp {
- @Input() class: string = '';
- @Input() className: string = '';
- }
- @Component({
- template: ``,
- })
- class App {
- }
-
- TestBed.configureTestingModule({declarations: [Comp, App]});
- expect(() => {
- const fixture = TestBed.createComponent(App);
- fixture.detectChanges();
- })
- .toThrowError(
- '[class] and [className] bindings cannot be used on the same element simultaneously');
- });
-
onlyInIvy('only ivy persists static class/style attrs with their binding counterparts')
.it('should write to a `class` input binding if there is a static class value and there is a binding value',
() => {
@@ -3475,6 +3449,44 @@ describe('styling', () => {
expectClass(cmp1).toEqual({foo: true, bar: true});
expectClass(cmp2).toEqual({foo: true, bar: true});
});
+
+ it('should not bind [class] to @Input("className")', () => {
+ @Component({
+ selector: 'my-cmp',
+ template: `className = {{className}}`,
+ })
+ class MyCmp {
+ @Input()
+ className: string = 'unbound';
+ }
+ @Component({template: ``})
+ class MyApp {
+ }
+
+ TestBed.configureTestingModule({declarations: [MyApp, MyCmp]});
+ const fixture = TestBed.createComponent(MyApp);
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toEqual('className = unbound');
+ });
+
+ it('should not bind class to @Input("className")', () => {
+ @Component({
+ selector: 'my-cmp',
+ template: `className = {{className}}`,
+ })
+ class MyCmp {
+ @Input()
+ className: string = 'unbound';
+ }
+ @Component({template: ``})
+ class MyApp {
+ }
+
+ TestBed.configureTestingModule({declarations: [MyApp, MyCmp]});
+ const fixture = TestBed.createComponent(MyApp);
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toEqual('className = unbound');
+ });
});
});