Search code examples
knockout.jsknockout-mapping-plugin

UI locks when mapping large knockout object


I've got a few bits in play but think my issue is down to knockout mapping.

I'm sending a large spreadsheet (4000 rows) validating it server side, sending 4000 objects back to a call to knockout mapping.

When it hits the mapping - all gifs (ajax loaders) on the page stop for a few seconds (until the mapping is finished). When it finally comes back, all the the returned objects are displayed and the whole process has worked as expected.

The pause looks like a crash - and in production there may well be larger spreadsheets in play so I really want to keep things look responsive.

Does anyone know how I can keep those gifs spinning? Thanks

    public csvToWebAPIObject(file, onFinished, onError) {

        var formData = new FormData();
        formData.append("upload", file);

        var url = this.getFullURL();
        $.ajax({
            url: url,
            data: formData,
            processData: false,
            contentType: false,
            type: 'POST',
            success: (data) => {
                var mapped = ko.mapping.fromJS(data);
                onFinished(mapped);
            },
            error: function (err, ajaxOptions, thrownError) {
                if (onError != null) {
                    onError(err.responseText);
                }
            }
        });
    }

Solution

  • Knockout was never made to process that much data into the DOM. The delay is coming from actually rending the data onto the page. Unfortunately the fastest way to do this is to do it server side, process the data, and return the completed page.

    If you must use knockout, can you investigate doing a paging system? I would recommend only rendering 100 or so rows at a time. You could do this like the following

    javascript

    this.items = ko.observableArray(dataFromTheServer);
    this.currentPage = ko.observable(0);  
    this.pageSize = ko.observable(100);
    this.visibleItems = ko.computed(function(){
       return self.items.splice(self.currentPage() * self.pageSize(), self.pageSize()*self.currentPage() + self.pageSize());  //Returns 0 - 100, or 101 -> 200, etc     
    });
    

    html

    <!-- do visibleItems instead of items -->
    <div data-bind="foreach: visibleItems">
       //render items
    </div>