Tech and Media Labs
This site uses cookies to improve the user experience.




AngularJS Forms

Jakob Jenkov
Last update: 2014-08-26

AngularJS Form Handling Introduction

AngularJS has some features for binding data of HTML form input fields to the model object ($scope). These features makes it easier to work with forms.

You bind an input field to a model property using the ng-model directive like this:

<input type="text" id="firstName" ng-model="myForm.firstName">

This binding is two-way, meaning if the $scope.myForm.firstName has a value set inside the corresponding controller function, the input field will start with that value. Additionally, once the user types something into the text field, that value will be copied from the text field into the $scope.myForm.firstName property.

Here is a full AngularJS form example you can play with:

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

<body ng-app="myapp">

<div ng-controller="MyController" >
    <form>
        <input type="text" name="firstName" ng-model="myForm.firstName"> First name <br/>
        <input type="text" name="lastName"  ng-model="myForm.lastName"> Last name <br/>
    </form>

    <div>
        {{myForm.firstName}} {{myForm.lastName}}
    </div>
</div>

<script>
    angular.module("myapp", [])
            .controller("MyController", function($scope) {
                $scope.myForm = {};
                $scope.myForm.firstName = "Jakob";
                $scope.myForm.lastName  = "Jenkov";
            } );
</script>

</body>
</html>

This example binds the two input fields in the form to the $scope.myForm.firstName and $scope.myForm.lastName properties. The two properties both have a value set in the controller function. These values will be displayed in the input fields when the page is first rendered. When the user types in something in the text fields it will get copied into the these properties too.

The example also contains two interpolation directives ({{}}). These two directives will insert the value of the myForm.firstName and myForm.lastName below the form fields. When you enter something in the text boxes it will be copied into the myForm.firstName and myForm.lastName properties. From there it will be inserted into the HTML by the two interpolation directives. This data binding happens while the user enters data in the form fields, so the interpolation directives will update the values while typing (this looks cool, but I cannot remember the last time I actually needed this in a web app form).

Binding Checkboxes

If you bind a checkbox (<input type="checkbox">) to a model property, the model property will be set to true if the checkbox is checked, and false if not.

If you need other values instead of true and false inserted into your model, you can use the ng-true-value and ng-false-value directives, like this:

 <input type="checkbox" ng-model="myForm.wantNewsletter"
                        ng-true-value="yes" ng-false-value="no" >

Binding Radio Buttons

Radio buttons are also easy to bind to model properties. If you have a group of radio buttons, just bind them all to the same model property. The radio button that is chosen will have its value copied into the model property. Here is an example:

<input type="radio" ng-model="myForm.whichNewsletter" value="weeklyNews">    
<input type="radio" ng-model="myForm.whichNewsletter" value="monthlyNews">    

Binding Select Boxes

Binding select boxes to model properties is reasonably straightforward too. Here is an example:

<div ng-controller="MyController" >
    <form>
        <select ng-model="myForm.car">
            <option value="nissan">Nissan</option>
            <option value="toyota">Toyota</option>
            <option value="fiat">Fiat</option>
        </select>
    </form>

    <div>
        {{myForm.car}}
    </div>
</div>

<script>
    angular.module("myapp", [])
            .controller("MyController", function($scope) {
                $scope.myForm = {};
                $scope.myForm.car  = "nissan";
            } );
</script>

ng-options

Instead of using static HTML options you can have AngularJS create option elements based on data from the $scope object. You do so using the ng-options directive inside the select element. Here is an example:

<div ng-controller="MyController" >
    <form>
        <select ng-model="myForm.car"
                ng-options="obj.id as obj.name for obj in myForm.options">
        </select>
    </form>

    <div>
        {{myForm.car}}
    </div>
</div>

<script>
    angular.module("myapp", [])
        .controller("MyController", function($scope) {
            $scope.myForm = {};
            $scope.myForm.car  = "nissan";

            $scope.myForm.options = [
              { id : "nissan", name: "Nissan" }
             ,{ id : "toyota", name: "Toyota" }
             ,{ id : "fiat"  , name: "Fiat" }
             ];

        } );
</script>

The ng-options directive follows the following format:

optionBinding expression   dataSource expression

The dataSource expression speficies what data in the $scope object is to be used as data source for the option elements. In the example above, the dataSource expression is this part:

for obj in myForm.options

It defines obj as each object in the myForm.options array. Thus, an option element will be generated from each element in the myForm.options array in the $scope object.

The optionBinding expression specifies what properties are to be used as value and label for each option element. In the example above the optionBinding is this part:

obj.id as obj.name

This defines the obj.id property of each object as the value of each option element generated, and the obj.name property as the label. If you want the value and label to come from the same property, just leave out the as obj.name part (the label part of the expression).

You can call functions on the $scope object from inside both the optionBinding expression and dataSource expression. Here is an example:

obj.id as getLabelName(obj) for obj in getOptionArray()

This example will use the value returned from the getLabelName(obj) function call as label, and will iterate the objects returned by the getOptionArray() function call.

You can create option groups ( <optgroup> HTML elements with option elements inside) by adding a group by section to the optionBinding expression. Here is an example:

obj.id as obj.name group by obj.type

This example will group the generated option elements into optgroup elements using the obj.type property to determine which option elements to group together. Objects with the same value in the obj.type property will be grouped into the same optgroup element.

You can also iterate the properties of an object instead of iterating an array of objects. Here is an example:

propName as propValue for (propName, propValue) in objectWithProperties

This example will bind the property name as the option element value and the property value as the label, of all properties in the $scope.objectWithProperties object.

Empty Options

If the value set for a select box in the $scope object by the controller function does not match a value attribute of any of the option elements, AngularJS inserts an empty option element in the select box.

You can set a label for this empty option element by inserting an option element in the HTML, like this:

    <form>
        <select ng-model="myForm.car"
                ng-options="obj.id as obj.name for obj in myForm.options">
            <option value="">Please choose a car</option>
        </select>
    </form>

Selecting Multiple Options

If you need a select box that enables the user to choose multiple options, you need to insert the multiple="true" attribute inside the <select> element.

Once you enable multiple option selection, the data binding changes. Instead of binding the <select> element to a single string value, it is now bound to an array of values. Thus, you also set the selected values using an array. Here is an example showing that:

<div ng-controller="MyController" >
    <form>
        <select multiple="true" ng-model="myForm.car"
                ng-options="obj.id as obj.name for obj in myForm.options">
        </select>
    </form>

    <div>
        {{myForm.car}}
    </div>
</div>

<script>
    angular.module("myapp", [])
        .controller("MyController", function($scope) {
            $scope.myForm = {};
            $scope.myForm.car  = ["nissan"];

            $scope.myForm.options = [
              { id : "nissan", name: "Nissan" }
             ,{ id : "toyota", name: "Toyota" }
             ,{ id : "fiat"  , name: "Fiat" }
             ];

        } );
</script>

Form Validation

AngularJS has a set of form validation directives you can use. AngularJS validates form fields before copying their value into the $scope properties to which the form fields are bound. If a form field is invalid, its value is not copied into the $scope property it is bound to. Instead the corresponding $scope property is cleared. That is done to prevent the $scope properties from containing invalid values.

Each of the form validating directives are covered in the following sections.

ng-minlength + ng-maxlength

The ng-minlength and ng-maxlength form validation directives can be used to validate the length of data entered in a form field. Here is an example:

<div ng-controller="MyController" >
    <form>
        <input type="text" id="name" ng-model="myForm.name" ng-minlength="5" ng-maxlength="12"> Name <br/>
    </form>

    <div>
        {{myForm.name}}
    </div>
</div>

<script>
    angular.module("myapp", [])
            .controller("MyController", function($scope) {
                $scope.myForm = {};
                $scope.myForm.name = "Jakob Jenkov";
            } );
</script>

This example sets the ng-minglength to 5 and ng-maxlength to 12. That means that if the text in the input field is less than 5 or more than 12 characters long, the value from the input field will not be copied into the $scope.myForm.name property. You can try this example yourself and see what happens.

Notice the div element which displays the value of the $scope.myForm.name. This wil show you what value was copied from the text field into the $scope.myForm.name property. Notice how it is empty when the text field contains less than 5 or more than 12 characters.

ng-pattern

The ng-pattern directive can be used to validate the value of an input field against a regular expression. Here is an example:

<input type="text" id="name" ng-model="myForm.name" ng-pattern="/^\d+$/"> Name <br/>

The regular expressions must follow the JavaScript regular expression syntax. This example defines a regular expression that matches strings of digits containing at least 1 digit.

ng-required

The ng-required directive checks if the value of the form field is empty or not. Actually, you just use the required attribute of HTML5, and AngularJS detects it automatically.

Checking Field Validation State

If you give the <form> element a name attribute, then the form will be add to the $scope object as a property. Here is an example:

  <form name="myFormNg" ng-submit="myForm.submitTheForm()" >
  ...
  </form>    

When you call a function on the $scope object (a function added to the $scope object by your controller function), you can access the ngFormController object via its name, like this:

$scope.myFormNg

If you add a name attribute to the form fields inside the form, their ngModelController objects will be accessible as properties on the ngFormController object. Here is an example:

  <form name="myFormNg" ng-submit="myForm.submitTheForm()" >
    <input name="firstName" type="text" ng-model="myForm.firstName">
  </form>    

You can now access the ngModelController of the firstName input field like this:

$scope.myFormNg.firstName

Both ngFormController and ngModelController objects contain a set of properties that tell if the form or input field is valid. The properties are:

PropertyDescription
$pristine    True if the form has not been changed (no form fields has changed), false if some fields have been changed.
$dirty The reverse of $pristine - false if the form has not been changed - true if it has.
$valid True if the form field (or the whole form = all form fields) is valid. False if not.
$invalid The reverse of the $valid - false if the field (or all fields in the form) is valid, true if the field (or a single field in the for) is invalid.

You can use these properties to set a matching CSS class on the input fields. Here is an example :

<style>
    .fieldValid {
        border: 1px solid #00ff00;
    }
    .fieldInvalid {
        border: 1px solid #ff0000;
    }
</style>

<div ng-controller="MyController" >
    <form name="myFormNg" >
        <input type="text" ng-class="myForm.getFormFieldCssClass(myFormNg.name)"
               id="name" name="name" ng-model="myForm.name" ng-minlength="2"> Name <br/>
    </form>
</div>

<script>
    angular.module("myapp", [])
            .controller("MyController", function($scope, $http) {
                $scope.myForm = {};

                $scope.myForm.getFormFieldCssClass = function(ngModelController) {
                    //console.log("getting css class: " + ngModelController.$valid) ;
                    if(ngModelController.$pristine) return "";
                    return ngModelController.$valid ? "fieldValid" : "fieldInvalid";
                }
            } );
</script>
    

Notice the ng-class directive on the input field. This directive calls the myForm.getFormFieldCssClass() function using myFormNg.name as parameter. This is the ngModelController for the name input element. The myForm.getFormFieldCssClass() just returns the matching CSS class as a string.

You can also use the validation properties to show or hide div elements with validation messages. Here is the form from before, with a div added:

<div ng-controller="MyController" >
    <form name="myFormNg" ng-submit="myForm.submitTheForm()" novalidate>
    
        <input type="text" ng-class="myForm.getFormFieldCssClass(myFormNg.name)"
               id="name" name="name" ng-model="myForm.name" ng-minlength="2"> Name <br/>

        <div ng-show="myFormNg.name.$invalid">
            You must enter a valid name.
        </div>
    
    </form>
</div>

Notice the ng-show directive on the div element. This directive uses the value of the myFormNg.name.$invalid validation property to determine if the div should be shown or not.

Remember that you can access the $pristine, $dirty, $valid and $invalid properties of the ngFormController too. These properties contain the validation state of the whole form. This button code example disables the submit button if the form is invalid:

<button ng-disabled="myFormNg.$invalid">Submit Form</button>

Submitting Forms

You can submit a form in two ways:

  • Using a button element with an ng-click attribute.
  • Using an ng-submit attribute (directive) on the form element.

In both cases a JavaScript function is called on the $scope object. You attach this JavaScript function to the $scope object in your controller function. The JavaScript function should send the data from the form to your server via AJAX.

Here is a form that uses the ng-click attribute on a button element:

<div ng-controller="MyController" >
  <form>
    <input type="text" id="name" ng-model="myForm.name" ng-minlength="5" ng-maxlength="12"> Name <br/>

    <select ng-model="myForm.car">
      <option value="nissan">Nissan</option>
      <option value="toyota">Toyota</option>
      <option value="fiat">Fiat</option>
    </select>

    <button ng-click="myForm.submitTheForm()">Submit Form</button>
  </form>

  <div>
    {{myForm.name}}
  </div>
  <div>
    {{myForm.car}}
  </div>
</div>

<script>
  angular.module("myapp", [])
     .controller("MyController", function($scope, $http) {
       $scope.myForm = {};
       $scope.myForm.name = "Jakob Jenkov";
       $scope.myForm.car  = "nissan";

     $scope.myForm.submitTheForm = function(item, event) {
       console.log("--> Submitting form");
       var dataObject = {
          name : $scope.myForm.name
          ,car  : $scope.myForm.car
       };

       var responsePromise = $http.post("/angularjs-examples/json-test-data.jsp", dataObject, {});
       responsePromise.success(function(dataFromServer, status, headers, config) {
          console.log(dataFromServer.title);
       });
        responsePromise.error(function(data, status, headers, config) {
          alert("Submitting form failed!");
       });
     }

  });
</script>

Notice how the ng-click attribute points to the myForm.submitTheForm() function, and notice how the submitTheForm() function is attached to the $scope object inside the controller function.

Here is the same form using an ng-submit attribute to submit the form:

  <form ng-submit="myForm.submitTheForm()">
    <input type="text" id="name" ng-model="myForm.name" ng-minlength="5" ng-maxlength="12"> Name <br/>

    <select ng-model="myForm.car">
      <option value="nissan">Nissan</option>
      <option value="toyota">Toyota</option>
      <option value="fiat">Fiat</option>
    </select>

    <input type="submit" value="Submit Form">

  </form>

As you can see, the two mechanisms are very similar. They both call a submit function on the $scope object.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC