I have a datapicker class (datepicker_nfd) that is attached to an input field that uses maxdate to limit the date widget to only current dates. I have a custom knockoutjs binding to display a custom error message and reset the date when the users leaves the field (onchange). My problem is if a user manually enters a date in the future and presses enter, the widget sets the date to the current date before the onchange is fired and therefore I am unable to check to display the message. I have tried the onseect method of the datepicker and also tried to capture the "enter" keypress to no avail. Any suggestion on how to capture the manually entered date before the datepicker widget resets to current date?
Javascript (applicable code only for brevity)
$(document).ready(function () {
$(".datepicker_nfd").datepicker({maxDate: 0});
});
KnockoutJS Binding (applicable code only for brevity)
ko.bindingHandlers.preventFutureDate = {
init: function (element, valueAccessor, allBindingsAccessor, vm) {
$(element).change(function () {
var selectedDate = Date.parse(ko.unwrap(valueAccessor()));
var curDate = Date.now();
if (selectedDate > curDate) {
var value = valueAccessor();
var eMsg = ($(element).data("emessage") || "Date") + " can not be set to a future date. Defaulting to current day.";
value(DateTime.fromMillis(Date.now()).toFormat('MM/dd/yyyy'));
showToast("Invalid Date", eMsg.trim(), "exclamation-triangle", "yellow");
}
});
}
};
HTML (applicable code only for brevity)
<div class="form-group col-md-2">
<label for="someDate-datepicker_nfd">Some Date Label</label>
<input type="text" class="form-control datepicker_nfd" id="someDate-datepicker_nfd" data-bind="value: model.SomeDate, preventFutureDate: model.SomeDate" data-emessage="Some Date Error Message" />
</div>
So the solution that I found is that you indeed you have to use the keydown press, which MUST be before the date picker is initialized...... so in my case I had the binding being initialized in one file and the datepicker in another. This was causing the keydown to not fire(assuming the datepicker prevents default. So HTML remains the same and the js is now as follows (order is important).
Javascript/KnockoutJS (applicable code only for brevity)
let keyedDate;
ko.bindingHandlers.preventFutureDate = {
init: function (element, valueAccessor, allBindingsAccessor, vm) {
$(element).keydown(function (e) {
if (e.keyCode === 13) {
checkForFutureDate(element, valueAccessor, keyedDate);
}
});
$(element).change(function () {
checkForFutureDate(element, valueAccessor, ko.unwrap(valueAccessor()));
});
}
};
$($(".datepicker_nfd")).keydown(function (e) {
if (e.keyCode === 13) {
keyedDate = $(this).val();
}
});
// NOTE: This needs to be placed after the Knockout Binding Handler for preventFutureDate to capture the keydown event.
// The companion selector for $(".datepicker").datepicker(); in in site.js
$(".datepicker_nfd").datepicker({maxDate: 0});
function checkForFutureDate(element, valueAccessor, dateToVerify) {
var selectedDate = Date.parse(dateToVerify);
var curDate = Date.now();
if (selectedDate > curDate) {
var value = valueAccessor();
var eMsg = ($(element).data("emessage") || "Date") + " can not be set to a future date. Defaulting to current day.";
value(DateTime.fromMillis(Date.now()).toFormat('MM/dd/yyyy'));
showToast("Invalid Date", eMsg.trim(), "exclamation-triangle", "yellow");
}
};
HTML (applicable code only for brevity)
<div class="form-group col-md-2">
<label for="someDate-datepicker_nfd">Some Date Label</label>
<input type="text" class="form-control datepicker_nfd" id="someDate-datepicker_nfd" data-bind="value: model.SomeDate, preventFutureDate: model.SomeDate" data-emessage="Some Date Error Message" />
</div>
Kind of quirky but gets the job done. Noticed the let with keyedDate to capture the date before the datepicker changes it outside the binding. Which need the binding to access the other values in the HTML bind.