Building a Custom AngularJS Unique Value Directive

Khi phát triển ứng dụng, một trong những validate dữ liệu phổ biến trên form đó là "unique value". Ví dụ, khi nhập email, user name ta thường sẽ phải kiểm tra email hoặc user name đó đã tồn tại hay chưa. Tất nhiên là có rất nhiều cách để giải quyết vấn đề này, chẳng hạn như bạn có thể gửi AJAX request trong controller của Angular. Tuy nhiên, nếu như bạn muốn tối ưu và có thể tái sử dụng nhiều lần việc validate unique này thì sử dụng một custom directive là một lựa chọn tốt.

Trong bài viết này, tôi sẽ trình bày cách tạo ra một unique directive đơng giản cũng như cách sử dụng chúng.

Tạo data service

Kiểm tra dữ liệu thường phải tực hiện một yêu cầu đến API để thực hiện việc truy vấn và kiểm tra trong database. Trong AngularJS, ta có được cung cấp một service $http để thực hiện mọi AJAX request một cách đơn giản như trong ví dụ dưới đây:

angular.module('customersApp')
    .factory('dataService', ['$http', function ($http) {
        var serviceBase = '/api/dataservice/',
            dataFactory = {};

        dataFactory.checkUniqueValue = function (property, value) {
            if (!id) id = 0;
            return $http.get(serviceBase + 'checkUnique/' + '?property=' +
              property + '&value=' + escape(value)).then(
                function (results) {
                    return results.data.status;
                });
        };

        return dataFactory;

}]);

Tạo Unique Directive

Để handle việc kiểm tra unique value, tiếp theo ta sẽ tạo một custom directive có tên là myUnique :

angular.module('customersApp')
    .directive('myUnique', ['dataService', function (dataService) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
        }
    }
}]);

Trong đoạn mã trên, ta có thể thấy directice này yêu cầu quyền truy cập đến ngModel. Vậy tại sao ta cần quyền truy cập đến ngModel?. Lý do là bởi vì nó sẽ cho phép ta sử dụng được đối tượng ngModelController để handle việc data binding, validate...

Khi một directive đã được load thì sẽ gọi đến phương thức link, trong phương thức này ta có thể biết được element đang sử dụng, attributes và ngModelController. Dưới đây, là full code của directive:

angular.module('customersApp')
    .directive('myUnique', ['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.myUnique);
                var currentValue = element.val();
                dataService.checkUniqueValue( keyProperty.property, currentValue)
                    .then(function (unique) {
                        if (currentValue == element.val()) {
                            ngModel.$setValidity('unique', unique);
                        }
                    }, function () {

                        ngModel.$setValidity('unique', true);
                    });
            });
        }
    }
}]);

Trong đoạn code trên, tôi đã bắt đầu bằng việc sử dụng sự kiện blur của element. Tôi không muốn request lên server quá nhiều lần mỗi khi element thay đổi giá trị (dù chỉ là một ký tự), ta chỉ kiểm tra khi người sử dụng ngừng thay đổi giá trị và focus sang element khác.

Ngoài ra, tôi có dùng hàm $eval() để convert giá trị trong attributes myUnique thành một đối tượng có định dạng như sau:

{property: 'email'}

Trong đó, property để lưu thong tin attribute cần kiểm tra unique value (Ví dụ: email, user_mane).

Bên cạnh đó, ta cần phải handle kết quả trả về từ server (true - có unique, false - đã tồn tại) và set nó vào trong phương thức $setValidity. Đây là phương thức đặc biệt của AngularJS để quyết định xem giá trị hiện tại của model đã hợp lệ hay chưa.

Hiển thị Error Messages#

Để hiển thị error message của unique value ta sử dụng directive ng-show và kiểm tra key unique của object $error như sau:

<form name="editForm">
<input type="text" name="email"
    ng-model="customer.email"
    my-unique="{property: 'email'}"
    required />
<span class="errorMessage" ng-show="editForm.email.$dirty && editForm.email.$error.unique">
    Email already in use
</span>
</form>

Kết quả sẽ được hiển thị như sau:

image_4255F39B.png

Kết luận#

Directive cung cấp cho ta một cách tuyệt vời để đóng gói các chức năng. Trên đây là một ví dụ nhỏ về việc ứng dụng directive trong Angular để kiểm tra giá trị email phải là duy nhất và hiển thị thông báo lỗi cho người dùng.

Cảm ơn các bạn đã theo dõi! (Thankyou)


All Rights Reserved