Question

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. :)

Was it helpful?

Solution 2

In your JS Fiddle it's because your foreach is bound to people... not to sortedPeople.

The reason its sorted the first time is because the computed runs once... but doesn't subscribe.

However, the computed ends up running again when you use the parenthesis because of some subscription on the underlying array.

Edit:

When you use the parenthesis, the computed is subscribing to the array when the observable is invoked. When the subscribed item changes, the compute is re-executed.

OTHER TIPS

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/

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