JQGrid - Cannot call ASP.NET WebMethod but can with Ajax

Scruffy The Janitor picture Scruffy The Janitor · Mar 1, 2011 · Viewed 10.2k times · Source

I am new to jqGrid and I have found it difficult to follow the documentation jqGrid Documentation

I cannot figure out how to call a WebMethod when setting up the JQGrid. I have been successful in making an Ajax call to get the data and then setting up the JQGrid with local data. I think its an extra step in the setup process and that I should be able to provide the path to the webmethod using the url property.

The editurl property is the same way. I am never actually receiving the post to the server.

Original Code

Attempted JQGrid Setup


function GetData()
{
    $('#list').jqGrid({
        type: "POST",
        url: "Default.aspx/GetUsersJSON",
        datatype: "json",
        height: 250,
        colName: ['Username', 'Email'],
        colModel: [
                ...
    }).jqGrid(
                'navGrid',
                '#pager',
                {
                    edit: true,
                    add: true,
                    del: true
                });
}

WebMethod



        [WebMethod]
        public static string GetUsersJSON()
        {
            var users = new List();
            using(UserAdministrationSandboxDataContext uasd = new UserAdministrationSandboxDataContext())
            {
                users = uasd.GetUserList();                
            }
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            return serializer.Serialize(users); 

        }

Current Code

I got it working correctly now, but I still have one final question. Why did I have to set the 'repeatitems: false' in order to display the content?

Some of the caveats to get this to work include the different ways to setup the ajax request.

(Ajax: type) is (jqgrid : mtype) (Ajax: contentType) is (jqgrid : ajaxGridOptions: { contentType: })

And finally understanding the documentation from the documentation on how to setup the JSONReader.

Hope this helps others and thanks Oleg for all your help.

JS



function GetUserDataFromServer()
{
    $('#list').jqGrid({
        url: "Default.aspx/GetUsersJSON",
        mtype: 'POST',        
        ajaxGridOptions: { contentType: "application/json" },
        datatype: "json",
        serializeGridData: function (postData)
        {
            return JSON.stringify(postData);
        },
        jsonReader: {
            root: function (obj) { return obj.d; },
            page: function (obj) { return 1; },
            total: function (obj) { return 1; },
            records: function (obj) { return obj.d.length; },
            id:'0',
            cell:'',
            repeatitems: false            
        },
        datatype: "json",
        height: 250,
        colName: ['Username', 'Email'],
        colModel: [
                {
                    name: 'Username',
                    index: 'Username',
                    width: 100,
                    editable: true
                },
                {
                    name: 'Email',
                    index: 'Email',
                    width: 220,
                    editable: true
                },
                {
                    name: 'IsLockedOut',
                    index: 'IsLockedOut',
                    width: 100,
                    editable: true,
                    edittype: 'checkbox'
                }
        ],
        caption: "Users"
    })
}

Web Method


        [WebMethod]
        [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
        public static List GetUsersJSON()
        {
            using (UserAdministrationSandboxDataContext uasd = new UserAdministrationSandboxDataContext())
            {
                return uasd.GetUserList();
            }
        }

One JSON Object from the List


{"__type":"UserAdministrationSandbox.UserData","PKID":"00000000-0000-0000-0000-000000000001","Username":"TestUser","ApplicationName":"Test","Email":"[email protected]","Comment":"TestUser","Password":"D41D8CD98F00B204E9800998ECF8427E","PasswordQuestion":"Is this a blank Password?","PasswordAnswer":null,"IsApproved":true,"LastActivityDate":"\/Date(1298869200000)\/","LastLoginDate":"\/Date(1298869200000)\/","LastPasswordChangedDate":"\/Date(1298869200000)\/","CreationDate":"\/Date(1298869200000)\/","IsOnLine":false,"IsLockedOut":false,"LastLockedOutDate":"\/Date(1298869200000)\/","FailedPasswordAttemptCount":0,"FailedPasswordAttemptWindowStart":null,"FailedPasswordAnswerAttemptCount":null,"FailedPasswordAnswerAttemptWindowStart":null}

Answer

Oleg picture Oleg · Mar 1, 2011

First of all I hope the code examples from the answer could help you (see also this answer). The main idea, that you should use following additional jqGrid parameters

ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
serializeGridData: function (postData) {
    return JSON.stringify(postData);
},
jsonReader: { root: "d.rows", page: "d.page", total: "d.total",
              records: "d.records" };

If the server not set rows, page, total and records parameter in the response and just return the list of data like in your case you can use the following jsonReader

jsonReader: {
    root: function (obj) { return obj.d; },
    page: function (obj) { return 1; },
    total: function (obj) { return 1; },
    records: function (obj) { return obj.d.length; }
}

(see here and here). In the case if you don't want implement server side data paging, sorting and filtering I recommend you to use loadonce:true.

Moreover your code have some problems. The first one is that you call JavaScriptSerializer.Serialize manually in your web method. If you use dataType: "json" the JSON response will be converted to object by $.ajax. It is so in your case also. Because of that the msg parameter of the success handler has d property. But msg.d is not the object, but one more JSON string which you convert to object with eval(msg.d). The reason is that the results of your method will be converted to JSON one more time.

To fix the problem you should change the web method GetUsersJSON to the following:

[WebMethod]
[ScriptMethod (ResponseFormat = ResponseFormat.Json)]
public static List<User> GetUsersJSON()
{
    using(UserAdministrationSandboxDataContext uasd =
                                    new UserAdministrationSandboxDataContext())
    {
        return uasd.GetUserList();                
    }
}

then you can place data: eval(msg.d) in your previous example to data: msg.d.

Typically one use additional [ScriptMethod (ResponseFormat = ResponseFormat.Json)] or [ScriptMethod (UseHttpGet = true, ResponseFormat = ResponseFormat.Json)] attribute for the web method, but in many cases (it seems also in your case) it is not needed.

After the usage of ajaxGridOptions, serializeGridData and jsonReader jqGrid will able to read the page of data, but the data should be in JSON format and not twice encoded JSON format.

UPDATED: You ask me to comment why you need to use repeatitems:false setting in the jsonReader to be able to read your data. It is important question for understanding how jsonReader work, but the answer will take a little place.

In general there are two main styles how the JSON data can be formatted for jqGrid. It should be array of data for grid rows. Every item of the array represent the row in grid and the row should be in one from the two main form

1) as an object with named properties like

{"Username":"TestUser","Email":"[email protected]","Comment":"..","IsApproved":true}

or 2) an array of strings like

["TestUser","[email protected]","true"]

or

["TestUser","[email protected]","1"]

jqGrid map both "true" and "1" values to the boolean value "true" in case of edittype:'checkbox' setting. How you can understand if the grid has many checkbox-columns the usage of "1"/"0" format can reduce the size of transfered data.

The repeatitems:false option means that jqGrid should scan JSON data for the first (object style) representation of data. The repeatitems:true means the second (array style) representation.

By the way if you use the object style (repeatitems:false) the cell setting of the jsonReader will be not used and you can remove cell:'' setting which you use.

The id option of the jsonReader in numeric form is practical if you have one column in the grid with unique values. The option id:'0' means that the value of the column "Username" will be used as the row id. If you examine the grid with Firebug of Developer tools of IE or Chrome you will see that the corresponding <tr> element has attribute id="TestUser" (used in your data). Because duplicate in ids are not allowed on one HTML page, you can understand that it is very important to define grid with correct unique ids. If jqGrid not find id column in the data it will use ids "1", "2", ... So if you see that your grid has the values you should search for the error in the id property of the jsonReader.

The next important thing is the advantages and disadvantages of two ways of the data representation: object style (repeatitems:false) and array style (repeatitems:true)

The object style has advantage in two main cases

  1. you want just post existing objects with as less work on the server as possible (quick and dirty solution)
  2. you get data from the server which interface you can not change.

In all other situations the array style (repeatitems:true) has advantages compared with the object style. The main from there

  1. In the object style representation will be send frequently more data as needed. In your example the "Comment" property for example will be send which will not be used by jqGrid.
  2. The data from array style are much more compacter because you will not transfer the name of properties (which are constants) in every row.

So if you want to reduce the size of transfered data and you can make changes on the server side I would recommend you to use array style (repeatitems:true) of representation of data. In the case the cell:'' property of the jsonReader can be good used.

I recommend you to look though the part of jqGrid documentation about jsonReader, xmlReader and localReader.