web-dev-qa-db-fra.com

À l'aide de Knockout.js, comment lier une propriété Date à un sélecteur de date HTML5?

(cela ne fonctionne que dans Chrome pour le moment, car la plupart des navigateurs n'implémentent pas encore le sélecteur de date pour le type d'entrée = "date")

Dans l'exemple suivant, MyDate commence en tant qu'objet Date avec la date actuelle, mais cette entrée n'est pas prise en compte par l'entrée de date (qui s'attend à ce que son format soit une chaîne au format AAAA/MM/JJ).

Une fois que vous avez sélectionné une date dans le sélecteur, MyDate devient une chaîne au format ci-dessus.

Comment pouvez-vous lier cela pour que MyDate conserve une date javascript et soit interprété correctement par le contrôle d'entrée?

Voir Voir http://jsfiddle.net/LLkC4/3/ : -

<input data-bind="value : MyDate" type="date">
<hr>   
<span data-bind="html: log" />

<script>
var viewModel = {    
    MyDate : ko.observable(new Date()),
    log : ko.observable(""),
    logDate : function () { 
            this.log(this.log() + this.MyDate() + " : " +
                     typeof(this.MyDate()) + "<br>");
                     }
};

viewModel.MyDate.subscribe(function (date) {    
    viewModel.logDate();    
});

ko.applyBindings(viewModel);

viewModel.logDate()
</script>
14
Ryan

Alors que la réponse de @amakhrov fonctionnera (et serait encore mieux si elle était utilisée comme une écriture observable calculée comme sujested par @Stijn), j'ai décidé de le faire en utilisant Custom Bindings .

Le principal avantage de cela est la réutilisabilité - je dois juste utiliser data-bind="datePicker : MyDate" là où je veux le lier. Je peux aussi modifier d'autres propriétés de l'élément input afin que cela puisse être vraiment utile si vous liez à des contrôles jQuery complexes (et autres).

( Lisez ici pour plus d'avantages/inconvénients à propos des 3 choix de faire ce genre de chose)

HTML

<input data-bind="datePicker : MyDate" type="date">

JS

ko.bindingHandlers.datePicker = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {                    
        // Register change callbacks to update the model
        // if the control changes.       
        ko.utils.registerEventHandler(element, "change", function () {            
            var value = valueAccessor();
            value(new Date(element.value));            
        });
    },
    // Update the control whenever the view model changes
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value =  valueAccessor();        
        element.value = value().toISOString();
    }
};

var viewModel = {    
    MyDate : ko.observable(new Date())
};     

ko.applyBindings(viewModel);

Voir http://jsfiddle.net/LLkC4/5/

9
Ryan

Vous pouvez utiliser le vartiable calculé pour l'objet de date dans votre modèle:

En html:

<input data-bind="value : rawDate" type="date">

Dans du code:

var currentDate = (new Date()).toISOString().split('T')[0];

// this is used instead of MyDate in the data binding
rawDate : ko.observable(currentDate),

...
// and then set up the dependent variable
viewModel.MyDate = ko.computed(function () {
    var val = this.rawDate();
    if (typeof val === 'string') val = new Date(val);

    return val;
}, viewModel)

S'il vous plaît voir la démo: http://jsfiddle.net/gcAXB/1/

7
amakhrov

Voici une solution qui fonctionne pour moi avec les derniers knockoutjs, basée sur le lien ci-dessous et modifiée pour avoir une fonction init personnalisée permettant de gérer la mise à jour des propriétés ko.computed à mesure que votre valeur de date change.

Notez que utils.formatDate est simplement une fonction utilitaire permettant de formater la date dans la chaîne de votre choix. Remplacez-la donc par votre propre code de formatage de date, que vous utilisiez momentjs ou autre chose.

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {    
        ko.utils.registerEventHandler(element, 'change', function () {
            var value = valueAccessor();

            if (element.value !== null && element.value !== undefined && element.value.length > 0) {
                value(element.value);
            }
            else {
                value('');
            }
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);

        var output = '';
        if (valueUnwrapped !== null && valueUnwrapped !== undefined && valueUnwrapped.length > 0) {
            output = utils.formatDate(valueUnwrapped);
        }

        if ($(element).is('input') === true) {
            $(element).val(output);
        } else {
            $(element).text(output);
        }
    }
};

    <div>
        <label>Date of Birth:</label>
        <input type="text" data-bind="date: dateOfBirth, format: 'DD MMM YYYY'" />
    </div>

DATES DE LIAISON ET DE FORMATAGE EN UTILISANT KNOCKOUT ET MOMENT JS

6
Justin

C'est tellement plus facile avec Moment.js

this.sessionDate = ko.observable(moment().format('YYYY-MM-DD'));
this.getFormattedDate = () => { return moment(this.sessionDate()'YYYY-MM-DD').format('MM/DD/YYYY') }; // Note this is ES2015 syntax

Dans votre html, vous pouvez le lier avec

<input class="form-control" name="date" type="date" id="date" data-bind="value: sessionDate">

Et l'afficher au format

<p data-bind="text : getFormattedDate()">Loading Date</p>

Il n'est pas nécessaire de créer des liaisons personnalisées et vous pouvez utiliser un shim pour les navigateurs plus anciens.

3
brianlmerritt

Basé sur la réponse de Ryan ci-dessus, cela fonctionne un peu mieux avec les nouveaux widgets ko/chrome. Il supprime également l’heure de la date.

ko.bindingHandlers.datePicker = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        // Register change callbacks to update the model
        // if the control changes.
        ko.utils.registerEventHandler(element, "change", function () {
            var value = valueAccessor();
            var target_date = element.valueAsDate;
            var truncated = new Date(target_date.getFullYear(), target_date.getMonth(), target_date.getDate());
            value(truncated);
        });
    },
    // Update the control whenever the view model changes
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value =  valueAccessor();
        var unwrapped = ko.utils.unwrapObservable(value());
        if(unwrapped === undefined || unwrapped === null) {
            element.value = '';
        } else {
            element.valueAsDate = unwrapped;
        }
    }
};
2
Brian Arsuaga

La même liaison personnalisée en utilisant momentJS

ko.bindingHandlers.datePicker = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        // Register change callbacks to update the model
        // if the control changes.
        ko.utils.registerEventHandler(element, "change", function () {
            var value = valueAccessor();
            value(moment(element.value).format());
        });
    },
    // Update the control whenever the view model changes
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value =  valueAccessor();
        element.value = moment(value()).format("YYYY-MM-DD");
    }
};
2
Yannick Chartois

De HTML 5 - Formatage de la date du type d'entrée sur iOS

Il y a deux formats en jeu:

  • format affiché
  • format interne exposé à JavaScript et envoyé au serveur

Vous ne pouvez pas changer le format d'affichage. C'est au navigateur de décider comment la date est présentée à l'utilisateur (en pratique, elle est déterminée par les paramètres régionaux de system).

Vous ne pouvez pas non plus changer le format interne. C'est toujours ISO8601, indépendamment du navigateur/locale.

Vous devrez le pré-remplir avec ce format spécifique, et vous pouvez ajouter un observable calculé pour l'analyser dans un objet Date, afin de pouvoir le lire à d'autres endroits de votre application.

Si vous souhaitez également y écrire à partir de JS, vous pouvez configurer un observable calculé en écriture et analyser l'analyse afin de déterminer s'il s'agit d'une chaîne du champ de saisie ou d'un objet Date de votre JS.

1
Stijn

Cela a fonctionné pour moi

ko.bindingHandlers.momentDate = {
_parseDateTime: function (element, valueAccessor) {
    var value = valueAccessor();
    var valueUnwrapped = ko.utils.unwrapObservable(value);
    var datetime = moment(valueUnwrapped);
    var date = moment($(element).val(), 'YYYY-MM-DD');
    datetime = datetime.set({
        'year': date.get('year'),
        'month': date.get('month'),
        'date': date.get('date')
    });
    value(datetime.toDate());
},
init: function (element, valueAccessor) {
    function bind() {
        ko.bindingHandlers.momentDate._parseDateTime(element, valueAccessor);
    }
    $(element).change(bind).blur(bind);
},
update: function (element, valueAccessor) {
    var value = valueAccessor();
    var valueUnwrapped = ko.utils.unwrapObservable(value);
    var date = moment(valueUnwrapped);
    $(element).val(date.format('YYYY-MM-DD'));
}

};

<input type="date" data-bind="momentDate: $data.Date" class="form-control"/>
0
KillerKiwi