Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout Observable Array with Sorting and Foreach Data-Binding

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

like image 829
ecozoic Avatar asked Jan 21 '14 20:01

ecozoic


People also ask

For what purpose do we use foreach binding in Ko?

Purpose. The foreach binding duplicates a section of markup for each entry in an array, and binds each copy of that markup to the corresponding array item. This is especially useful for rendering lists or tables.

How do you sort an observable array?

Description. The KnockoutJS Observable sort() method sorts all items in the array. By default, items are sorted in an ascending order. For sorting an array in a descending order, use reverse() method on sorted array.


2 Answers

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/

like image 169
Anshul Nigam Avatar answered Oct 17 '22 00:10

Anshul Nigam


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.

like image 1
LameCoder Avatar answered Oct 17 '22 00:10

LameCoder