I’m going to write an AngularJs Directive version of the Age Limit Validator Plugin I wrote for one of my jQuery projects. Default settings such as age limit value, invalid age message, etc. can be easily configured. Also this directive leverages HTML5 form validation thus it plays nice with existing HTML5 forms.
Checkout the demo or download source code on Github.
I start by writing the directive’s bare structure.
angular.module('app.widget', [])
.directive('ageLimit', function () {
return {
link: function ($scope, $element, $attrs, ngModelCtrl) {
}
};
});
The directive’s name is ageLimit
and is inside the app.widget
module.
Next is to add some default settings.
angular.module('app.widget', [])
.directive('ageLimit', function () {
return {
link: function ($scope, $element, $attrs, ngModelCtrl) {
var settings = {
minAge: 18,
underAgeMsg: 'You need to be at least 18 years old.',
title: null,
pattern: null
},
.... more code
}
};
});
As you can see, I have 4 default settings.
Keep in mind that all the default settings are actual attributes on the input element. Also the default values will be overridden by the set values on the elements attributes. So for example, if I do this on my view,
<input type="text" age-limit min-age=20 title="Hi, you are under age!" />
The new minAge
value is now 20 and the overridden title
value is now Hi, you are under age!.
Thus, I need a function that will override these default setting values with the set values from the element’s attributes.
I’ll call it copyUserSettings
.
angular.module('app.widget', [])
.directive('ageLimit', function () {
return {
link: function ($scope, $element, $attrs, ngModelCtrl) {
var settings = {
minAge: 18,
underAgeMsg: 'You need to be at least 18 years old.',
title: null,
pattern: null
},
//override default values
copyUserSettings = function (attrs, settings) {
var property;
for (property in settings) {
if (settings.hasOwnProperty(property) && attrs.hasOwnProperty(property)) {
settings[property] = attrs[property];
}
}
},
.... more code
}
};
});
Next is I need to write a function that will calculate age based from the entered date.
angular.module('app.widget', [])
.directive('ageLimit', function () {
return {
link: function ($scope, $element, $attrs, ngModelCtrl) {
....more code
calculateAge = function (birthday, settings) {
var regex, today, birthDate, age, month;
if (!settings.pattern) {
//validate in mm-dd-yyyy format
regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/;
if (!regex.test(birthday)) {
return -1;
}
}
today = new Date();
birthDate = new Date(birthday);
age = today.getFullYear() - birthDate.getFullYear();
month = today.getMonth() - birthDate.getMonth();
if (month < 0 || (month === 0
&& today.getDate() < birthDate.getDate())) {
age = age - 1;
}
if (isNaN(age)) {
return -1; //if age is invalid
}
return age;
};
.... more code
}
};
});
The calculateAge
function primarily checks for the value of the pattern
attribute from the element.
If there’s no set pattern
value, it will validate the birthday
value (entered date) in mm-dd-yyyy
format. If the entered date is in the right format,
function will then proceed to calculate the age based from the birthday
value.
Now for the actual validation,
angular.module('app.widget', [])
.directive('ageLimit', function () {
/*jslint unparam: true*/
return {
link: function ($scope, $element, $attrs, ngModelCtrl) {
....more code
//where actual validation happens
copyUserSettings($attrs, settings);
$element.on('focusout keydown', function (e) {
var age = calculateAge(e.target.value, settings);
e.target.setCustomValidity(""); //clear validation
if (-1 === age && settings.title) {
e.target.setCustomValidity(settings.title);
return;
}
if (age < parseInt(settings.minAge, 10)) {
e.target.setCustomValidity(settings.underAgeMsg);
}
});
}
};
});
Actual validation starts by calling the copyUserSettings
function. AngularJs’ $attrs
object exposes all the set attribute values from the element ($element
).
Next is I hooked up an event listener function to the element which fires if either focusout or keydown event occurs.
See the directive’s whole code here or see the readme on how to use on your existing AngularJs application.