I'm trying to make paging work with the KnockoutJs KOGrid. I've been following this: http://knockout-contrib.github.io/KoGrid/#paging
The data that I'm passing into my view model (the vm param) contains the following:
My knockout view model is as follows:
function ViewModel(vm) {
var self = this;
this.myData = ko.observableArray([]);
this.rows = ko.observableArray(vm.Rows);
this.deleteInvisibleColumns = function () {
for (var i = 0; i < vm.Rows.length; i++) {
var row = vm.Rows[i];
var keys = Object.keys(row);
for (var k = 0; k < keys.length; k++) {
if (vm.VisibleColumns.indexOf(keys[k]) === (-1)) {
delete row[keys[k]];
};
};
};
};
self.deleteInvisibleColumns();
this.filterOptions = {
filterText: ko.observable(""),
useExternalFilter: true
};
this.pagingOptions = {
pageSizes: ko.observableArray([2, 500, 1000]),
pageSize: ko.observable(2),
totalServerItems: ko.observable(0),
currentPage: ko.observable(1)
};
this.setPagingData = function(data, page, pageSize){
var pagedRows = data.Rows.slice((page - 1) * pageSize, page * pageSize);
var pagedData = { Rows: pagedRows, VisibleColumns: data.VisibleColumns };
self.myData(pagedData);
self.pagingOptions.totalServerItems(data.Rows.length);
};
this.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$.getJSON('/SampleData/GetDataPage', function (returnedPayload) {
data = returnedPayload.filter(function (item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
self.setPagingData(data,page,pageSize);
});
} else {
$.getJSON('/SampleData/GetDataPage', function (returnedPayload) {
self.setPagingData(returnedPayload, page, pageSize);
});
}
}, 100);
};
self.filterOptions.filterText.subscribe(function (data) {
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage(), self.filterOptions.filterText());
});
self.pagingOptions.pageSizes.subscribe(function (data) {
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage(), self.filterOptions.filterText());
});
self.pagingOptions.pageSize.subscribe(function (data) {
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage(), self.filterOptions.filterText());
});
self.pagingOptions.totalServerItems.subscribe(function (data) {
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage(), self.filterOptions.filterText());
});
self.pagingOptions.currentPage.subscribe(function (data) {
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage(), self.filterOptions.filterText());
});
self.getPagedDataAsync(self.pagingOptions.pageSize(), self.pagingOptions.currentPage());
this.gridOptions = {
data: self.myData,
enablePaging: true,
pagingOptions: self.pagingOptions,
filterOptions: self.filterOptions
};
};
Andy my html (Asp.Net MVC Razor view) is:
@model ESB.BamPortal.Website.Models.SampleDataViewModel
@using System.Web.Script.Serialization
@{
ViewBag.Title = "Sample Data";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@ViewBag.Title</h2>
@{ string data = new JavaScriptSerializer().Serialize(Model); }
<div id="Knockout" data-bind="koGrid: gridOptions">
</div>
@section Scripts {
<script src="~/KnockoutVM/SampleData.js"></script>
<link rel="stylesheet" type="text/css" href="~/Content/KoGrid.css">
<script type="text/javascript">
var vm = new ViewModel(@Html.Raw(data));
ko.applyBindings(vm, document.getElementById("Knockout"));
</script>
}
When the page loads, the following error is being thrown from within kogrid.js Uncaught TypeError: grid.sortedData.peek(...).filter is not a function
self.evalFilter = function () {
if (searchConditions.length === 0) {
grid.filteredData(grid.sortedData.peek().filter(function(item) {
If I inspect the sortedData property of the grid object it looks ok:
The last line of my knockout viewmodel js to execute is self.myData(pagedData); within the this.SetPagingData function.
Using Fiddler I pulled the following out of the response from the server:
<script type="text/javascript">
var vm = new ViewModel({"Rows":[{"SampleDataId":1,"Manufacturer":"Ford","Model":"Escort","Style":"Hatch"},{"SampleDataId":2,"Manufacturer":"Vauxhall","Model":"Cavalier","Style":"Saloon"},{"SampleDataId":3,"Manufacturer":"Rover","Model":"Montego","Style":"Saloon"},{"SampleDataId":4,"Manufacturer":"Ford","Model":"Escort","Style":"Hatch"},{"SampleDataId":5,"Manufacturer":"Vauxhall","Model":"Cavalier","Style":"Saloon"},{"SampleDataId":6,"Manufacturer":"Rover","Model":"Montego","Style":"Saloon"},{"SampleDataId":7,"Manufacturer":"Opel","Model":"Monza","Style":"Coupe"},{"SampleDataId":8,"Manufacturer":"BMW","Model":"325i","Style":"Saloon"},{"SampleDataId":9,"Manufacturer":"Ford","Model":"Escort","Style":"Hatch"},{"SampleDataId":10,"Manufacturer":"Vauxhall","Model":"Cavalier","Style":"Saloon"},{"SampleDataId":11,"Manufacturer":"Rover","Model":"Montego","Style":"Saloon"},{"SampleDataId":12,"Manufacturer":"Opel","Model":"Monza","Style":"Coupe"},{"SampleDataId":13,"Manufacturer":"BMW","Model":"325i","Style":"Saloon"},{"SampleDataId":14,"Manufacturer":"Ford","Model":"Escort","Style":"Hatch"},{"SampleDataId":15,"Manufacturer":"Vauxhall","Model":"Cavalier","Style":"Saloon"},{"SampleDataId":16,"Manufacturer":"Rover","Model":"Montego","Style":"Saloon"},{"SampleDataId":17,"Manufacturer":"Opel","Model":"Monza","Style":"Coupe"},{"SampleDataId":18,"Manufacturer":"BMW","Model":"325i","Style":"Saloon"},{"SampleDataId":19,"Manufacturer":"Ford","Model":"Escort","Style":"Hatch"},{"SampleDataId":20,"Manufacturer":"Vauxhall","Model":"Cavalier","Style":"Saloon"},{"SampleDataId":21,"Manufacturer":"Rover","Model":"Montego","Style":"Saloon"},{"SampleDataId":22,"Manufacturer":"Opel","Model":"Monza","Style":"Coupe"},{"SampleDataId":23,"Manufacturer":"BMW","Model":"325i","Style":"Saloon"}],"VisibleColumns":[]});
ko.applyBindings(vm, document.getElementById("Knockout"));
</script>
Where did I go wrong?
You are correctly specifying a ko.observableArray
property (myData
) as data
parameter of the gridOptions
object, however you have to make sure that it always contains an JavaScript array.
Knockout does not "type-check" what you put into an ko.observableArray
so you need to make sure that you always use arrays.
However in your setPagingData
you try to assign an object to your myData
instead of an array, and this throws this error:
Uncaught TypeError: grid.sortedData.peek(...).filter is not a function
because a plain JavaScript object litaral does not have a filter
function.
The fix is quite easy, you just need to pass pagedData.Rows
as the value of myData
:
this.setPagingData = function(data, page, pageSize){
var pagedRows = data.Rows.slice((page - 1) * pageSize, page * pageSize);
var pagedData = { Rows: pagedRows, VisibleColumns: data.VisibleColumns };
self.myData(pagedData.Rows);
self.pagingOptions.totalServerItems(data.Rows.length);
};
Sidenote: Your current implementation does not do a proper server side paging. Because you don't pass any parameters to the server and you process the returned data on the client side.