Knockout-Contrib / Knockout-Contrib/Knockout-Validation

Problem with multiple, concurrent async validators

Open
#276 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
1k
Forks
366
PR merge metrics
No merged PRs in 30d

Description

I've got async validations working just fine for a single DOM element [single view model].

However, when I have a collection of view-models in an observableArray bound to a with a ko foreach binding, the "context" of the validation is somehow associated with the wrong view-model.

In both cases I am calling the same ZipCode validation service api. One for the main address and one for all sites associated with the main address.

The behavior I see is that after the initial bind, when I type in a zip code (for say the third item in a list of 5) and the validator fires, if I modify any other properties on the view-model the element bound to the last item in the collection gets the modifications -- not the third item -- the one that was modified in this example. However, the validation message is applied to the correct element.

I've tried adding the view-model as the context option to my ajax call, so I can retrieve the correct view-model, but I still get the last view-model in the list. It's very strange, but this is a major requirement of the application.

I'm desperate.

Here's what my validator currently looks like:

In my helpers the ajax call:

AjaxHelpers.postWithContext = function (urlString, dataObject, successFunction, onAfterErrorFunction, context)
{
var myContext = context;
$.ajax({
type: "POST",
url: urlString,
contentType: "application/json; charset=utf-8",
data: AjaxHelpers.jstring(dataObject),
dataType: "json",
context: myContext,
error: function (request, status, error)
{
if (request.status == "500")
{
alert(error);
if (typeof onAfterErrorFunction === 'function')
{
onAfterErrorFunction(request, status, error);
}
}
else if (request.status == "403")
{
window.location.replace("/Clear/HomeNews.aspx?s=to");
}
else if (request.status == "401")
{
window.location.replace("/Clear/HomeNews.aspx?s=to");
}
},
success: function (data)
{
var self = this;
successFunction(self, data);
$.ajax({ type: "GET", url: '/ComplineWeb/keepalive.asp' });
}
});
};

And in my view-model:

```
function after_get_zip_info(self, data, cv, zc, callback)
{
var zipInfo = AjaxHelpers.unwrap(data);
if (null == zipInfo || $.trim(JavaScriptHelpers.isNull(zipInfo.ZipCode, '')) == '')
{
callback({ isValid: false, message: cv + " is not a valid Zip Code" });
}
else
{
self.County(zipInfo.County); // <---------------- gets applied to the last view-model in the observableArray, not the current view-model.
callback(true);
}
}

ko.validation.rules['RatingLocationZipCode'] = {
async: true,
validator: function (cv, otherVal, callback)
{
cv = $.trim(cv);
var zc = $.trim(_model.ZipCode);
var mySelf = self;

AjaxHelpers.postWithContext(
"/ComplineService.asmx/GetZipInfo",
{ zipCode: cv },
function (context, data) { after_get_zip_info(context, data, cv, zc, callback); },
function () { },
mySelf // <------- should be the current view-model.
);
},
message: 'The Zip Code entered is not valid'
};
```

Contributor guide

Open the contributing guide

Research direction

Start at the ko.validation.rules['RatingLocationZipCode'] validator and AjaxHelpers.postWithContext entry points shown in the issue. Reproduce concurrent validation with an observableArray and foreach binding, then trace which view-model reaches after_get_zip_info; done means County updates the initiating view-model while the validation callback remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.