0

I'm using jqGrid version 4.5.2 and jquery 1.9.1 as well as MVC 4.

The grid I'm using has about 2000 rows total, and I'm displaying 100 of them at a time. I'm using virtual grid scroll, not pagination, with the following grid definition:

$("#colorsGrid").jqGrid({
            url: '@Url.Action("Colors")',
            //datatype: 'xml',
            datatype: 'json',
            colNames: ['id', 'RGB', 'FS', 'RAL', 'Humbrol', 'Revell', 'Tamiya', 'RLM', 'Vallejo', 'Testors / Model Master','ANA','Games Workshop / Citadel'],
            colModel: [
                { name: 'id', index: 'id', hidden: true },
                { name: 'RGB', sorttype: rgbColorSorter, formatter: rgbColumnFormatter, width: 70 },
                { name: 'FS', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'RAL', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'Humbrol', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'Revell', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'Tamiya', sorttype: tamiyaColorSorter, formatter: colorFormatter, width: 200 },
                { name: 'RLM', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'Vallejo', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'Testors / Model Master', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'ANA', sorttype: colorSorter, formatter: colorFormatter, width: 200 },
                { name: 'GamesWorkshop', sorttype: colorSorter, formatter: colorFormatter, width: 200 }
            ],
            rowNum: 50,
            scroll: 1,
            emptyrecords: "No colors found",
            loadonce: false,
            autowidth: false,
            sortable: true,
            afterInsertRow: afterInsertRowFunction,
            multipleSearch: true,
            ignoreCase: true,
            postData: { filterText: function () { return $('#colorFilter').val(); }},
            loadComplete: function () {
                if (!resizeGridOnLoadComplete) {
                    resizeGridOnLoadComplete = true;
                    resizeGrid();
                }
            }

The function afterInsertRowFunction only sets some css style for the row. The resizeGrid function only calls setGridHeight on the grid to dynamically fix the height.

On the controller side i'm returning just the number of rows and not the entire dataset as requested in the Request.QueryString parameters, along with the total number of records and pages. Abbreviated example:

{"page":1,"total":38,"records":1918,"rows":[{"id":1,"cell":["1","654037","^10075^10075^^~","","","","","","","","4F2E26^510^510 - Maroon^gloss^Pre/Early WWII~",""]},{"id":2,"cell":["2","7c3925","^10076^10076 - Coast Guard Deck Red, Metallic Red-Brown^^~","","","","","","","","",""]},

My problem is that the following happens: 1. On loading the page, jqGrid sends get an ajax request for page no.1 2. After scrolling past the initial 50 rows, jqgrid sends an ajax request for page no. 2 3. After scrolling past the next 50 rows, jqgrid sends an ajax request for page no.2 again instead of page no.3

If I keep scrolling, then jqGrid will send a request for page no.3 (which should have been page no.4 by now).

Due to the duplicate request and sending back the same data, it messes up the grid.

Things I've tried: Either xml or json format. Leaving the minimum settings on the jqGrid element. Different number of rows in rowNum. Scroll parameter as "true".

I've seen people mention something very similar but it was two years and two versions ago of jqGrid, and was fixed.

Why is jqGrid sending duplicate requests for the same page ?

Miki Watts
  • 1,746
  • 1
  • 17
  • 27

1 Answers1

1

First of all sending of two requests to the server is by design in case of usage virtual scrolling (scroll: 1 option).

I personally don't like the implementation of virtual scrolling in jqGrid. I find that it has some bugs (inclusive some bugs in design of the implementation of virtual scrolling). So I don't use the feature myself and don't recommend the feature to other. Standard scrolling could be a little strange for some users, but the users need to spend just some minutes to study the scrolling with buttons in the navigator bar. You can consider to use toppager: true option which create the pager on the top of grid. One can use additionally pager option to have two pagers. You can use navGrid with cloneToTop: true options to add navigator buttons in both pager.

In general it has not much sense to display unfiltered data with 2000 rows. One uses typically filterToolbar additionally. So the user could filter the data to see the subset of data which he really look for. If one think about the feature then the kind of paging will be not really important. The data which need the user will be typically displayed on one page.

I have more important remarks to your code which not directly concerned with your question, but which I find very important. First of all you should not use afterInsertRow. It make filling of grid slowly. I recommend you to read the answer where I describe the problem detailed. Instead of that you should use rowattr, cellattr or custom formatters and use gridview: true option. If I understand correctly what you do inside of afterInsertRowFunction you can use rowattr in the way close to described in the answer. You should take in consideration that the first parameter of rowattr in your case will be array instead of objects with named properties. So you should use integer indexes to access properties of the row.

After the above changes you can consider to use loadonce: true option. In the case the server shoould return all 2000 rows of data. The data should be still sorted corresponds to sortname option which you use (if you use it). I think that you could have very good performance in local paging and sorting. You server code could be simplified. By the way you will don't need return "page":1,"total":38,"records":1918 part in the server response. The returned data could be just array of items which represent the rows. The row can be array of items which represent the columns or object of named properties (the same as values of name properties in colModel).

Another remark. You should change name: 'Testors / Model Master' to some name without special character. You should understand the value as the name of variable or like the value of id attribute.

The last remark: you can remove id column. The value of id attribute of every row (id of <tr>) will be assigned based on "id" property of JSON response from the serevr.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • I used loadonce before, and it did work fine, but I want to add complex filtering, and it's rather hard to do it in javascript. I will change the other things you've mentioned though. The thing is, I don't have a problem with jqGrid sending the same page request, but in this case it's sending the same page number even after scrolling past that page. – Miki Watts Jun 17 '13 at 08:23
  • @MikiWatts: If you use `scroll: 1` then previously loaded page can be loaded once more time. In general virtual scrolling loads one page more as required. I want not go deep in the implementation of virtual scrolling, but if for example the answer of the second request come first then jqGrid can display the page on the wrong place. One have to change design of the implementation of virtual scrolling to fix the problem. It's the reason why I don't use virtual scrolling myself and don't recommend other to do this. – Oleg Jun 17 '13 at 08:33
  • @MikiWatts: Complex filtering of local data is even more easy as with remote data. You should describe more exactly what you need. You can consider to use searching templates. For filtering in general I recommend you first look at old answers like [this one](http://stackoverflow.com/a/5750127/315935). – Oleg Jun 17 '13 at 08:35
  • I see, thanks. This gives me an idea to maybe keep a list of already requested pages, and not return them again if requested. – Miki Watts Jun 17 '13 at 08:36
  • I basically want to enable the user to be able to specify in which columns to search, while combining search terms. The reason I prefer to do this on the server side, is that I also have an Android application and I want to share the code between the applications. – Miki Watts Jun 17 '13 at 08:37
  • @MikiWatts: I don't recommend you to cache pages manually. Usage of `loadonce: true` is really the best choice. You should measure how long time take the loading of 2000 rows (only loading not filling). If you see that it's less as 1 sec then you should really use the option. All other problems could be easy solved. You should just use other jqGrid possibilities probably in a little another way, but all will work typically *more quickly* with `loadonce: true` as with remote data. – Oleg Jun 17 '13 at 08:40
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31853/discussion-between-miki-watts-and-oleg) – Miki Watts Jun 17 '13 at 08:41
  • @MikiWatts: Mobile version of jqGrid exist only as beta. You can still have server side searching on the server, but if you use `loadonce: true` on non-mobile client no requests for filtering will be send to the server. For me the choice between `loadonce: true` and `loadonce: false` was clear **after measure**. Just try to use it (don't forget about `rowattr` and `gridview: true`) and measure sorting, paging and filtering in both cases **from the users point of view**. The choice depend on the results of the measure. – Oleg Jun 17 '13 at 08:45
  • One thing I forgot to ask, if I use loadonce:true, can I somehow still have the filtering take place on the server side? – Miki Watts Jun 17 '13 at 13:11
  • @MikiWatts: `loadonce: true` follows to changing of `datatype` to `"local"` at the first loading. You can any time rest `datatype` to `"json"`: $(this).jqGrid("setGridParam", {datytype: "json"}). One uses for example `beforeRefresh` callback of `navGrid` to make "Reload" button to reload the data from the server. Then the next filtering follow to reloading from the server. One can uses `beforeSearch` callback of `filterToolbar` to do this. The problem is only that local data will hold **only filtered data**. If it is OK for your case you can do the approach. – Oleg Jun 17 '13 at 13:19
  • Just wanted to say thank you very much :) I've got a cleaner code now, and because of loadonce, no more virtual scroll problems, and I still can do the filtering on the server side, which will enable me to share it between the online and the mobile application. – Miki Watts Jun 21 '13 at 18:20
  • @MikiWatts: Congratulations! Very good news! You are welcome! – Oleg Jun 21 '13 at 19:09