Question

I am working on a Single Page Application with knockout. I have been banging my head against the wall for this very strange problem. IE has strangely decided not to Sort my observable arrays, whereas Chrome and Firefox do work as expected.

Here's the code: The tr click event calls the sort function but IE simply doesn't sort it

var lastResponseSort = '';

var sortResponses = function (data, event) {
var sortField = $(event.target).attr("data-arg");

   if (sortField !== undefined) {
   var descending = lastResponseSort != "";
   lastResponseSort == "" ? lastResponseSort = sortField : lastResponseSort = "";

   // Sort Array here
   arrResponses.sort(function (x, y) {
      if (descending) {
      return x[sortField] > y[sortField] ? -1 : 1;
      } else {
       return x[sortField] < y[sortField] ? -1 : 1;
      }
    });
   }

HTML:

   <table id="crisis-info">
   <thead>
     <tr data-bind="click: sortResponses">
        <th data-arg="eventName">Response name</th>
        <th data-arg="eventRelatesCrisisTypes()[0].crisisType().name">Crisis Type</th>
        <th data-arg="numberOfPeopleAffected">Affected</th>
        <th data-arg="country().name">Country &amp; Region</th>
     </tr>
   </thead>
   <tbody data-bind="visible: arrResponses().length > 0, foreach: arrResponses">
       <tr>
        <td><span data-bind="text: eventName(), attr: { title:eventName() }"></span>
        </td>
        <td><span data-bind="text: eventRelatesCrisisTypes()[0].crisisType().name(),attr: { title: eventRelatesCrisisTypes()[0].crisisType().name() }"></span>
        </td>
        <td align="center"><span data-bind="text: numberOfPeopleAffected()"></span>
        </td>
        <td><span data-bind="text: country().name() + ',  ' + country().region().name()"></span></td>
        </tr>
       </tbody>
   </table>

The part of the problem which is more fascinating is that on applying debugger and going through the sort function, IE is not even sorting the array, let alone updating UI which I thought was the problem, and not throwing any error also. If anyone has faced this problem or knows the workaround, please share it. Thanks

Was it helpful?

Solution

Since you're trying to dynamically access properties and/or subproperties of your objects, you'll need to use eval instead of dynamic property access. Thus x[sortField] will need to be eval('x.' + sortField).

Also because the properties are observables, you'll need to add trailing parentheses too: eval('x.' + sortField)().

Here is your updated sort function (with some other optimizations):

arrResponses.sort(function (x, y) {
    var xLess = eval('x.' + sortField + '() < y.' + sortField + '()');
    if (descending) {
        return xLess ? 1 : -1;
    } else {
        return xLess ? -1 : 1;
    }
});

Another solution, one that I'd recommend more strongly, is to use computed observables in your objects to encapsulate the displayed data. Thus your Response object would contain the following:

this.crisisType = ko.computed(function () {
    return this.eventRelatesCrisisTypes()[0].crisisType().name();    
}, this);

this.countryRegion = ko.computed(function () {
    return this.country().name() + ', ' + this.country().region().name();
}, this);

Now you could bind to these computed observables in your view and use them in your sort function without having to use eval.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top