38

In my view I wish to display a knockout.js binded field that contains a date. It is just a display field and not an input field. Something like below when basemodel.actionDate = ko.observable()

<p class="display-field" data-bind="text: baseModel.actionDate"/> 

However this is displayed as follows:

2013-06-17T11:56:18.4537687Z

What is the easiest way to format this dd mm yyyy. eg: 17 June 2013?

serenesat
  • 4,611
  • 10
  • 37
  • 53
Martin
  • 1,001
  • 3
  • 13
  • 17
  • Are you storing the `date` object in your observable or the string "2013-06-17T11:56:18.4537687Z"? – nemesv Jun 17 '13 at 13:29

5 Answers5

76

I recommend the moment.js date formatting library.

Using it, you can do something like this in your view:

<p class="display-field" data-bind="text: moment(baseModel.actionDate()).format('LL')"/>
Brandon
  • 38,310
  • 8
  • 82
  • 87
  • 1
    Thanks. Looks good. But I get an "undefined NaN 0NaN" error displayed. Any ideas? – Martin Jun 17 '13 at 15:50
  • 8
    ah if your `actionDate` is a ko.observable, then you need to put `()` in there to get the value out before passing it to moment. I modified the post. – Brandon Jun 17 '13 at 15:54
  • But you have to add it to the client side... which might be slower plus forcing to load more resources. – Alvaro Jun 29 '15 at 14:01
  • if you're using browserify, make sure you declare moment in global scope: global.moment = require('moment'); – Ben Sewards Jul 11 '16 at 15:45
  • @lola use `format('LLLL')` or read the [documentation](http://momentjs.com/docs/#/displaying/) – Brandon Nov 26 '16 at 19:37
  • Doing it like this will break two way binding. Don't do this on an HTML5 date input field. – CarComp Nov 29 '18 at 15:17
13

Either make sure the service output it in a correct format, or format in client side

If you want todo it client side then you can parse the ISO date string into a Date object and then use jQuery globalize to format it to desired format.

I use KO extenders for this

http://jsfiddle.net/vRf5B/42/

ko.extenders.date = function(target, format) {
    return ko.computed({
        read: function() {
            var value = target();
            if(typeof value === "string") {
                value = new Date(value);                
            }

            format = typeof format === "string" ? format: undefined;
            value = Globalize.format(value, format);

            return value;        
        },
        write: target
    });
}

update

I got a question on my blog how to retrieve the raw date value It can be done my exposing the raw value on the computed like

http://jsfiddle.net/vRf5B/91/

Anders
  • 17,306
  • 10
  • 76
  • 144
  • Good reminder re: changing service output! I intially googled onto this.. then, remembered I can control the serialization server side. This line globally affects how an ASP.NET MVC app handles all JSON serialization (placed in WebApiConfig.Register method): GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.DateFormatString = "dd/MM/yyyy" – bkwdesign Oct 23 '13 at 20:22
  • 4
    For modern MVVM sites using KO or Angular, you want real Date objects so that you can apply logic to them – Anders Oct 24 '13 at 09:23
  • I tried your solution, it's working for your format but if I want to put my format, what needs to be changed ? I tried something like that but it doesn't work: `````` – YoyoS Apr 14 '15 at 08:27
  • Ok I had to understand your ```date: true``` was the format. Hard thing ! ```.extend({ date: "dd/MM/yyyy" });``` it is thanks ! – YoyoS Apr 14 '15 at 14:38
3

Declare format function:

Date.prototype.toFormattedDate = function () {
  var dd = this.getDate();
  if (dd < 10) dd = '0' + dd;
  var mm = this.getMonth() + 1;
  if (mm < 10) mm = '0' + mm;
  var yyyy = this.getFullYear();
  /* change format here */
  return String(mm + "/" + dd + "/" + yyyy);
};

And use it with the date strings as:

<span data-bind="text: new Date(TaxAuthority.RegDate).toFormattedDate()"></span>
Vladislav
  • 1,696
  • 27
  • 37
1

I had some issues (I think) with the mapping plugin trying to use the extender. Since I'm only displaying dates and not allowing them to be edited I prefer to just use a binding handler like this:

Shipped on <span data-bind="date: shipDt"></span>

Here's the handler:

    ko.bindingHandlers.date =
    {
        update: function (element, valueAccessor: () => any, allBindingsAccessor: any)
        {
            return ko.bindingHandlers.text.update(element, function ()
            {
                var value: any = ko.utils.unwrapObservable(valueAccessor());

                if (value == null)
                {
                    return null;
                }

                if (typeof value === "string")
                {
                    value = new Date(value);
                }

                return value.toShortDateString();

            }, allBindingsAccessor, null, null);
        }
    };

I chose to add a prototype to Date object like this (and call toShortDateString on the Date object created in the handler)- but you can replace the logic above with Globalize or whatever you prefer.

Date.prototype.toShortDateString = function ()
{
    return (this.getMonth() + 1) + "/" + this.getDate() + "/" + this.getFullYear();
};
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
0

If you are referencing moment.js then I would actually format in the knockout model.

var BiographicViewModel = function (person) {
    this.FirstName = ko.observable(person.first_name);
    this.LastName = ko.observable(person.last_name);
    this.DOB = ko.observable(moment(person.birth_date).format("MM/DD/YYYY"));
    this.Gender = ko.observable(person.gender);
    this.Country = ko.observable(person.country);
    this.City = ko.observable(person.city);        
};