KnockoutJS no detecta el evento de cambio del selector de fechas de jQuery UI

Resuelto Jose asked hace 13 años • 13 respuestas

Estoy intentando usar KnockoutJS con jQuery UI. Tengo un elemento de entrada con un selector de fecha adjunto. Actualmente estoy corriendo knockout.debug.1.2.1.jsy parece que Knockout nunca detecta el evento de cambio. El elemento se ve así:

<input type="text" class="date" data-bind="value: RedemptionExpiration"/>

Incluso intenté cambiar el valueUpdatetipo de evento pero fue en vano. Parece que Chrome causa unfocus evento justo antes de cambiar el valor, pero IE no.

¿Existe algún método Knockout que "vuelva a vincular todos los enlaces"? Técnicamente solo necesito cambiar el valor antes de enviarlo de vuelta al servidor. Entonces podría vivir con ese tipo de solución.

Creo que el problema es culpa del selector de fechas, pero no sé cómo solucionarlo.

¿Algunas ideas?

Jose avatar Jul 07 '11 22:07 Jose
Aceptado

Creo que para el selector de fechas de jQuery UI es preferible usar un enlace personalizado que lea/escriba con objetos Date usando las API proporcionadas por el selector de fechas.

El enlace podría verse así (según mi respuesta aquí ):

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, "$1")));
        }

        var current = $el.datepicker("getDate");

        if (value - current !== 0) {
            $el.datepicker("setDate", value);
        }
    }
};

Lo usarías como:

<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />

Muestra en jsFiddle aquí: http://jsfiddle.net/rniemeyer/NAgNV/

RP Niemeyer avatar Jul 07 '2011 15:07 RP Niemeyer

Aquí hay una versión de la respuesta de RP Niemeyer que funcionará con los scripts de validación de knockout que se encuentran aquí: 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);
        }
    }
};

Los cambios son para que el controlador de eventos de cambio pase primero el valor ingresado y no la fecha a los scripts de validación, y luego solo establezca la fecha en el observable si es válida. También agregué validationCore.init que se necesita para los enlaces personalizados que se analizan aquí:

http://github.com/ericmbarnard/Knockout-Validation/issues/69

También agregué la sugerencia de rpenrose de desenfocar el cambio para eliminar algunos escenarios molestos del selector de fechas que interfieren con las cosas.

Brad M avatar Nov 07 '2012 18:11 Brad M