Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout observableArray sort not reflecting in the UI (foreach binding)

I'm adding data into a tag list from a grid (by checking rows of data). When that happens, I want the tag list to be sorted (for example sorted alphabetically by name).

The sort result obviously has to reflect in the UI, but from what I've tried that does not work in my case.

Here's the fiddle example: http://jsfiddle.net/hLpLobo2/5/

In order to make sure it's sorted, i'm calling the sortPersonsAlphabetically function on the foreach afterAdd callback:

<div class="tag-list" data-bind="foreach: { 
                                data: tags, as: 'tag',
                                afterAdd: sortPersonsAlphabetically
}">
    <div class="tag-item">
        <span class="tag-item-value tag-item-value-name" data-bind="text: tag.name"></span>
        <span class="tag-item-separator">:</span>
        <span class="tag-item-value tag-item-value-age" data-bind="text: tag.age"></span>
    </div>
</div>

but that strangely works only after adding another item ("second selection").

In the fiddle example I provided, I've also added a <pre> tag where it is clear that the array is properly sorted, yet it's not reflected in the tag list UI.

I've also tried wrapping the sort function in a setTimeout of 1ms delay and that seems to fix it, but with some visible flicker, which in my opinion, is not acceptable and it's more of a hack.

Is there a clean way to do this ?

like image 650
Zubzob Avatar asked Feb 11 '26 06:02

Zubzob


1 Answers

Afteradd is really intended for updating the DOM in response to changes in data, not for making further changes to the data. The result you are getting are weird, though, I agree.

I recommend you use a computed to generate the sorted tags,

self.sortedTags = ko.computed(function () {
    var data = self.tags();
    return data.sort(function(left, right) {
        return left.name == right.name ? 0 : (left.name < right.name ? -1 : 1);
    });
});

and display that in your foreach:

<div class="tag-list" data-bind="foreach: { 
                                data: sortedTags, as: 'tag'
}">

http://jsfiddle.net/hLpLobo2/6/

like image 170
Roy J Avatar answered Feb 14 '26 22:02

Roy J