You don't need to make $http request in directive, better place for it is controller.
You can specify method inside controller - $scope.saveDeployment = function () { // here you make and handle your error on request ... };
you'll save error to scope and then create a directive that will watch $scope.yourResponseObject
and set validity based on it.
Also if you need something like request and error on input field blur instead, you need to create a simple directive with elem.bind('blur', ...)
where you call $scope.saveDeployment
with callback to handle validity.
Take a look on the examples, there might be something similar - https://github.com/angular/angular.js/wiki/JsFiddle-Examples
Validating a form input field using an asynchronous $http
ajax call is a common need, but I haven't found any implementations that were complete, reusable, and easy to use so I tried my best to make one.
This functionality is especially useful for checking if a username, email, or other field/column is unique, but there are plenty of other use cases where a value must be validated with an ajax call (as in your example).
My solution has the following features:
- Accepts a "check" function from the
$scope
that makes the $http
call or any kind of validation (synchronous or asynchronous) - Accepts a "gate" function from the
$scope
that allows the check to be bypassed based on the value or ngModel
state. - Debounces the "check" function's execution until the user has stopped typing
- Ensure that only the latest
$http
call result is used (in case multiple are fired and return out of order). - Allows for state bindings so that the UI can respond appropriately and conveniently.
- Customizable debounce time, check/gate functions, binding names and validation name.
My directive is pmkr-validate-custom (GitHub)
. It can be used for any asynchronous validation. I've tested it in several versions as far back as 1.1.5.
Here is a sample usage with Twitter Bootstrap in which I check if a username is unique.
<form name="the_form"class="form-group has-feedback">
<divng-class="{'has-success':userNameUnique.valid, 'has-warning':userNameUnique.invalid}"><labelfor="user_name">Username</label><inputname="user_name"ng-model="user.userName"pmkr-validate-custom="{name:'unique', fn:checkUserNameUnique, gate:gateUserNameUnique, wait:500, props:'userNameUnique'}"pmkr-pristine-original=""class="form-control"
><spanng-show="userNameUnique.valid"class="glyphicon glyphicon-ok form-control-feedback"></span><spanng-show="userNameUnique.invalid"class="glyphicon glyphicon-warning-sign form-control-feedback"></span><ing-show="userNameUnique.pending"class="glyphicon glyphicon-refresh fa-spin form-control-feedback"></i><png-show="userNameUnique.valid"class="alert alert-success">"{{userNameUnique.checkedValue}}" is availiable.</p><png-show="userNameUnique.invalid"class="alert alert-warning">"{{userNameUnique.checkedValue}}" is not availiable.</p><buttonng-disabled="the_form.$invalid || the_form.user_name.$pristine || userNameUnique.pending"class="btn btn-default"
>Submit</button></div></form>
Sample controller:
$scope.checkUserNameUnique = function(value) {
return$http.get(validationUrl+value).then(function(resp) {
return isValid;
});
}
$scope.gateUserNameUnique = function(value, $ngModel) {
return !value || $ngModel.$pristine;
};
If I make any improvements, they will be up-to-date on GitHub, but I am also going to put the code here for this directive and its dependencies (may not be updated). I welcome suggestions or issues though GitHub issues!
angular.module('pmkr.validateCustom', [
'pmkr.debounce'
])
.directive('pmkrValidateCustom', [
'$q',
'pmkr.debounce',
function($q, debounce) {
var directive = {
restrict: 'A',
require: 'ngModel',
priority: 1,
link: function($scope, $element, $attrs, $ngModel) {
var opts = $scope.$eval($attrs.pmkrValidateCustom);
var props = {
pending : false,
validating : false,
checkedValue : null,
valid : null,
invalid : null
};
opts.props && ($scope[opts.props] = props);
var debouncedFn = debounce(validate, opts.wait);
var latestFn = debounce.latest(debouncedFn);
$ngModel.$setValidity(opts.name, true);
var gate;
$scope.$watch(function() {
return$ngModel.$viewValue;
}, valueChange);
functionsetValidity(isValid) {
$ngModel.$setValidity(opts.name, isValid);
if (gate) {
props.valid = props.invalid = null;
} else {
props.valid = !(props.invalid = !isValid);
}
}
functionvalidate(val) {
if (gate) { return; }
props.validating = true;
return opts.fn(val);
}
functionvalueChange(val) {
if (opts.gate && (gate = opts.gate(val, $ngModel))) {
props.pending = props.validating = false;
setValidity(true);
return;
}
props.pending = true;
props.valid = props.invalid = null;
latestFn(val).then(function(isValid) {
if (gate) { return; }
props.checkedValue = val;
setValidity(isValid);
props.pending = props.validating = false;
});
}
}
}; return directive;
}
])
;
angular.module('pmkr.debounce', [])
.factory('pmkr.debounce', [
'$timeout',
'$q',
function($timeout, $q) {
var service = function() {
return debounceFactory.apply(this, arguments);
};
service.immediate = function() {
return debounceImmediateFactory.apply(this, arguments);
};
service.latest = function() {
return debounceLatestFactory.apply(this, arguments);
};
functiondebounceFactory(fn, wait) {
var timeoutPromise;
functiondebounced() {
var deferred = $q.defer();
var context = this;
var args = arguments;
$timeout.cancel(timeoutPromise);
timeoutPromise = $timeout(function() {
deferred.resolve(fn.apply(context, args));
}, wait);
return deferred.promise;
}
return debounced;
}
functiondebounceImmediateFactory(fn, wait) {
var timeoutPromise;
functiondebounced() {
var deferred = $q.defer();
var context = this;
var args = arguments;
if (!timeoutPromise) {
deferred.resolve(fn.apply(context, args));
}
$timeout.cancel(timeoutPromise);
timeoutPromise = $timeout(function() {
timeoutPromise = null;
}, wait);
return deferred.promise;
}
return debounced;
}
functiondebounceLatestFactory(fn) {
var latestArgs;
functiondebounced() {
var args = latestArgs = JSON.stringify(arguments);
var deferred = $q.defer();
fn.apply(this, arguments).then(function(res) {
if (latestArgs === args) {
deferred.resolve(res);
}
}, function(res) {
if (latestArgs === args) {
deferred.reject(res);
}
});
return deferred.promise;
}
return debounced;
}
return service;
}
])
;
angular.module('pmkr.pristineOriginal', [])
.directive('pmkrPristineOriginal', [
function() {
var directive = {
restrict : 'A',
require : 'ngModel',
link: function($scope, $element, $atts, $ngModel) {
var pristineVal = null;
$scope.$watch(function() {
return$ngModel.$viewValue;
}, function(val) {
if (pristineVal === null) {
pristineVal = $ngModel.$isEmpty(val) ? '' : val.toString();
}
if (pristineVal === val) {
$ngModel.$setPristine();
}
});
}
};
return directive;
}
])
;
Post a Comment for "Angularjs Custom Form Validation Using $http"