Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I partially display an observableArray, with a "Show more..." button

Tags:

knockout.js

I would like to add a list of options to a page from a knockout observableArray. When there are more than a configurable number of items to be displayed, a "More options..." button (or link) should be displayed. Pressing this button should display all items and change the button text to "Less options". To make it more interesting: the button should only be displayed when there is more than 1 item that would be hidden.

The code below works (see this fiddle), but isn't there a cleaner and more generic solution (using a custom binding for example)?

<ul data-bind="foreach: options">
    <li data-bind="visible: $root.showMore() || $index() < $root.showMoreCount() || $root.options().length <= $root.showMoreCount()+1, text: $data"></li>
</ul>
<a data-bind="visible: options().length-1 > showMoreCount(), text: showMore() ? 'Less options' : 'More options', click: function () { showMore(!showMore()) }"></a>
like image 615
mhu Avatar asked Mar 24 '23 16:03

mhu


1 Answers

You could write a custom observable function to incorporate all your functionality:

ko.showMoreArray = function(initial) {
    var observable = ko.observableArray(initial);

    //observables to change behaviour
    observable.limit = ko.observable(3).extend({numeric:true});
    observable.showAll = ko.observable(false);

    //function to toggle more/less
    observable.toggleShowAll = function() {
        observable.showAll(!observable.showAll());
    };

    //computed observable for filtered results
    observable.display = ko.computed(function() {
        if (observable.showAll()) { return observable(); }
        return observable().slice(0,observable.limit());
    }, observable);

    return observable;
};

This really just wraps what you have written already, but it is reusable and leaves your HTML much neater:

<input data-bind="value: $root.orders.limit, valueUpdate: 'afterkeyup'" /><br/>

<ul data-bind="foreach: orders.display">
    <li data-bind="text: $data"></li>
</ul>

<a data-bind="text: orders.showAll() ? 'Less options' : 'More options', 
    click: orders.toggleShowAll" href="#"></a>

I've put a working version on jsFiddle.

In the example above you need to bind to a display property on the original array, but it will otherwise behave as the "full" array to all your code (which I think generally makes more sense). However, if you prefer that it behaves as the filtered (i.e. max 3 items) array to your code then you can achieve that in a similar way as demonstrated here

like image 103
Steve Greatrex Avatar answered Apr 30 '23 03:04

Steve Greatrex