1

I've an editable grid and a form to add data to the grid. User can edit the grid data directly and he can use the form section to enter new entry. I've had this working fine with ASP.Net MVC combined with ajax form. I updated the whole grid html upon successful ajax postback. Nothing complex, a traditional implementation.

Now, I've been working to enhance this with knockoutjs. The idea is to persist the UI but do everything in backend so that the user experience is better with least flickers and less traffic / postback time. As expected, everything goes in backend. Here's the summary -

I've a viewModel which consists of a 'commentToAdd' object and a 'commentList' object which is as expected a list of comment objects. I bind the 'commentList' object with my Grid and the 'commentToAdd' binds with the form.

var viewModel = new commentsModel();        
$.getJSON('@Url.Action("CommentsKOVM", "Claim", new { ClaimGUID = commentObj.ClaimGUID })',
 function(data) { //upon success
    viewModel.commentToAdd = ko.mapping.fromJS(data.CommentToAdd);
    viewModel.allComments = ko.mapping.fromJS(data.AllComments, mapping);// Initial items         
  ko.applyBindings(viewModel);

Works fine. But I've a 'date' field. JSON renders it as '/Date(1224043200000)' So, I've to format it - How do I format a Microsoft JSON date? like:

<span data-bind="text:new Date(parseInt(PostedOn.toString().substr(6))).toLocaleFormat('%d/%m/%Y')"></span>

But it doesn't work because it seems that 'PostedOn' is converted into an observable! The .toString returns a function definition!

I've also tried to implement the date binding as explained by Hanselman article.

I don't seem to get my date displayed correctly in the Grid. I also tried to 'ignore the mapping' :

var mapping = {'ignore': ["PostedOn"]};

but don't know how to make it work for my child object (i.e. commentList.PostedOn).

I might not be doing it in the right manner so pls suggest or atleast help me get a correct date in my Grid.

PS: Works as expected when I use ko.observableArray(data.commentList) but it doesn't go well with edit feature.

Community
  • 1
  • 1
Hemant Tank
  • 1,724
  • 4
  • 28
  • 56

2 Answers2

1

This question here has a lot of good examples:

Handling dates with Asp.Net MVC and KnockoutJS

I personally would recommend using a custom binding as such:

var jsDateFormat = "%d/%m/%Y"; // 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) {
    }
};

That's if you have jQuery UI and datepicker, and if not, just change:

jQuery.datepicker.formatDate(jsDateFormat, jsonDate) to your parseInt(jsonDate.substr(6))).toLocaleFormat(jsDateFormat)

And then instead of:

<span data-bind="text:new Date(parseInt(PostedOn.toString().substr(6))).toLocaleFormat('%d/%m/%Y')"></span>

Use:

Look how clean it is now! :)

You can read more about custom binding in the knockout.js docs: http://knockoutjs.com/documentation/custom-bindings.html

Community
  • 1
  • 1
povilasp
  • 2,386
  • 1
  • 22
  • 36
  • Thanks for your info on jQueryUI (I've yet to use it like my other web apps, this one is simple). Your code, I've already reviewed it in Hanselman article as mentioned. My original issue is that the "valueAccessor" evaluates as null! when using ko.mapping.fromJS – Hemant Tank Jan 03 '13 at 16:39
  • hmm, show me the fiddle, so I can help you. Also you can check whether the actual date is not null before or after mapping, so maybe it won't be the bindings problem :) – povilasp Jan 03 '13 at 20:53
  • oh wait. "But it doesn't work because it seems that 'PostedOn' is converted into an observable! The .toString returns a function definition!" try doing PostedOn.peek() – povilasp Jan 03 '13 at 20:54
  • Binding works with "PostedOn" if I use plaim ko.observable. However, if I dynamically load it with ko.mapping.from js (i.e. server refresh) then I've to use "PostedOn()". Pls view my answer below. I'd appreciate if you can help me differentiate both this approach. – Hemant Tank Jan 07 '13 at 13:56
0

At the end it turned out that the KO.mapping.fromJS was doing its job, it converted my data.allComments (array of comments) into an "array of observables". I believe internally it converted each of its properties into observables.

So, when I accessed the date field as

PostedOn: <input type="text" data-bind="date: PostedOn" />

The custom binder for date got an observable instead of value -

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var jsonDate = valueAccessor();

After some debugging, I updated as follows and bingo, it worked as expected!

PostedOn: <input type="text" data-bind="date: PostedOn()" />

I agree it was my misunderstanding about KO. Yet, I'm still a bit unclear about the difference between ko.mapping.fromJS and ko.Observable and more importantly when to use which?

Hemant Tank
  • 1,724
  • 4
  • 28
  • 56