AngularJS Forms and Validation

Learn how to bind form inputs with ng-model and use AngularJS's automatic validation state classes to give users real-time feedback.

Two-Way Binding with ng-model

The ng-model directive binds a form control's value to a property on the scope. Typing into the input updates the property immediately, and changing the property from code updates the input just as immediately - this is AngularJS's classic two-way data binding applied to forms.

Example

<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
</head>
<body>

<div ng-app="formApp" ng-controller="ProfileController as vm">
  <label>Display name:
    <input type="text" ng-model="vm.name" placeholder="Enter your name">
  </label>
  <p>Hello, {{ vm.name || 'stranger' }}!</p>
</div>

<script>
angular.module('formApp', [])
  .controller('ProfileController', function() {
    var vm = this;
    vm.name = '';
  });
</script>

</body>
</html>

Automatic Validation CSS Classes

Every ng-model bound field is automatically tracked by AngularJS's form controller. As the user interacts with it, AngularJS toggles a set of CSS classes on the element so you can style valid, invalid, touched, and untouched states without writing any JavaScript.

ClassMeaning
ng-pristineThe field has not been touched by the user yet
ng-dirtyThe user has changed the field's value
ng-validThe field's current value passes all validation rules
ng-invalidThe field's current value fails at least one validation rule
ng-touchedThe field has lost focus (been blurred) at least once

Example

<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
</head>
<body>

<div ng-app="formApp" ng-controller="SignupController as vm">
<form name="signupForm" novalidate ng-submit="vm.submit()">
  <input type="text" name="username" ng-model="vm.username" required>
  <span class="error" ng-show="signupForm.username.$invalid && signupForm.username.$dirty">
    Username is required.
  </span>

  <button type="submit" ng-disabled="signupForm.$invalid">Sign Up</button>
</form>

<style>
  input.ng-invalid.ng-dirty { border: 2px solid #d9534f; }
  input.ng-valid.ng-dirty   { border: 2px solid #5cb85c; }
</style>
</div>

<script>
angular.module('formApp', [])
  .controller('SignupController', function() {
    var vm = this;
    vm.submit = function() {};
  });
</script>

</body>
</html>
  • required - value must not be empty
  • ng-minlength / ng-maxlength - enforce minimum and maximum string length
  • type="email" - built-in email format validation
  • type="number" - restricts input and parses the value to a number
  • pattern - validates the value against a custom regular expression

Because the form and each input were given a name attribute, AngularJS registers them on the scope as nested objects: signupForm.username. That gives you direct access to per-field state such as $invalid, $dirty, and a detailed $error map keyed by which validation rule failed.

Example

<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
</head>
<body>

<div ng-app="formApp" ng-controller="ContactController as vm">
<form name="contactForm" novalidate>
  <input type="email" name="email" ng-model="vm.email" required>

  <div ng-show="contactForm.email.$dirty && contactForm.email.$invalid">
    <span ng-show="contactForm.email.$error.required">Email is required.</span>
    <span ng-show="contactForm.email.$error.email">Please enter a valid email.</span>
  </div>
</form>
</div>

<script>
angular.module('formApp', [])
  .controller('ContactController', function() {
    var vm = this;
    vm.email = '';
  });
</script>

</body>
</html>
Note: Add novalidate to the <form> tag so the browser's own validation bubbles stay out of the way and your AngularJS-driven messages are the only feedback the user sees.
Note: Checking only $invalid will show error messages the instant the page loads, before the user has typed anything. Always combine it with $dirty (or check form.$submitted after a submit attempt) so errors appear only after the user has actually interacted with the field.

Exercise: AngularJS Forms

What does ng-model do on a form input?