[jquery-ui] jQuery UI DatePicker modifica l'evento non rilevato da KnockoutJS



Answers

Ecco una versione della risposta di RP Niemeyer che funzionerà con gli script di convalida a eliminazione diretta qui: 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);
        }
    }
};

Le modifiche riguardano il gestore dell'evento change per passare prima il valore immesso e non la data agli script di convalida, quindi solo impostando la data sull'osservabile se è valida. Ho anche aggiunto la validationCore.init che è necessaria per i collegamenti personalizzati discussi qui:

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

Ho anche aggiunto il suggerimento di rpenrose per una sfocatura sul cambiamento per eliminare alcuni fastidiosi scenari di datepicker che intralciano le cose.

Question

Sto cercando di usare KnockoutJS con l'interfaccia utente di jQuery. Ho un elemento di input con un datepicker allegato. Al momento sto eseguendo knockout.debug.1.2.1.js e sembra che l'evento change non venga mai catturato da Knockout. L'elemento si presenta così:

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

Ho anche provato a cambiare il tipo di evento valueUpdate ma inutilmente. Sembra che Chrome causi un evento di focus a focus poco prima che cambi il valore, ma IE no.

Esiste un metodo Knockout che "rebinds tutti i binding"? Tecnicamente ho solo bisogno di modificare il valore prima di inviarlo al server. Quindi potrei vivere con quel tipo di soluzione alternativa.

Penso che il problema sia colpa del datepicker, ma non riesco a capire come risolvere questo problema.

Qualche idea?




Basato sulla soluzione di Ryan, myDate restituisce la stringa di data standard, che non è quella ideale nel mio caso. Ho usato date.js per analizzare il valore in modo che restituisca sempre il formato della data desiderato. Dai un'occhiata a questo esempio di esempio .

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



Poche persone hanno chiesto opzioni di Datepicker dinamiche. Nel mio caso avevo bisogno di un intervallo di date dinamico, quindi il primo input definisce il valore minimo del secondo e il secondo imposta il valore massimo per il primo. L'ho risolto estendendo il gestore di RP Niemeyer. Quindi al suo originale:

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

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

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

Ho aggiunto altri due gestori corrispondenti alle opzioni che volevo modificare:

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

E li ho usati così nel mio modello:

<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />       
<input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />



Ho risolto questo problema modificando l'ordine dei miei file di script inclusi:

<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>



Penso che possa essere fatto molto più facilmente: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

Quindi non è necessaria la gestione manuale delle modifiche nella funzione init.

Ma in questo caso, la variabile "myDate" otterrà solo il valore visibile, non l'oggetto Date.




Sebbene tutte queste risposte mi abbiano salvato un sacco di lavoro, nessuno di questi ha funzionato pienamente per me. Dopo aver selezionato una data, il valore associato non si aggiornava. Ho potuto farlo aggiornare solo cambiando il valore della data usando la tastiera, quindi facendo clic fuori dalla casella di input. Ho risolto questo problema aumentando il codice di RP Niemeyer con il codice di syb per ottenere:

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

Sospetto di mettere l'osservabile ($ (elemento) .datepicker ("getDate")); affermazione nella propria funzione e registrando ciò con options.onSelect ha fatto il trucco?




Related