web-dev-qa-db-fra.com

knockoutjs databind avec jquery-ui datepicker

J'utilise un jQuery UI datepicker. Le champ de saisie HTML derrière est actuellement connecté à KnockoutJS en tant que dependObservable, mais lorsque sa valeur est définie dans le viewmodel, le sélecteur de date perd son format.

Comment dois-je faire cela et ne pas perdre le format? Je voudrais que le viewModel ne sache pas qu'il s'agit d'un jQuery datepicker.

39
Rasmus Christensen

Vous pouvez écrire une liaison personnalisée qui définit la date dans le champ à l'aide des API datepicker et définit également la valeur de votre observable en lisant correctement la date.

La liaison personnalisée peut ressembler à:

ko.bindingHandlers.datepicker = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var options = allBindingsAccessor().datepickerOptions || {},
            $el = $(element);

        //initialize datepicker with some optional options
        $el.datepicker(options);

        //handle the field changing
        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),
            current = $el.datepicker("getDate");

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

Vous l'utiliseriez comme:

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

datepickeroptions serait facultatif et pourrait inclure tout ce que vous voulez passer dans l'appel datepicker().

En outre, cela suppose que vous utilisez un observable pour la date. La liaison doit faire un peu plus de travail si vous voulez faire une liaison unidirectionnelle avec un non observable, mais cela est peu probable.

Échantillon ici: http://jsfiddle.net/rniemeyer/NAgNV/

79
RP Niemeyer

J'utilise le code RP Niemeyer marqué comme la réponse ci-dessus, mais depuis que je l'utilise, j'y ai apporté quelques petites modifications. Je pensais que je posterais ici. Peut-être que cela aidera les autres. C'est à peu près la même chose, la seule différence est que si l'élément a une valeur lors du chargement de la page, il conservera sa valeur. De plus, j'ai fait de $elem Une variable pour qu'il y ait moins de traitement de $(element) que jQuery devra faire.

ko.bindingHandlers['jqDatePicker'] = {
    'init': function(element, valueAccessor, allBindingsAccessor) {
        /* Initialize datepicker with some optional options */
        var options = allBindingsAccessor().jqDatePickerOptions || {},
            prop = valueAccessor(),
            $elem = $(element);

        prop($elem.val());

        $elem.datepicker(options);

        /* Handle the field changing */
        ko.utils.registerEventHandler(element, "change", function () {
            prop($elem.datepicker("getDate"));
        });

        /* Handle disposal (if KO removes by the template binding) */
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $elem.datepicker("destroy");
        });
    },
    'update': function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $elem = $(element),
            current = $elem.datepicker("getDate");

        if (value - current !== 0) {
            $elem.datepicker("setDate", value);
        }
    }
};
6
Evan Larsen

J'ai dû apporter une légère modification au code RP Niemeyer pour travailler dans mon code en utilisant l'option dateFormat, en remplaçant

$(element).datepicker("getDate")

Avec

$(element).val()

Ainsi, la version formatée de la date a été correctement passée sous le capot.

6
OddEssay

Voici ce qui a fonctionné pour mon ensemble particulier de circonstances. J'exécute une nouvelle version assez de MVC, que le sérialiseur datetime par défaut rend à ISO 8601 (voir Sur le cauchemar qu'est JSON Dates. Plus, JSON.NET et ASP.NET Web API). Mes liaisons n'écrivent pas directement dans le sélecteur de date, mais à la place dans l'attribut "valeur" d'une balise d'entrée.

De plus, notez que j'utilise date.js

ko.bindingHandlers.dateValue = {
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor(),
            allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var pattern = allBindings.datePattern || 'MM/dd/yyyy';
        var date = Date.parse(valueUnwrapped)
        $(element).val(date.toString(pattern));
    },

    init: function(element, valueAccessor, allBindingsAccessor) {
        //handle the field changing
        ko.utils.registerEventHandler(element, "change", function () {
            var observable = valueAccessor();
            var date = Date.parse($(element).val());
            observable(date.toString("yyyy-MM-ddThh:mm:ss"));
        });
    }
}

La liaison ressemble à ceci:

<input class="date" type="text" data-bind="dateValue: SomeViewModelDate" />

Et le code JavaScript pour activer le sélecteur de date:

$(document).ready(function () {
    $('.date').datepicker({ dateFormat: "mm/dd/yy" });
});
2
viggity

Les exemples de sélecteur de dates ci-dessus modifient le format de la date dans le modèle d'affichage du format WCF au format de date JavaScript lorsque l'utilisateur sélectionne une nouvelle date dans le contrôle datepicker.

Dans mon cas, je transmettais la date à un service WCF, et il n'acceptait pas une date JavaScript désérialisée, il avait besoin de la date au format WCF. J'ai modifié le script ci-dessus pour qu'il stocke la date dans le viewmodel au format WCF afin qu'il puisse être renvoyé au serveur dans ce format.

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).datepicker("getDate"));
            // store the date in 'WCF String format"
            var tempdate=$(element).datepicker("getDate");
            var tempdatestr="/Date("+tempdate.getTime()+")/";
            observable(tempdatestr);
        });
        //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, "$1")));
        }
        current = $(element).datepicker("getDate");
        if (value - current !== 0) {
            $(element).datepicker("setDate", value);
        }
    }
};
1
Russellg

Le formatage de la date (en mm/jj/aaaa) dans le dependObservable est exactement ce que je me demandais comment faire. Je posterai un peu de mon code si vous pouvez m'aider, Peter Mortensen et/ou nEEBz.

    <div data-bind="with: technology">  
        <div class="titleblock">
            <label><b>End of Life Date</b></label> 
            <Input  type="text" class="ui-datepicker" id="datepicker" data-bind="value: END_OF_LIFE_DATE"/>
        </div>       
    </div>

dans ViewModel - technologydetail.js:

var technology = ko.observable();

dans Activer:

return dataContext.getTechnologyById(currentTechnologyId, technology);

Cela affiche une date qui ressemble à ceci dans la zone de texte: Wed 29 Jan 19:00:00 EST 2014 mais je veux juste qu'elle s'affiche: 29/01/2014. J'utilise cette option de datepicker - dateFormat: "mm/dd/yy" mais cela n'a aucun effet sur la valeur initiale affichée.

J'ai pu le formater en utilisant moment et cela fonctionne bien pour afficher la valeur actuelle de la base de données, mais il semble rompre la liaison avec l'observable car il ne sera plus mis à jour sur un SAVE.

<Input type="text" class="ui-datepicker" id="datepicker" data-bind="value: moment(END_OF_LIFE_DATE()).format('MM/DD/YYYY')" />
0
Debbie A

Une solution consiste à formater la date vous-même dans la fonction dependObservable. Vous devez donc renvoyer quelque chose comme return viewModel.someOtherObservable() dans la fonction. Formatez la valeur de retour.

Si vous incluez votre code, je peux vous en expliquer plus.

0
neebz