Today I am tasked with an issue that I have been pondering for a while. I have a form on a page that, when an item is selected from a dropdown, there is a database call that gets information and sets a Required Date field according to the retrieved data. This works fine, but I have the dropdown set to knockout's change
event binder, and when revisiting the page for an update, this event runs twice before the user interacts with the page, thus setting the Required Date to whatever the database call initially gets. Problem here is that the end user could set a different date than the estimated date, and I need this call to not overwrite the existing date unless the user actually interacts with the dropdown.
I'll try and trim this to save some headaches...
The dropdown:
@Html.SearchPickerFor(m => m.ItemNumber, "ItemPicker", new { @class = "form-control" }.AddMore("data-bind", "value: $data.ItemNumber, event: { change: ItemNumericDetails($data) }" ))
Goes to the ItemNumericDetails function on change:
Essentially a redirect to two other functions.
var ItemNumericDetails = function (lineData) {
GetItemInfo(lineData);
GetPriceUpdateTolerance(lineData);
}
These two functions are very similar AJAX calls, the one we're concerned with is GetItemInfo
. lineData is a single line in my model (a child in a 1-to-many model relationship), coming from knockout's $data
functionality.
var GetItemInfo = function (lineData) {
var orgId = lineData.ShipToOrgID();
var orgType = lineData.ShipToOrgType();
var itemNo = lineData.ItemNumber();
if ((orgId !== undefined && orgId !== "" && orgId !== null) &&
(orgType !== undefined && orgType !== "" && orgType !== null) &&
(itemNo !== undefined && itemNo !== "" && itemNo !== null)) {
var model = {
orgId: orgId,
orgType: orgType,
itemNumber: itemNo,
destTPID: koModel.ModelData.DestTPID()
};
var model = ko.mapping.toJS(model);
$.ajax({
url: '@Url.Action("ItemInfo")',
method: 'GET',
headers: addAntiForgeryHeader(),
datatype: 'json',
contentType: "application/json; charset=utf-8",
traditional: true,
data: model
}).done(function (data) {
//...
//model value assignment
//...
// V V V DATE CHANGING CODE HERE
var date = new Date();
lineData.CalculatedDate(date.addDays(lineData.LeadTime()));
lineData.RequiredDate(lineData.CalculatedDate());
// ^ ^ ^
}).fail(function () {
console.log("An error occurred while getting order quantities data.");
});
}
}
What I have tried:
The working method I have at the moment is that I have set a global-level iterator, called OQIterations
that starts at zero and counts up each time the call is run, and after it has run twice the date changes are then permitted to be made. This is what that looks like:
var OQIterations = 0;
var GetItemInfo = function (lineData) {
if (lineData.OQIterations === undefined) {
lineData.OQIterations = OQIterations;
// Assigns each line it's own iteration counter if it does not have one.
}
var orgId = lineData.ShipToOrgID();
var orgType = lineData.ShipToOrgType();
var itemNo = lineData.ItemNumber();
if (...) {
var model = {
...
};
var model = ko.mapping.toJS(model);
$.ajax({
...
}).done(function (data) {
...
// V V V Checks if the call has been made twice and if it has the next call will be able to modify the date
if (lineData.OQIterations >= 2) {
var date = new Date();
lineData.CalculatedDate(date.addDays(lineData.LeadTime()));
lineData.RequiredDate(lineData.CalculatedDate());
}
lineData.OQIterations++;
}).fail(function () {
console.log("An error occurred while getting order quantities data.");
});
}
}
This works, but it seems like a really hacky way to do it, and I also can't guarantee that the function will always run twice before the page has loaded so I don't see this as anything but a temporary fix.
What could I use instead of this method to keep the automatic date assignment in place and prevent the changes from happening until the user actually interacts with the dropdown?
Any help is greatly appreciated.