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 виноват, но я не могу понять, как это исправить.



какие идеи?

540   13  

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);
            }
        }
    };

наслаждайтесь :)

http://jsfiddle.net/yechezkelbr/nUdYH/

Я думаю, что это можно сделать гораздо проще: <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

    Ничего не найдено.