1

I think this user was asking the same question I'm asking, but the reason driving his question was different, so he accepted a different solution. I need to do what he was originally asking:

Automatically cancelling jqgrid request

My jqGrid has a search toolbar (the input fields that appear below the column headers but above the data). Most of the columns have dropdowns (stype=select) that list the available filter options for that column, with an 'All' option at the top. The server-side code that responds with the JSON data that populates my jqGrid is complex and therefore somewhat slow, especially when there isn't much in the way of filter criteria. This means that if the user selects 'All', it may be several seconds before any results show up. This is fine by itself, but if the user makes another filter selection before the response comes back, the grid does not cancel the existing request, nor does it submit a new one, so at some point after the user makes the second selection, the grid updates to show the response for the first selection. Furthermore, the grid auto-updates when you change the filter selection, so to get it to show the data you wanted, you have to change your selection to something else, wait for that to load, and then change it back.

Here's what I've tried so far:

$(document).ready(setupGrid);

var currentGridRequest;

function setupGrid() {
    var grid = $("#grid").jqGrid({
        ...
        loadError: function(xhr, status, error) {
            currentGridRequest = undefined;
        },
        loadComplete: function(data) {
            currentGridRequest = undefined;
        },
        loadBeforeSend: function(xhr, settings) {
            if (currentGridRequest !== undefined) {
                currentGridRequest.abort();
            }
            currentGridRequest = xhr;
        }
    });
}

The problem I have is that the loadBeforeSend event doesn't actually fire if there's already a request in flight. I've tried a other events from the event documentation page (http://www.trirand.com/jqgridwiki/doku.php?id=wiki:events): beforeRequest and jqGridToolbarBeforeSearch both exhibit the same behavior.

What can I do to abort the in-flight request and proceed with searching based on the user's most recent selection? Is this a bug in jqGrid? I initially encountered this issue on version 4.2; I've just upgraded to 4.4.4 with no change in this behavior.

Looking at the jqGrid code, the behavior I'm seeing appears to be unavoidable without changing the jqGrid code itself. jqGrid sets a flag (ts.grid.hDiv.loading) at the beginning of each request and clears it at the end; when this flag is set, new filter criteria will not be submitted and no events will fire.

I also considered modifying my JSON response so that it included the request criteria, and then write some JS code to compare the response to the current criteria selection. If they didn't match, I'd ignore the response and not render it to the grid. Since jqGrid doesn't even SUBMIT the second set of criteria, that option is off the table (and even if I can fix that problem, my current approach seems preferable).

Community
  • 1
  • 1
JakeRobb
  • 1,711
  • 1
  • 17
  • 32

2 Answers2

1

I've found a way to do what I wanted. I use jqGrid in several places (including multiple grids on the same page in a few cases), so I factored it out into a function I can call from anywhere and which contains its own scope for managing whether there is a current request; just pass it a grid and it will attach all of the necessary event handlers. Note that this will overwrite any existing handlers you may have defined for the relevant events.

function applyJqgridHandlers(grid) {
    (function(self, undefined) {
        var currentRequest;
        function abortCurrentRequestIfAny() {
            if (currentRequest !== undefined) {
                currentRequest.abort();
            }
        }
        self.clearCurrentRequest = function() {
            abortCurrentRequestIfAny();
            currentRequest = undefined;
        };
        self.setCurrentRequest = function(xhr) {
            abortCurrentRequestIfAny();
            currentRequest = xhr;
        };
    })(grid.currentRequestHolder = {});

    grid.currentRequestHolder.clearCurrentRequest();
    grid.jqGrid('setGridParam', {
        loadError: function(xhr, status, error) {
            grid.currentRequestHolder.clearCurrentRequest();
        },
        loadBeforeSend: function(xhr, settings) {
            grid.currentRequestHolder.setCurrentRequest(xhr);
        },
        loadComplete: function(data) {
            grid.currentRequestHolder.clearCurrentRequest();
        }
    });
    grid.bind('jqGridToolbarBeforeSearch', function() {
        grid.currentRequestHolder.clearCurrentRequest();
        this.grid.hDiv.loading = false;
    });
};

Then I simply add a call to this function from the end of setupGrid().

This code explicitly clears the ts.grid.hDiv.loading flag, which is not part of the published jqGrid API, so I consider this solution to be fairly fragile; there's no guarantee that it would continue to work for future versions of jqGrid. If anyone has any suggestions for better (less fragile) ways to solve this problem, I'm all ears. :)

JakeRobb
  • 1,711
  • 1
  • 17
  • 32
1

@JakeRobb Thank you for the idea! I've took the liberty to create a function that can be used universally without disabling any functionality of the original jqGrid.

Tested on jqGrid version 5.2.1

jqGrid Rapid Search

a.k.a automatically cancel old requests if a new filter is applied by the user.

/**
 * Add the ability to rapidly search using the jqGrid Toolbar
 *
 * @param jqGridInstance string|object
 *
 * @author Kiril Reznik
 */
var jqGridRapidSearch = function(jqGridInstance) {
    var currentRequest = null,
        abortRequest = function() {
            if (currentRequest !== null) {
                currentRequest.abort();
            }
        },
        setRequest = function(xhr) {
            abortRequest();
            currentRequest = xhr;
        },
        clearRequest = function() {
            abortRequest();
            currentRequest = null;
        },
        $jqGrid = null;
    if ($.type(jqGridInstance) === 'string') {
        $jqGrid = $(jqGridInstance);
    } else if ($.type(jqGridInstance) === 'object' && $.type(jqGridInstance.jquery) === 'string') {
        $jqGrid = jqGridInstance;
    } else {
        throw 'jqGridRapidSearch requires a valid jqGrid instance parameter: Grid ID string or jQuery object.';
    }
    $jqGrid.on('jqGridLoadBeforeSend', function(e, xhr, settings) {
        setRequest(xhr);
    });
    $jqGrid.on('jqGridLoadComplete', function(e, data) {
        clearRequest();
    });
    $jqGrid.on('jqGridLoadError', function(e, xhr, status, error) {
        clearRequest();
    });
    $jqGrid.on('jqGridToolbarBeforeSearch', function(e) {
        clearRequest();
    });
};
Aternus
  • 3,375
  • 1
  • 14
  • 9
  • Hi @Aternus, i am using jqgrid 4.3.1 version, i checked 4.3.1 jqgrid doesn't support the event triggerhandler, is there any other option can we use – karthickeyan Sep 17 '19 at 16:38