Knockout Observable Array with Sorting and Foreach Data-Binding

ecozoic picture ecozoic · Jan 21, 2014 · Viewed 10.1k times · Source

I have an array of objects that are hooked up into a knockout observable array. I needed to apply sorting to these arrays and I ran into some behavior that is a bit confusing.

My first attempt involved applying the sort in the foreach data-binding.
http://jsfiddle.net/wnfXV/

<ul data-bind="foreach: people.sort(function(l,r) { return l.name == r.name ? 0 : (l.name < r.name ? -1 : 1)})">

This performs the proper sorting, but I lose the ability to dynamically add/remove elements from the array and have the DOM update.

If I add a set of parenthesis to access the underlying JavaScript array, everything works fine.

<ul data-bind="foreach: people().sort(function(l,r) { return l.name == r.name ? 0 : (l.name < r.name ? -1 : 1)})">

Based on some SO answers I found, I ended up creating a computed observable for the sorted array. http://jsfiddle.net/wnfXV/2/

self.sortedPeople = ko.computed(function() {
    return self.people().sort(function(l,r) {
        return l.name == r.name ? 0 : (l.name < r.name ? -1 : 1);
    });
});

This also works. And I don't even need to data-bind to the computed observable since it is executed immediately. I can push and remove array items and the DOM updates appropriately.

However, if I change the code to:

self.sortedPeople = ko.computed(function() {
    return self.people.sort(function(l,r) {
        return l.name == r.name ? 0 : (l.name < r.name ? -1 : 1);
    });
});

Now, I am able to push items to the array and the DOM updates, but the data is not being sorted.

I think these differences are related to knockout dependency tracking, and the difference between operating on an observable array and the native JavaScript array underneath it, but I'm having a hard time conceptualizing why the behavior is changing. I am able to get it to work, but I'm also curious as to what is considered best practice.

Thanks for any help. :)

Answer

Anshul Nigam picture Anshul Nigam · Feb 2, 2015

Problem with using sort in views is actually not recommended by KO because with this approach observableArray.sort doesn't establish a dependency on the array, so the binding won't get updated.

So if you want to make it work one can use

items.slice(0).sort()

for more detailed look at
https://github.com/knockout/knockout/issues/1380

Fiddle:http://jsfiddle.net/mbest/6dmAn/