Search code examples
knockout.jsforeachbreezeko.observablearray

Knockout - Using foreach and sort together


I am having trouble combining the foreach binding with a sort. I have a list bound like so:

<article data-bind="foreach: widgets">  

Widgets is a simple obvservable array:

var widgets= ko.observableArray();

This works nicely giving me a list of my "widgets". If I add a new "widget" to this list then it appears dynamically in the list via data binding.

However as soon as I add sorting to the array:

<article data-bind="foreach: widgets.sort(function (left, right) { return left.order() == right.order() ? 0 : (left.order() < right.order() ? -1 : 1); })">

Then newly added widgets no longer appear in my list - unless I reload the page. (The sorting works nicely at this point - if I update the "order" field that I am sorting on then the items in my list are dynamically re-sorted).

How can I go about getting the sorting to play nicely with the dynamic updating of new items in my observable array?

I am using Breezejs in order to retrieve my data, however I do not think that is impacting on this scenario.


Solution

  • The observableArray.sort returns the sorted underlying ("regular") array and not a observableArray that is why the changes not shown on the UI.

    To have the sorting and the UI updated you need to create a ko.computed which does the sorting and use the computed in your binding. Because the ko.computed will listen on the widgets changes and recalculate the sorting.

    var widgets= ko.observableArray();
    
    var sortedWidgets = ko.computed(function() {
       return widgets().sort(function (left, right) { 
            return left.order() == right.order() ? 
                 0 : 
                 (left.order() < right.order() ? -1 : 1); 
       });
    });
    

    Then you can bind it with:

    <article data-bind="foreach: sortedWidgets" />