1

I am working on a simple Web App, where Users can comment articles chronologically (like comments on an blog article). Every comment has a timestamp. I use KnockoutJS for a client side view model and due to problems with the date object in IE9 I use MomentJS for crossbrowser timestamp parsing (the Timestamp Property of every comment is in fact a MomentJS generated object). Data comes from an REST Endpoint as JSON to the client where it is instantiated in the Knockout view model. The constructor of my article model looks like this (shortened):

GC.ko.modelArticle = function (a) {
    this.Id = ko.observable(a.Id);
    this.Title = ko.observable(a.Title).extend({ required: true, minLength: 3 });
        :     //some more Properties
    this.Comments = ko.observableArray();
    if (util.isDefined(a.Comments)) {
        for (var i = 0; i < a.Comments.length; i++) {
            this.Comments.push(new GC.ko.modelComment(a.Comments[i]));
        }
    }
    this.Comments.sort(function (left, right) {
        return left.Timestamp == right.Timestamp ? 0 : (left.Timestamp < right.Timestamp ? -1 : 1);
    });
};

As you can see, if the JSON (a) contains comments, these are pushed onto a Knockout observableArray. Afterwards I am sorting the Array chronologically ascending, so that newer Comments appear after older ones in the UI.

In Firefox and Chrome the array gets sorted ascending, as it should.

In IE9 it is sorted descending.

Does this happen because of

  • crossbrowser issues of the Array().push() function
  • or the Array().sort() function,
  • or because Knockout observable Arrays have issues with sorting,
  • or is it because of some error in my code?

EDIT: comment.Timestamp is an Knockout Observable. I tried two variants:

First returning a plain Javascript Date Object (which had Timestamp parsing issues in IE, so I had to modify this):

this.Timestamp = ko.observable(c.Timestamp)

Second returning a moment Object:

this.Timestamp = ko.observable(moment(c.Timstamp)
  • 'c' is the JSON for a comment

EDIT 2: It turns out, that the sort() function of observableArray() in Knockout 2.2.1 seems to be the problem. I modified my code to the following (first sorting the plain javascript array, then pushing the elements to the KO Observable Array) and everything works as it should now. Here's the code:

GC.ko.modelArticle = function (a) {
    this.Id = ko.observable(a.Id);
    this.Title = ko.observable(a.Title).extend({ required: true, minLength: 3 });
        :     //some more Properties
    this.Comments = ko.observableArray();
    if (util.isDefined(a.Comments)) {
        a.Comments.sort(function(left,right) {
            return left.Timestamp == right.Timestamp ? 0 : (left.Timestamp < right.Timestamp ? -1 : 1);
        });

        for (var i = 0; i < a.Comments.length; i++) {
            this.Comments.push(new GC.ko.modelComment(a.Comments[i]));
        }
    }
};
4
  • Try return +right.Timestamp - left.Timestamp Commented Oct 29, 2013 at 13:19
  • Easier and more elegant sorting statement, unfortunately the resulting array is still sorted the other way round in IE9. But thanks! Nevertheless I will simplify it that way. Commented Oct 29, 2013 at 13:40
  • What is the structure of GC.ko.modelComment? Is either it or Timestamp observable, or are they just plain JS properties? Commented Oct 29, 2013 at 13:46
  • @Paul I tried two variants, both with knockout observables. First one was just "this.Timestamp = ko.observable(c.Timestamp);" so the resulting Observable returns a javascript Date Object (with IE parsing issues returning 'NaN' sometimes). So I defined it "this.Timestamp = ko.observable(moment(c.Timestamp));" which returns a moment object. So I used the sorting function that wdosanjos pointed out below (added this to my question). Commented Oct 29, 2013 at 15:23

1 Answer 1

3

Given that .Timestamp is a moment you should use the .isBefore() and .isSame() comparison methods as follows:

this.Comments.sort(function (left, right) {
    return left.Timestamp.isSame(right.Timestamp) ? 0 : (left.Timestamp.isBefore(right.Timestamp) ? -1 : 1);
});
Sign up to request clarification or add additional context in comments.

4 Comments

thanks for this advice, did not know that. Unfortunately it does not help. The array is still sorted the other way round n IE9. I checked it, because article also has Knockout computed fields for "firstComment = model.Comments()[0];" and "lastComment = model.Comments()[model.Comments().length - 1];" and these two are exactly vice versa in the different browsers. Do I have to use browser detection to solve this???
I found this question (stackoverflow.com/a/7501593/2711582), which should explain why the different sort order in IE9. But unfortunately it does not provide a solution.
thats a possible explanation, because my REST service delivers the comments already sorted in the JSON, and IE just does not sort anything. Did not think of that, I'll see if I can get IE to sort it anyway by modifying the sort function.
ok, I tried around a little, and it there seems to be a bug in knockouts sort() implementation provided by observableArray(). Now I do sorting already on the plain JavaScript Array delivered by the JSON Object before I push the elements into the observableArray. The functions are the same and now everything works at it is supposed to. I am using Knockout 2.2.1. My solution code looks like EDIT 2 in my question points out. Thanks for the hints!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.