Search code examples
knockout.jsdurandalko.observablearray

Binding around 5000 records using knockout


I am trying to show around 5000 records in a web page using knockout observable array, which is taking hell lot of time,

Is there any way to handle this without pagination??

please help..

JS code in view model, data is coming via ajax call in gridData source:

 groupGrid.prototype.updateGrid = function (gridDataSource, groupGridOptions) {
        var self = this;
        self.ColumnName(groupGridOption.ColumnNameList); //   List of column name available in the data source.
        self.gridData(gridDataSource);    //  taking time while executing this code  
        self.totalRowCount(self.gridData().length);
        self.selectedItems.removeAll();
        self.selectedRowCount(0);
    };

HTML Code:

<tbody class="ngTBody" data-bind="foreach: gridData">
<tr class="ngdatarow">
<td>
    <span class="nameHeader" data-bind="text: $data[$root.ColumnName()[0]], click: $root.gridNameClick" style="cursor: pointer; text-decoration: underline"></span>
</td>
<td>
    <span class="displayBlock" data-bind="text: $data[$root.ColumnName()[1]]"></span>
</td>
<td>
    <span class="displayBlock" data-bind="text: $data[$root.ColumnName()[3]"></span>
</td>
</tr>
</tbody>

Solution

  • I've done a lot of research into generating data tables quickly in the browser. The standard Knockout method of using the foreach and text bindings is quite slow. Simplifying the binding code can realize some improvement, as my Repeat binding shows. But the fastest way to generate a data table is to assemble it as a string in JavaScript code and then use innerHTML to insert it into the DOM. My Table binding is an example of how this can be done.

    In your case, a custom binding that assembles the data table will provide a huge speed increase. Here's a custom binding that I put together based on your example:

    ko.bindingHandlers.myDataTable = {
        init: function () {
            return { controlsDescendantBindings: true };
        },
        update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            var output = [],
                value = valueAccessor(),
                columns = ko.unwrap(value.columns),
                column1 = columns[0],
                column2 = columns[1],
                column3 = columns[2],
                data = ko.unwrap(value.data),
                dataLength = data.length,
                clickFunction = value.click;
            output.push('<table><tbody class="ngTBody">');
            for (var i = 0; i < dataLength; ++i) {
                output.push('<tr class="ngdatarow"><td><span class="nameHeader" data-index="');
                output.push(i + '">');
                output.push(data[i][column1]);
                output.push('</span></td><td><span class="displayBlock">');
                output.push(data[i][column2]);
                output.push('</span></td><td><span class="displayBlock">');
                output.push(data[i][column3]);
                output.push('</span></td></tr>');
            }
            output.push('</tbody></table>');
            element.innerHTML = output.join('');
            $(element).on('click', 'span.nameHeader', function (event) {
                var index = event.target.getAttribute('data-index');
                if (index) {
                    clickFunction(data[index], event);
                }
            });
        }
    };
    

    For comparison, I put together the following two fiddles:

    1. http://jsfiddle.net/mbest/csP6k/ using foreach. It takes between 2 and 3 seconds to render the table on my computer.
    2. http://jsfiddle.net/mbest/8cKuP/ using the custom binding. It's almost 25 times faster to render the table.

    I also created an example that includes click bindings and uses ko.applyBindingsToDescendants, but it takes much longer than the example above because it has to set up 5000 event handlers versus just one.