Knockout-Contrib / Knockout-Contrib/Knockout-Validation
Problem with multiple, concurrent async validators
- 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
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