Ryan Ryan - 5 months ago 140
Javascript Question

Using Knockout.js how do bind a Date property to a HTML5 date picker?

(this only works in Chrome at the moment as most browsers don't yet implement date picker for input type="date")

In the following example MyDate starts out as a Date object with the current date, but this isn't picked up by the date input (which expects its format to be a string in format YYYY/MM/DD).

Once you've picked a date in the picker then MyDate becomes a string in format above.

How can you bind this so MyDate stays a javascript Date and is interpreted by the input control correctly?

See See 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>

Answer

While @amakhrov answer will work (and would be even better if used writeable computed observable like sujested by @Stijn) I decided to do this using Custom Bindings.

The main advantage of doing this is reusability - I just have to use data-bind="datePicker : MyDate" whereever I want to tie this in. I can also modify other properties of the input element so this could be really useful if binding to complex jQuery (and other) controls.

(Read here for more pro/cons about the 3 choices to do this sort of thing)

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

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