Skip to content

Commit e17d83c

Browse files
committed
feat(migrations): add migration to convert ngClass to use class
feat #61661 - add migration to convert ngClass to use class
1 parent c96deb3 commit e17d83c

File tree

4 files changed

+28
-20
lines changed

4 files changed

+28
-20
lines changed

adev/src/content/reference/migrations/ngclass-to-class.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Migration to ngclass to class
1+
# Migration from NgClass to class bindings
22

3-
This schematic migrates the ngClass to class in your application.
3+
This schematic migrates NgClass directive usages to class bindings in your application.
4+
It will only migrate usages that are considered safe to migrate.
45

56
Run the schematic using the following command:
67

@@ -19,4 +20,4 @@ ng generate @angular/core:ngclass-to-class
1920
#### After
2021

2122
```html
22-
<div [class.admin]="isAdmin" [class.dense]="density === 'high'">
23+
<div [class]="{admin: isAdmin, dense: density === 'high'}">

packages/core/schematics/migrations/ngclass-to-class-migration/ngclass-to-class-migration.spec.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,22 @@ describe('ngClass migrator', () => {
2525
await verifyDeclarationNoChange(`<div [class.active]="isActive"></div>`);
2626
});
2727

28-
it('should not change empty ngClass binding', async () => {
29-
await verifyDeclarationNoChange(`<div [ngClass]="{}"></div>`);
28+
it('should change empty ngClass binding', async () => {
29+
await verifyDeclaration({
30+
before: `<div [ngClass]="{}"></div>`,
31+
after: `<div [class]=""></div>`,
32+
});
3033
});
3134

3235
it('should not change ngClass with empty string key', async () => {
3336
await verifyDeclarationNoChange(`<div [ngClass]="{'': condition}"></div>`);
3437
});
3538

36-
it('should not change ngClass with empty array', async () => {
37-
await verifyDeclarationNoChange(`<div [ngClass]="[]"></div>`);
39+
it('should change ngClass with empty array', async () => {
40+
await verifyDeclaration({
41+
before: `<div [ngClass]="[]"></div>`,
42+
after: `<div [class]=""></div>`,
43+
});
3844
});
3945
});
4046

packages/core/schematics/migrations/ngclass-to-class-migration/util.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -200,10 +200,14 @@ export class NgClassCollector extends RecursiveVisitor {
200200

201201
const staticMatch = tryParseStaticObjectLiteral(expr);
202202

203-
if (staticMatch && staticMatch.length > 0) {
204-
const replacement = staticMatch
205-
.map(({key, value}) => `[class.${key}]="${value}"`)
206-
.join(' ');
203+
if (staticMatch !== null) {
204+
let replacement: string;
205+
206+
if (staticMatch.length === 0) {
207+
replacement = '[class]=""';
208+
} else {
209+
replacement = staticMatch.map(({key, value}) => `[class.${key}]="${value}"`).join(' ');
210+
}
207211

208212
this.replacements.push({
209213
start: attr.sourceSpan.start.offset,
@@ -221,14 +225,12 @@ export class NgClassCollector extends RecursiveVisitor {
221225
function tryParseStaticObjectLiteral(expr: string): {key: string; value: string}[] | null {
222226
const trimmedExpr = expr.trim();
223227

224-
// Early validation
225-
if (!isObjectLiteralSyntax(trimmedExpr)) {
226-
return null;
228+
if (trimmedExpr === '{}' || trimmedExpr === '[]') {
229+
return [];
227230
}
228231

229-
// Handle empty object
230-
if (trimmedExpr === '{}') {
231-
return [];
232+
if (!isObjectLiteralSyntax(trimmedExpr)) {
233+
return null;
232234
}
233235

234236
try {

packages/core/schematics/ng-generate/ngclass-to-class-migration/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
# ngClass to class migration
3-
This schematic helps developers to convert ngClass to class attributes.
4-
This is a purely aesthetic change and does not affect the behavior of the application.
3+
This schematic helps developers to convert ngClass directive usages to class bindings where possible.
54

65
## How to run this migration?
76
The migration can be run using the following command:
@@ -26,5 +25,5 @@ Example:
2625
<div [ngClass]="{admin: isAdmin, dense: density === 'high'}">
2726

2827
<!-- After -->
29-
<div [class.admin]="isAdmin" [class.dense]="density === 'high'">
28+
<div [class]="{admin: isAdmin, dense: density === 'high'}">
3029
```

0 commit comments

Comments
 (0)