Skip to content

Commit e2ab99b

Browse files
imirkinAndrewKushnir
authored andcommitted
fix(common): allow null/undefined to be passed to ngClass input (#39280) (#46906)
With strict template type checking, a null/undefined value will raise an error. However the implementation is completely fine with it, and it would be pointless to "fix" it at the callsite and convert to e.g. an empty string. Allow all of the "supported types" to be passed in directly to ngClass. Fixes #39280 PR Close #46906
1 parent f364378 commit e2ab99b

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

goldens/public-api/common/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ export class NgClass implements DoCheck {
403403
// (undocumented)
404404
set ngClass(value: string | string[] | Set<string> | {
405405
[klass: string]: any;
406-
});
406+
} | null | undefined);
407407
// (undocumented)
408408
ngDoCheck(): void;
409409
// (undocumented)

packages/common/src/directives/ng_class.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class NgClass implements DoCheck {
6161
}
6262

6363
@Input('ngClass')
64-
set ngClass(value: string|string[]|Set<string>|{[klass: string]: any}) {
64+
set ngClass(value: string|string[]|Set<string>|{[klass: string]: any}|null|undefined) {
6565
this._removeClasses(this._rawClass);
6666
this._applyClasses(this._initialClasses);
6767

packages/common/test/directives/ng_class_spec.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
115115
detectChangesAndExpectClassName('bar');
116116
}));
117117

118+
it('should remove active classes when expression evaluates to undefined', waitForAsync(() => {
119+
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
120+
121+
detectChangesAndExpectClassName('foo');
122+
123+
getComponent().objExpr = undefined;
124+
detectChangesAndExpectClassName('');
125+
126+
getComponent().objExpr = {'foo': false, 'bar': true};
127+
detectChangesAndExpectClassName('bar');
128+
}));
118129

119130
it('should allow multiple classes per expression', waitForAsync(() => {
120131
fixture = createTestComponent('<div [ngClass]="objExpr"></div>');
@@ -246,6 +257,15 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
246257
detectChangesAndExpectClassName('');
247258
}));
248259

260+
it('should remove active classes when switching from string to undefined',
261+
waitForAsync(() => {
262+
fixture = createTestComponent(`<div [ngClass]="strExpr"></div>`);
263+
detectChangesAndExpectClassName('foo');
264+
265+
getComponent().strExpr = undefined;
266+
detectChangesAndExpectClassName('');
267+
}));
268+
249269
it('should take initial classes into account when switching from string to null',
250270
waitForAsync(() => {
251271
fixture = createTestComponent(`<div class="foo" [ngClass]="strExpr"></div>`);
@@ -255,6 +275,15 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
255275
detectChangesAndExpectClassName('foo');
256276
}));
257277

278+
it('should take initial classes into account when switching from string to undefined',
279+
waitForAsync(() => {
280+
fixture = createTestComponent(`<div class="foo" [ngClass]="strExpr"></div>`);
281+
detectChangesAndExpectClassName('foo');
282+
283+
getComponent().strExpr = undefined;
284+
detectChangesAndExpectClassName('foo');
285+
}));
286+
258287
it('should ignore empty and blank strings', waitForAsync(() => {
259288
fixture = createTestComponent(`<div class="foo" [ngClass]="strExpr"></div>`);
260289
getComponent().strExpr = '';
@@ -275,6 +304,9 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
275304

276305
getComponent().objExpr = null;
277306
detectChangesAndExpectClassName('init foo');
307+
308+
getComponent().objExpr = undefined;
309+
detectChangesAndExpectClassName('init foo');
278310
}));
279311

280312
it('should co-operate with the interpolated class attribute', waitForAsync(() => {
@@ -289,6 +321,9 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
289321

290322
getComponent().objExpr = null;
291323
detectChangesAndExpectClassName(`init foo`);
324+
325+
getComponent().objExpr = undefined;
326+
detectChangesAndExpectClassName(`init foo`);
292327
}));
293328

294329
it('should co-operate with the interpolated class attribute when interpolation changes',
@@ -315,6 +350,9 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
315350

316351
getComponent().objExpr = null;
317352
detectChangesAndExpectClassName(`init foo`);
353+
354+
getComponent().objExpr = undefined;
355+
detectChangesAndExpectClassName(`init foo`);
318356
}));
319357

320358
it('should co-operate with the class attribute and class.name binding', waitForAsync(() => {
@@ -351,6 +389,9 @@ import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
351389

352390
cmp.objExpr = null;
353391
detectChangesAndExpectClassName('init baz');
392+
393+
cmp.objExpr = undefined;
394+
detectChangesAndExpectClassName('init baz');
354395
}));
355396
});
356397

@@ -419,8 +460,8 @@ class TestComponent {
419460
items: any[]|undefined;
420461
arrExpr: string[] = ['foo'];
421462
setExpr: Set<string> = new Set<string>();
422-
objExpr: {[klass: string]: any}|null = {'foo': true, 'bar': false};
423-
strExpr: string|null = 'foo';
463+
objExpr: {[klass: string]: any}|null|undefined = {'foo': true, 'bar': false};
464+
strExpr: string|null|undefined = 'foo';
424465

425466
constructor() {
426467
this.setExpr.add('foo');

0 commit comments

Comments
 (0)