One of the tasks that comes up fairly frequently in application development is ensuring that a specific value is unique as a user enters it into a form. For example, you may have a user name or email that is entered by a user that you want to ensure is unique before allowing them to submit the form. There are a variety of ways to handle that type of scenario including making standard Ajax calls directly from an AngularJS controller, but if you find yourself performing this type of task over and over it may be time to consolidate your code and make a re-useable component. Enter directives â the perfect way to add âunique valueâ functionality into a form.
In this post Iâm going to walk through a simple unique value directive and show how it can be used. Along the way Iâll also discuss a few aspects of validation in AngularJS as well. An example of it in action can be seen here:
Â
The directive that controls checking for a unique value is applied to an element in the following way (note that I prefer to prefix directives with the HTML5 data- prefix):
<input type="email" name="email" data-ng-model="customer.email" data-wc-unique="{key: customer.id, property: 'email'}" required />
Before jumping into the directive letâs take a look at an AngularJS factory that can be used to check if a value is unique or not.
Unique value checks typically rely on a call to a back-end data store of some type. AngularJS provides services such as $http and $resource that are perfect for that scenario. For this demonstration Iâll use a simple custom factory named dataService that relies on $http to make Ajax calls back to a server. Note that I donât typically distinguish between the term âfactoryâ and âserviceâ as far as the name goes since they ultimately do the same thing and âserviceâ just sounds better to me (personal preference).
Â
angular.module('customersApp') .factory('dataService', ['$http', function ($http) { var serviceBase = '/api/dataservice/', dataFactory = {}; dataFactory.checkUniqueValue = function (id, property, value) { if (!id) id = 0; return $http.get(serviceBase + 'checkUnique/' + id + '?property=' +
property + '&value=' + escape(value)).then( function (results) { return results.data.status; }); }; return dataFactory; }]);
Â
Creating the Unique DirectiveTo handle checking unique values I created a custom directive named wcUnique (the wc stands for Wahlin Consulting â my company). Itâs a fairly simple directive that is restricted to being used as an attribute. The shell for the directive looks like the following:
angular.module('customersApp') .directive('wcUnique', ['dataService', function (dataService) { return { restrict: 'A', require: 'ngModel', link: function (scope, element, attrs, ngModel) {
} } }]);
Looking through the code you can see that the directive relies on the dataService shown earlier. It restricts the directive to being used as an attribute and requires access to ngModel. Why require ngModel? Itâll be used to access an ngModelController object that sets a validity value depending on whether or not a given value is unique. Every time you use the ngModel directive thereâs a little ngModelController helper object working behind the scenes to handle data binding, validation, and more. Hereâs what it contains. Take special note of the $error property since itâll be discussed later.
Â
Â
As the directive is loaded the link function will be called which gives access to the current scope, the element the directive is being applied to, attributes on the element, and the ngModelController object. Hereâs the full directive with the contents of the link function included:
Â
angular.module('customersApp') .directive('wcUnique', ['dataService', function (dataService) { return { restrict: 'A', require: 'ngModel', link: function (scope, element, attrs, ngModel) { element.bind('blur', function (e) { if (!ngModel || !element.val()) return; var keyProperty = scope.$eval(attrs.wcUnique); var currentValue = element.val(); dataService.checkUniqueValue(keyProperty.key, keyProperty.property, currentValue) .then(function (unique) { //Ensure value that being checked hasn't changed //since the Ajax call was made if (currentValue == element.val()) { ngModel.$setValidity('unique', unique); } }, function () { //Probably want a more robust way to handle an error //For this demo we'll set unique to true though ngModel.$setValidity('unique', true); }); }); } } }]);
The code within the link function starts off by binding to the blur event of the element. I didnât want to call back to the server every time a user enters something so blur worked out well in this situation. Once the blur event fires the attributes of the element are accessed. Specifically the value assigned to the wcUnique directive is accessed. AngularJSâs $eval() function is used to convert the value into an object literal that is assigned to a keyProperty variable. Hereâs what the keyProperty value looks like:
{key: customer.id, property: 'email'}
The key represents the unique key for the customer object (ultimately the unique identifier for the record). This is used so that we exclude the current customer when checking for a unique value across customers. The property represents the name of the customer object property that should be checked for uniqueness by the back-end system.
Once the keyProperty variable is filled with data, the key and property values are passed along with the elementâs value (the value of the textbox for example) to a function named checkUniqueValue() thatâs provided by the factory shown earlier. This triggers an Ajax call back to the server which returns a true or false value. Once the value is returned from the back-end service itâs used in a call to ngModel.$setValidity(). As mentioned earlier, ngModel represents a special object which is an ngModelController (you can think of it as a little mini-controller used for data binding, validation, and other functionality) thatâs created due to the presence of the ngModel directive shown next. It can be used to determine if a value bound to a control is dirty and if itâs valid or not.
Â
<input type="email" name="email" data-ng-model="customer.email" data-wc-unique="{key: customer.id, property: 'email'}" required />
The $setValidity() function is a built-in AngularJS function that is used to assign a key/value combination that can be used to check the validity of a specific model value. The key in this case is âuniqueâ and the value is either true or false. When the $setValidity() function is called, the unique value is added to an $error object that can be used to check if a value is valid or not.
The unique property added to the $error object can be used to show and hide error messages. In the previous section you saw that the $error object is updated but how do you access the $error object? When using AngularJS forms, a name attribute is typically added to the <form> element as shown next:
<form name="editForm">
Â
The editForm value causes AngularJS to create a child controller named editForm that is associated with the current scope. In other words, editForm is added as a property of the current scope (the scope originally created by your controller). The textbox shown earlier has a name attribute value of âemailâ which gets converted to a property that is added to the editForm controller. Itâs this email property that gets the $error object. Confusing? Letâs look at an example of how we can check the unique value to see if the email address is unique or not which should clear things up:
<input type="text" name="email" data-ng-model="customer.email" data-wc-unique="{key: customer.id, property: 'email'}" required /> <span class="errorMessage" ng-show="editForm.email.$dirty && editForm.email.$error.unique"> Email already in use </span>
Notice that the ngShow directive checks the editForm property of the current scope and then drills down into the email property. It checks if the value is dirty and if the $error.unique value is true or false. The unique value was set using the $setValidity() function shown earlier. If the value is dirty and the unique value is false then the span is hidden. If itâs dirty and the unique value is true then the contents of the span are shown and the user sees that they need to enter a different email address. The end result is the red error message shown next:
Â
Â
ConclusionDirectives provide a great way to encapsulate functionality that can be used in views. In this post youâve seen a simple AngularJS unique directive that can be applied to textboxes to check if a specific value is unique or not and display a message to the user. To see the directive in an actual application check out the Customer Manager sample application at https://github.com/DanWahlin/CustomerManagerStandard.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4