Angular 1 ng-class doesn't work as expected

335 Views Asked by At

I have a simple ng-class that switches two classes based on the condition. When the class is switched, the order of the classes is messed up not sure why. Has anyone a solution for this?

<div class="ui" ng-class="{'two column grid' : submitNow, 'one column grid' : defaultState}"></div>

Rendered HTML when submitNow is true. This works as expected

<!-- submitNow is true -->
<div class="ui ng-scope two column grid"></div>

Rendered HTML when defaultState is true. This messes up the order of classes added by ng-class

<!-- defaultState is true -->
<div class="ui ng-scope column grid one"></div>

*** Edit ****

Quite strange because it works on jsfiddle. But here's the screenshot of my rendered html code

enter image description here

Here is a demo

https://codepen.io/vibwaj/pen/KKPBdNp

2

There are 2 best solutions below

5
charlietfl On

OK...looking at the style rules in elements inspector, semantic ui uses selectors like .ui[class*="two column"].grid > .row > .column

Not sure why they do it that way which is unusual and does make the order important.

Also not sure if it is angular or the browser that sorts the order of those classes. I suspect it is the browser, but that is a guess.

Rather than try to figure out what causes the sort you can add the following rule to fix layout for non specific class order.

.ui.two.column.grid > .row > .column, 
.ui.two.column.grid > .column:not(.row){
  width:50%!important;
}

Working codepen

3
yazantahhan On

Update:

I didn't notice the semantic UI framework that is using this approach. If you still need the same approach, you can check the forked Codepen which I created a custom directive to be alternative than the original NgClass directive.

app.directive("myNgClass", function() {
  return {
    link: function(scope, element, attrs) {
      scope.$watch(
        function() {
          return attrs.isExpand;
        },
        function(isExpand) {
          element.removeAttr("class");
          if (JSON.parse(isExpand)) {              
            element.addClass("two column grid");
          } else {
            element.addClass("one column grid");
          }
        }
      );
    }
  };
});

Original Answer

So diving deep into the NgClass directive implementation in Angularjs source code and checking how they update the classes, there is a function called updateClasses. In this function, it finds which classes should be removed and added.

Instead of replacing all the classes when the Boolean flag gets inverted, NgClass keeps the overlapping classes and checks which classes should be added / removed.

So in your case,one column grid (the default case) and two column grid have the column grid classes in common, so it will keep them, but remove the one from the start and add two at the end. So the result will be column grid one.

I really don't suggest to use the order of the classes as CSS selectors. This will make it more harder to select elements and make things more complex.

I also have a comment regarding the CSS selectors that you are using. I really suggest you to read Keep your CSS selectors short article so you can have a better practice of using shorter selector and why keeping the CSS selectors short can help with many things.

For example, If you don't need the one, column and grid classes seprately, you can just use .one-column-grid as a class name / CSS Selector instead of .one.column.grid.