Dean Friedland
Dean Friedland

Reputation: 803

Knockout custom validation with AJAX call issue

My custom validation will validate whether or not a zip code entered into a form field is valid for a state chosen from a dropdown in the preceeding field. It works fine when I first select a state and then type in a valid/invalid zip. However, if it validates and then I go and choose a different state the validation error message does not trigger. Can anyone tell me what I am missing and why it is not re-validating?

Custom Validation:

ko.validation.rules["isValidZipCode"] =
    {
        async: true,
        getValue: function (o) {
            return (typeof o === 'function' ? o() : o);
        },
        validator: function (val, fields, callBack) {
            var self = this;
            var anyOne = ko.utils.arrayFirst(fields, function (field) {
                var val = self.getValue(field);

                if (val === undefined || val === null) 
                    return "";                
                return true;
            });

            var ajaxData = { state: anyOne, zipCode: val }
            $.ajax({
                url: $("a#ValidateZipByState").attr("href"),
                type: "POST",
                data: ajaxData,
                success: function (isValid) {
                    if (isValid) {
                        callBack(true);
                    } else {
                        callBack(false);
                    }
                },
                error: handleSubmitError
            });

            return;
        },
        message: "Invalid zip code for this state"
    };

ViewModel:

self.State = ko.observable(model.State).extend({ required: true });
self.ZipCode = ko.observable(model.ZipCode).extend({ required: true, pattern: /^[0-9]{5}(?:-[0-9]{4})?$/g, isValidZipCode: [self.State, self.ZipCode] });

Upvotes: 1

Views: 754

Answers (2)

Jason Spake
Jason Spake

Reputation: 4304

It looks like the validation rule is only attached to the ZipCode observable so updating the State value by itself would not trigger a new validation. You would have to update the state and then make another change to the zipcode before an update happens if I'm understanding the problem correctly.

If so you might want to try this quick hack to get the validation to update when the State is changed:

self.State.subscribe(function(newValue){
    self.ZipCode.valueHasMutated();
});

Upvotes: 0

Rafael Companhoni
Rafael Companhoni

Reputation: 1790

You can add a subscription to State and use the ko.validation.validateObservable method to trigger the validation on ZipCode -- also, since this validation rule is specific to ZipCode you could use an anonymous custom rule.

self.State = ko.observable("StateB").extend({ 
    required: true
});

self.State.subscribe(function(){
    ko.validation.validateObservable(self.ZipCode);
})

self.ZipCode = ko.observable(789).extend({
    validation: {
        async: true,
        validator: function (val, params, callback) {
            setTimeout(function() {
                var zips = stateZips.get(self.State());
                var exists = zips.some(function(zip) {
                    return zip == new Number(val);
                });
                return callback(exists);
            });
        },
        message: "Invalid zip code for this state"
    }
});

Complete example here: JSFiddle

EDIT: In order to make State trigger the validation (without changing ZipCode first) you have to use:

ko.validation.init({ messagesOnModified: false });

Upvotes: 1

Related Questions