jQuery UI changement de curseur de données événement non détecté par KnockoutJS

j'essaie d'utiliser KnockoutJS avec jQuery UI. J'ai un élément d'entrée avec un curseur de données attaché. Je suis en train de courir knockout.debug.1.2.1.js et il semble que l'événement de changement ne soit jamais pris par K. O. L'élément ressemble à ceci:

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

j'ai même essayé de changer le type d'événement valueUpdate mais en vain. Il semble que Chrome provoque un événement focus juste avant qu'il ne change la valeur, mais pas IE.

est là une méthode qui "rebinde toutes les fixations"? Techniquement, je n'ai besoin que de changer la valeur avant de la renvoyer au serveur. Pour que je puisse vivre avec ce genre de contournement.

je pense que le problème est la faute du sélectionneur de données, mais je ne peux pas trouver comment réparer ça.

des idées?

134
demandé sur Jeroen 2011-07-07 19:00:02

13 réponses

je pense que pour le datepicker de jQuery il est préférable d'utiliser une liaison personnalisée qui lira/écrira avec les objets Date en utilisant les API fournies par le datepicker.

la reliure pourrait ressembler à (de ma réponse ici ):

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

vous l'utilisez comme:

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

échantillon en jsFiddle ici: http://jsfiddle.net/rniemeyer/NAgNV /

252
répondu RP Niemeyer 2017-05-23 12:34:20

Voici une version de la réponse de RP Niemeyer qui fonctionnera avec les scripts de validation knockout trouvés ici: 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);
        }
    }
};

les modifications sont apportées au gestionnaire d'événement de changement pour passer la valeur entrée et non la date aux scripts de validation en premier, puis seulement en réglant la date sur l'observable si elle est valide. J'ai aussi ajouté le validationCore.init qui est nécessaire pour les reliures personnalisées discutées ici:

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

j'ai aussi ajouté rpenrose suggestion pour un flou sur le changement d'éliminer certains satanés datepicker scénarios de choses.

13
répondu Brad M 2012-11-07 18:51:25

j'ai utilisé une approche différente. Depuis knock-out.js ne semble pas déclencher l'événement sur change, j'ai forcé le curseur de données à appeler change () pour son entrée une fois fermée.

$(".date").datepicker({
    onClose: function() {
        $(this).change(); // Forces re-validation
    }
});
11
répondu ThiagoPXP 2012-12-20 01:10:12

bien que toutes ces réponses m'aient épargné beaucoup de travail, aucune d'entre elles n'a fonctionné pleinement pour moi. Après avoir choisi une date, la valeur liée ne serait pas mise à jour. Je ne pouvais le mettre à jour qu'en changeant la valeur de la date en utilisant le clavier puis en cliquant sur la case d'entrée. J'ai corrigé ceci en augmentant le code de RP Niemeyer avec le code de syb pour obtenir:

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

je soupçonne de mettre l'observable($(élément).datepicker("getDate")); déclaration dans sa propre fonction et l'enregistrement avec des options.onSelect a fait le coup?

9
répondu brudert 2013-05-08 21:56:55

Merci pour cet article je l'ai trouvé très utile.

si vous voulez que le curseur de données se comporte exactement comme le comportement par défaut de L'interface utilisateur JQuery, je recommande d'ajouter un flou sur l'élément dans le gestionnaire d'événement de changement:

c'est à dire

    //handle the field changing
    ko.utils.registerEventHandler(element, "change", function () {
        var observable = valueAccessor();
        observable($(element).datepicker("getDate"));

        $(element).blur();

    });
6
répondu rpenrose 2011-12-02 09:52:20

j'ai résolu ce problème en changeant l'ordre de mes fichiers de script inclus:

<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
3
répondu Susanna 2013-10-23 15:08:00

identique à RP Niemeyer, mais meilleur support de DateTime, Timezones et en utilisant la propriété 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);
            }
        }
    };

Enjoy:)

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

2
répondu syb 2013-06-14 10:18:17

je pense qu'il peut être fait beaucoup plus facile: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

donc vous n'avez pas besoin de manipulation manuelle de changement dans la fonction init.

mais dans ce cas, votre variable 'myDate' n'obtiendra que la valeur visible, pas l'objet Date.

1
répondu mot 2011-10-09 15:19:55

alternativement, vous pouvez spécifier ceci dans binding:

mise à jour:

 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);
    }               
}
1
répondu Martin 2012-10-26 06:45:58

basé sur la solution de Ryan, myDate renvoie la chaîne de date standard, qui n'est pas idéale dans mon cas. J'ai utilisé de la date.js pour analyser la valeur ainsi il retournera toujours le format de date que vous voulez. Jetez un oeil à cet exemple exemple de violon .

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"));   
    }
}
0
répondu Princa 2013-05-24 21:51:13

j'ai eu besoin de mettre à jour mes données à plusieurs reprises à partir du serveur est tombé sur ce mais n'a pas tout à fait terminé le travail pour mes besoins de partage ci-dessous(mon format de date / Date (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();                   
        });
    };
}

après le modèle est formaté correctement pour la sortie j'ai ajouté un modèle avec la documentation 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>
0
répondu Ken 2013-11-03 07:24:50

peu de gens ont demandé des options dynamiques pour les lecteurs de données. Dans mon cas, j'avais besoin d'une plage de dates dynamique - donc la première entrée définit la valeur min de la seconde et la seconde fixe la valeur max pour la première. Je l'ai résolu en prolongeant le gestionnaire du RP Niemeyer. Ainsi à son original:

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

j'ai ajouté deux handlers supplémentaires correspondant aux options que je voulais modifier:

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

et les a utilisés comme ça dans mon modèle:

<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />       
<input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />
0
répondu Adam Bilinski 2016-01-19 11:52:56

L'utilisation de reliures personnalisées fournies dans les réponses précédentes n'est pas toujours possible. Appeler $(element).datepicker(...) prend un temps considérable, et si vous avez, par exemple, quelques dizaines, ou même des centaines d'éléments pour appeler cette méthode, vous devez le faire" paresseux", c.-à-d. sur demande.

par exemple, le modèle de vue peut être initialisé, le input s étant inséré dans le DOM, mais les lecteurs de données correspondants ne seront initialisés que lorsqu'un utilisateur les cliquera.

donc, voici ma solution:

ajouter une liaison personnalisée qui permet d'attacher des données arbitraires à un noeud:

KO.bindingHandlers.boundData = {
  init: function(element, __, allBindings) {
    element.boundData = allBindings.get('boundData');
  }
};

utiliser la liaison pour attcah l'observable utilisé pour la valeur de input :

<input type='text' class='my-date-input'
       data-bind='textInput: myObservable, boundData: myObservable' />

et enfin, lors de l'initialisation du curseur de données, utilisez son option onSelect :

$('.my-date-input').datepicker({
  onSelect: function(dateText) {
    this.myObservable(dateText);
  }
  //Other options
});

de cette façon, chaque fois qu'un utilisateur change la date avec le le Knockout observable correspondant est également mis à jour.

0
répondu ololoepepe 2016-07-27 06:50:02