Easy form validation with AngularJS AngularJS 10.10.2015

Error messages

In earlier versions of Angular, showing form validation error message was an exercise in combining ng-if directives and plenty boolean logic to display the right error message at the right time. Version 1.3 introduces the ngMessages module as an improved way to deal with complex validation error messages.

Validating the standard set of HTML5 input fields is now easier than ever because of improved support within Angular. Angular will populate a special property called $error for each field containing the states of validation.

This is the format

<div ng-messages="formName.inputName.$error">
    <p ng-message="validationName">Your message here.</p>
</div>

Example

<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-messages.js"></script>
<script>
    var app = angular.module('TstApp', ['ngMessages']);
</script>
</head>

<body ng-app="TstApp" ng-controller="TstCtrl">
<form name="mf">
    <div class="form-group" ng-class="{ 'has-error': mf.title.$touched && mf.title.$invalid }">
        <input type="text" class="form-control" name="title" ng-model="data.title" minlength="6" required />
        <div ng-messages="mf.title.$error" ng-if="mf.title.$touched">
            <div ng-message="required">This field is required.</div>
            <div ng-message="minlength">The min length is 6.</div>
        </div>
    </div>
</form>
</body>

With ngMessage directive, we can just conditionally display messages by providing it with a name of a validator that is applied to the corresponding form field.

We can define errors in one place and use it everywhere. Template must be remote.

# error-messages.html
<div class="error-messages.html">
    <div ng-message="required">This field is required.</div>
    <div ng-message="minlength">The min length is 3 characters.</div>
    <div ng-message="maxlength">The max length is 10 characters.</div>
    <div ng-message="checkCode">The code must be numbers.</div>
    <div ng-message="checkUser">The user must be unique.</div>
</div>

<div class="form-group" ng-class="{ 'has-error': mf.title.$touched && mf.title.$invalid }">
    <input type="text" class="form-control" name="title" ng-model="data.title" minlength="6" required />
    <div ng-messages="mf.title.$error" ng-if="mf.title.$touched" ng-messages-include="error-messages.html"></div>
</div>

Custom validator

For more advanced needs, Angular now (starting from version 1.3) provides a much easier way to write custom validators using the $validators pipeline.

If, for instance, you want to validate an input such that it can only contain three numbers all you will need to do is create a regular expression.

We can use the regular expression above in conjunction with $validators to validate the input, like so:

HTML

<input type="text" placeholder="code" name="code" ng-model="data.code" required check-code />

<div ng-messages="mf.code.$error" ng-if="mf.code.$touched">
    <div ng-message="required">This field is required.</div>
    <div ng-message="checkCode">The code must be numbers.</div>
</div>

JavaScript

app.directive('checkCode', function() {
  return {
    require: 'ngModel',
    link: function($scope, element, attrs, ngModel) {
      ngModel.$validators.checkCode = function(modelValue, viewValue) {
        var value = modelValue || viewValue;
        return /^[0-9]{3}$/.test(value);
      }
    }
  }
});

When the validator returns false, it will mark the property as $error.

There is also $asyncValidators for when you need to call an http service to check the validity. A promise will be delivered to set the validation status when the http request has completed. All the asynchronous validators must return before the status is updated.

HTML

<input type="text" placeholder="Your name" name="name" ng-model="data.name" required check-user />

<div ng-messages="mf.user.$error" ng-if="mf.user.$touched">
    <div ng-message="required">This field is required.</div>
    <div ng-message="checkUser">Username must be unique.</div>
</div>

JavaScript

app.directive('checkUser', function($q, $http) {
    return {
        require: 'ngModel',
        link: function(scope, element, attributes, ngModel) {
            ngModel.$asyncValidators.checkUser = function(modelValue, viewValue) {
                $http.get('/api/user/' + modelValue).then(function resolved() {
                    return $q.reject('exist');
                }, function rejected() {
                    return $q.resovle();
                });
            };
        }
    };
});

Useful links