3

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.

6
  • Should the call to resolve() be in the success callback instead of the failure in your service call? Commented Dec 16, 2015 at 16:34
  • It shoud, my bad :) stuck on this for a whie now, and must have switched 'em at some point. But that still doesn't solve the issue. I edited the Q Commented Dec 16, 2015 at 16:37
  • 1
    It should be codeForm.$submitted too in your markup ;-) Commented Dec 16, 2015 at 16:53
  • Just read that that's been implemented, so I'm adding it to the list of changes I need to make Commented Dec 16, 2015 at 16:55
  • Beyond that, assuming you're getting the correct data from the server I'm not sure what to suggest. If you stick a break point on your service call or on link you 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. Commented Dec 16, 2015 at 16:55

2 Answers 2

1

You didn't provide proper validator function. It should use anonymous function that returns promise object (from your service). You also don't need blur event:

var uniqueCode = ["CodeService", function(codeService) {
    return {
        restrict: "A",
        require: "ngModel",
        link: function(scope, element, attrs, ctrl) {            
            ctrl.$asyncValidators.uniqueCode = function(value) {
                return codeService.CheckIfUnique(value);
            };
        }
    };
}];
codeModule.directive("uniqueCode", uniqueCode);

In addition, you should clean up service method to not use redundant deferred object:

this.CheckIfUnique = function(code) {
    return $http.get("/Api/Codes/Unique/" + code);
};
Sign up to request clarification or add additional context in comments.

3 Comments

This fixed it, thanks, but I still need the blur event because, after I start typing the new code, I make an api call on every key pressed
And, because it was a two part question, what about the validation message? It still doesn't show it
fixed the blur issue with this ng-model-options="{ updateOn: 'blur' }"
0

About the other part of your question, the message is not showing because: For the "Checking the code" message you have codeForm.code.$pending.code and it should be codeForm.code.$pending.uniqueCode

Here is the plunker working: http://plnkr.co/edit/FH8GBhOipgvvUzrFYlwx?p=preview

1 Comment

Noticed that literary 10mins ago and I was about to post it here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.