ngClass expressions in AngularJS

Arjan Wulder

AngularJSThe ngClass directive allows you to dynamically set CSS classes by databinding an expression. The documentation on angularjs.org is pretty clear how to write different kind of expressions but not everything is documented. I will briefly explain the documented expressions and also the one that is not documented.



String syntax

The string syntax is straightforward, the value of the input element will directly added as a CSS class on the legend element.

<!DOCTYPE html>
<html ng-app>

  <head>
    <link data-require="bootstrap-css@3.0.3" data-semver="3.0.3" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
    <script data-require="angular.js@1.2.10" data-semver="1.2.10" src="http://code.angularjs.org/1.2.10/angular.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="row">
        <form role="form">
          <fieldset>
            <legend ng-class="text">String syntax</legend>
            <div class="form-group">
              <input class="form-control" ng-model="text" placeholder="Type: text-info, text-warning or text-danger"><br>
            </div>
          </fieldset>
        </form>
      </div>
    </div>
  </body>

</html>


Array syntax

The array syntax behaves the same as the string syntax but that it allows you to add more then one CSS class on a single HTML element.

<!DOCTYPE html>
<html ng-app>

  <head>
    <link data-require="bootstrap-css@3.0.3" data-semver="3.0.3" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
    <script data-require="angular.js@1.2.10" data-semver="1.2.10" src="http://code.angularjs.org/1.2.10/angular.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="row">
        <form role="form">
          <fieldset>
            <legend ng-class="[label, text]">Array syntax</legend>
            <div class="form-group">
              <input class="form-control" ng-model="label" placeholder="Type: label-info, label-warning or label-danger"><br>
              <input class="form-control" ng-model="text" placeholder="Type: text-muted or text-success"><br>
            </div>
          </fieldset>
        </form>
      </div>
    </div>
  </body>

</html>


Map syntax

The map syntax allows you to set the CSS class based on comma separated key-value pairs. In the following example the CSS class label-info is added when the value of info is true. If the values of info and muted are both true then both CSS classes are added. So for all the expressions whose values are true the CSS class is added.

<!DOCTYPE html>
<html ng-app>

  <head>
    <link data-require="bootstrap-css@3.0.3" data-semver="3.0.3" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
    <script data-require="angular.js@1.2.10" data-semver="1.2.10" src="http://code.angularjs.org/1.2.10/angular.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="row">
        <fieldset>
          <legend ng-class="{'label-info': info, 'text-muted': muted}">Map syntax</legend>
          <div class="form-group">
            <input type="checkbox" ng-model="info"> info (apply "label-info" class)<br>
            <input type="checkbox" ng-model="muted"> muted (apply "text-muted" class)
        </fieldset>
      </div>
    </div>
  </body>

</html>


Undocumented expression syntax

The previous examples are well documented and the examples speak for themselves. But what if you want to mark a required input element of a form invalid after you have submitted the form? Though it is not documented it is possible.

First we have to keep track if the form is submitted. When the controller is invoked the first time the scope variable submitted is set to false. After the form is submitted the scope variable submitted is set to true. After that we check if the form is invalid. If the form is invalid we return to the page or else we can do something with the form data like posting it to a backend for example.

'use strict';

angular.module('myApp', []).
  controller('MyAppCtrl', function() {
    this.submitted = false;
    
    var self = this;
    this.submit = function(form) {
      self.submitted = true;
      if (form.$invalid) {
        return;
      } else {
        // Do something with the form like posting it to the backend
      }
    }
  });

So how can we write an expression for the ngClass directive that checks if the scope variable submitted is true and if the value of the input element is invalid? The HTML provides the solution.

<!doctype html>
<html ng-app="myApp">
  <head>
    <link src="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="http://code.angularjs.org/1.2.10/angular.min.js"></script>
  </head>
  <body ng-controller="MyAppCtrl as ctrl">
    <div class="container">
      <div class="row">
        <form class="form-horizontal" name="myForm" novalidate>
          <fieldset>
            <div class="form-group" ng-class="{true: 'has-error'}[ctrl.submitted && myForm.myField.$error.required]">
              <label for="myField" class="control-label">My Field</label>
              <input type="text" name="myField" class="form-control" id="myField" ng-model="myField" required/>
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-primary" ng-click="ctrl.submit(myForm)">Save</button>
            </div>
          </fieldset>
        </form>
      </div>  
    </div>
    <script src="script.js"></script>
  </body>
</html>

How does it work? The expression between the square brackets is evaluated. If the result of that expression is equal to true, then the CSS class has-error is added. That is it. To see the code in action go to this Plunker

Comments (3)

  1. Bastien - Reply

    January 31, 2014 at 1:58 pm

    Actually, you can directly write your condition in the map syntax:

    {'has-error': ctrl.submitted && myForm.myField.$error.required}

    See it in this plunker:
    http://plnkr.co/edit/xDIzC0A50cXxMpIHeP3C

    • Vibhor Mahajan - Reply

      February 10, 2014 at 7:11 pm

      +1

  2. Arjan Wulder - Reply

    January 31, 2014 at 2:39 pm

    Nice! I've tried that once but it didn't work. Maybe I did something wrong or that it depends on the angular version you use, I don't know but I will try to figure it out.

Add a Comment