98

I am trying to work how to add a class with ngClick. I have uploaded up my code onto plunker Click here. Looking at the angular documentation i can't figure out the exact way it should be done. Below is a snippet of my code. Can someone guide me in the right direction

 <div ng-show="isVisible" ng-class="{'selected': $index==selectedIndex}" class="block"></div>

Controller

var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){

        $scope.toggle = function (){
            $scope.isVisible = ! $scope.isVisible;
        };

        $scope.isVisible = false;
    });
Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76
NewKidOnTheBlock
  • 1,491
  • 3
  • 16
  • 32
  • not clear from demo or explnation what objective is. Seem to be trying to toggle a menu, but why aare you only toggling menu link in demo? – charlietfl Dec 08 '13 at 23:14

10 Answers10

144

I want to add or remove "active" class in my code dynamically on ng-click, here what I have done.

<ul ng-init="selectedTab = 'users'">
   <li ng-class="{'active':selectedTab === 'users'}" ng-click="selectedTab = 'users'"><a href="#users" >Users</a></li>
   <li ng-class="{'active':selectedTab === 'items'}" ng-click="selectedTab = 'items'"><a href="#items" >Items</a></li>
</ul>
Mohammad Kermani
  • 5,188
  • 7
  • 37
  • 61
cutedevil086
  • 1,540
  • 2
  • 9
  • 3
  • 13
    -1 for ng-init. According to AngularJS docs - `The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.` – Mike Grabowski Sep 22 '14 at 14:42
  • 2
    I just avoiding the controller part "here", as this is just for showing the basic functionality of how to do that... – cutedevil086 Dec 08 '14 at 18:07
  • 1
    You can also use the undocumented syntax of ` ng-class="{'active': true}[selectedTab === 'users']"` – Cody Dec 29 '14 at 20:36
  • I don't understand why this would work. I'm doing something very similar in Angular 1.3.8 and the conditional class is not removed from an element when another is clicked. I would assume because the other elements are not re-rendered. Why then, did this ever work? Did old versions of Angular re-build the entire list when a single element was clicked? – Matt Molnar Jan 06 '15 at 22:01
  • I'm just adding this because it may help someone else down the line. angular-ui-router has the functionality you are specifying and much more. You create states which are represented by a uri. Each state may have 1 or more controllers, 1 or more templates, and 1 or more views bound to them. Links are generated using the ui-sref directive. The ui-sref-active directive will bind a specific class to that element when the state is active. [Angular UI-Router Documentation](http://angular-ui.github.io/ui-router/site/#/api/ui.router) – deadbabykitten Mar 25 '15 at 23:20
  • I would declare seletedTab in controller. – Kishor Pawar Aug 08 '15 at 14:06
  • @MikeGrabowski I guess the [documentation](https://docs.angularjs.org/api/ng/directive/ngInit) was updated. It now says it can also be used `for injecting data via server side scripting` – Carcamano Apr 14 '16 at 15:33
  • I think this answer is really awesome and I'm using this solution now, but I am not sure for a website that is going to have many tabs in different pages, if is solution the best? Because it can add more lines to DOM and it could be better if we use a functional way... – Mohammad Kermani Jul 18 '16 at 08:20
  • But on reloading the page, active class is set to the default element. – Avinash Raj Nov 22 '16 at 12:30
110

You just need to bind a variable into the directive "ng-class" and change it from the controller. Here is an example of how to do this:

var app = angular.module("ap",[]);

app.controller("con",function($scope){
  $scope.class = "red";
  $scope.changeClass = function(){
    if ($scope.class === "red")
      $scope.class = "blue";
    else
      $scope.class = "red";
  };
});
.red{
  color:red;
}

.blue{
  color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ap" ng-controller="con">
  <div ng-class="class">{{class}}</div>
  <button ng-click="changeClass()">Change Class</button>    
</body>

Here is the example working on jsFiddle

geonunez
  • 1,309
  • 1
  • 9
  • 9
  • 29
    `class` is a reserved word, use `className` instead, YUI compiler will fail to minify this. – Orlando Mar 26 '14 at 21:19
  • 8
    What about if I want use this code for more than one div in the same view? this code actuali change class for all div, how I can apply class only to the selected clicked item – xzegga Apr 24 '14 at 04:44
  • Thanks. To fully understand what is happening when the change Class button is clicked open the console and view the code. – fidev Aug 28 '15 at 09:24
  • 1
    Have a look at this SO thread also. Might not be 100% linked with the question scope but still provides extra useful information: https://stackoverflow.com/questions/31047094/how-to-add-class-name-with-the-existing-class-name – BiLaL Oct 17 '16 at 09:33
12

There is a simple and clean way of doing this with only directives.

<div ng-class="{'class-name': clicked}" ng-click="clicked = !clicked"></div>
artecher
  • 339
  • 3
  • 8
9

you can also do that in a directive, if you want to remove the previous class and add a new class

    .directive('toggleClass', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.bind('click', function() {
                if(element.attr("class") == "glyphicon glyphicon-pencil") {
                    element.removeClass("glyphicon glyphicon-pencil");
                    element.addClass(attrs.toggleClass);
                } else {
                    element.removeClass("glyphicon glyphicon-ok");
                    element.addClass("glyphicon glyphicon-pencil");
                }
            });
        }
    };
});

and in your template:

<i class="glyphicon glyphicon-pencil" toggle-class="glyphicon glyphicon-ok"></i>
Shilan
  • 813
  • 1
  • 11
  • 17
  • why do you have the names of the icons in the tag and the directive? – Robert Johnstone Jul 03 '15 at 08:59
  • That's a dumb comment. It's a perfectly legit thing to do although I agree that it's perhaps not the place for it when you're explaining how to do something in Angular – bert Sep 16 '16 at 15:25
  • why wouldn't you just do: angular.element('glyphicon glyphicon-pencil).removeClass('glyphicon glyphicon-pencil')? angular.element is pretty much angular's jqLite version of $ in jquery. You could just create a service or directive that calls this function and pass in removedClasses and addedClasses in the constructor – MattE Jan 01 '17 at 05:02
  • That's true, but I was trying to use plain angular js. – Shilan Jan 01 '17 at 10:11
7

You have it exactly right, all you have to do is set selectedIndex in your ng-click.

ng-click="selectedIndex = 1"

Here is how I implemented a set of buttons that change the ng-view, and highlights the button of the currently selected view.

<div id="sidebar" ng-init="partial = 'main'">
    <div class="routeBtn" ng-class="{selected:partial=='main'}" ng-click="router('main')"><span>Main</span></div>
    <div class="routeBtn" ng-class="{selected:partial=='view1'}" ng-click="router('view1')"><span>Resume</span></div>
    <div class="routeBtn" ng-class="{selected:partial=='view2'}" ng-click="router('view2')"><span>Code</span></div>
    <div class="routeBtn" ng-class="{selected:partial=='view3'}" ng-click="router('view3')"><span>Game</span></div>
  </div>

and this in my controller.

$scope.router = function(endpoint) {
    $location.path("/" + ($scope.partial = endpoint));
};
Zack Argyle
  • 8,057
  • 4
  • 29
  • 37
4

var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){

        $scope.toggle = function (){
            $scope.isVisible = ! $scope.isVisible;
        };

        $scope.isVisible = false;
    });
<div ng-show="isVisible" ng-class="{'active':isVisible}" class="block"></div>
3

I used Zack Argyle's suggestion above to get this, which I find very elegant:

CSS:

.active {
    background-position: 0 -46px !important;
}

HTML:

<button ng-click="satisfaction = 'VeryHappy'" ng-class="{active:satisfaction == 'VeryHappy'}">
    <img src="images/VeryHappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Happy'" ng-class="{active:satisfaction == 'Happy'}">
    <img src="images/Happy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Indifferent'" ng-class="{active:satisfaction == 'Indifferent'}">
    <img src="images/Indifferent.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Unhappy'" ng-class="{active:satisfaction == 'Unhappy'}">
    <img src="images/Unhappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'VeryUnhappy'" ng-class="{active:satisfaction == 'VeryUnhappy'}">
    <img src="images/VeryUnhappy.png" style="height:24px;" />
</button>
Adriaan Davel
  • 710
  • 1
  • 11
  • 25
2

If you prefer separation of concerns such that logic for adding and removing classes happens on the controller, you can do this

controller

 (function() {
    angular.module('MyApp', []).controller('MyController', MyController);

    function MyController() {
      var vm = this;
      vm.tab = 0;

      vm.setTab = function(val) {
          vm.tab = val;
       };
      vm.toggleClass = function(val) {
          return val === vm.tab;
           };
        }
    })();

HTML

<div ng-app="MyApp">
  <ul class="" ng-controller="MyController as myCtrl">
    <li ng-click="myCtrl.setTab(0)" ng-class="{'highlighted':myCtrl.toggleClass(0)}">One</li>
    <li ng-click="myCtrl.setTab(1)" ng-class="{'highlighted':myCtrl.toggleClass(1)}">Two</li>
    <li ng-click="myCtrl.setTab(2)" ng-class="{'highlighted':myCtrl.toggleClass(2)}">Three</li>
   <li ng-click="myCtrl.setTab(3)" ng-class="{'highlighted':myCtrl.toggleClass(3)}">Four</li>
 </ul>

CSS

.highlighted {
   background-color: green;
   color: white;
}
Dennis Wanyonyi
  • 368
  • 1
  • 5
  • 18
0

I can't believe how complex everyone is making this. This is actually very simple. Just paste this into your html (no directive./controller changes required - "bg-info" is a bootstrap class):

<div class="form-group col-md-12">
    <div ng-class="{'bg-info':     (!transport_type)}"    ng-click="transport_type=false">CARS</div>
    <div ng-class="{'bg-info': transport_type=='TRAINS'}" ng-click="transport_type='TRAINS'">TRAINS</div>
    <div ng-class="{'bg-info': transport_type=='PLANES'}" ng-click="transport_type='PLANES'">PLANES</div>
</div>
-1

for Reactive forms -

HTML file

<div class="col-sm-2">
  <button type="button"  [class]= "btn_class"  id="b1" (click)="changeMe()">{{ btn_label }}</button>
</div>

TS file

changeMe() {
  switch (this.btn_label) {
    case 'Yes ': this.btn_label = 'Custom' ;
    this.btn_class = 'btn btn-danger btn-lg btn-block';
    break;
    case 'Custom': this.btn_label = ' No ' ;
    this.btn_class = 'btn btn-success btn-lg btn-block';
    break;
    case ' No ': this.btn_label = 'Yes ';
      this.btn_class = 'btn btn-primary btn-lg btn-block';
      break;
  }
M Singh
  • 57
  • 1
  • 4