There are a couple of things going wrong. First, how do I catch the error in the template, because the has-error class doesn't get applied, even though the response is 404 and the proper code is executed.
Second, this only works the first time around. If I leave the field and then enter it again, each time I press a key I get a TypeError: validator is not a function exception (as you can see in the code, I execute this only on blur). Where am I going wrong?
The service call
this.CheckIfUnique = function(code) {
var deferred = $q.defer();
$http.get("/Api/Codes/Unique/" + code).then(function() {
deferred.resolve();
}, function() {
deferred.reject();
});
return deferred.promise;
};
The directive
var uniqueCode = [ "CodeService", function(codeService) {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attrs, ctrl) {
element.bind("blur", function(e) {
if (!ctrl || !element.val()) return;
var currentValue = element.val();
ctrl.$asyncValidators.uniqueCode = codeService.CheckIfUnique (currentValue);
});
}
};
}];
codeModule.directive("uniqueCode",uniqueCode);
The HTML
<div class="form-group" ng-class="{'has-error' : ( codeForm.submitted && codeForm.code.$invalid ) || ( codeForm.code.$touched && codeForm.code.$invalid ) }">
<label class="col-md-4 control-label" for="code">Code</label>
<div class="col-md-8">
<input class="form-control" id="code" name="code" ng-model="newCode.code" ng-required="true" type="text" unique-code />
<span class="help-block" ng-show="( codeForm.submitted && codeForm.code.$error.required ) || ( codeForm.code.$touched && codeForm.code.$error.required)">Please enter a code</span>
<span class="help-block" ng-show="codeForm.code.$pending.code">Checking if the code is available</span>
<span class="help-block" ng-show="( codeForm.submitted && codeForm.code.$error.uniqueCode ) || ( codeForm.code.$touched && codeForm.code.$error.uniqueCode)">This code already exist</span>
</div>
</div>
The MVC controller
public async Task<ActionResult> Unique(string code)
{
if (string.IsNullOrWhiteSpace(code))
{
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}
return _db.Codes.Any(x => x.Code = code)
? new HttpStatusCodeResult(HttpStatusCode.NotFound)
: new HttpStatusCodeResult(HttpStatusCode.Accepted);
}
EDIT:
Just to clarify, the API gets called only when I leave the field, the exception gets thrown on key down (and only after the first time I leave the field)
EDIT 2:
In case someone misses the comment, dfsq's answer works and if you add ng-model-options="{ updateOn: 'blur' }" to the input it'll validate on blur only.
resolve()be in the success callback instead of the failure in your service call?codeForm.$submittedtoo in your markup ;-)linkyou should be able to see a snapshot of the DOM at the point validation occurs (in Chrome at least) including the dynamic form classes, which might help diagnose it.