I recently had a project that required a simple grid with the ability to add and edit records (your typical crud operations).  As with most of the stuff on my blog I thought it would make sense to document how the grid functions…..should I ever need it again.

image

I have also attached the project files below.

Download: Source

The core functionality of the grid is provided in the JavaScript below.  from app.grid.js:

var lastsel2;
//you can pass in parameters if needed
var id = $(document).getUrlParam("id");
var type = $(document).getUrlParam("type");
jQuery("#grid").jqGrid({
    datatype: "local",
    colNames: ['ContactID', 'FirstName', 'LastName', 'Address1', 'Address2', 'City', 'State', 'ZipCode', 'Phone', 'Fax', 'Email', 'ContactType'],
    colModel: [
		{ index: 'ContactID', name: 'ContactID', hidden: true, width: 40, sortable: true, align: 'center', editable: true },
		{ index: 'FirstName', name: 'FirstName', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'LastName', name: 'LastName', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'Address1', name: 'Address1', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'Address2', name: 'Address2', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'City', name: 'City', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'State', name: 'State', width: 50, sortable: true, align: 'left', editable: true },
		{ index: 'ZipCode', name: 'ZipCode', width: 50, sortable: true, align: 'left', editable: true },
		{ index: 'Phone', name: 'Phone', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'Fax', name: 'Fax', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'Email', name: 'Email', width: 80, sortable: true, align: 'left', editable: true },
		{ index: 'ContactTypeName', name: 'ContactTypeName', width: 80, sortable: true, align: 'left', editable: true, edittype: "select" }
		],
    rowNum: 20,
    rowList: [10, 20, 30],
    imgpath: 'themes/steel/images',
    sortname: 'ID',
    viewrecords: true,
    sortorder: "desc",
    editurl: 'datahandler.ashx?method=updatecontact&insertid=' + id + "&contactupdatetype=" + type,
    caption: 'contacts',
    onSelectRow: function(id) {
        if (id && id !== lastsel2) {
            jQuery('#grid').restoreRow(lastsel2);
            jQuery('#grid').editRow(id, true);
            lastsel2 = id;
        }
    }
});
$("#badata").click(function() {
    jQuery("#grid").editGridRow("new", {
        height: 300,
        reloadAfterSubmit: false,
        closeAfterAdd: true,
        afterComplete: function() { loaddata() }
    });
});
var contacttypes = {
    1: "Admin",
    2: "Manager",
    4: "Building Manager",
    6: "Location Manager",
    9: "Other"
}
loaddata();
function loaddata() {
    $("#grid").clearGridData();
    jQuery(function($) {
        $.ajax({
            type: "POST",
            url: "datahandler.ashx",
            data: "method=getcontacts&id=" + id + "&contactedittype=" + type,
            success: function(jsondata) {
                if (jsondata == undefined) { return; }                
                var data = JSON.decode(jsondata)
                for (var i = 0; i < data.length; i++) {
                    jQuery("#grid").addRowData(data[i].ID, data[i]);
                }
                $("#grid").setColProp('ContactTypeName', { editoptions: { value: contacttypes} });
            }
        });
        var b = $(document).getUrlParam("test");
    })
}

The grid calls a .ashx handler file that returns the appropriate json data.  On the server side reside the handler file and the command files.

datahandler.ashx:

public class datahandler : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        string responseText = "";
        string method = context.Request.Params["method"] ?? "";
        ICommand command = CommandFactory.Create(method);
        if (command != null)
            responseText = command.Execute(context.Request.Params);
        
        
        context.Response.Write(responseText);
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

The handler uses the CommandFactory to build the appropriate command based on the “method” parameter that is passed in.  The execute method on the command does all the grunt work.   For this demo the “GetContacts” create test data objects but you can easily insert your own data access functionality. 

CommandFactory:

    public class CommandFactory
    {
        public static ICommand Create(string commandName)
        {
            switch (commandName.Trim().ToLower())
            {
                case "getcontacts":
                    return new GetContacts();
                case "updatecontact":
                    return new UpdateContact();
                default:
                    return null;
            }
        }
    }

The server side builds the JSON using helper methods that leverage the functionality of the .Net’s built in DataContractJsonSerializer

JsonHelper.cs

public static class JsonHelper
{
    public static string ToJsonItem(this object item)
    {
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(item.GetType());
        using (MemoryStream ms = new MemoryStream())
        {
            serializer.WriteObject(ms, item);
            StringBuilder sb = new StringBuilder();
            sb.Append(Encoding.Default.GetString(ms.ToArray())); return sb.ToString();
        }
    }
    public static T FromJsonTo<T>(this string jsonString)
    {
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
        MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString));
        T jsonObject = (T)ser.ReadObject(ms);
        return jsonObject;
    }
}