John Giotta
John Giotta

Reputation: 16934

How to disable if observableArray array has no matching index with Knockout JS

I have a simple model object with one array property.

Example:

{name: 'Foo', tags = ['fun', 'cool', 'geek']};

I add more one of these models to an obervableArray in my view model.

//pseudo code
oa.add({name: 'Foo', tags: ['fun', 'cool', 'geek']});
oa.add({name: 'Bar', tags: ['sad', 'dorky', 'uncool']});
oa.add({name: 'Qwerty', tags: ['keys', '101', 'geek']});

Now when I filter an item based on a tag I would like to display a message that there are no more items with a certain tag.

Filter code:

// self = this;
self.filter = ko.observable('');
self.filterItems = ko.dependentObservable (function() {
    var filter = this.filter();
    if (!filter) {
        return this.items();
    } else {
        return ko.utils.arrayFilter(this.items(), function(item) {
                try {
                    if (compareAssociativeArrays(item.tags, filter)) {
                        return true;
                    }
                } catch (e) {}
                self.items.remove(item);
        });
    }
}, this);

Is it possible to data-bind a given length of items with an indexOf a tag value ?

UPDATE

I did come up with a solution, but not sure if best. With it I can modify and retrieve totals as well:

self.hasGeek = ko.computed(function () {
    var sum = 0;
    var item;
    for (var i=0; i<self.items().length; i++) {
        var item = self.items()[i];
        if (item.tags().indexOf('geek') != -1) {
            sum++;
        }
    }
    return (sum > 0) ? true : false;
});

Upvotes: 0

Views: 1887

Answers (2)

RP Niemeyer
RP Niemeyer

Reputation: 114792

I am not sure about your exact structure, but I would setup a computed observable to represent your filtered items. Then, you can include a section that has its visibility controlled by the length of the filtered items computed observable.

Would be like: http://jsfiddle.net/rniemeyer/aVtpc/

Tag Filter: <input data-bind="value: tagFilter" />

<hr/>

<div data-bind="visible: !filteredItems().length">
    No items found
</div>

<ul data-bind="foreach: filteredItems">
    <li data-bind="text: name"></li>
</ul>​

js:

var ViewModel = function() {
    this.tagFilter = ko.observable();
    this.items = ko.observableArray([
        {name: 'Foo', tags: ['fun', 'cool', 'geek']},
        {name: 'Bar', tags: ['sad', 'dorky', 'uncool']},
        {name: 'Qwerty', tags: ['keys', '101']}
    ]);

    this.filteredItems = ko.computed(function() {
        var filter = this.tagFilter();

        if (!filter) {
           return this.items();   
        }

        return ko.utils.arrayFilter(this.items(), function(item) {       
            return ko.utils.arrayFirst(item.tags, function(tag) {
                  return tag === filter;         
            });                
        });
    }, this);
};

ko.applyBindings(new ViewModel());

Upvotes: 1

John Papa
John Papa

Reputation: 22298

Quick psuedo code off my head ... After you filter down the oa, perhaps using a computed function, you can do something like this:

<span data-bind="text: filterOa().length"></span>
<!-- ko: foreach filterOa -->
   <span data-bind="text: name"></span>
<!-- /ko -->
<!-- if: filterOa().length === 0 -->
    You got nothing
<!-- /ko -->

Upvotes: 0

Related Questions