Search code examples
javascriptknockout.jskogrid

Uncaught TypeError: grid.sortedData.peek(...).filter is not a function


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:

enter image description here

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:

enter image description here

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?


Solution

  • 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.