Ibrahim Ahmed
Ibrahim Ahmed

Reputation: 2345

How to apply filtering and paging at the same time using knockout?

I've been toying around with knockoutjs trying to create html tables that can be filtered and paged so far i've been able to create the filtering and paging separately but it seems like i can't find a way to combine the two together down i put two example of what i've achieved any help would be great thanks in advance :)

Knockout Filtering Example:

Filtering Example

HTML:

<table id="tview" class="table table-bordered table-striped table-hover">
    <thead>
        <tr>
            <td>ID</td>
            <td>Name</td>
        </tr>
        <tr>
            <td>
                <input type="number" data-bind="event: { keyup: FilterItems }" id="ID" class="form-control filter" placeholder="Filter By ID" />
            </td>
            <td>
                <input type="text" data-bind="event: { keyup: FilterItems }" id="Name" class="form-control filter" placeholder="Filter By Name" />
            </td>
        </tr>
    </thead>
    <tbody data-bind="foreach: $root.FilteredItem">
        <td data-bind="text: ID"></td>
        <td data-bind="text: Name"></td>
    </tbody>
</table>

Javascript:

var model = function () {
    var self = this;
    self.Item = ko.observableArray([]);
    self.FilteredItem = ko.observableArray([]);
    self.Filter = new Filter();
    self.FilterItems = function (object, event) {
        var currentFilter = event.currentTarget.id;
        var currentValue = $("#" + currentFilter).val();
        if (currentValue != "") {
            if (self.Filter.ActiveList().indexOf(currentFilter) < 0) {
                self.Filter[currentFilter](true);
                self.Filter.ActiveList.push(currentFilter);
            }
            self.ApplyFilters();
        } else {
            self.Filter[currentFilter](false);
            self.Filter.ActiveList.remove(currentFilter);
            if (self.Filter.ActiveList().length == 0) {
                self.FilteredItem(self.Item());
            } else {
                self.ApplyFilters();
            }
        }
    };

    self.ApplyFilters = function () {
        var tempArray = self.Item();
        self.Filter.ActiveList().forEach(function (item) {
            var value = $("#" + item).val();
            var FilterResult = [];
            if (isNaN(value)) {
                value = value.toLowerCase();
                FilterResult = ko.utils.arrayFilter(tempArray, function (obj) {
                    return obj[item]().toLowerCase().indexOf(value) > -1;
                });
            } else {
                FilterResult = ko.utils.arrayFilter(tempArray, function (obj) {
                    if (isNaN(obj[item]())) {
                        return obj[item]().toLowerCase().indexOf(value) > -1;
                    } else {
                        return obj[item]() == value;
                    }
                });
            }
            tempArray = FilterResult;
            self.FilteredItem(FilterResult);
        });
    };
};

var Item = function () {
    var self = this;
    self.ID = ko.observable();
    self.Name = ko.observable();
};

var Filter = function () {
    var self = this;
    self.ActiveList = ko.observableArray([]);
    self.ID = ko.observable(false);
    self.Name = ko.observable(false);
};

var modelInstance = new model();

for (i = 0; i < 10; i++) {
    var MyItem = new Item();
    MyItem.ID(i);
    MyItem.Name("Name" + i);
    modelInstance.Item.push(MyItem);
    MyItem = new Item();
    MyItem.ID(i);
    MyItem.Name("Name" + i+1);
    modelInstance.Item.push(MyItem);
}

modelInstance.FilteredItem(modelInstance.Item());

ko.applyBindings(modelInstance);


Knockout Paging Example:

Paging Example

HTML:

<section data-bind="foreach: Pages">
    <article  data-bind="visible: $root.CurrentPage() == $data">
        <table id="tview" class="table table-bordered table-striped table-hover">
            <thead>
                <tr>
                    <td>ID</td>
                    <td>Name</td>
                </tr>
            </thead>
            <tbody data-bind="foreach: $root.Item()[$data]">
                <tr>
                    <td data-bind="text: ID"></td>
                    <td data-bind="text: Name"></td>
                </tr>
            </tbody>
        </table>
    </article>
</section>
<ul class="pagination" data-bind="foreach: Pages">
    <li data-bind="attr: { id: 'Page' + $index(), 'class': $root.checkCurrentPage($data) }, click: $root.ChangePage"><a href="#" data-bind="text: $data"></a>
    </li>
</ul>

Javascript:

var Model = function () {
    var self = this;

    self.Item = ko.observableArray([]);

    self.CurrentPage = ko.observable(0);

    self.Pages = ko.observableArray([]);

    self.ChangePage = function (data, event) {
        if (!$(event.currentTarget).hasClass("active")) {
            $(".pagination li").removeClass("active");
            self.CurrentPage(data);
        }
    };

    self.checkCurrentPage = function (data) {
        if (self.CurrentPage() == data) {
            return "active";
        }
    };
};

var Item = function () {
    var self = this;
    self.ID = ko.observable();
    self.Name = ko.observable();
};

var modelInstance = new Model();

function Chunk(Arr, ChunkSize) {
    var Set = [];

    var PageCount = 0;

    for (var Page = 0; Page < Arr.length; Page += ChunkSize) {
        var TempArr = Arr.slice(Page, Page + ChunkSize);

        var ObservableItemArr = [];

        TempArr.forEach(function (obj) {
            ObservableItemArr.push(obj);
        });

        Set.push(ko.observableArray(ObservableItemArr));

        modelInstance.Pages.push(PageCount);

        PageCount++;
    }

    return Set;
}

var arr = [];

for (i = 0; i < 20; i++) {
    var MyItem = new Item();
    MyItem.ID(i);
    MyItem.Name("Name" + i);
    arr.push(MyItem);
    MyItem = new Item();
    MyItem.ID(i);
    MyItem.Name("Name" + i+1);
    arr.push(MyItem);
}

modelInstance.Item(Chunk(arr, 10));

ko.applyBindings(modelInstance);

Upvotes: 0

Views: 1217

Answers (1)

Anders
Anders

Reputation: 17564

Here is a Quick fiddle I did with LinqJS

http://jsfiddle.net/rL6p4onw/2

The structure looks like this

ViewModel = function() {
    var items = [];
    for(var i = 0; i < 500; i++) {
        items.push({ name: "Foo " + i });
    }

    this.items = Enumerable.From(items);   

    this.page = ko.observable(0);
    this.pageSize = ko.observable(10);
    this.filter = ko.observable();
    this.filteredCount = ko.observable();

    this.currentPage = ko.computed(this.getCurrentPage, this);
    this.pages = ko.computed(this.getPages, this);
}

Relevant code for paging / filtering

getCurrentPage: function() {   
    var filter = this.filter();

    var filtered = this.items
        .Where(function(i) { 
            return filter == null || i.name.indexOf(filter) != -1; 
        });

    this.filteredCount(filtered.Count());
    return filtered
        .Skip(this.page() * this.pageSize())
        .Take(this.pageSize())
    .ToArray();
}

The filter function can easily be modfied to be dynamic since javascript is dynamic

Upvotes: 3

Related Questions