JQuery UI datepicker change событие не попало в KnockoutJS
Я пытаюсь использовать KnockoutJS с пользовательским интерфейсом jQuery. У меня есть элемент ввода с прикрепленным дампикером. В настоящее время я запускаю knockout.debug.1.2.1.js
, и кажется, что событие изменения никогда не попадает в Knockout. Элемент выглядит следующим образом:
<input type="text" class="date" data-bind="value: RedemptionExpiration"/>
Я даже попытался изменить тип события valueUpdate
, но безрезультатно. Кажется, что Chrome вызывает событие focus
непосредственно перед изменением значения, но IE этого не делает.
Есть ли метод нокаута, который "перепроверяет все привязки"? Мне технически требуется только изменение значения, прежде чем я отправлю его обратно на сервер. Поэтому я мог бы жить с таким обходом.
Я думаю, что проблема с ошибкой datepicker, но я не могу понять, как это исправить.
Любые идеи?
Ответы
Ответ 1
Я думаю, что для jQuery UI datepicker предпочтительнее использовать настраиваемую привязку, которая будет читать/записывать объекты Date с использованием API, предоставленных datepicker.
Связывание может выглядеть (из моего ответа здесь):
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
$el.datepicker(options);
//handle the field changing by registering datepicker changeDate event
ko.utils.registerEventHandler(element, "changeDate", function () {
var observable = valueAccessor();
observable($el.datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
Вы бы использовали его как:
<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />
Образец в jsFiddle здесь: http://jsfiddle.net/rniemeyer/NAgNV/
Ответ 2
Вот версия ответа RP Niemeyer, которая будет работать с скриптами проверки нокаута, найденными здесь: http://github.com/ericmbarnard/Knockout-Validation
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).val());
if (observable.isValid()) {
observable($(element).datepicker("getDate"));
$(element).blur();
}
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor);
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
current = $(element).datepicker("getDate");
if (value - current !== 0) {
$(element).datepicker("setDate", value);
}
}
};
Изменения относятся к обработчику события изменения, чтобы передать введенное значение, а не дату в сценарии проверки, а затем установить только дату наблюдения, если она действительна. Я также добавил validationCore.init, который необходим для настраиваемых привязок, обсуждаемых здесь:
http://github.com/ericmbarnard/Knockout-Validation/issues/69
Я также добавил предложение rpenrose о размытии изменений, чтобы устранить некоторые досадные сценарии датпикера, мешающие вещам.
Ответ 3
Я использовал другой подход. Поскольку knockout.js, похоже, не запускает событие при изменении, я заставил datepicker вызывать change() для его ввода после закрытия.
$(".date").datepicker({
onClose: function() {
$(this).change(); // Forces re-validation
}
});
Ответ 4
Хотя все эти ответы сэкономили мне много работы, никто из них не работал у меня. После выбора даты привязанное значение не будет обновляться. Я мог бы только обновить его при изменении значения даты с помощью клавиатуры, а затем щелкнуть из поля ввода. Я исправил это, добавив код RP Niemeyer с кодом syb, чтобы получить:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
var funcOnSelectdate = function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
}
options.onSelect = funcOnSelectdate;
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (typeof(value) === "string") { // JSON string from server
value = value.split("T")[0]; // Removes time
}
var current = $(element).datepicker("getDate");
if (value - current !== 0) {
var parsedDate = $.datepicker.parseDate('yy-mm-dd', value);
$(element).datepicker("setDate", parsedDate);
}
}
};
Я подозреваю, что можно установить наблюдаемый ($ (элемент).datepicker( "getDate" )); в своей собственной функции и регистрируя это с помощью options.onВыберите трюк?
Ответ 5
Спасибо за эту статью, я нашел ее очень полезной.
Если вы хотите, чтобы DatePicker вел себя точно так же, как поведение по умолчанию JQuery UI, я рекомендую добавить размытие элемента в обработчик события изменения:
то есть.
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
$(element).blur();
});
Ответ 6
Я решил эту проблему, изменив порядок моих включенных файлов script:
<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
Ответ 7
То же, что и RP Niemeyer, но лучше поддерживать WCF DateTime, Timezones и использовать свойство DatePicker onSelect JQuery.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
var funcOnSelectdate = function () {
var observable = valueAccessor();
var d = $(element).datepicker("getDate");
var timeInTicks = d.getTime() + (-1 * (d.getTimezoneOffset() * 60 * 1000));
observable("/Date(" + timeInTicks + ")/");
}
options.onSelect = funcOnSelectdate;
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
current = $(element).datepicker("getDate");
if (value - current !== 0) {
$(element).datepicker("setDate", value);
}
}
};
Наслаждайтесь:)
http://jsfiddle.net/yechezkelbr/nUdYH/
Ответ 8
Я думаю, что это можно сделать намного проще: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />
Таким образом, вам не нужна ручная обработка изменений в функции init.
Но в этом случае ваша переменная myDate получит только видимое значение, а не объект Date.
Ответ 9
В качестве альтернативы вы можете указать это в привязке:
Обновление:
function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("getDate");
if (typeof value === "string") {
var dateValue = new Date(value);
if (dateValue - current !== 0)
$(element).datepicker("setDate", dateValue);
}
}
Ответ 10
На основе решения Ryan myDate возвращает стандартную строку даты, которая не является идеальной в моем случае. Я использовал date.js для анализа значения, чтобы он всегда возвращал формат даты, который вы хотите. Взгляните на этот пример скрипка Пример.
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("getDate");
var d = Date.parse(value);
if (value - current !== 0) {
$(element).datepicker("setDate", d.toString("MM/dd/yyyy"));
}
}
Ответ 11
Мне нужно было многократно обновлять мои данные с сервера, наткнулся на это, но не совсем закончил работу по моему обмену потребностями ниже (мой формат даты/дата (1224043200000)/):
//Object Model
function Document(data) {
if (String(data.RedemptionExpiration).indexOf('/Date(') == 0) {
var newDate = new Date(parseInt(data.BDate.replace(/\/Date\((.*?)\)\//gi, "$1")));
data.RedemptionExpiration = (newDate.getMonth()+1) +
"/" + newDate.getDate() +
"/" + newDate.getFullYear();
}
this.RedemptionExpiration = ko.observable(data.RedemptionExpiration);
}
//View Model
function DocumentViewModel(){
///additional code removed
self.afterRenderLogic = function (elements) {
$("#documentsContainer .datepicker").each(function () {
$(this).datepicker();
});
};
}
После корректной форматирования модели для вывода я добавил шаблон с документацией knockoutjs:
<div id="documentsContainer">
<div data-bind="template: { name: 'document-template', foreach: documents, afterRender: afterRenderLogic }, visible: documents().length > 0"></div>
</div>
//Inline template
<script type="text/html" id="document-template">
<input data-bind="value: RedemptionExpiration" class="datepicker" />
</script>
Ответ 12
Немногие спрашивали о динамических параметрах датпикера. В моем случае мне нужен динамический диапазон дат, поэтому первый ввод определяет минимальное значение второго, а второе устанавливает максимальное значение для первого. Я решил это, расширив обработчик RP Niemeyer. Итак, к его оригиналу:
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
$el = $(element);
$el.datepicker(options);
//handle the field changing by registering datepicker changeDate event
ko.utils.registerEventHandler(element, "change", function() {
var observable = valueAccessor();
observable($el.datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') == 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
Я добавил еще два обработчика, соответствующие параметрам, которые я хотел изменить:
ko.bindingHandlers.minDate = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("option", "minDate", value);
}
};
ko.bindingHandlers.maxDate = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).datepicker("option", "maxDate", value);
}
};
И использовал их в моем шаблоне:
<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />
<input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />
Ответ 13
Использование пользовательских привязок, предоставленных в предыдущих ответах, не всегда возможно. Вызов $(element).datepicker(...)
занимает довольно много времени, и если у вас есть, например, несколько десятков или даже сотни элементов для вызова этого метода, вы должны сделать это "ленивым", то есть по требованию.
Например, модель представления может быть инициализирована, а input
вставлена в DOM, но соответствующие датыпикеры будут инициализироваться только тогда, когда пользователь нажимает на них.
Итак, вот мое решение:
Добавьте настраиваемую привязку, которая позволяет прикреплять произвольные данные к node:
KO.bindingHandlers.boundData = {
init: function(element, __, allBindings) {
element.boundData = allBindings.get('boundData');
}
};
Используйте привязку к attcah наблюдаемому, используемому для значения input
:
<input type='text' class='my-date-input'
data-bind='textInput: myObservable, boundData: myObservable' />
И, наконец, при инициализации datepicker используйте его onSelect
:
$('.my-date-input').datepicker({
onSelect: function(dateText) {
this.myObservable(dateText);
}
//Other options
});
Таким образом, каждый раз, когда пользователь меняет дату с помощью datepicker, также обновляется соответствующий наблюдаемый нокаут.