How can I add a class to an input if the input is changed with AngularJS?

14,833

Solution 1

There are two good ways to approach this problem:

1. Use the built-in ng-dirty class that Angular puts on the element.

When you change an input managed by Angular, it adds some CSS classes to the input for various states. These include:

  • ng-pristine - the input has not been modified
  • ng-dirty - the input has been modified

So, if you can modify your CSS to be based off the .ng-dirty class, you're good to go.

2. Use a form directive with the $dirty flag.

When you use a form element, Angular assigns a FormController instance on the scope with the same name as the name attribute on the form; each input inside the form gets attached to that FormController instance as a property, again with the same name as the name attribute on the input. For example,

<form name="myForm">
  <input type="text" name="myInput">
</form>

gives you

$scope.myForm.myInput

Each input property has some of its own properties on it, including $pristine and $dirty; these work just like the CSS classes listed above. Thus, you can check for the $dirty flag on the input and use ng-class to conditionally apply a class to the element. An example:

<div ng-controller="MainController">
  <form name="myForm">
    <input name="myInput" ng-model="model" ng-maxlength="3"
      ng-class="{changed: myForm.myInput.$dirty}">
  </form>
</div>

You can find a working example here: http://jsfiddle.net/BinaryMuse/BDB5b/

Solution 2

Take a look at this jsfiddle: http://jsfiddle.net/hNrEV/2/

The main idea is using $scope.$watch to watch for changes to the input box. I gave it an id of rowTitle, and used a directive called watchRowTitle that watches for changes to $scope.row.title, and adds a class 'red' that colors the text red whenever the text in the input box is equal to 'wrong title'.

It is probably good practice to do DOM manipulation in directives. Here, the watchRowTitle directive returns an object with 4 keys:

  • template - the html that replaces the watch-row-title tag. we dont need this here
  • scope - Here we make use of an isolated scope. Basically, the '=' establishes a 2-way data binding between between scope.title inside the watch-row-title directive and the $scope.row.title value inside the MyCtrl controller.
  • restrict - We give it a value of E, which stands for element. So this restricts the use of the watch-row-title directive within html tags, in other words: <watch-row-title></watch-row-title>
  • link - this is the link function, where the interesting stuff happens. In here, we use scope.$watch on title. We have to supply a function with 2 parameters newValue and oldValue (you can name them to something else, but naming them this way is more meaningful), that holds the new and old values of the variable being watched. Whenever the scope.title variable becomes the string 'wrong title', it adds the CSS class 'red' to the input box with id rowTitle (notice how the text in the input box turns red). Otherwise, it removes that CSS class. This portion is done using JQuery.

HTML:

<div ng-app="myApp" ng-controller="MyCtrl">
    <input id="rowTitle" type="text" ng-model="row.title" class="ng-pristine ng-valid" />
    <watch-row-title title="row.title"></watch-row-title>
</div>

CSS:

.red {
    color: red;
}

JavaScript:

angular.module('myApp', [])
    .controller('MyCtrl', [
        '$scope',
        function ($scope) {
            $scope.row = {};
        }
    ])
    .directive('watchRowTitle', [
        function () {
            return {
                template: '',
                scope: {
                    title: '='
                },
                restrict: 'E',
                link: function(scope, element, attr) {
                    scope.$watch('title', function(newValue, oldValue) {
                        if (newValue === 'wrong title') {
                            $('#rowTitle').addClass('red');
                        } else {
                            $('#rowTitle').removeClass('red');
                        }
                    });
                }
            };
    }
]);
Share:
14,833

Related videos on Youtube

Admin
Author by

Admin

Updated on September 15, 2022

Comments

  • Admin
    Admin over 1 year

    I coded the following in my form:

    <td><input type="text" ng-model="row.title" /></td>
    

    When I look at my DOM with Chrome developer tools I see the following:

    <input type="text" ng-model="row.title" class="ng-pristine ng-valid">
    

    How can I make it so that when there is a change made to the input that the input has a class added to it?

  • Ivan Chernykh
    Ivan Chernykh almost 11 years
    and what if you haven't jQuery?
  • EpokK
    EpokK almost 11 years
    You must have lite jQuery or Zepto with AngularJS ;)
  • Ivan Chernykh
    Ivan Chernykh almost 11 years
    absolutely no , but what you have is jqLite. so the question actually is how to do what you did with jqLite ?
  • EpokK
    EpokK almost 11 years
    "Does Angular use the jQuery library? Yes, Angular can use jQuery if it's present in your app when the application is being bootstrapped. If jQuery is not present in your script path, Angular falls back to its own implementation of the subset of jQuery that we call jQLite."
  • Ivan Chernykh
    Ivan Chernykh almost 11 years
    exactly. so i'm asking how can you do it with jqLite ?
  • Michelle Tilley
    Michelle Tilley almost 11 years
    From the AngularJS FAQ "Common Pitfalls": "Stop trying to use jQuery to modify the DOM in controllers. Really. That includes adding elements, removing elements, retrieving their contents, showing and hiding them. Use built-in directives, or write your own where necessary, to do your DOM manipulation."
  • Irshu
    Irshu about 8 years
    It's an anti-pattern to manipulate dom inside the controller.