Introduction
Now a days, client side MVVM is a popular pattern using knockout.js. But the problem comes when we try to deal with date-time pickers and View Model binding. We can solve such problems using custom blinding handlers for knockout.
Background
I am working new with Knockout and MVVM. Everything was just fine except the date binding from a bootstrap date-time picker/ date time object send from server end. After Googling for sometime, I got a solution, which I am going to demonstrate here. Before we start, we need some .js libraries like:
- jquery
- bootstrap-datepicker
- knockout
- moment (really a good and simple js lib to deal with date time objects)
Using the Code
Let's start with the basic HTML to hold our date-time picker. Here two things are quite important:
data-date-format
: to point format of the date picker datepicker
: which is a custom binding handler work for data binding, like value: in regular knockout
<div>
<label class="control-label">Date From Client</label>
<input type="text" readonly="readonly"
data-date-format="dd-mm-yyyy" class="datepicker"
data-bind="datepicker: dateFromClient" />
<span class="help-block">Date with Formate of (dd-mm-yyyy)</span>
</div>
<div>
<label class="control-label">Date From server</label>
<input type="text" readonly="readonly"
data-date-format="dd-mm-yyyy" class="datepicker"
data-bind="datepicker: dateFromServer" />
<span class="help-block">ClientDate - 5 days, (dd-mm-yyyy)</span>
</div>
The custom binding handler datepicker
is declared like this. We can customize it even for the jquery date time picker if we want to.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options).on("changeDate", function (ev) {
var observable = valueAccessor();
observable(ev.date);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).datepicker("setValue", value);
}
};
Now, we are going to use some utility functions to use moment.js to get current date time or to convert a server side datetime
object to a given date format for client end.
function currentDate() {
return moment();
};
function clientDateStringFromDate(date) {
return moment(date).format('DD-MM-YYYY');
};
Now, here is the View model for our view:
function ViewModel() {
var self = this;
self.dateFromClient = ko.observable(currentDate());
self.dateFromServer = ko.observable(currentDate());
self.init = function() {
self.getServerDate();
};
self.getServerDate = function () {
var json = JSON.stringify({ date: self.dateFromClient()});
$.ajax({
url: '../../Services/DateTestService.asmx/GetDate',
dataType: "json",
contentType: 'application/json; charset=utf-8',
type:"POST",
data:json,
async: true,
processData: false,
cache: false,
success: function (data) {
var date = clientDateStringFromDate(data.d);
self.dateFromServer(date);
},
error: function (xhr) {
alert('Error to connect to server.');
}
});
};
self.dateFromClient.subscribe(function(newValue) {
self.getServerDate();
});
}
Now let's finish with date picker wrapper and model binding.
$(document).ready(function () {
$('.datepicker').datepicker();
var vm = new ViewModel();
vm.init();
ko.applyBindings(vm);
});
I have demonstrated the basic date binding here. You can find more details in the project attachment, which is a Visual Studio 2010 solution.