user2975436
user2975436

Reputation: 235

knockoutjs: foreach binding with filter

I am new to Knockoutjs and I am trying to accomplish two things:

A. Hide/remove #TrueListSection or #FalseListSection if ul#TrueList is empty or ul#FalseList is empty accordingly

B. Print out $index in each li

C. Is it possible to get the key value with $index in each li

<li>0 - hasCar</li>
<li>2 - hasTruck</li>

D. I will also appreciate if you know the better way to solve, for instance, instead of doing below, do something else (without changing my viewmodel)

foreach: [data.hasCar, data.HasPlain, data.hasTruck, data.Bike]

Here's my view model

var ViewModel = function() {
    var self = this;
    this.data = {
        hasCar: true,
        hasPlain: false,
        hasTruck: true,
        hasBike: false
    };
};

And here's my HTML:

<div id="TrueListSection">
    <h2><b>Has</b></h2>
    <ul id="TrueList" data-bind="foreach: [data.hasCar, data.HasPlain, data.hasTruck, data.Bike]">
        <!-- ko if: $data -->
        <li data-bind="text: $index"></li>
        <!-- /ko -->
    </ul>
</div>
<hr/>
<div id="FalseListSection">
    <h2><b>Does Not Have</b></h2>
    <ul id="FalseList" data-bind="foreach: [data.hasCar, data.HasPlain, data.hasTruck, data.Bike]">
        <!-- ko ifnot: $data -->
        <li data-bind="text: $index"></li>
        <!-- /ko -->
    </ul>
</div>

It currently throws the following error:

Uncaught Error: Unable to parse bindings.
Message: ReferenceError: $index is not defined;
Bindings value: text: $index 

And here's my JSFiddle: http://jsfiddle.net/tuJtF/3/

Thank you so much in advance.

Upvotes: 2

Views: 2802

Answers (1)

Hans Roerdinkholder
Hans Roerdinkholder

Reputation: 3000

I think you supplied the wrong Fiddle :) anyway, I used the code from your post and edited it, it now does what you want (I think):

http://jsfiddle.net/tuJtF/2/

What I did:

  1. Update your Knockout library. The version you linked does not seem to support $index. I linked Knockout 3.0 and that fixed your requirement B.
  2. I changed your viewmodel to fix requirement D. I used an observable array that is used in your foreach bindings now. Observable arrays can be passed directly into a foreach, where the object you were using before cannot be passed in directly.
  3. The observable array is filled with objects, that have a description and a value. I changed your data that way to fulfill requirement C: print the key value.
  4. I created computed observables that return observable arrays with ONLY the true or false values in your original array. This was done to fulfill requirement A (I can just check the length of the corresponding array to know if the section should be visible) and also to make your binding code cleaner.

Relevant changes:

// Changed the structuring of your data to use observable arrays and include the description property so you can bind against it
this.data = ko.observableArray([
    { description: 'hasCar', value: true },
    { description: 'hasPlain', value: false },
    { description: 'hasTruck', value: true },
    { description: 'hasBike', value: false }
]);

// Made two computed observables so you can separate the true from the false values more easily.
this.trueData = ko.computed({
    read: function () {
        return ko.utils.arrayFilter(this.data(), function (item) {
            return item.value === true;
        });
    },
    owner: this
});

this.falseData = ko.computed({
    read: function () {
        return ko.utils.arrayFilter(this.data(), function (item) {
            return item.value === false;
        });
    },
    owner: this
});

Upvotes: 3

Related Questions