From 62d92f8091a487fb94657d964ecea45a63f18e8e Mon Sep 17 00:00:00 2001 From: crisbeto Date: Fri, 30 Aug 2019 20:19:39 +0200 Subject: [PATCH] fix(ivy): unable to bind to properties that start with class or style (#32421) Fixes Ivy picking up property bindings that start with `class` or `style` as if they're style bindings. Fixes #32310 PR Close #32421 --- .../src/render3/view/styling_builder.ts | 7 +-- packages/core/test/acceptance/styling_spec.ts | 44 ++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/compiler/src/render3/view/styling_builder.ts b/packages/compiler/src/render3/view/styling_builder.ts index b7d06f64d4..ccf82f8b3b 100644 --- a/packages/compiler/src/render3/view/styling_builder.ts +++ b/packages/compiler/src/render3/view/styling_builder.ts @@ -149,9 +149,10 @@ export class StylingBuilder { registerInputBasedOnName(name: string, expression: AST, sourceSpan: ParseSourceSpan) { let binding: BoundStylingEntry|null = null; - const nameToMatch = name.substring(0, 5); // class | style - const isStyle = nameToMatch === 'style'; - const isClass = isStyle ? false : (nameToMatch === 'class'); + 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!'); 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/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index 858943f1be..fb3724c7dd 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, ElementRef, Input} from '@angular/core'; +import {Component, Directive, ElementRef, HostBinding, Input, ViewChild} from '@angular/core'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; import {TestBed} from '@angular/core/testing'; import {By, DomSanitizer, SafeStyle} from '@angular/platform-browser'; @@ -631,4 +631,46 @@ describe('styling', () => { }); }); + + it('should be able to name inputs starting with `class` or `style`', () => { + @Directive({selector: '[dir]'}) + class Dir { + @Input('classesInSchool') classes = ''; + @Input('styleOfClothing') style = ''; + } + + @Component({ + template: '', + }) + class App { + @ViewChild(Dir, {static: false}) dir !: Dir; + + classes = 'math'; + style = '80s'; + } + + TestBed.configureTestingModule({declarations: [App, Dir]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const directive = fixture.componentInstance.dir; + + expect(directive.classes).toBe('math'); + expect(directive.style).toBe('80s'); + }); + + it('should be able to bind to `className`', () => { + @Component({template: ''}) + class App { + @HostBinding('className') + klass = 'one two'; + } + + TestBed.configureTestingModule({declarations: [App]}); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const classList = fixture.nativeElement.classList; + + expect(classList.contains('one')).toBe(true); + expect(classList.contains('two')).toBe(true); + }); });