Handling dates with Asp.Net MVC and KnockoutJS

16,297

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

I'm using the following code to generate short date strings. I use it for my date strings and jQueryUi Date Picker.

class T
    {
        public DateTime d { get; set; }
    }

static void Main(string[] args)
    {
        var k = new T { d = DateTime.Now };

        var formatter = new IsoDateTimeConverter();
        formatter.DateTimeFormat = "d";
        var s = JsonConvert.SerializeObject(k, formatter);
    }

This generates the following JSON

"{"d":"4/21/2012"}"

This results clean JavaScript code for me.

Solution 5

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>
Share:
16,297
Mark Coleman
Author by

Mark Coleman

Mark Coleman is a full stack developer focusing on the latest in web technologies. Mark enjoys learning about new technologies. Mark also likes to share his knowledge by attending local development groups and blogging about programming topics. When Mark is not absorbing everything development he enjoys photography, anything pertaining to The Simpsons, and a part time craft beer/bacon aficionado.

Updated on June 12, 2022

Comments

  • Mark Coleman
    Mark Coleman almost 2 years

    I recently started working with KnockoutJs and quickly realized using the default Json(myModelWithADate) resulted in the default json encoding of \/Date(-62135578800000)\/ With a bit of research I located four potential ways to handle the display of my dates in dom elements.

    1) Create a binding that handles the conversion from the Json date to the format you desire

    ko.bindingHandlers.date = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var jsonDate = valueAccessor();
            var value = new Date(parseInt(jsonDate.substr(6)));
            var ret = value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
            element.innerHTML = ret;
        },
        update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
    
        }
    };
    

    Usage

    <td data-bind="date: DueDate">
    </td>
    

    2) Return “strings” from your Controller

    return Json(new {MyDate = DateTime.Now.ToShortDateString()});
    

    3) Use the JSON.NET to specify a Date Time format seen over at james.newtonking.com

    Example

    string isoJson = JsonConvert.SerializeObject(entry, new IsoDateTimeConverter());
    // {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}
    

    4) use JSON.parse to handle your dates as seen in this stackoverflow answer.

    JSON.parse(jsonText, function(key, value) {
        // Check for the /Date(x)/ pattern
        var match = /\/Date\((\d+)\)\//.exec(value);
        if (match) {
            var date = new Date(+match[1]); // Convert the ticks to a Date object
            return humanReadable(date); // Format the date how you want it
        }
    
        // Not a date, so return the original value
        return value;
    });
    

    They all appear to work, but I am still struggling with which one feels “right”. Right now my gut is going with a mix with the binding and returning strings. As I could see myself extending the binding to handle input with jQuery UI datepicker controls.

    Is there an accepted practice when handling displaying dates or other types such as currency? Is there another option I am missing that solves this problem?

  • Mark Coleman
    Mark Coleman over 12 years
    Interesting nuget project, I will certainly check it out.
  • Mr. Young
    Mr. Young over 11 years
    I replaced my Json.Encode(Model) to the JSON.NET JsonConvert.SerializeObject(Model) for this exact reason. Works like a charm and I didn't have to write a single other line of code. Nice tip I've been searching for this all day.
  • D S
    D S almost 9 years
    Tnx ajay, it moment js saved my day, i like the Approach 1 easy method
  • Ashi
    Ashi almost 7 years
    Best solution : 1- no additional libraries to add to your project. 2- your code does not traverse all fields; it directly updates the fields you specify. Only one problem here is input fields really need formatted strings.