AngularJS Fundamental Concepts for Building Web Applications: Part 3
This article, which expands on Part 1 and Part 2 of my AngularJS fundamentals series, provides an advanced example of using web forms with input property validation. If you are unfamiliar with AngularJS, I suggest you begin by reading prior AngularJS Fundamentals articles before continuing with this article.
Most web-based applications have to use web forms to collect, submit, and/or update important application input data coming from users. One simple example is a registration form. Countless web applications have these forms to acquire useful user information that the site can use later, such as a user login and password to gain access to important yet private resources that are user-specific.
Forms validation in AngularJS can often seem confusing (it is also complicated in other MVC languages such as Java Struts). The complication stems from automating errors based on certain field property tags. These tags, or (in Angular's case) directives, deal with details the developer no longer has to deal with. It is a mostly transparent process.
To understand validation in Angular, you must first know about models, controllers, and scope. Furthermore, an understanding of two-way binding and dependency injection is also recommended.
This article describes a fast and effective way to set up validation for your Angular forms without getting into too much detail about every directive available for validation (it is better that you conceptually understand forms validation before a bunch of options are thrown at you). You also learn how validation works between a form, model, and controller.
Web Forms Validation
Web forms is a generic term that refers to forms in your web-based application used to collect user input. For example, the code below is a simple registration web form using an HTML table construct:
<table> <tr><td align='right'>Name</td><td align='left'><input type='text' name='name' value=''></td></tr> <tr><td align='right'>Email</td><td align='left'><input type='text' name='email' value=''></td></tr> <tr><td align='right'>Password</td><td align='left'><input type='text' name='password' value=''></td></tr> <tr><td align='right'>Verify Password</td><td align='left'><input type='text' name='passwordx' value=''></td></tr> <tr><td align='center' colspan='2'><input type='button' name='Register' value='Register'></td></tr> </table>
This simple form does nothing[md]it is just HTML. To make it more intelligible, you can "Angularize" it by preparing the form for some data validation before passing off the data to a back-end server.
The "Angularized" version of this form is as follows (the changes are highlighted in bold):
<form novalidate name="formx" class="css-form"> <table> <tr><td align='right'>Name</td><td align='left'><input type='text' name='name' ng-model='user.name' required value=''></td></tr> <tr><td align='right'>Email</td><td align='left'><input type='email' name='email' ng-model='user.email' value=''></td></tr> <tr><td align='right'>Password</td><td align='left'><input type='password' name='password' ng-model='user.password' required value=''></td></tr> <tr><td align='right'>Verify Password</td><td align='left'><input type='password' name='passwordx' ng-model='user.passwordx' required value=''></td></tr> </table> <button ng-click="reset()">RESET</button> <button ng-click="update(user)">SAVE</button> </form>
I explicitly created a model called user and bound the form's user-specific properties such as Name and Email to that model. To validate forms in Angular, the forms' properties must be property members of a model (implicitly or otherwise). If no controller is specified for the form, one is implicitly created by Angular: NgModelController. As with most MVC languages, a single model is usually associated with a single controller.
Now you have the basic setup to use with most of your forms. You can mix different validation directives from Angular (https://docs.angularjs.org/api/ng/directive/input) with validators from HTML5.
The advantage of using Angular validation directives is that they keep data more synchronized between models and controllers. However, when it comes to up-front form validation, it is not that necessary because the validation is occurring before any posting of the data. The data posted by the form is handled by the controller after the form has been validated.
Angular also works with HTML5 validators to provide quick user feedback without having to write any extra code for handling error responses.
Let's look at a complete example that includes the code for the scope as well:
<html lang="en"> <head> <meta charset="UTF-8"> <title>Example - example-example98-production</title> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.19/angular.min.js"></script> <style type="text/css"> .css-form input.ng-invalid.ng-dirty { background-color: #FA787E; } .css-form input.ng-valid.ng-dirty { background-color: #78FA89; } </style> </head> <body ng-app="formExample"> <div ng-controller="ExampleController"> <form novalidate name="formx" class="css-form"> <table> <tr><td align='right'>Name</td><td align='left'><input type='text' name='name' ng-model='user.name' required value=''></td></tr> <tr><td align='right'>Email</td><td align='left'><input type='email' name='email' ng-model='user.email' value=''></td></tr> <tr><td align='right'>Password</td><td align='left'><input type='text' name='password' ng-model='user.password' value=''></td></tr> <tr><td align='right'>Verify Password</td><td align='left'><input type='text' name='passwordx' ng-model='user.passwordx' value=''></td></tr> </table> <button ng-click="reset()">RESET</button> <button ng-click="update(user)">SAVE</button> </form> </div> <script> angular.module('formExample', []) .controller('ExampleController', ['$scope', function($scope) { $scope.master = {}; $scope.update = function(user) { if ($scope.formx.$valid) { $scope.master = angular.copy(user); alert('Form has been validated successfully'); } }; $scope.reset = function() { $scope.user = angular.copy($scope.master); }; $scope.reset(); }]); </script> </body> </html>
There are some key things to take away here. First is the use of CSS to demonstrate the data state of the input fields. If you were to run this code, say in an online editor like Plunker, any required field would be marked Red, and any valid field would be marked Green. If the email address were not correct, it would be Red until there was a proper value for this field. Otherwise, when all the fields are Green, all the validation passed and the form can be saved.
The form's CSS makes this all possible, and Angular takes care of hiding and showing the Red and Green input boxes using special CSS tags. The CSS used in this example has specific tags that Angular recognizes in advance.
For example, the css-form input.ng-invalid.ng-dirty CSS tag tells Angular about any input field that does not meet the criteria for its validator (i.e., HTML5 required) and then applies the Red color.
Otherwise, if the input field does meet the criteria, apply the css-form input.ng-valid.ng-dirty CSS tag, which uses the Green color. With these two CSS tags alone, you can cover most of your form's data-validation scenarios.
In the code, notice the block 'if ($scope.formx.$valid) {'. This condition tells the controller to do something with the data (such as post it to a back-end server) only if all the validation has passed for the form's property fields.