I have an async validator that calls a service will return 200 (OK) or 400 (BadRequest) with a message.
I'd like to return the message as the validation message but can't figure out how to get the message to show up. I've tried a few things without success.
<div ng-messages="searchFilterForm.search.$error">
<small ng-message="valid">Invalid search filter. Reason: {{MY_BAD_REQUEST_RESPONSE_MESSAGE_GOES_HERE}}</small>
</div>
Here's a jsfiddle: http://jsfiddle.net/javajunkie314/r6oyLe26/3/
The idea is that the myValidator
and myErrorMessage
directives synchronize their error message via the myErrorHandler
directive. Annoyingly, I can't find any way to access the reason passed to reject
by the validator.
Edit: I've updated the fiddle. Now myValidator
uses two asynchronous validators. I've also created a ValidatorPromise
to handle updating the myErrorHandler
controller. This is as I described in my comment below.
The HTML:
<form ng-controller="testCtrl">
<div my-error-handler="">
<input type="text" name="foo" ng-model="foo.text" my-validator="">
<div my-error-message=""></div>
</div>
</form>
The JavaScript:
(function () {
var app = angular.module('myApp', []);
/* Wraps a promise. Used to update the errorHandler controller when the
* validator resolves. */
app.factory('ValidatorPromise', function ($q) {
return function (errorHandler, name, promise) {
return promise.then(
// Success
function (value) {
// No error message for success.
delete errorHandler.error[name];
return value;
},
// Failure
function (value) {
// Set the error message for failure.
errorHandler.error[name] = value;
return $q.reject(value);
}
);
};
});
app.controller('testCtrl', function ($scope) {
$scope.foo = {
text: ''
};
});
app.directive('myErrorHandler', function () {
return {
controller: function () {
this.error = {};
}
};
});
app.directive('myValidator', function ($timeout, $q, ValidatorPromise) {
return {
require: ['ngModel', '^myErrorHandler'],
link: function (scope, element, attrs, controllers) {
var ngModel = controllers[0];
var myErrorHandler = controllers[1];
ngModel.$asyncValidators.test1 = function () {
return ValidatorPromise(
myErrorHandler, 'test1',
$timeout(function () {}, 1000).then(function () {
return $q.reject('Fail 1!');
})
);
};
ngModel.$asyncValidators.test2 = function () {
return ValidatorPromise(
myErrorHandler, 'test2',
$timeout(function () {}, 2000).then(function () {
return $q.reject('Fail 2!');
})
);
};
}
};
});
app.directive('myErrorMessage', function () {
return {
require: '^myErrorHandler',
link: function (scope, element, attrs, myErrorHandler) {
scope.error = myErrorHandler.error;
},
/* This template could use ngMessages to display the errors
* nicely. */
template: 'Error: {{error}}'
};
});
})();
I prepared a plunker that I think do what you want. It uses a modified version of ui-validate but I think you can get the idea anyway.
I defined service called 'remoteValidator'. It has a method 'validate' that simulates the trip to the server returning a promise that rejects with a message if the value is 'bad'.
app.service("remoteValidator", function($q, $timeout) {
this.validate = function(value) {
return $q(function(resolve, reject) {
setTimeout(function() {
if (value === "bad") {
reject("Value is bad");
} else {
resolve();
}
}, 1000);
});
}
});
Then I defined in the controller a validator that use that service and capture the error message and return the promise in order to your validator of choice do the work.
$scope.remoteValAsync = function(value) {
var promise = remoteValidator.validate(value);
promise.then(function() {
delete $scope.errorMsg;
}, function(error) {
$scope.errorMsg = error;
});
return promise;
};
This way, when you write 'bad' in the async inputbox, when promises resolve, error message appears in the scope along with the validators name inside form.field.$error list.
A snippet of the html:
<div class="input-group-inline">
<input class="form-control" type="text" name="nextPassword" ng-model="passwordForm.nextPassword"
ui-validate-async="{ badValue: 'remoteValAsync($value)' }" />
<p>Pending validations:</p>
<ul>
<li ng-repeat="(key, errors) in myForm.nextPassword.$pending track by $index">{{ key }}</li>
</ul>
<p>Bad validations:</p>
<ul>
<li ng-repeat="(key, errors) in myForm.nextPassword.$error track by $index">{{ key }}</li>
</ul>
<p>{{ errorMsg }}</p>
</div>
I know droping the message in 'errorMsg' is not the best. It could be made better but I have to get in into de ui-validate-async directive.
I hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With