AngularJS: Form validation built in support

[Fuente: https://scotch.io/tutorials/angularjs-form-validation]

We’ll focus on client side validation and using the built in Angular form properties.

Demo

See the Pen AngularJS Form Validation by Chris Sevilleja (@sevilayha) on CodePen.

Requirements

  • Name is required
  • Username is not required, minimum length 3, maximum length 8
  • Email is not required, but has to be a valid email
  • Form submit is disabled if the form isn’t valid
  • Show a required or invalid email error
  • Alert awesome if submitted correctly

Angular Form Properties $valid, $invalid, $pristine, $dirty

Property Class Description
$valid ng-valid Boolean Tells whether an item is currently valid based on the rules you placed.
$invalid ng-invalid Boolean Tells whether an item is currently invalid based on the rules you placed.
$pristine ng-pristine Boolean True if the form/input has not been used yet.
$dirty ng-dirty Boolean True if the form/input has been used.
$touched ng-touched Boolean True if the input has been blurred.

Angular also provides classes on the form and its inputs so that you can style each state accordingly.

ACCESSING ANGULAR FORM PROPERTIES

  • To access the form: <form name>.<angular property>
  • To access an input: <form name>.<input name>.<angular property>

 Setting up our form

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <!-- CSS ===================== -->
    <!-- load bootstrap -->
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"> 
    <style>
        body    { padding-top:30px; }
    </style>
    
    <!-- JS ===================== -->
    <!-- load angular -->
    <script src="http://code.angularjs.org/1.2.6/angular.js"></script> 
    <script src="app.js"></script>
</head>

<!-- apply angular app and controller to our body -->
<body ng-app="validationApp" ng-controller="mainController">
<div class="container">
<div class="col-sm-8 col-sm-offset-2">
    
    <!-- PAGE HEADER -->
    <div class="page-header"><h1>AngularJS Form Validation</h1></div>
   
    <!-- FORM -->
    <!-- pass in the variable if our form is valid or invalid -->
    <form name="userForm" ng-submit="submitForm(userForm.$valid)" novalidate> 
<!-- novalidate prevents HTML5 validation since we will be validating ourselves -->

        <!-- NAME -->
        <div class="form-group">
            <label>Name</label>
            <input type="text" name="name" class="form-control" ng-model="name" required>
        </div>

        <!-- USERNAME -->
        <div class="form-group">
            <label>Username</label>
            <input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
        </div>
        
        <!-- EMAIL -->
        <div class="form-group">
            <label>Email</label>
            <input type="email" name="email" class="form-control" ng-model="email">
        </div>
        
        <!-- SUBMIT BUTTON -->
        <button type="submit" class="btn btn-primary">Submit</button>
        
    </form>

</div><!-- col-sm-8 -->
</div><!-- /container -->
</body>
</html>

A few key points to note here:

  • novalidate: This will prevent the default HTML5 validations since we’ll be doing that ourselves (ours will be much prettier)
  • We have applied ng-model to our inputs so that we have data from our forms bound to Angular variables
  • ng-minlength and ng-maxlength on username will create those rules
  • The name input is required
  • The email input is type=”email”

Validation Rules

Angular provides many validation rules that we can use in addition to ng-minlength and ng-maxlength.

These are the available parameters for an Angular input to create validation rules. Read the Angular input directive for more information.

  <input
       ng-model="{ string }"
       name="{ string }"
       required
       ng-required="{ boolean }"
       ng-minlength="{ number }"
       ng-maxlength="{ number }"
       ng-pattern="{ string }"
       ng-change="{ string }">
    </input>

Now that we have our simple form, let’s create our Angular app and controller that we have already applied to it using ng-app and ng-controller.

OUR ANGULAR APP CODE APP.JS

// app.js
// create angular app
var validationApp = angular.module('validationApp', []);

// create angular controller
validationApp.controller('mainController', function($scope) {

  // function to submit the form after all validation has occurred            
  $scope.submitForm = function(isValid) {

    // check to make sure the form is completely valid
    if (isValid) {
      alert('our form is amazing');
    }

  };

});

Disabling the Submit Button ng-disabled

<!-- index.html -->
...

<!-- SUBMIT BUTTON -->
<button type="submit" class="btn btn-primary" ng-disabled="userForm.$invalid">Submit</button>

...

Showing an Error Message ng-show

ng-valid and ng-invalid will automatically determine if an input is good based on the rules placed on it in your form.

Let’s go through and add an error message for each of our inputs if they are not $valid and have already been used (since we don’t want to show an error before they’ve been used).

<!-- index.html -->
...

<!-- NAME -->
<div class="form-group">
  <label>Name</label>
  <input type="text" name="name" class="form-control" ng-model="name" required>
  <p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">You name is required.</p>
</div>

<!-- USERNAME -->
<div class="form-group">
  <label>Username</label>
  <input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
  <p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
  <p ng-show="userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>

<!-- EMAIL -->
<div class="form-group">
  <label>Email</label>
  <input type="email" name="email" class="form-control" ng-model="email">
  <p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>

...

Styling Classes

Angular already provides classes on our inputs and our forms based on if they are valid or not. Look at the table at the beginning of this article for those classes (ng-valid, ng-invalid, ng-pristine and ng-dirty).

You can style those in CSS if you like. You can do anything you like with those classes. There will even be classes based on the certain rules applied if you wanted to get really specific.

.ng-valid       {  }
.ng-invalid     {  }
.ng-pristine    {  }
.ng-dirty       {  }
.ng-touched     {  }

/* really specific css rules applied by angular */
.ng-invalid-required        {  }
.ng-invalid-minlength       {  }
.ng-valid-max-length        {  }

Adding Conditional Classes ng-class

Since we are using Bootstrap, we will use the classes they provide (has-error). This will get us that nice error and color around our form-group.

ng-class allows us to add classes based on an expression. In this case, we want to add a has-error class to our form-group if an input is $invalid and not pristine.

The way it works is ng-class="{ <class-you-want> : <expression to be evaluated > }". For more information, read the Angular ngClass docs.

<!-- index.html -->
...

<!-- NAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }">
    <label>Name</label>
    <input type="text" name="name" class="form-control" ng-model="user.name" required>
    <p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">You name is required.</p>
</div>

<!-- USERNAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.username.$invalid && !userForm.username.$pristine }">
    <label>Username</label>
    <input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
    <p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
    <p ng-show="userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>
    
<!-- EMAIL -->
<div class="form-group" ng-class="{ 'has-error' : userForm.email.$invalid && !userForm.email.$pristine }">
    <label>Email</label>
    <input type="email" name="email" class="form-control" ng-model="user.email">
    <p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>

...

Only showing errors after submitting the form

Sometimes it is not desirable to show errors while a user is typing. The errors currently show immediately as a user is typing into the form. This happens because of Angular’s great data-binding feature. Since everything changes immediately, it can be a downside when talking about form validation.

For the scenario where you only want to show errors after a form is submitted, you would adjust the above code a little bit.

  1. You would need to take away the ng-disabled on the submit button since we want a user to be able to submit a form even if it is not fully valid.
  2. You would add a variable after the form has been submitted. Inside of your submitForm() function, just add $scope.submitted = true;. This stores the submitted variable as true as soon as the form is submitted.
  3. Adjust the error rules from ng-class="&#123; 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }" to ng-class="&#123; 'has-error' : userForm.name.$invalid && !userForm.name.$pristine && submitted }". This ensures that the error will only show after the form is submitted. You would need to adjust all the other ng-class and ng-show to account for this variable.

Now the form only shows errors if the submitted variable is set to true.

Only showing errors after clicking out of an input