I recently started working with KnockoutJs and quickly realized using the default Json(myModelWithADate)
resulted in the default json encoding of \\/Date(
I always use a data converter instead of sending data directly to server to fix any client encoding or parsing issues, without the need to use additional tools.
In the Knockout JS view model file, I add the following code before the view model setup, which intercept selected proeprries of the view model and use moment.js to take care of date convertions:
// converting data before sending to controller
var dataConverter = function (key, value) {
if (key === 'InvoiceDate') {
return moment(value).format("YYYY MMMM DD");
}
return value;
};
Then I use the dataConverter
instead of data
in the ajax save method within the view model:
// Example view model for sales invoice
SalesInvoiceViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.SaveInvoice = function () {
$.ajax({
url: '/SalesInvoices/SaveInvoice/',
type: 'POST',
data: ko.toJSON(self, **dataConverter**),
contentType: 'application/json',
success: function (data) {
if (data.invoiceViewModel !== null) {
ko.mapping.fromJS(data.invoiceViewModel, {}, self);
}
if (data.redirectToLocation !== null) {
window.location = data.redirectToLocation;
}
},
error: function (xhr, ajaxOptions, thrownError) {
// report error to user
}
});
}
A cleaner alternative to @photo_tom's answer is to decorate the property with the IsoDateTimeConverter via the JsonConverter attribute, like so:
public class MyClass
{
[JsonConverter(typeof(IsoDateTimeConverter))]
public DateTime Timestamp { get; set; }
}
The better way to handle dates in knockoutjs is to use moment library and handle dates like boss. You can easily deal with dates like /Date(-62135578800000)/. No need to bother of how your serialize date in controller.
Approach 1 : Directly in view:
Lets say your knockout model gets such date in a observable called sentDate and now it has value /Date(-62135578800000)/. To bind it in view you can do :
<p><label>Date</label>: <span data-bind="moment(sentDate).format('MM/DD/YYYY')"></span></p>
Approach 2 : In custom binding
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var jsonDate = valueAccessor();
var ret = moment(jsonDate).format('MM/DD/YYYY');
element.innerHTML = ret;
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
}
};
Usage same as you had said :
<td data-bind="date: sentDate">
</td>
momentjs supports lots of date time formats and utility functions on dates.
Just came up on this question because we also started using knockout.js on our MVC3 app. Since we already have jQuery datepicker and we need to format dates differently by locale (portal has different languages and different formats are presented per language), so maybe this mashup of technological requirements arise somewhere else and will be useful:
var jsDateFormat = "@CultureHelper.JsDateFormat"; // can be something like yy-mm-dd
//...
ko.bindingHandlers.date = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var value = valueAccessor();
if (value != null) {
var jsonDate = new Date(parseInt(valueAccessor().substr(6)));
element.innerHTML = jQuery.datepicker.formatDate(jsDateFormat, jsonDate);
}
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
}
};
And in the view then for example:
<p><label>Date</label>: <span data-bind="date: SentDate"></span></p>
Personally I think the JSON.NET solution is the best simply because it imposes less on the client. All the other solutions require additional client parsing or additional client code.
I have switched over to using JSON.NET for all of my ASP .NET code that uses JSON because its a much more customizable library.
For example I have had to implement JSON data in MVC that conformed to Google's Chart API (used in combination with Knockout for paging, etc.) and the default JavascriptSerializer
simply cannot do it.
In addition with JSON.NET you can customize it to actually spit out full Knockout view models so you don't even need to employ the mapping plugin.
I wrote a sample library called FluentJson.NET which lets you do things in Razor like:
var viewModel = @JsonObject.Create()
.AddProperty("name", "value")
.AddObservable("knockoutProperty", 123)
And get:
var viewModel = {"name":"value","knockoutProperty":ko.observable(123)}
So you can get a Knockout view model without any client side hoops to jump through.
You could easily extend something like that to handle date values however you would prefer.
I would suggest a middle man approach through ko.mapping.fromJS( data, mapping )
this would allow you to customize even with a user defined object.
var $data = { _ID : '1', _Created : someDate };
var $mapping = {
'_Created' : {
update: function (options) {
return convertdata( options.data );
}
}
}
var viewDataModel = ko.mapping( data, mapping );
ko.applyBindings( viewDataModel );
mapping parameter allows you handle changes easily and can easily be leveraged with arrays also.