RationalGeek
RationalGeek

Reputation: 9599

KO template with filter based on variable

I have an array of items, and I want to group them into multiple sections based on the data within that array. I'm using KO if conditionals successfully, but I have to repeat the section markup and conditional for each section I want. I want to convert the section to a template to avoid repetition, but I don't know how to include the if condition in the template using a variable rank. Is there a way to pass a variable to a template and reference that variable within the template?

Here is the HTML:

<strong>Rank 1:</strong><br/>
<ul data-rank="1" data-bind="foreach: listItems">
    <!-- ko if: rank == 1 -->
    <li data-bind="text: name + ' (' + rank + ')'"></li>
    <!-- /ko -->
</ul>

<strong>Rank 2:</strong><br/>
<ul data-rank="2" data-bind="foreach: listItems">
    <!-- ko if: rank == 2 -->
    <li data-bind="text: name + ' (' + rank + ')'"></li>
    <!-- /ko -->
</ul>

<strong>Rank 3:</strong><br/>
<ul data-rank="3" data-bind="foreach: listItems">
    <!-- ko if: rank == 3 -->
    <li data-bind="text: name + ' (' + rank + ')'"></li>
    <!-- /ko -->
</ul>

And the JavaScript:

$(document).ready(function() {
    var viewModel = {};

    viewModel.listItems = ko.observableArray();
    viewModel.listItems.push({name: "A", rank: 1});
    viewModel.listItems.push({name: "B", rank: 1});
    viewModel.listItems.push({name: "C", rank: 2});
    viewModel.listItems.push({name: "D", rank: 2});
    viewModel.listItems.push({name: "E", rank: 1});
    viewModel.listItems.push({name: "F", rank: 3});
    viewModel.listItems.push({name: "G", rank: 2});

    ko.applyBindings(viewModel);
});

And a working fiddle:
http://jsfiddle.net/RationalGeek/9eftr/

Upvotes: 1

Views: 363

Answers (1)

nemesv
nemesv

Reputation: 139788

I would create a filter function on the viewmodel:

viewModel.filterByRank = function(rank) {
    return ko.utils.arrayFilter(viewModel.listItems(), function(item) {
        return item.rank == rank;
    });
}

And then use this function in the bindings:

<strong>Rank 1:</strong><br/>
<ul data-rank="1" 
    data-bind="template: { name: 'temp', foreach: filterByRank(1) }">
</ul>

<strong>Rank 2:</strong><br/>
<ul data-rank="2" 
    data-bind="template: { name: 'temp', foreach: filterByRank(2) }">
</ul>

<strong>Rank 3:</strong><br/>
<ul data-rank="3" 
    data-bind="template: { name: 'temp', foreach: filterByRank(3) }">
</ul>

<script id="temp" type="text/html">
    <li data-bind="text: name + ' (' + rank + ')'"></li>
</script>

Demo JSFiddle.

Or you can move your whole markup to a template and use it like:

<!-- ko template: { name: 'temp', data: { rank: 1, items: filterByRank(1) } } -->
<!-- /ko -->

<script id="temp" type="text/html">
    <strong>Rank <!-- ko: text: rank --><!-- /ko --></strong>
    <br/>
    <ul data-bind="foreach: items, attr: { 'data-rank': rank}">
        <li data-bind="text: name + ' (' + rank + ')'"></li>
    </ul>
</script>

Demo JSFiddle.

Upvotes: 1

Related Questions