Knockout computed observable boolean that uses an ajax call not returning boolean on sucess

893 Views Asked by At

I have a knockout pureComputed observable that is supposed to return if a account number is valid. First it checks if the field is empty. (returns false) Then it checks to see if the account number has changed from what was loaded. (returns true) Finally it will run the ajax call to get the account info. If successful it will set the ErrorMessage from the object and the Customer info. Then it is supposed to return true or false based on the ErrorMessage. If unsuccessful it will set the ErrorMessage and return false.

self.isAccountValid = ko.computed(function () {
        if (!self.account()) {//If not complete mark as InValid
            self.accountError("Account Number Incomplete.")
            return false;
        } else if (vm.account == self.account()) {
            self.accountError("");
            return true;
        } else {
            var deferred = $.Deferred();
            return $.ajax({
                url: '/Order/NumberValidation',
                data: { account: self.account() }
            }).success(function (data) {
                self.accountError(data.ErrorMessage);
                //Set customer info properties
                self.setCustomerInfo(data);
                //Set success or warn icon based on error message field
                var isValid = !data.ErrorMessage;
                deferred.resolve(isValid);
                return deferred;
            }).error(function (data) {
                //Set error message for invalid account
                self.accountError("Server error. Please try again in a few minutes.");
                //Set warning icon
                return false;
            })
        }
    }).extend({ async: ko.observable() });

They extend is a method I found to allow the computed to wait on the ajax call:

ko.extenders.async = function (nonObservableComputed, observableResult) {
    nonObservableComputed.subscribe(function (result) {
        jQuery.when(result).then(observableResult);
    });
    return observableResult;
}

This is setting the other data correctly on success, but the return of !data.ErrorMessage which evaluates to false if looking at it through debug is not what is set for the value of isAccountValid. Instead it is being set to the whole data object even though I am trying to return just the boolean.

myViewModel.isAccountValid()
    Object {FirstName: null, LastName: null, StreetAddress: null, City: null, State: null…}
City: null
ErrorMessage: "Sequence contains no matching element"
FirstName: null
LastName: null
PhoneNumber: 0
State: null
StreetAddress: null
ZipCode: 0
1

There are 1 best solutions below

3
On

One problem is that you're using a pureComputed for a function with side effects.

You also have an extra c in acccountError in your success section.

The extender is pretty horrible. It becomes trivial if you pass it the computed you want to use instead of its type, and don't need the added inProgress member.

ko.extenders.async = function (nonObservableComputed, observableResult) {
    nonObservableComputed.subscribe(function (result) {
        jQuery.when(result).then(observableResult);
    });
    return observableResult;
}

Demo: http://jsfiddle.net/sp160tan/1/

Update Pretty sure your problem is that you're returning the ajax result rather than the deferred. Also, the success and error callbacks don't need to return anything; their return values are not used. This could be a source of confusion.

} else {
        var deferred = $.Deferred();
        $.ajax({
            url: '/Order/NumberValidation',
            data: { account: self.account() }
        }).success(function (data) {
            self.accountError(data.ErrorMessage);
            //Set customer info properties
            self.setCustomerInfo(data);
            //Set success or warn icon based on error message field
            var isValid = !data.ErrorMessage;
            deferred.resolve(isValid);
        }).error(function (data) {
            //Set error message for invalid account
            self.accountError("Server error. Please try again in a few minutes.");
            //Set warning icon
            deferred.resolve(false);
        })
        return deferred;
    }