AngularJS Form Validation Example

In this post, we will learn How to use Forms in AngularJS to take user input, validate it, send to server or process it. We will make a complete AngularJS CRUD application. You will get insight into several AngularJS built-in directives used in day-to-day usage. We will start from ground zero and will build upon peace by peace. By the end of this post, you will be able to make your own Stunning AngularJS Forms for your application.

Live Demo

Just to show, Our final form [An AngularJS CRUD application] will look like this. Don’t worry. We will start from scratch and will build up step by step while learning the deep details.


Let’s get started.

1. Very Basic Form

This very first example shows a really simple form without any validation or styling. It is intentional because our goal is to first understand some basics of form handling in AngularJS here. Later in this post, we will make some exotic forms with validation, error handling and styling with bootstrap.

<html ng-app="myApp">
  <head>  
    <title>Form Demo</title>  
    <style>body{font-family:"Arial";background-color:#E2E2DC}</style>
  </head>
  <body ng-controller="AppController as ctrl">
      
      <form ng-submit="ctrl.submit()">
          <input type="text"  ng-model="ctrl.user.username" placeholder="Enter your name"/><br/><br/>
          <input type="text"  ng-model="ctrl.user.address" placeholder="Enter your Address"/><br/><br/>
          <input type="text"  ng-model="ctrl.user.email" placeholder="Enter your Email"/><br/><br/>
          
          <input type="submit"    value="Submit">
      </form>
      
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
      </script>  
      <script>
          angular.module('myApp', [])
          .controller('AppController', [function() {
              var self = this;
              self.submit = function() {
                  console.log('Form is submitted with following user', self.user);
            };
      }]);
  </script>
  </body>
</html>

Live Demo

This form takes user input and simply logs user in console. Check your browser console output to see the result.

Main Highlights

In this simplistic example, 3 text field and a button is wrapped inside a form.

  • First highlight of this Form is that, we have nowhere declared/or instantiated a user object in the controller. With ng-model, AngularJS automatically creates the objects and it’s properties on the fly if it does not exist. In this case, until the user types something into the username or any other field, there is no user object. As the user types in the first letter, it causes the user object to be created, and the typed-in value to be assigned to the respective field.
  • Second interesting point of this form is that instead of binding to ctrl.username, ctrl.address & ctrl.email, we are binding to ctrl.user.username, ctrl.user.address and ctrl.user.email. This has an advantage that after submitting the form, we don’t have to prepare a user object to fill in the values from form. It is already prepared with all input values, you can send it as it is to server. In this example, we simply logged it on console. Check your browser console output.
  • Last striking thing of this form is that we have used ng-submit directive on form here instead of using ng-click on submit button. This is because a form can be submitted by clicking the Submit button, or hitting Enter on a text field. The ng-submit gets triggered on all those events, whereas the ng-click will only be triggered when the user clicks the button.

2. Form with Error Handling

This example is build upon previous example, and shows how to show validation error message to user to inform what went wrong.

In order to take advantage of AngularJS’s form bindings to be able to fetch errors for individual field, we can use name attribute on that field and on form. When we add a name to any input field, it creates a model on the form for that particular input, with the error state.

<html ng-app="myApp">
  <head>  
    <title>Form Demo</title>  
    <style>body{font-family:"Arial";background-color:#E2E2DC}</style>
  </head>
  <body ng-controller="AppController as ctrl">
      
      <form ng-submit="ctrl.submit()" name="myForm">
          <input type="text" ng-model="ctrl.user.username" name="uname" placeholder="Enter your name" required ng-minlength="3"/>
              <span ng-show="myForm.$dirty && myForm.uname.$error.required">This is a required field</span>
              <span ng-show="myForm.$dirty && myForm.uname.$error.minlength">Minimum length required is 4</span>
              <span ng-show="myForm.$dirty && myForm.uname.$invalid">This field is invalid </span><br/><br/>
          
          <input type="text" ng-model="ctrl.user.address" placeholder="Enter your Address"/><br/><br/>

          <input type="email" ng-model="ctrl.user.email" name="email" placeholder="Enter your Email" required/>
              <span ng-show="myForm.$dirty && myForm.email.$error.required">This is a required field</span>
              <span ng-show="myForm.$dirty && myForm.email.$invalid">This field is invalid </span><br/><br/>
          
          <input type="submit"    value="Submit" ng-disabled="myForm.$invalid">
      </form>
      
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
      </script>  
      <script>
          angular.module('myApp', [])
          .controller('AppController', [function() {
              var self = this;
              self.submit = function() {
                  console.log('Form is submitted with following user', self.user);
            };
      }]);
  </script>
  </body>
</html>

Live Demo

Again, Check your browser console output to see the result.

Main Highlights

We have included some error messages on individual field level.

  • Both Form and individual fields which needs to be validated now contains a name attribute. For instance, we don’t want address field to be validated, hence no name attribute on that field.
  • We have marked the fields with HTML5 required validator as well as AngularJS ng-minlength and type="email" validators. Each of the validators exposes a key on the $error object, so that we can pick it up and display the error message for that particular error to the user
  • In order to check & show validation errors, we have used another popular AngularJS directive ng-show. ng-show and ng-hide are two directives in AngularJS that deal with hiding and showing HTML elements. They inspect a variable and, depending on the truthiness of its value, show or hide elements in the UI, respectively. AngularJS treats true, nonempty strings, nonzero numbers, and nonnull JS objects as truthy.
  • On individual field we have created specific span element with specific error which are shown only when certain type of validation error is present. Look at myForm.$dirty condition. it will be true only if something was typed in/edited in form. It will avoid showing errors on form load. Different form states are shown further in post.
  • If a certain validator failed, then AngualrJS marks that field as invalid [myForm.email.$invalid], which can again be used with ng-show to display further error to user.
  • If there is any validation error in form, form itself is marked as invalid by AngualarJs. Here we used another AngularJS directive ng-disabled to disable the submit button if the form is invalid [ng-disabled="myForm.$invalid"].

Different Form states in AngularJS

  • $invalid : AngularJS sets this state when any of the validations (required, ng-minlength, and others) mark any of the fields within the form as invalid.
  • $valid : The inverse of the previous state, which states that all the validations in the form are currently evaluating to correct.
  • $pristine : All forms in AngularJS start with this state. This allows you to figure out if a user has started typing in and modifying any of the form elements. Possible usage: disabling the reset button if a form is pristine.
  • $dirty : The inverse of $pristine, which states that the user made some changes (he can revert it, but the $dirty bit is set).
  • $error : This field on the form houses all the individual fields and the errors on each form element.

3. Form styling with CSS to highlight fields on validation success/failure

On each validation step, AngularJS adds/removes certain CSS classes to/from form and individual fields. It means highlighting error fields are just matter of providing your own styles to these classes.

<html ng-app="myApp">
  <head>  
    <title>Form Demo</title>  
    <style>body{font-family:"Arial";background-color:#E2E2DC}</style>
    <style>
      .username.ng-valid {
          background-color: lightgreen;
      }
      .username.ng-dirty.ng-invalid-required {
          background-color: red;
      }
      .username.ng-dirty.ng-invalid-minlength {
          background-color: yellow;
      }

      .email.ng-valid {
          background-color: lightgreen;
      }
      .email.ng-dirty.ng-invalid-required {
          background-color: red;
      }
      .email.ng-dirty.ng-invalid-email {
          background-color: yellow;
      }

</style>
  </head>
  <body ng-controller="AppController as ctrl">
      
      <form ng-submit="ctrl.submit()" name="myForm">
          <input type="text" ng-model="ctrl.user.username" name="uname" class="username" placeholder="Enter your name" required ng-minlength="3"/>
              <span ng-show="myForm.$dirty && myForm.uname.$error.required">This is a required field</span>
              <span ng-show="myForm.$dirty && myForm.uname.$error.minlength">Minimum length required is 4</span>
              <span ng-show="myForm.$dirty && myForm.uname.$invalid">This field is invalid </span><br/><br/>
            
          <input type="text" ng-model="ctrl.user.address" placeholder="Enter your Address"/><br/><br/>

          <input type="email" ng-model="ctrl.user.email" name="email" class="email" placeholder="Enter your Email" required/>
              <span ng-show="myForm.$dirty && myForm.email.$error.required">This is a required field</span>
              <span ng-show="myForm.$dirty && myForm.email.$invalid">This field is invalid </span><br/><br/>
          
          <input type="submit"  value="Submit" ng-disabled="myForm.$invalid">
      </form>
      
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
      </script>  
      <script>
          angular.module('myApp', [])
          .controller('AppController', [function() {
              var self = this;
              self.submit = function() {
                  console.log('Form is submitted with following user', self.user);
            };
      }]);
  </script>
  </body>
</html>

In above example, first we added two placeholder classes in username and email field, so that we can further refer to them in our stylesheet.Then we took those classes and added specific validation classes from AngularJS which will be added/removed on specific steps in validation cycle.

Live Demo

Try filling these fields to see them in action.

Shown below are the form states and respective AngularJS classes being added/removed on form and individual fields.

  • $invalid : ng-invalid
  • $valid : ng-valid
  • $pristine: ng-pristine
  • $dirty : ng-dirty

Similarly , for each validation failure, following CSS classes are added/removed from individual fields.

  • $invalid : ng-invalid
  • $valid : ng-valid
  • $pristine: ng-pristine
  • $dirty : ng-dirty
  • $required: ng-valid-required or ng-invalid-required
  • min : ng-valid-min or ng-invalid-min
  • max : ng-valid-max or ng-invalid-max
  • minlength : ng-valid-minlength or ng-invalid-minlength
  • maxlength : ng-valid-maxlength or ng-invalid-maxlength
  • pattern : ng-valid-pattern or ng-invalid-pattern
  • url : ng-valid-url or ng-invalid-url
  • email : ng-valid-email or ng-invalid-email
  • date : ng-valid-date or ng-invalid-date
  • number : ng-valid-number or ng-invalid-number

4. Form with Bootstrap. ng-repeat example

Below example is a full blown AngularJS based CRUD application. In this final example, we are using Bootstrap CSS to stylify our form. In order to show a list of all users of application, we are using another very popular AngularJS directive ng-repeat, which is the standard choice to iterate over list of Items [can also iterate over keys of a given object]. For the moment, we are not communicating with Server, data is local, but in next post on AngularJS Service, we will fetch the data from [and update it in] real server.

<html>
  <head>  
    <title>Form Demo</title>  
    <style>
      .username.ng-valid {
          background-color: lightgreen;
      }
      .username.ng-dirty.ng-invalid-required {
          background-color: red;
      }
      .username.ng-dirty.ng-invalid-minlength {
          background-color: yellow;
      }

      .email.ng-valid {
          background-color: lightgreen;
      }
      .email.ng-dirty.ng-invalid-required {
          background-color: red;
      }
      .email.ng-dirty.ng-invalid-email {
          background-color: yellow;
      }

    </style>
     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
     <link rel="stylesheet" href="app.css">
  </head>
  <body ng-app="myApp">
      <div class="generic-container" ng-controller="AppController as ctrl">
          <div class="panel panel-default">
              <div class="panel-heading"><span class="lead">User Registration Form </span></div>
              <div class="formcontainer">
                  <form ng-submit="ctrl.submit()" name="myForm" class="form-horizontal">
                      <input type="hidden" ng-model="ctrl.user.id" />
                      <div class="row">
                          <div class="form-group col-md-12">
                              <label class="col-md-2 control-lable" for="uname">Name</label>
                              <div class="col-md-7">
                                  <input type="text" ng-model="ctrl.user.username" id="uname" class="username form-control input-sm" placeholder="Enter your name" required ng-minlength="3"/>
                                  <div class="has-error" ng-show="myForm.$dirty">
                                      <span ng-show="myForm.uname.$error.required">This is a required field</span>
                                      <span ng-show="myForm.uname.$error.minlength">Minimum length required is 3</span>
                                      <span ng-show="myForm.uname.$invalid">This field is invalid </span>
                                  </div>
                              </div>
                          </div>
                      </div>
                        
                      
                      <div class="row">
                          <div class="form-group col-md-12">
                              <label class="col-md-2 control-lable" for="address">Address</label>
                              <div class="col-md-7">
                                  <input type="text" ng-model="ctrl.user.address" id="address" class="form-control input-sm" placeholder="Enter your Address. [This field is validation free]"/>
                              </div>
                          </div>
                      </div>

                      <div class="row">
                          <div class="form-group col-md-12">
                              <label class="col-md-2 control-lable" for="email">Email</label>
                              <div class="col-md-7">
                                  <input type="email" ng-model="ctrl.user.email" id="email" class="email form-control input-sm" placeholder="Enter your Email" required/>
                                  <div class="has-error" ng-show="myForm.$dirty">
                                      <span ng-show="myForm.email.$error.required">This is a required field</span>
                                      <span ng-show="myForm.email.$invalid">This field is invalid </span>
                                  </div>
                              </div>
                          </div>
                      </div>

                      <div class="row">
                          <div class="form-actions floatRight">
                              <input type="submit"  value="{{!ctrl.user.id ? 'Add' : 'Update'}}" class="btn btn-primary btn-sm" ng-disabled="myForm.$invalid">
                              <button type="button" ng-click="ctrl.reset()" class="btn btn-warning btn-sm" ng-disabled="myForm.$pristine">Reset Form</button>
                          </div>
                      </div>
                  </form>
              </div>
          </div>
          <div class="panel panel-default">
                <!-- Default panel contents -->
              <div class="panel-heading"><span class="lead">List of Users </span></div>
              <div class="tablecontainer">
                  <table class="table table-hover">
                      <thead>
                          <tr>
                              <th>ID.</th>
                              <th>Name</th>
                              <th>Address</th>
                              <th>Email</th>
                              <th width="100">
                          </tr>
                      </thead>
                      <tbody>
                          <tr ng-repeat="u in ctrl.users">
                              <td><span ng-bind="u.id"></span></td>
                              <td><span ng-bind="u.username"></span></td>
                              <td><span ng-bind="u.address"></span></td>
                              <td><span ng-bind="u.email"></span></td>
                              <td>
                              <button type="button" ng-click="ctrl.edit(u.id)" class="btn btn-success custom-width">Edit</button>  <button type="button" ng-click="ctrl.remove(u.id)" class="btn btn-danger custom-width">Remove</button>
                          </tr>
                      </tbody>
                  </table>
              </div>
          </div>
      </div>
      
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
      </script>  
      <script>
          angular.module('myApp', [])
          .controller('AppController', ['$scope', function($scope) {
              var self = this;
              self.user={id:null,username:'',address:'',email:''};
              self.id = 4;
              
              self.users = [// In future posts, we will get it from server using service
                      {id:1, username: 'Sam', address: 'NY', email: 'sam@abc.com'},
                      {id:2, username: 'Tomy', address: 'ALBAMA', email: 'tomy@abc.com'},
                      {id:3, username: 'kelly', address: 'NEBRASKA', email: 'kelly@abc.com'}
              ];
              
              self.submit = function() {
                  if(self.user.id === null){
                      self.user.id = self.id++;
                      console.log('Saving New User', self.user);    
                      self.users.push(self.user);//Or send to server, we will do it in when handling services
                  }else{
                      for(var i = 0; i < self.users.length; i++){
                          if(self.users[i].id === self.user.id) {
                            self.users[i] = self.user;
                            break;
                          }
                      }
                     console.log('User updated with id ', self.user.id);
                  }
                  self.reset();
              };
              
              self.edit = function(id){
                  console.log('id to be edited', id);
                  for(var i = 0; i < self.users.length; i++){
                      if(self.users[i].id === id) {
                         self.user = angular.copy(self.users[i]);
                         break;
                      }
                  }
              }
              
              self.remove = function(id){
                  console.log('id to be deleted', id);
                  for(var i = 0; i < self.users.length; i++){
                      if(self.users[i].id === id) {
                         self.users.splice(i,1);
                         if(self.user.id === id){//It is shown in form, reset it.
                            self.reset();
                         }
                         break;
                      }
                  }
              }
              
              self.reset = function(){
                  self.user={id:null,username:'',address:'',email:''};
                  $scope.myForm.$setPristine(); //reset Form
              }

      }]);
  </script>
  </body>
</html>

Live Demo

This example is build upon previous example.

Main Highlights

  • Form contains the CSS to highlight the fields on different validation states. Same as previous example.
  • Form shows a simple CRUD application, where a user can be added, edited or removed. It also shows a list of existing users (hard coded for this simple example) in application using AngularJS ng-repeat directive.
  • We have added Bootstrap CSS to stylify our form. Usual bootstrap setup, Nothing special
  • Note that we are using AngularJS new controllerAs syntax in view. It makes things easy to understand. With this syntax, we hardly need to use $scope explicitly, (which was heavily used in previous releases of AngularJS and created lots of confusion among new to AngularJS). This link is a must to know more about AngularJS scopes.
  • We did include $scope dependency in our controller, this time just to get a handle on something from view [form] in controller. Every controller has an associated $scope object, which inherits from $rootScope. Every application has a single root scope. All other scopes inherit from rootScope. Scope is the glue between application controller and the view. In this example, we used $scope to get the handle on form from view, into our controller , in order to reset it.
  • Look at reset function in controller. It calls $scope.myForm.$setPristine(); to make the form pristine. In other words , we set the form as a Blank Slate where nothing has been written yet. It cleans out any previous validation state change effects. We also reset the user object used in form.
  • Also look at edit function, specially self.user = angular.copy(self.users[i]);. Once we clicked on edit, that specific user details are shown on the form. But we don’t want the actual object to change when we are just typing, and we did not click on Update yet. To handle that, we make a copy of the object to make changes, and once user clicked on Update, on that moment, we will replace the actual user object details, with the new details typed in.

That’s it. In next post we will discuss about AngularJS services and will extend our example to communicate with server using AngularJS built-in service $http. Stay tune.