<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Angular Form Validation Demo</title>
  
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.13/angular.min.js"></script>
  <script src="controller.js"></script>
  
  <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body ng-app="FormValidationExample">
<div ng-controller="ctrlForm">
    
  <form name="myForm" novalidate>
    <div class="input-item">
      <label for="txtEmail">Your email</label><br>
      <input type="text" name="email" id="txtEmail" class="text-box" ng-model="email" email-regex required custom-focus />
  
      <div class="error-wrapper" ng-show="myForm.email.$dirty && myForm.email.$invalid && !myForm.email.$focused">
        <span ng-show="myForm.email.$error.required">Email is required</span>
        <span ng-show="myForm.email.$error.emailregex">Please enter a valid email address</span>
      </div>
    </div>

    <div class="input-item">
      <label for="interests">What interests you?</label><br>
      <textarea name="interests" id="interests" class="text-box" ng-model="interests" ng-maxlength="255" ng-minlength="10" required custom-focus></textarea>
      
      <div class="error-wrapper" ng-show="myForm.interests.$dirty && myForm.interests.$invalid">
        <span ng-show="myForm.interests.$error.maxlength">Must be less than 255 characters</span>
        <span ng-show="myForm.interests.$error.minlength && !myForm.interests.$focused">Must be more than 10 characters</span>
      </div>
    </div>
    
    <div class="input-item">
      <label for="txtPets">How many pets do you have?</label><br>
      <input type="number" min="0" pattern="-?\d*" name="pets" id="txtPets" class="text-box" ng-model="pets" required custom-focus />
      
      <div class="error-wrapper" ng-show="myForm.pets.$dirty && myForm.pets.$invalid">
        <span ng-show="myForm.pets.$error.required && myForm.pets.$dirty">Please enter 0 or more</span>
        <span ng-show="myForm.pets.$error.number && myForm.pets.$dirty">Must be numeric</span>
        <span ng-show="myForm.pets.$error.min && myForm.pets.$dirty">You can't have negative pets!</span>
        <span ng-show="myForm.pets.$error.pattern && myForm.pets.$dirty">Must be an integer</span>
      </div>
    </div>
    
    <!-- Bonus check-all demo! -->
    <div class="input-item">
      <label>What are you reading this for?</label>
      <span class="inline-note" ng-show="checkedCount > 0">{{checkedCount}} selected</span><br>
      
      <input type="checkbox" class="select-all" ng-model="checkboxes_motives.checked" ng-change="selectAllMotives(checkboxes_motives.checked)" /><br>
  
      <input type="checkbox" name="motivation" id="play" ng-model="checkboxes_motives.items['play']" 
             ng-change="updateCheckedCount(checkboxes_motives.items['play'])" />
      <label for="play">Play</label>
      <br>
      <input type="checkbox" name="motivation" id="work" ng-model="checkboxes_motives.items['work']" 
             ng-change="updateCheckedCount(checkboxes_motives.items['work'])" />
      <label for="work">Work</label>
    </div>

    
    <input type="submit" value="Submit" ng-disabled="myForm.$invalid" />
  </form>
</div>

</body>
</html>
.input-item {
  margin: 18px 0;
}

.error-wrapper {
  display: inline-block;
  color: red;
}

.inline-note {
  margin-left: 20px;
  color: blue;
}

.text-box {
  width: 200px;
}

.text-box.ng-valid:not(.custom-focused) {
  border-color: green;
}

.text-box.ng-invalid.ng-dirty:not(.custom-focused) {
  border-color: red;
}
Not only does Angular allow you to validate user input using built-in and custom directives, it also automatically furnishes your elements with helpful CSS classes.

Visit http://info.easydynamics.com/blog/angularjs-form-validation to learn about Angular form validation. 
Visit http://info.easydynamics.com/blog/write-custom-angular-directives to learn how to write custom directives.
var myApp = angular.module('FormValidationExample', []);

myApp.controller('ctrlForm', ['$scope', function($scope) {
  $scope.checkboxes_motives = { 'checked': false, items: {} };
  $scope.choices = ['work', 'play'];
      
  $scope.checkedCount = 0;
      
  $scope.selectAllMotives = function(checked) {
    angular.forEach($scope.choices, function (motive) {
      var isChecked = $scope.checkboxes_motives.items[motive];
      $scope.checkboxes_motives.items[motive] = checked;
          
      if (checked != isChecked) {
        $scope.updateCheckedCount(checked);
      }
    });
  };
      
  $scope.updateCheckedCount = function (checked) {
    if (checked) $scope.checkedCount++;
    else $scope.checkedCount--;
  };
      
}]);
  
// Attaches event listeners
myApp.directive('customFocus', [function() {
  var FOCUS_CLASS = "custom-focused"; //Toggle a class and style that!
  return {
    restrict: 'A', //Angular will only match the directive against attribute names
    require: 'ngModel', 
    link: function(scope, element, attrs, ctrl) {
      ctrl.$focused = false;
    
      element.bind('focus', function(evt) {
        element.addClass(FOCUS_CLASS);
        scope.$apply(function() {ctrl.$focused = true;});
    
      }).bind('blur', function(evt) {
        element.removeClass(FOCUS_CLASS);
        scope.$apply(function() {ctrl.$focused = false;});
      });
    }
  }
}]);

// Adds custom validation test to element
myApp.directive('emailRegex', [function (ngModel) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function (scope, linkElement, linkAttributes, controller) {
    
      var regex = /^[a-zA-Z0-9-\_]+[a-zA-Z0-9-_\.]*(\.[a-zA-Z0-9-_]+)*([a-zA-Z0-9]+)@[a-zA-Z]+[0-9a-zA-Z-\.]*[0-9a-zA-Z]+\.[a-zA-Z]{2,5}$/;
    
      function checkValidity(value) {
        var valid = true;
      
        // This directive should consider an empty value to be valid
        // because the required test, if specified, occurs separately
        if (value != null && value.length > 0) {
          valid = regex.test(value);
        }

        return valid;
      }

      controller.$parsers.unshift(function (value) {
        // View to model
        var valid = checkValidity(value);
        
        // Note "emailregex" instead of "emailRegex" - should be all lowercase
        // This allows "$error.emailregex" to work in the DOM
        controller.$setValidity('emailregex', valid);

        // Only return the value to the model if it's valid
        return valid ? value : undefined;
      });

      controller.$formatters.unshift(function (value) {
        // Model to view
        controller.$setValidity('emailregex', checkValidity(value));

        // Return the value regardless of validity,
        // otherwise nothing will appear on the DOM
        return value;
      });
    }  
  };
}]);