jQuery UI datepicker событие изменения не пойман KnockoutJS
Я пытаюсь использовать KnockoutJS с jQuery UI. У меня есть элемент input с прикрепленным управления datepicker. Я в настоящее время работает knockout.debug.1.2.1.js и кажется, что событие изменения никогда не ловится нокаутом. Элемент выглядит так:
<input type="text" class="date" data-bind="value: RedemptionExpiration"/>
Я даже пытался изменить valueUpdate тип события, но безрезультатно. Похоже, хром вызывает focus событие непосредственно перед изменением значения, но IE не делает.
есть ли какой-то метод нокаута ,который " связывает все привязки"? Мне технически нужно только изменить значение, прежде чем я отправлю его обратно на сервер. Так что я мог бы жить с таким обходным путем.
Я думаю, что проблема в том, что datepicker виноват, но я не могу понять, как это исправить.
какие идеи?
13 ответов:
Я думаю, что для 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's 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, ""))); } 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/
вот версия ответа 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, ""))); } current = $(element).datepicker("getDate"); if (value - current !== 0) { $(element).datepicker("setDate", value); } } };изменения относятся к обработчику событий изменения, чтобы сначала передать введенное значение, а не дату сценариям проверки, а затем только установить дату в наблюдаемое, если она действительна. Я также добавил validationCore.init, который необходим для пользовательских Привязок обсуждается здесь:
http://github.com/ericmbarnard/Knockout-Validation/issues/69
Я также добавил предложение rpenrose для размытия при изменении, чтобы устранить некоторые досадные сценарии datepicker, мешающие вещам.
я использовал другой подход. С момента нокаута.js, похоже, не запускает событие при изменении, я заставил datepicker вызвать change() для его ввода после закрытия.
$(".date").datepicker({ onClose: function() { $(this).change(); // Forces re-validation } });
хотя все эти ответы спасли меня много работы, ни один из них не работал для меня. После выбора даты привязанное значение не будет обновляться. Я мог только получить его для обновления при изменении значения даты с помощью клавиатуры, а затем нажав из поля ввода. Я исправил это, увеличив код 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); } } };Я подозреваю, что ставлю observable ($(element).datepicker ("getDate")); оператор в своей собственной функции и регистрация этого с помощью опции.onSelect сделал трюк?
Спасибо за эту статью, я нашел его очень полезным.
Если вы хотите, чтобы DatePicker вел себя точно так же, как поведение по умолчанию jQuery UI, я рекомендую добавить размытие элемента в обработчике событий изменения:
т. е.
//handle the field changing ko.utils.registerEventHandler(element, "change", function () { var observable = valueAccessor(); observable($(element).datepicker("getDate")); $(element).blur(); });
Я решил эту проблему, изменив порядок моих файлов, включенных скрипт:
<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script> <script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
то же, что и RP Niemeyer, но лучше поддерживает WCF DateTime, часовые пояса и использование свойства 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, ""))); } current = $(element).datepicker("getDate"); if (value - current !== 0) { $(element).datepicker("setDate", value); } } };наслаждайтесь :)
Я думаю, что это можно сделать гораздо проще:
<input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />таким образом, вам не нужна ручная обработка изменений в функции init.
но в этом случае ваша переменная 'myDate' получит только видимое значение, а не объект даты.
кроме того, вы можете указать это в обязательные:
обновление:
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); } }
основываясь на решении Райана, myDate возвращает стандартную строку даты, которая не является идеальной в моем случае. Я использовал дату.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")); } }
мне нужно было неоднократно обновлять мои данные с сервера наткнулся на это, но не совсем закончил работу для моих потребностей обмена ниже (мой формат даты /дата(1224043200000)/):
//Object Model function Document(data) { if (String(data.RedemptionExpiration).indexOf('/Date(') == 0) { var newDate = new Date(parseInt(data.BDate.replace(/\/Date\((.*?)\)\//gi, ""))); 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>
несколько человек попросили динамические параметры элемента datepicker. В моем случае мне нужен динамический диапазон дат-поэтому первый вход определяет минимальное значение второго, а второй устанавливает максимальное значение для первого. Я решил это, расширив обработчик RP Нимейера. Итак, к его оригиналу:
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's 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, ""))); } 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" />
использование пользовательских Привязок, указанных в предыдущих ответах, не всегда возможно. Звоню
$(element).datepicker(...)занимает некоторое значительное время, и если у вас есть, например, несколько десятков или даже сотен элементов для вызова этого метода, вы должны сделать это "лениво", т. е. по требованию.например, модель представления может быть инициализирована,
inputs вставляется в DOM, но соответствующие datepickers будут инициализированы только тогда, когда пользователь нажимает на них.Итак, вот мой решение:
добавить пользовательскую привязку, которая позволяет прикреплять произвольные данные к узлу:
KO.bindingHandlers.boundData = { init: function(element, __, allBindings) { element.boundData = allBindings.get('boundData'); } };используйте привязку к attcah наблюдаемое используется для ':
<input type='text' class='my-date-input' data-bind='textInput: myObservable, boundData: myObservable' />и, наконец, при инициализации datepicker, используйте его :
$('.my-date-input').datepicker({ onSelect: function(dateText) { this.myObservable(dateText); } //Other options });таким образом, каждый раз, когда пользователь изменяет дату с помощью datepicker, соответствующий нокаут observable также обновляется.
Comments