Skip to main content Skip to footer

Easy Form Validation in AngularJS

HTML5 validation is fairly simple. It consists of three steps:

  1. Specify the "type" for each input field on a form,
  2. Add a few attributes that establish constraints on the value of each field, and
  3. Add a CSS rule for the pseudo-selector ":invalid".

For example, the HTML snippet below represents a simple form with basic HTML5 validation:

<form>
    <input placeholder="User Name" name="name" required pattern=".{2,}" />
    <input placeholder="Email" name="email" required type="email" />
    <input placeholder="Password" name="pwd" required pattern=".{2,}" type="password" />
    <input placeholder="Check Password" name="chk" type="password" />
    <button type="submit">Create Account</button>
</form>

And here is the CSS rule applied to invalid elements:

input:invalid {
    border: 1px solid red;
}

Once you do that, two things happen:

  1. The style specified by the CSS rule will be applied to all elements that have invalid content, and
  2. When you press the "submit" button on the form, the browser will show an error message on the first invalid field instead of submitting the form.

The image below shows the form in action, after entering some data and pressing the submit button:

Wijmo HTML5 Form Validation

This mechanism is simple and works well, but it's limited. For example, there's no simple way to specify that the "check password" field must contain the same value as the "password" field. There's also little room for customization other than the CSS used to show invalid fields. Finally, some validation attributes are not well-supported by all browsers (for example, IE does not support the "minlength" attribute, so we use "pattern" instead).

Adding Custom Validation with AngularJS

Angular does not provide validation directives that can be used in this case, but it makes it easy to create one. The wijmo.angular.js module includes this wjValidationError directive that allows you to specify validation rules using JavaScript expressions:

// Custom validator based on expressions.
// see: https://docs.angularjs.org/guide/forms
app.directive('wjValidationError', function () {
  return {
    require: 'ngModel',
    link: function (scope, elm, attrs, ctl) {
      scope.$watch(attrs['wjValidationError'], function (errorMsg) {
        elm[0].setCustomValidity(errorMsg);
        ctl.$setValidity('wjValidationError', errorMsg ? false : true);
      });
    }
  };
});

The directive is used as an attribute added to input elements. The attribute value is an expression that returns an error message. Empty strings indicate there's no error. Here's a revised version of the form that uses the wjValidationError directive:

<form name="form">
    <input placeholder="User Name"
           name="name" ng-model="theName"
           required pattern=".{2,}" />
    <input placeholder="Email"
           name="email" ng-model="theEmail"
           type="email" required />
    <input placeholder="Password"
           name="pwd" ng-model="thePwd"
           type="password" pattern=".{2,}" required />
    <input placeholder="Check Password"
           name="chk" ng-model="chkPwd"
           type="password"
           wj-validation-error="chkPwd != thePwd ? 'Passwords don't match' : ''" />
    <button type="submit">
        Create Account
    </button>
</form>

This version of the form adds ngModel directives to all input elements so the controller can access their values. And it uses the wjValidationError directive to specify that the 'chk' field is valid only if the password values match. Now check what happens when the user tries to create an account but misses the password check: Wijmo HTML5 Validation Error Our custom error appears exactly like the standard ones. The wjValidationError directive is flexible and easy to use, and it integrates perfectly with basic HTML5 validation. If you want to customize validation even further, you can take advantage of Angular's form validation system.

Switching to Angular Validation

Angular extends basic HTML5 validation with a powerful and flexible mechanism. In Angular, a form is an instance of FormController, and it can be published into the scope using the "name" attribute. Similarly, input controls that have an ngModel directive can be published as a property of the form instance using the "name" attribute on the input control. So when you add name attributes to forms and input controls, Angular automatically adds variables to the scope that describe the element's validation status. For example, the "form.name.$invalid" expression returns a value that indicates whether the "name" field is in an invalid state. Similarly, the "form.name.$pristine" expression returns a value that indicates whether the "name" field has been modified by the user. You can take advantage of these variables and expressions to create forms that show error messages on the form as the user types into the fields, and to disable the submit button until the form is valid. Let's update our form to take advantage of Angular validation. Here's a list of the changes: 1) Add a "novalidate" attribute to the form This isn't strictly required, but it helps prevent interference between the native HTML validation and Angular. 2) Add a "name" attribute to the form and to all input controls in it This is required so Angular can add the form and input elements to the scope. 3) Disable the submit button when the form is in an invalid state This is done by adding an ngDisabled directive to the button element. The directive uses the "form.$invalid" scope variable added by Angular:

<button type="submit" ng-disabled="form.$invalid">
    Create Account
</button>

4) Add error messages to the input elements The error messages are just regular HTML elements with an ngShow directive that makes them visible only when the field contains errors. For example:

<input placeholder="User Name"
       name="name"
       ng-model="theName" ng-model-options="{ updateOn: 'blur' }"
       required pattern=".{2,}" />
<div class="error-message"
     ng-show="form.name.$invalid &amp;&amp; !form.name.$pristine">
    We need a name with 2 chars or more.
</div>

Notice how the ngShow directive uses the '$invalid' and '$pristine' variables added to the scope by Angular to show the error message only if the field is invalid and has been modified by the user. 5) Control when the validation happens By default, Angular will update scope variables bound to input elements when the input value changes. This works in most cases, but you probably don't want to show error messages while users are filling out the field. So you can use the ngModelOptions directive to tell Angular you want the updates to happen only when the input field loses focus:

<input placeholder="User Name"
       name="name"
       ng-model="theName" ng-model-options="{ updateOn: 'blur' }"
       required pattern=".{2,}" />

6) Update the CSS rules used to show errors Finally, we change the CSS to customize the appearance of input fields that contain errors and are not in their initial/pristine state, as well as the appearance of the error messages displayed next to fields that contain errors:

form input.ng-invalid:not(.ng-pristine) {
    box-shadow: 0px 0px 13px rgba(255, 133, 0, 0.9);
}
.error-message {
    padding: 12px;
    background-color: rgba(255, 133, 0, 0.1);
    font-weight: bold;
}

Here is the revised version of our form:

<form name="form" novalidate>

    <input placeholder="User Name"
           name="name" ng-model="theName" ng-model-options="{updateOn:'blur'}"
           required pattern=".{2,}" />
    <div class="error-message"
         ng-show="form.name.$invalid &amp;&amp; !form.name.$pristine">
        We need a name with 2 chars or more.
    </div>

    <input placeholder="E-mail"
           name="email" ng-model="theEmail" ng-model-options="{updateOn:'blur'}"
           required type="email" />
    <div class="error-message"
         ng-show="form.email.$invalid &amp;&amp; !form.email.$pristine">
        We need a valid e-mail address.
    </div>

    <input placeholder="Password"
           name="pwd" ng-model="thePwd" ng-model-options="{ updateOn: 'blur' }"
           required type="password" pattern=".{2,}" />
    <div class="error-message"
         ng-show="form.password.$invalid &amp;&amp; !form.password.$pristine">
        Passwords must have at least 2 chars.
    </div>

    <input placeholder="Check Password"
           name="check" ng-model="chkPwd"
           type="password" wj-valid="chkPwd == thePwd" />
    <div class="error-message"
         ng-show="form.check.$invalid &amp;&amp; !form.check.$pristine">
        Sorry, the passwords don't match.
    </div>
    <button type="submit"
            ng-disabled="form.$invalid">
        Log In
    </button>


</form>

If you run the form now, it looks like the original version, except the submit button is disabled since the form is in an invalid state: Wijmo AngularJS Form Validation

As the user fills out the fields, errors are displayed using the new custom elements:

Wijmo AngularJS Validation Error

We've covered the main aspects of the Angular form validation system. If you want to learn more, check the Angular documentation: Angular Forms.

If you want to learn more about the HTML5 validation API, check the MDN docs: HTML5 Forms Validation.

Bernardo de Castilho

comments powered by Disqus