In the previous post, we learnt how to delay model update in AngularJS, In this post we will learn how to validate the form using custom directive in AngularJS.
In below script block, we have created two custom directive, "adult" and "country".
The purpose of this custom directive is to ensure that user does not enter less than 18 and greater than 100 values in the control
In the first "adult" directive, we have made the ng-model directive mandatory using "require" keyword as we are going to perform validation based on model value of the input element.
Then we are using "link" keyword that is used to call a function for the directive. This link function defines APIs and functionality are necessary for the custom directive. This link funciton is executed for each instance of the directive.
This link function accepts four parameter by default and they are
validator
object and calling a function that accepts modelValue
and viewValue
parameters. If the model value of the control is empty ($isEmpty), we are returning true (as we want empty text box treated as valid), otherwise we are converting the viewValue to number and then checking if its value is greater than 18 and less than 100 that returns true otherwise false. Returning false mark the $validator.adult as invalid and ultimately overall element on which "adult" directive is attached as invalid.<script>
var app = angular.module("app", []);
app.directive('adult', function () {
return {
require: 'ngModel',
link: function (scope, element, attributes, control) {
control.$validators.adult = function (modelValue, viewValue) {
if (control.$isEmpty(modelValue)) // if empty, correct value
{
return true;
}
var age = Number(viewValue);
if (age >= 18 && age <= 100) // correct value
{
return true;
}
return false; // wrong value
};
}
};
});
app.directive('country', function ($q, $timeout) {
return {
require: 'ngModel',
link: function (scope, element, attributes, control) {
// temporary data from local variable
var countryNames = ['India', 'USA', 'Canada'];
control.$asyncValidators.country = function (modelValue, viewValue) {
if (control.$isEmpty(modelValue)) {
return $q.when();
}
var defer = $q.defer();
// to imitate the behavior of service access
// In real time, fire $http and get value then validate
$timeout(function () {
if (countryNames.indexOf(modelValue) === -1) // not found
{
defer.resolve(); // wow, this country available
}
else
{
defer.reject();
}
}, 1000);
return defer.promise;
};
}
};
});
</script>
The purpose of this custom directive is to ensure that user doesn't enter the same country name that already exists in the database (in this case, JavaScript array).
In the country directive also, we have set ng-model directive as mandatory as we will be working on this value. As we are going to use $q (a service that help us to run function asynchronously and use their return value) and $timeout (a wrapper of window.setTimeout that helps us to execution a block of code after certain interval) to and $timeout Under the link funciton, for this demonstration we have taken array of country as "countryNames" against which we will validate. In this case, we have used $asyncValidators
object that handles asynchrnous validation (generally used to make server side request).
Here also we are checking for empty model value, and then calling $q.when()
that marks it valid. Then we are getting the $.defer()
function that creates a deferred object that represents a task which will finish in future.
To imitate the behavior of the server side call, we are using $timeout
funciton that delays the execution of the code inside. In this function we are checking for the modelValue and if not found, we return defer.reject()
(kind of false) otherwise defer.resolve()
(kind of true). The last 2nd parameter of $timeout
is 1000 that instruct that this function should take 1 second time to respond. As said, this is just to imitate the behavior of server side call.
Ultimately, it returns defer.promise. Now based on whether $q.when(), $q.resolve() or $q.reject() function is called the defer.promise
returns true and false.
HTML code
<div ng-app="app">
<form novalidate name="myForm">
Your age: <input type="number" name="txtAge" ng-model="age" adult />
<span style="color:red;" ng-show="myForm.txtAge.$error.adult">Should be => 18 and <= 100</span>
<hr />
Country : <input type="text" name="txtCountry" ng-model="myCountry" country />
<span style="color:red;" ng-show="myForm.txtCountry.$pending.country">Validating ....</span>
<span style="color:brown;" ng-show="myForm.txtCountry.$error.country">Already exists, try another</span>
</form>
</div>
In the HTML block, we Age text box with "adult" directive set and model as age. We also have a error message written with <span ng-show> that is shown based on whether myForm.txtAge.$error.adult
returns true or false. The $validator.adult
function defined in the "adult" directive returns true or false based on logic written.
In the Country text box, "country" directive is set. Based on the validation logic written in the country directive, $asyncValidators.country
function returns true or false that sets myForm.txtCountry.$pending.country
and myForm.txtCountry.$error.country
. As execution of the function defined is taking 1 second time, the <span> message of myForm.txtCountry.$pending.country
will be shown (notice $pending) and when done, based on whether the function returns true or false
<span> message is shown.myForm.txtCountry.$error.country
Output
Views: 23221 | Post Order: 56