Skip to content

feat(migrations): add migration to convert ngClass to use class #61971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

akib1997
Copy link
Contributor

@akib1997 akib1997 commented Jun 9, 2025

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

Added Migration Feature to migrate ngClass to class

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.dev application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Added Migration Feature to migrate ngClass to class

Issue Number:
#61661

What is the new behavior?

It'll migrate ngClass to class bindings to follow the style guide Prefer class over ngClass

Now
<div [class.admin]="isAdmin" [class.dense]="density === 'high'">

Before
<div [ngClass]="{admin: isAdmin, dense: density === 'high'}">

Does this PR introduce a breaking change?

  • Yes
  • No

@pullapprove pullapprove bot requested a review from devversion June 9, 2025 14:22
@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: migrations Issues related to `ng update` migrations labels Jun 9, 2025
@ngbot ngbot bot added this to the Backlog milestone Jun 9, 2025
Comment on lines +51 to +52
before: `<div [ngClass]="{'class1': condition1, 'class2': condition2}"></div>`,
after: `<div [class.class1]="condition1" [class.class2]="condition2"></div>`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, it should be <div [class]="{'class1': condition1, 'class2': condition2}"></div>

Comment on lines +64 to +66
await verifyDeclaration({
before: `<div [ngClass]="{'class1 class2': condition}"></div>`,
after: `<div [class.class1]="condition" [class.class2]="condition"></div>`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should discuss if this migration should be behind a flag.
It can be far from ideal in projects with tailwind when there are several classnames in the key. (could be 10+)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, how should we discuss it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll discuss it with the team soon, we'll let you know.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this shouldn't be migrated by default.
Maybe we can do that in a follow-up P and behind a prompt with "no" by default.

Comment on lines +57 to +59
await verifyDeclaration({
before: `<div [ngClass]="{'admin-panel': isAdmin, 'user-dense': isDense}"></div>`,
after: `<div [class.admin-panel]="isAdmin" [class.user-dense]="isDense"></div>`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same should be <div [class]="{'admin-panel': isAdmin, 'user-dense': isDense}"></div>

@akib1997
Copy link
Contributor Author

akib1997 commented Jun 9, 2025

@JeanMeche Thanks for the feedback. I'll resolve them.

Comment on lines +138 to +142
function tryParseStaticObjectLiteral(expr: string): {key: string; value: string}[] | null {
// Basic object literal regex: matches `{ key: value, ... }`
const objectLiteralRegex = /^\s*\{\s*([^}]*)\s*\}\s*$/;
const match = expr.match(objectLiteralRegex);
if (!match) return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done using the AST, not a regex.

it('should migrate single condition', async () => {
await verifyDeclaration({
before: `<div [ngClass]="{'active': isActive}"></div>`,
after: `<div [class.active]="isActive"></div>`,
Copy link
Member

@JeanMeche JeanMeche Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As highlighted by #45734, this is indeed the right change if there is a single key.

Comment on lines +115 to +117
it('should not migrate object with spread syntax', async () => {
await verifyDeclarationNoChange(`<div [ngClass]="{foo: true, ...other}"></div>`);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw, this is not a valid angular syntax. Template don't support the spread operator.

await verifyDeclarationNoChange(`<div [ngClass]="{foo isActive}"></div>`);
});

it('should not migrate string literal class list', async () => {
Copy link
Member

@JeanMeche JeanMeche Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be migrated. [class]="'class1 class2'" is valid.
Ideally this should even be class="class1 class2" Either we need to make sure that we merge with existing class decleration.

await verifyDeclarationNoChange(`<div [ngClass]="{foo: true, ...other}"></div>`);
});
});
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also have unit tests that cover the removal of unused NgClass imports

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: migrations Issues related to `ng update` migrations detected: feature PR contains a feature commit
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants