Thursday 21 February 2013

Form validation using Angular JS

User input validation is a crucial feature to be implemented in any data driven application. We have a number of libraries that support input validation on client side and all of them address this issue in a very effective way. Since Angular JS is designed to make the process of client side data driven application development easier, it also has a nice support of user input validation.

Angular is capable of validating HTML5 input types like required, url, number and email. It also provides some directives like min, max, minlength, maxlength and required for validation. If any of these built-in features is not able to address the business purpose, we can define our own directive and use it for validation.

To indicate the user about validity of form data, the following CSS classes can be defined. They will be applied to the input fields by Angular JS whenever they are required:


  • ng-valid – Applied if the value satisfies validation rules
  • ng-invalid – Applied if the value doesn’t satisfy validation rules
  • ng-dirty – Applied only when there is change in the value of the field
  • ng-pristine – Applied if the field is untouched

Let’s apply some validations to the shopping cart item form that we have been using in earlier posts. If you didn’t read old posts yet, refer to the jsfiddle sample of the last post. Following are the CSS styles that will be applied on the form elements:

input.ng-invalid.ng-dirty {
 background-color: #FA787E;
}
input.ng-valid.ng-dirty {
 background-color: #78FA89;
}

Let's apply the following validations on the cart item form:

  • All fields are required – can be done using required attribute
  • Item name should contain only alphabets and spaces, quantity should be an integer – can be achieved using pattern directive
  • Price should be a float number with value between 50 and 5000 – can be done using a custom validation (Note: Though it can be achieved using pattern, I am using custom validator for demonstration)
  • Quantity should be an integer with minimum value 1 and maximum value 90 – can be achieved using min and max directives and a regular expression to check for integer

If the browser supports HTML5, browser will also validate the form elements. This behaviour can be supressed using novalidate attribute on the form tag. Following is the mark-up of item form with validation attributes applied:

<form novalidate name="itemForm">
 <table>
  <tr>
   <td>Name: </td>
   <td><input name="name" type="text" ng-model="item.Name" required ng-pattern="name" /></td>
  </tr>
  <tr>
   <td>Price: </td>
   <td><input name="price" type="text" ng-model="item.Price" required  valid-price /></td>
  </tr>
  <tr>
   <td>Quantity: </td>
   <td><input name="quantity" type="number" ng-model="item.Quantity" min="1" max="90" ng-pattern="integerval" required /></td>
  </tr>
  <tr>
   <td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" ng-disabled="itemForm.$invalid" /> </td>
  </tr>
 </table>
</form>

Notice the ng-disabled directive on the button. It is used to disable the button if any of input elements in the form is not valid.

To match the pattern of name and quantity fields, ng-pattern attribute is applied on them. The corresponding patterns have to be assigned to a field in the controller.

$scope.name=/^[a-zA-Z ]*$/;

$scope.integerval=/^\d*$/;

For the custom validation to validate price, a directive with the name validPrice has to be created. Let’s add this directive to the shopping module we created in last post.
angular.module(‘shopping’,[])
 .value(…)
 .filter(…)
 .directive('validPrice',function(){
  return{
   require: "ngModel",
   link: function(scope, elm, attrs, ctrl){
    var regex=/^\d{2,4}(\.\d{1,2})?$/;
    ctrl.$parsers.unshift(function(viewValue){
     var floatValue = parseFloat(viewValue);
     if( floatValue >= 50 && floatValue <=5000 && regex.test(viewValue)){
      ctrl.$setValidity('validPrice',true);
     }
     else{
     ctrl.$setValidity('validPrice',false);
     }
     return viewValue;
    });
   }
  };
 });


With this, we are done with all validations. But this leaves the form in invalid state whenever the input fields are cleared after adding an item to the cart. To prevent this behaviour, we have to manually set the form’s state as pristine. Following statement does this for us:
$scope.itemForm.$setPristine();

This statement has to be added to the addItem function of the controller. Though the validations are doing a great job by indicating the invalidity of data on input fields, the user won’t get to know about what has to be modified unless a friendly message is displayed. Message can be displayed in any format that you like. To keep it simple, I am displaying the messages in span elements. An invalid message should be displayed when both of the following conditions meet:

  • An input field is modified – can be checked using $dirty
  • Contains an invalid value – can be checked using $invalid

Following is the markup for displaying error message:

<div ng-show="itemForm.name.$dirty && itemForm.name.$invalid">
 <span ng-show="itemForm.name.$error.required">Name cannot be left blank</span>
 <span ng-show="itemForm.name.$error.pattern">Name cannot contain numbers or special characters</span>
</div>
<div ng-show="itemForm.price.$dirty && itemForm.price.$invalid">
 <span ng-show="itemForm.price.$error.required">Price cannot be blank</span>
 <span ng-show="itemForm.price.$error.validPrice">Price should be a number between 50 and 5000 with maximum 2 digits after decimal point</span>
</div>
<div ng-show="itemForm.quantity.$dirty && itemForm.quantity.$invalid">
 <span ng-show="itemForm.quantity.$error.required">Quantity cannot be blank</span>
 <span ng-show="itemForm.quantity.$error.pattern || itemForm.quantity.$error.min || itemForm.quantity.$error.max">Quantity should be an integer between 1 and 90</span>
</div>

Carefully examine the ng-show directives applied on the div and span elements. The pattern of each condition is in every ng-show directive is:

<form-name>.<element-name>.$error.<type-of-validation>

If there are multiple conditions for one error message, they have to be combined using AND(&&) or OR(||) depending upon the need.

The complete sample is available at the following jsfiddle:



Further learning:

Happy coding!

2 comments:

  1. Great information!, thanks a lot for sharing your knowledge.

    ReplyDelete
  2. Ah, sorry about that! I refer to them just as drones now. Thanks for the sharing!

    Yii developer

    ReplyDelete

Note: only a member of this blog may post a comment.