This is the datepicker binding i'm using (im sorry but i forgot where i took it so i can't give the author proper credit. I will edit if i remember.)
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function(event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function(element, valueAccessor) {
var widget = $(element).data("datepicker");
//when the view model is updated, update the widget
if (widget) {
widget.date = ko.utils.unwrapObservable(valueAccessor());
if (widget.date) {
widget.setValue();
}
}
}
};
function Model() {
this.date = ko.observable();
}
function VM() {
this.model = new Model();
this.save = function(model) {
$.post(someEndpoint, {model: model});
}
}
<input type='text' data-bind='datepicker: model().date,
datepickerOptions: { format:"dd/mm/yyyy"}' />
This datepicker binding, and Bootstrap datepicker in general, deals with Date objects. So my observable will contain Dates.
When i post data i see in console that it's posted with natural JS toString() format, which can vary according to your locale settings. For me, its
Mon Dec 02 2013 01:00:00 GMT+0100 (ora solare Europa occidentale)
When i receive this value server-side, i can't handle it because of this weird format.
I could of course rewrite my model date attribute before posting, like so
this.save = function(model) {
model.date(model.date().format('YYYY-MM-DD'));
$.post(someEndpoint, {model: model});
}
but this is not a general purpose solution, furthermore when model.date is updated to string representation, datepicker triggers an error since it's expecting Date.
How would you deal with this?
For Bootstrap 3.0 and higher, these work for me:
Date Only
For displaying the date, I use this bindingHandler:
window.ko.bindingHandlers.datetext = {
init: function (element, valueAccessor, allBindingsAccessor) {
// Provide a custom text value
var value = valueAccessor(), allBindings = allBindingsAccessor();
var dateFormat = allBindingsAccessor.dateFormat || "M/D/YYYY";
var strDate = window.ko.utils.unwrapObservable(value);
if (strDate) {
if (moment(strDate).year() > 1970) {
var date = moment(strDate).format(dateFormat);
$(element).text(date);
}
else {
$(element).text("-");
}
}
},
update: function (element, valueAccessor, allBindingsAccessor) {
// Provide a custom text value
var value = valueAccessor(), allBindings = allBindingsAccessor();
var dateFormat = allBindingsAccessor.dateFormat || "M/D/YYYY";
var strDate = window.ko.utils.unwrapObservable(value);
if (strDate) {
if (moment(strDate).year() > 1970) {
var date = moment(strDate).format(dateFormat);
$(element).text(date);
}
else {
$(element).text("-");
}
}
}
};
HTML
<!-- Display the date -->
<span data-bind="datetext: StartingDate"></span>
<!-- Set the date -->
<div class="input-group date">
<input type="text" class="form-control" data-provide="datepicker" data-bind="value: StartingDate"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
You'll need Moment.js. The datepicker used for this is located here
I'm using a C# backend, and posting to an MVC Controller or Web API controller poses no issues with DateTime
variables. I.e:
this.save = function(model) {
$.post(someEndpoint, {model: ko.toJSON(model)});
}
Date and Time
For displaying, I use this bindingHandler:
window.ko.bindingHandlers.datetimetext = {
init: function(element, valueAccessor, allBindingsAccessor) {
// Provide a custom text value
var value = valueAccessor(), allBindings = allBindingsAccessor();
var dateFormat = allBindingsAccessor.dateFormat || "M/D/YYYY h:mm a";
var strDate = window.ko.utils.unwrapObservable(value);
if (strDate) {
if (moment(strDate).year() > 1970) {
var date = moment(strDate).format(dateFormat);
$(element).text(date);
} else {
$(element).text("-");
}
}
},
update: function(element, valueAccessor, allBindingsAccessor) {
// Provide a custom text value
var value = valueAccessor(), allBindings = allBindingsAccessor();
var dateFormat = allBindingsAccessor.dateFormat || "M/D/YYYY h:mm a";
var strDate = window.ko.utils.unwrapObservable(value);
if (strDate) {
if (moment(strDate).year() > 1970) {
var date = moment(strDate).format(dateFormat);
$(element).text(date);
} else {
$(element).text("-");
}
}
}
};
For setting the datetime, I use this handler:
window.ko.bindingHandlers.datetimepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
$(element).parent().datetimepicker();
window.ko.utils.registerEventHandler($(element).parent(), "change.dp", function (event) {
var value = valueAccessor();
if (window.ko.isObservable(value)) {
var thedate = $(element).parent().data("DateTimePicker").getDate();
value(moment(thedate).toDate());
}
});
},
update: function(element, valueAccessor) {
var widget = $(element).parent().data("DateTimePicker");
//when the view model is updated, update the widget
var thedate = new Date(parseInt(window.ko.utils.unwrapObservable(valueAccessor()).toString().substr(6)));
widget.setDate(thedate);
}
};
HTML
<!-- Display the date -->
<span data-bind="datetimetext: FromDate"></span>
<!-- Set the date -->
<a data-bind="attr:{id: Id}" class="input-group date">
<input type="text" class="form-control" data-bind="datetimepicker: FromDate" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</a>
My server side method looks something like this:
[HttpPost]
public async Task<ActionResult> Update(MyViewModel myModel)
{
if (myModel.FromDate == new DateTime(0001, 01, 01))
{
if (!string.IsNullOrEmpty(myModel.sFrom))
{
myModel.sFrom = myModel.sFrom.Replace("/Date(", "").Replace(")", "").Replace("/", "").Trim();
var ticks = Convert.ToInt64(myModel.sFrom);
myModel.FromDate = new DateTime(1970, 1, 1).AddTicks(ticks * 10000).AddHours(-6);
}
}
var item = Mapper.Map<MyClass>(myModel);
await Task.Run(() => _myService.Save(item));
return Json(myModel);
}
With a model similar to this:
public class MyViewModel
{
public Guid Id { get; set; }
public DateTime FromDate { get; set; }
public string sFrom { get; set; }
}
The string version of the date (sFrom
), is generated in my Javascript before I post to the server:
function saveModel(model) {
model.sFrom(model.FromDate().toString());
$.post(someendpoint, { model: ko.toJSON(model) }, function(data) {
ko.mapping.fromJS(data, {}, model);
});
}
Again, this uses Moment.js and this version of the datetimepicker is here