Skip to content

ngClass giving removal precedence #17092

Closed

Description

@jehugaleahsa

I'm submitting a ... (check one with "x")

[x ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
Say you have [ngClass]="{'fa fa-thumbs-up': true, 'fa fa-thumbs-down': false}" specified.
Looking at your code, you apply classes where the value is true, then remove classes where the value is false. This means "fa" never appears as a class. I think this leads to surprising behavior. Classes traveling in pairs like this are really common.

Expected behavior
I think it makes more sense to first remove any classes associated with false, then re-add any associated with true, rather than the other way around. The logic is giving removal precedence, even though I see classes with false as should not be applied.

I mean, perhaps, there are people who use this directive the other way around, putting default classes in a "class" attribute and then removing them using this directive, but I would guess they are a rare group. It makes way more sense to add classes via ngClass than to remove them.

Minimal reproduction of the problem with instructions
Check out the ngClass example above.

What is the motivation / use case for changing the behavior?
This isn't the expected behavior and these types of class pairs are common.

Please tell us about your environment:
Windows, webpack

  • Angular version: 2.0.X
    Just ran this against 4.1.3.

  • Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
    all

  • Language: [all | TypeScript X.X | ES6/7 | ES5]
    TypeScript 2.3.3, ES5

Activity

  1. jehugaleahsa commented on May 29, 2017

    @jehugaleahsa
    Author

    "Why not just move the 'fa' out?", you might ask. Something like this: <span class="fa" [ngClass]="{'fa-thumbs-up': isPositive, 'fa-thumbs-down': !isPositive}"></span>. That would certainly solve the problem for some people.

    That makes sense when using the same condition, like isPositive in the example. But, consider this use case:

    You have a form and you want to use classes to indicate when the form is valid or invalid. However, prior to submitting the form, you don't want to apply any classes. You wouldn't want users to get blasted with validation errors the second they open the page. To support that, you create two boolean properties: isValid and isInvalid.

    public get isValid(): boolean {
        return this.form.submitted && this.form.valid;
    }
    public get isInvalid(): boolean {
        return this.form.submitted && this.form.invalid;
    }

    You use bootstrap4 to apply classes to a card, either card-inverse card-success or card-inverse card-danger. The card-inverse class actually turns the text white, making it impossible to see unless one of the other two classes are set. When you try the code below, you run into the issue I am describing:

    <div [ngClass]="{'card-inverse card-success': isValid, 'card-inverse card-danger': isInvalid}"></div>

    When valid, card-inverse is added then removed, and the text remains black on top of a green background, making it hard to see. If you try to move the card-inverse out into a class attribute, the text is always white, even on a white background.

    The workaround is to call a function; something like this:

    <div [ngClass]="getStatusClasses()"></div>
    private getStatusClasses(): string | null {
        if (this.isValid()) {
            return "card-inverse card-success";
        } else if (this.isInvalid()) {
            return "card-inverse card-danger";
        } else {
            return null;
        }
    }

    That works for now but that's a lot of overhead, also forcing class names into the typescript backend rather than in the HTML where I'd prefer it live.

  2. tytskyi commented on May 29, 2017

    @tytskyi

    Don't you find that this

    <div [ngClass]="{'card-inverse card-success': isValid, 'card-inverse card-danger': isInvalid}"></div>
    

    already reads hard?

    IMO this reads more obvious:

    <div 
      [class.card-inverse]="isValid || isInvalid" 
      [class.card-success]="isValid" 
      [class.card-danger]="isInvalid"
    >
    </div>
    

    I think the only good use case for [ngClass] - is when you don't know classnames in advance and you want to generate them in runtime.

  3. jehugaleahsa commented on May 29, 2017

    @jehugaleahsa
    Author

    @tytskyi I definitely prefer that. It is a good way to keep the classes in the HTML, too.

    When I submitted this, I figured there are too many people using ngClass to change its behavior at this point. I figured I'd put this out there so others might come across it and benefit from alternative approaches like the one you just posted. Thanks!

  4. ericmartinezr commented on May 29, 2017

    @ericmartinezr
    Contributor
  5. vicb commented on May 30, 2017

    @vicb
    Contributor

    <div class="card-inverse" [ngClass]="{'card-success': isValid, 'card-danger': isInvalid}"></div>

    should also work

  6. angular-automatic-lock-bot commented on Sep 11, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  7. locked and limited conversation to collaborators on Sep 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions