0

I'm using jqGrid (4.3.1) in an ASP.NET MVC 3 web application.

I have a unique situation (at least, one I can't find a solution for) in that I'm being supplied my data from an outside source - in paged fragments - so I cannot use the url property of the grid, yet I still must implement server side sorting, paging, & filtering.

my plan is (once i resolve the below) to use the onPaging, onSortCol, and beginSearch methods to call back to the web service and refresh the grid data in the callback.

The data is supplied in a format consumable by jqGrid (I can easily map it via the jsonReader option):

jsonReader: {
    repeatitems: false,
    root: "Rows",
    page: "Page",
    total: "TotalPages",
    records: "RecordCount",
    userdata: "FooterTotals",
    id: "ClaimId"
},

I pass in the data via the datastr option and set the datatype to 'jsonstring'.

loadonce is set to false (i've tried it both ways) and my rowNum is set to 10.

When I make the initial call a dataset with ~900 records comes back, and the grid displays the first 10 records, but the pager is ignoring the 'total' & 'records' and thinks i only have 1 page of data.

the root data is not being ignored (it displays 10 records) and the userdata is not being ignored (i.e. the footer is rendering correctly). so the grid is selectively ignoring the 'total' and 'records' data from the datastr.

My question is - does using datatype:jsonstring and datastr (and supplying json to the grid instead of using the url) prohibit the ability to take advantage of server side paging/sorting/filtering?

if so, is there a way to accomplish what I'm after?

note that if i return the entire 900 count record set and set loadonce to true, everything works fine, albeit client-side.

my entire jqGrid, for reference:

g.claims.LoadGrid = function (url, pagerDiv, grid, caption, drillData) {
    jQuery(grid).jqGrid({
        url: url,
        datastr: drillData != null ? drillData : g.claims.gridData,
        datatype: 'jsonstring',
        mtype: 'POST',
        ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
        serializeGridData: function (postData) {
            if (postData.filters === undefined) postData.filters = null;
            postData.quick = $("#quickSearchText").val();
            return JSON.stringify(postData);
        },
jsonReader: {
    repeatitems: false,
    root: "Rows",
    page: "Page",
    total: "TotalPages",
    records: "RecordCount",
    userdata: "FooterTotals",
    id: "ClaimId"
},
        colNames: ['ClaimId', 'Claim Reference', 'Status', 'Handler', 'Create Date', 'Division', 'Line Of Business', 'Policy Reference', 'Incurred Amount', 'Paid Amount'],
        colModel: [
                { name: 'ClaimId', index: 'ClaimId', width: 90, sorttype: 'integer', align: 'right', searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'ClaimRef', index: 'ClaimRef', width: 120, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'Status', index: 'Status', width: 100, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'Handler', index: 'Handler', width: 140, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'CreateDateDisplay', index: 'CreateDateDisplay', width: 90, align: 'center', sorttype: 'date', formatter: 'date', formatoptions: { srcformat: 'M-d-Y', newformat: 'd-M-Y' }, editable: true, datefmt: 'd-M-Y',
                editoptions: { dataInit: g.claims.initDateEdit },
                searchoptions: { sopt: ['eq', 'ne', 'lt', 'le', 'gt', 'ge'], dataInit: g.claims.initDateSearch }
            },
            { name: 'Division', index: 'Division', width: 90, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'LineOfBusiness', index: 'LineOfBusiness', width: 120, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'PolicyRef', index: 'PolicyRef', width: 120, searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'IncurredAmount', index: 'IncurredAmount', width: 120, sorttype: 'currency', align: 'right', searchoptions: { sopt: ['cn', 'eq', 'ne']} },
            { name: 'PaidAmount', index: 'PaidAmount', width: 120, sorttype: 'currency', align: 'right', searchoptions: { sopt: ['cn', 'eq', 'ne']} }

            ],
        rowNum: 10,
        rowList: [10, 20, 30],
        pager: pagerDiv,
        loadonce: false,
        sortname: 'ClaimId',
        viewrecords: true,
        sortorder: "desc",
        height: 250,
        ignoreCase: true,
        loadui: 'disable',
        autowidth: true,
        caption: caption,
        sortable: true,
        shrinkToFit: false,
        footerrow: true,
        userDataOnFooter: true,
        gridview: false,
        loadComplete: function () {
            var filters, quick, i, l, rules, rule, iCol, $this = $(this);
            if (this.p.search === true) {
                filters = $.parseJSON(this.p.postData.filters);
                if (filters !== null && typeof filters.rules !== 'undefined' && filters.rules.length > 0) {
                    rules = filters.rules;
                    l = rules.length;
                    for (i = 0; i < l; i++) {
                        rule = rules[i];
                        iCol = g.GetColumnIndexByName($this, rule.field);
                        if (iCol >= 0) {
                            $('>tbody>tr.jqgrow>td:nth-child(' + (iCol + 1) + ')', this).highlight(rule.data);
                        }
                    }
                }
            }
            quick = $("#quickSearchText").val();
            if (quick !== null) {
                var colCount = g.GetColumnCount($this);
                for (i = 0; i < colCount; i += 1) {
                    $('>tbody>tr.jqgrow>td:nth-child(' + (i + 1) + ')', this).highlight(quick);
                }
            }
            return;
        },
        onSelectRow: function (id) {
            var ret = jQuery(grid).jqGrid('getRowData', id);
            alert(ret.ClaimRef);
            //_currentRequestId = ret.RequestId;
            //ShowRequest();
        },
        loadError: function (xhr, textStatus, errorThrown) {
            var errorMsg = xhr.responseText;
            var msg = "Some errors occurred during processing:";
            msg += '\n\n' + textStatus + '\n\n' + errorMsg;
            g.trackError(msg, document.URL, 0);
        }

    });
    jQuery(grid).jqGrid('navGrid', pagerDiv, { refresh: false, edit: false, add: false, del: false, search: false });
    //jQuery(grid).jqGrid('setFrozenColumns');
    jQuery(grid).jqGrid(
        'navButtonAdd',
        pagerDiv,
        {
            caption: "",
            buttonicon: "ui-icon-newwin",
            title: "choose columns",
            onClickButton: function () {
                $(this).jqGrid('columnChooser', {
                    done: function () {
                        $(grid).trigger("resize");
                    }
                });
            }
        }
    );

    jQuery(grid).jqGrid(
        'navButtonAdd',
        pagerDiv,
        {
            caption: "",
            buttonicon: "ui-icon-refresh",
            title: $.jgrid.nav.refreshtitle,
            onClickButton: function () {
                $(this).jqGrid('setGridParam', { datatype: 'json' });
                $(this).trigger('reloadGrid', [{ page: 1}]);
            }
        }
    );

    jQuery(grid).jqGrid('filterToolbar', {
        stringResult: true,
        searchOnEnter: false,
        defaultSearch: 'cn',
        beforeSearch: function () {
            var postedData = $(this).jqGrid('getGridParam', 'postData');
            g.claims.FilterPageSort(postedData);
            return false;
        }
    });
    jQuery(grid).fluidGrid({ example: "#gridParent", offset: 0 });
};

UPDATE (more info for Oleg):

The ajax calls into a controller method (which has comments that reference the classes described below it). This method adheres to the jqGrid JSON-serialized postData structure (with a few extra parameters):

    [HttpPost]
    public ActionResult GetLagChart(int page, int rows, string sidx, string sord, bool _search, string filters, string quick)
    {
        var claims = WarehouseDataProvider.Instance.GetClaim(quick);

        //will eventually be passed in as jqGrid filters - not yet implemented
        var startPeriod = 201101;
        var endPeriod = 201112;

        //the return of this method is of type Chart - see below
        var lag = WarehouseDataProvider.Instance.GetLagChart(claims, startPeriod, endPeriod);


        var viewModel = new LagChartViewModel
                            {
                                LagChart = lag,
                                GridData = GetResults(claims, page, rows, sidx, sord)
                            };

        return this.JsonNet(viewModel);
    }

The Chart class mentioned in the code above:

public class Chart
{
    public List<ColumnSeries> Series { get; set; }
    public List<string> Categories { get; set; }
}

public class ColumnSeries
{
    public string Name { get; set; }
    public List<object> Values { get; set; }
}

the jqGrid GridData class mentioned above:

public class GridData<T>
{
    public int TotalPages { get; set; }
    public int Page { get; set; }
    public int RecordCount { get; set; }
    public List<T> Rows { get; set; }
    public object FooterTotals { get; set; }
}

Sample Post Json to web service (HttpPost-enabled Controller):

{"page":1,"rows":10,"sidx":"ClaimId","sord":"asc","_search":false,"filters":null,"quick":"exc"}

Response Ajax:

{
  "GridData": {
    "TotalPages": 92,
    "Page": 1,
    "RecordCount": 911,
    "Rows": [
      {
        "ClaimId": 229731,
        "ClaimRef": "XXX111345",
        "ClaimTitle": "title 1",
        "Status": "Claim - Finalised",
        "IncurredAmount": 0.00,
        "PaidAmount": 0.00,
        "Handler": "Person One",
        "Handler1": "Person One",
        "Handler2": "Person One",
        "Handler3": "Person One",
        "Division": "Person One",
        "Branch": null,
        "LineOfBusiness": "Wholesale Excess",
        "PolicyRef": "SFSF9090888",
        "CreateDateDisplay": "03-30-2012",
        "DateOfAdvice": "2009-06-01T00:00:00",
        "DateOfLoss": "2007-07-08T00:00:00",
        "LossPeriod": 200707,
        "DateOfFirstReserve": "2009-06-03T00:00:00",
        "AdviceLag": 695,
        "ReserveLag": 3
      },
      {
        "ClaimId": 229933,
        "ClaimRef": "EXC123488",
        "ClaimTitle": "Title 2",
        "Status": "Claim - Finalised",
        "IncurredAmount": 0.00,
        "PaidAmount": 0.00,
        "Handler": "Person Two",
        "Handler1": "Person Two",
        "Handler2": "Person Two",
        "Handler3": "Person Two",
        "Division": "Excess",
        "Branch": null,
        "LineOfBusiness": "Wholesale Excess",
        "PolicyRef": "NY676767777",
        "CreateDateDisplay": "03-30-2012",
        "DateOfAdvice": "2009-06-02T00:00:00",
        "DateOfLoss": "2006-01-01T00:00:00",
        "LossPeriod": 200601,
        "DateOfFirstReserve": "2009-06-18T00:00:00",
        "AdviceLag": 1249,
        "ReserveLag": 17
      },
      ...
    ],
    "FooterTotals": {
      "ClaimId": "Totals",
      "IncurredAmount": -27910474.80,
      "PaidAmount": -27910474.80
    }
  },
  "LagChart": {
    "Series": [
      {
        "Name": "Average Advice Lag",
        "Values": [
          1499,
          1048,
          897,
          2354,
          1450,
          444,
          334,
          816,
          508,
          108,
          256,
          109
        ]
      },
      {
        "Name": "Average Reserve Lag",
        "Values": [
          44,
          131,
          23,
          76,
          67,
          18,
          122,
          45,
          29,
          15,
          3,
          14
        ]
      }
    ],
    "Categories": [
      "Jan 2011",
      "Feb 2011",
      "Mar 2011",
      "Apr 2011",
      "May 2011",
      "Jun 2011",
      "Jul 2011",
      "Aug 2011",
      "Sep 2011",
      "Oct 2011",
      "Nov 2011",
      "Dec 2011"
    ]
  }
}
kmk
  • 613
  • 9
  • 22
  • I don't understand why you can't load the data directly from the "outside source". You wrote that you call "the web service". Do you make separate Ajax call? Do you use JSON or JSONP call? What is the interface of the web service? Because you wrote about ASP.NET MVC 3 web application I understand all event less. jqGrid allows very much flexibility. So if you include more details about the interface to the web service or post the code which you use currently to call it I would try to show you how to integrate the call in the jqGrid. – Oleg Mar 29 '12 at 19:21
  • @Oleg - Thanks for the quick response. The original web service call brings back the grid data along with many other data elements relevant to the rest of the page in a view model. If i use the url, I have no way of accessing this data. At least no way that i know of. the jqGrid is one piece of a larger page, with the first 10 records populated up front. This is why I was hoping i could pass the json (that is handed to me via a separate ajax call) to the grid using datastr/jsonstring, and then call the web service (again, that I do not control) for paging/filtering/sorting of the data. – kmk Mar 29 '12 at 20:05
  • You do can get full access to the server response, you can even modify it if needed before it will be processed by jqGrid. You can also fill or refresh the external fields after you get the data from the server. If you just describe on field of external data, post the Ajax call which you use to get data from the server and include the example of the JSON response (one row grid data and one external field would be enough) I could show how you can do this. – Oleg Mar 29 '12 at 20:13
  • @Oleg - I've added the detail requested from your comment. Many thanks for taking the time to help out. – kmk Mar 30 '12 at 12:07

1 Answers1

2

You can load the data in jqGrid directly. To do this you need just set url parameter to the GetLagChart action and change the jsonReader a little:

jsonReader: {
    repeatitems: false,
    root: "GridData.Rows",
    page: "GridData.Page",
    total: "GridData.TotalPages",
    records: "GridData.RecordCount",
    userdata: "GridData.FooterTotals",
    id: "ClaimId"
}

Inside of loadComplete you have full access to the data returned from the server and so you can use LagChart part of the server response to fill or update the chart.

The demo uses about the following loadComplete

loadComplete: function (data) {
    if (typeof data.LagChart !== "undefined") {
        var chartInfo = data.LagChart
        alert ("Categories count: " + data.LagChart.Categories.length +
               "\nSeries count: " + data.LagChart.Series.length +
               "\n\nWe can fill/update the Chart");
    }
}

So the most important part of your implementation will be the server method GetResults which provide the page of grid data. You can extend the method with filters parameter. For more implementation details I recommend you to read the answer or this one which contains more recent and extended code.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • thanks. I'll parse what I need from the data object in loadComplete as you suggest. – kmk Mar 30 '12 at 13:41
  • @kmk: You are welcome! Don't forget to set `gridview` to `true` too. The grid will just work quickly without disadvantages. One can really see the performance advantages only with many rows of data. – Oleg Mar 30 '12 at 13:43
  • I sent you an email concerning the data above. Please take a look when you get a chance (unrelated to SO). Thanks for your help. – kmk Mar 30 '12 at 13:54