- AngularJS Tutorial
- AngularJS Views And Directives
- AngularJS $scope Hierarchy
- AngularJS Events
- AngularJS $watch() , $digest() and $apply()
- AngularJS AJAX
- AngularJS Forms
- AngularJS $timeout and $interval
- AngularJS Custom Directives
- AngularJS Modularization & Dependency Injection
- AngularJS Routes
- AngularJS Internationalization
- AngularJS Critique
AngularJS Forms
Jakob Jenkov |
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:
Property | Description |
---|---|
$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 anng-click
attribute. - Using an
ng-submit
attribute (directive) on theform
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.
Tweet | |
Jakob Jenkov |