Jasinski Technical Wiki

Navigation

Home Page
Index
All Pages

Quick Search
»
Advanced Search »

Contributor Links

Create a new Page
Administration
File Management
Login/Logout
Your Profile

Other Wiki Sections

Software

PoweredBy

Page History: Quick Reference - jqGrid

Compare Page Revisions



« Older Revision - Back to Page History - Newer Revision »


Page Revision: Tue, Mar 05, 2013, 10:46 AM


Table of Contents [Hide/Show]


   Client Side
      UI Code
      JavaScript Code
      Dates Columns
      Dropdown Columns
         Sample Controller Method
      Validation
      Word Wrap
   Server Code
      Controller Code
      Business Layer
   Reusable Code
      LookupManager class
      LookupItem class
      PagingInfo class
      IQueryableHelper class
      JQGridController class
      IAuditable Interface
      IDeletable
      JavaScript Code


Client Side

UI Code

<div id="uxUserGridDiv"><table id="uxUserGrid"></table></div>
<div id="uxUserGridPager"></div>

Alternate
<input type="button" class="newItemButton" id="btnNewNote" onclick="newNote(@Model.ContactID);" value="New Note" /><br />
<div id="gridNotes"><table id="jqgridNotes"></table></div>
<div id="pagerNotes"></div>

To generate this code programmatically, see jQuery-UI Tab Code Generator

JavaScript Code

/*===============================================================================================*/
$(document).ready(function () {

    loadUserGrid();

});
/*===============================================================================================*/
function loadUserGrid() {

    $("#uxUserGrid").GridUnload();

    $("#uxUserGrid").jqGrid({
        url: '/Security/GetUsersForManager',
        editurl: '/Security/MaintainUserForManager',
        colModel: [
            {label: 'UserName', name: 'UserName',
                index: 'UserName', jsonmap: 'UserName',
                width: 150, align: 'left', editable: false
            },
            { label: 'Active', name: 'Active', index: 'Active', jsonmap: 'Active',
                width: 50, align: 'center', editable: true, edittype: 'checkbox',
                formatter: 'checkbox'
            },
            { label: 'Actions', name: 'Actions', index: 'Actions', width: 75, sortable: false,
                formatter: actionRowNoDelFormatter
            }
        ],
        pager: jQuery('#uxUserGridPager'),
        sortname: 'UserName',
        sortorder: 'asc',
        imgpath: '/Content/Themes/Redmond/Images',
        rowNum: -1,
        postData: { propertyId: getPropertyID },
        jsonReader: { id: 'PropertyID' } /* Primary Key - needed for delete operation */
        onSelectRow: function (id) { onGridRowSelect('uxUserGrid', id); }
    }).navGrid('#uxUserGridPager',
    {
        edit: false, add: false, del: false, search: false, refresh: true
    },
    updateDialog,
    updateDialog,
    updateDialog
    );
}

The following JavaScript function is the code behind the "Create New" button found in the second ("Alternate") UI above. We set the primary key field to -1 (to cause the code to create a new table row) and the "parent ID" to be the id passed in (ultimately from the view model).

function newNote(id) {
    rowData = { ContactID: id, ContactNoteID: -1 };
    actionRowAdd(this, 'jqgridNotes', rowData);
}

Dates Columns

In the colmodel for a date column, include the following, adjusting the newformat as needed.

align: 'center', formatter: 'date', formatoptions: { newformat: 'm/d/Y h:i:s A' },
                editoptions: { dataInit: function (el) { $(el).datepicker(); } }

Dropdown Columns

In the colmodel for a dropdown column, include the following.

editable: true, align: 'left', edittype: 'select',
formatter: function (cellValue, options, rowObject) {
    return rowObject.RelationType; /* display field for the dropdown */
},
editoptions: {
    value: $.ajax({
        type: "GET",
        url: "/Controller/Method",
        async: false
    }).responseText}

In addition, specify the following. (Both these fields should be returned by the server code behind url.
  • name and jsonmap = the name of the value field behind the dropdown
  • index = the name of the display field behind the dropdown

In addition, if your grid is sortable (i.e., by clicking a column header), be sure the loadonce option for the grid is false or any time you sort the grid, the column containing the dropdown(s) will show values of undefined.

Sample Controller Method

Note that data-access code does NOT belong in the controller, but rather the business layer. It is shown here in the controller for simplification only.

[HttpGet]
public string GetRelationTypes(string nullValue)
{
    using (var context = new MyEntities())
    {
        var result = (from o in context.RelationTypes
                      orderby o.Descrip
                      select new LookupItem
                      {
                          Descrip = o.Descrip,
                          ID = o.RelationTypeID
                      }).ToList().Nullify(nullValue);

        return LookupItem.ListToJson(result);
    }
    
}

Validation

Validation is defined by the editrules property within the colmodel element. The editrules element consists of key-value pairs separated by commas.

SyntaxNotes
number: trueField value must be numerical
integer: trueField value must be an integer
minValue: 0Minimum value allowed
date:trueField value must be a date
required:trueField value is required

Word Wrap

Word wrap is implemented via the following CSS.

#jqGridTableID td {
    white-space: normal !important;
}

Server Code

Controller Code

public class SecurityController : JQGridController
{
    [HttpGet]
    public JsonResult GetNotes(int contactID, PagingInfo paging)
    {
        var results = ContactManager.GetNotes(contactID, paging);
        var s = JQGridResult<ContactNoteView>(results, paging.page, paging.totalPages);
        return s;
    }

    [HttpPost]
    public JsonResult MaintainNote(ContactNoteView m, FormCollection form)
    {
        int id;
        var oper = form["oper"];
        int.TryParse(form["id"], out id);
        var username = HttpContext.User.Identity.Name;

        switch (oper)
        {
            case "edit":
                ContactManager.SaveContactNote(m, username);
                break;
            case "del":
                ContactManager.DeleteContactNote(id, username);
                break;
        }
        return Json(null, JsonRequestBehavior.AllowGet);
    }
}

Business Layer

The business layer class requires the following.

  • A using statement for the namespace containing the IQueryableHelper interface (recommended to be ACME.Framework.Helpers).
  • A using statement for the namespace containing the IAuditable and IDeletable interfaces(recommended to be ACME.Common).
  • The IAuditable and IDeletable interfaces applied to the database entity (ContactNote in the example below). This best done in the ACME.DAL namespace via a {public partial class} statement, similar to the following.

public partial class ContactNote : IAuditable, IDeletable { }

public static class ContactManager 
{
    public static List<ContactNoteView> GetNotes(int contactID, PagingInfo paging)
    {
        using (var context = new MyEntities())
        {
            var q = from o in context.ContactNotes
                    where o.ContactID == contactID && !o.IsDeleted
                    orderby o.DateCreated
                    select new ContactNoteView
                    {
                        ContactNoteID = o.ContactNoteID,
                        ContactID = o.ContactID,
                        Descrip = o.Descrip
                        /* etc */
                    };

            return IQueryableHelper.CreatePagedResult<ContactNoteView>(q, paging);
        }
    }
    /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
    private static DAL.ContactNote GetContactNote(MyEntities context, int id)
    {
        return (from o in context.ContactNotes
                where o.ContactNoteID == id
                select o).FirstOrDefault();
    }
    /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
    public static void SaveContactNote(ContactNoteView m, string username)
    {
        using (var context = new MyEntities())
        {
            /*--- Get Database Entity ---*/
            var d = GetContactNote(context, m.ContactNoteID);

            if (d != null)
            {
                d.Updated(username);
            }
            else
            {
                d = new DAL.ContactNote();
                context.ContactNotes.AddObject(d);
                d.Created(username);
                d.ContactID = m.ContactID;
            }

            /*--- Update properties of database entity ---*/
            d.Descrip = m.Descrip;
            /* etc */

            /*--- Save back to database ---*/
            context.SaveChanges();
        }
    }
    /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
    public static void DeleteContactNote(int contactNoteID, string username)
    {
        using (var context = new MyEntities())
        {
            var d = GetContactNote(context, contactNoteID);

            if (d != null)
            {
                d.Deleted(username);
                context.SaveChanges();
            }
        }
    }
}

Reusable Code

LookupManager class

public static class LookupManager
{
    public static List<LookupItem> Nullify(this List<LookupItem> result, string nullValue)
    {
        if (nullValue != null)
            result.Insert(0, new LookupItem { Descrip = nullValue, ID = -1 });

        return result;
    }
}

LookupItem class

public class LookupItem
{
    public virtual string Descrip { get; set; }
    public int ID { get; set; }

    /// <summary>
    /// Returns the list items as a string in the format expected for a dropdown list
    /// within a jqGrid
    /// </summary>
    public static string ListToJson(List<LookupItem> items)
    {
        var result = new List<string>();

        foreach (var item in items)
            result.Add(item.ID.ToString() + ":" + item.Descrip);

        return string.Join(";", result.ToArray());
    }
}

PagingInfo class

This class encapsulates the paging parameters used by jqGrid.

public class PagingInfo
{
    public string sidx { get; set; }
    public string sord { get; set; }
    public int page { get; set; }
    public int rows { get; set; }
    public int totalPages { get; set; }
}

IQueryableHelper class

This class hosts the extension method CreatePagedResult, which converts an set of results into a of paged results expected by jqGrid.

// To enable the next line, within Package Manager Console issue the "Install-Package System.Linq.Dynamic" 
// command, then set a reference to System.Linq.Dynamic.DLL.

using System.Linq.Dynamic

public static class IQueryableHelper
{
    public static List<T> CreatePagedResult<T>(IQueryable<T> results, PagingInfo paging)
    {
        try
        {
            int pageIndex = Convert.ToInt32(paging.page) - 1;
            int pageSize = paging.rows;
            int totalRecords = results == null ? 0 : results.Count();

            if (pageSize > 0)
            {
                paging.totalPages = (int)Math.Ceiling((float)totalRecords / (float)pageSize);
            }
            else
            {
                paging.totalPages = 1;
                pageSize = totalRecords;
            }

            if (!string.IsNullOrEmpty(paging.sidx))
            {
                if (string.IsNullOrWhiteSpace(paging.sord))
                    paging.sord = "asc";

                string sortString = string.Empty;

                var sortKeys = paging.sidx.Split(',');

                foreach (var sortKey in sortKeys)
                {
                    if (sortString.Length > 0)
                        sortString += ",";

                    sortString += sortKey + " " + paging.sord;
                }

                return results.OrderBy(sortString).Skip(pageIndex * pageSize).Take(pageSize).ToList<T>();
            }
            else
            {
                return results.Skip(pageIndex * pageSize).Take(pageSize).ToList<T>();
            }
        }
        catch (Exception ex)
        {
            paging.totalPages = 1;
            return results.ToList<T>();
        }
    }
}

JQGridController class

This class provides the JQGridResult method, which converts a List of results into a Json object expected by jqGrid.

public abstract class JQGridController : Controller
{
    protected JsonResult JQGridResult<T>(List<T> results, int page, int totalPages)
    {
        try
        {
            var jsonData = new
            {
                total = totalPages,
                page = page,
                records = results.Count(),
                rows = Json(results).Data
            };
            return Json(jsonData, JsonRequestBehavior.AllowGet);
        }
        catch
        {
            return Json(null, JsonRequestBehavior.AllowGet);
        }
    }
}

IAuditable Interface

This class encapsulates the date and usernames for the creation and last update of a database entity. It should be applied to any entity in your entity model that has these fields. In addition, the IAuditableExtension class provides the Created and Updated extensions methods to update these fields in a consistent way.

public interface IAuditable
{
    DateTime? DateCreated { get; set; }
    string CreatedBy { get; set; }
    DateTime? DateChanged { get; set; }
    string ChangedBy { get; set; }
}
public static class IAuditableExtension
{
    public static void Created(this IAuditable e, string user)
    {
        e.DateCreated = DateTime.Now;
        e.CreatedBy = user;
    }

    public static void Updated(this IAuditable e, string user)
    {
        e.DateChanged = DateTime.Now;
        e.ChangedBy = user;
    }
}

IDeletable

The IDeletable and IDeletableExtensions interfaces encapsulate soft deleting database entities. The IDeletable interface should be applied to any database entity that has the fields and therefore can be soft-deleted.

public interface IDeletable
{
    bool IsDeleted { get; set; }
    DateTime? DateChanged { get; set; }
    string ChangedBy { get; set; }
}
public static class IDeletableExtensions
{
    public static void Deleted(this IDeletable e, string user)
    {
        e.IsDeleted = true;
        e.DateChanged = DateTime.Now;
        e.ChangedBy = user;
    }

}

JavaScript Code

The following JavaScript provides support for the "Action" column used in many grids to edit and delete grid rows.

var gridSelectedIndexArray = new Array();

var EditButtonClicked;
var CancelButtonClicked;
var SaveButtonClicked;
var DeleteButtonClicked;
var AddRowClicked;

/*===============================================================================================*/
function editButtonHtml(options) {
    return "<div id=\"" + options.gid + "_" + options.rowId + "_editR\" onclick=\"actionRowEdit(this, '"
            + options.gid + "', " + options.rowId
            + ");\" class=\"ui-pg-div ui-inline-edit\" style=\"float: left; margin-left: 5px; "
            + "display: block;\" title=\"Edit selected row\"><span class=\"ui-icon ui-icon-pencil\"></span></div>";
}
/*===============================================================================================*/
function deleteButtonHtml(options) {
    return "<div id=\"" + options.gid + "_" + options.rowId + "_delR\" onclick=\"actionRowDelete(this, '"
            + options.gid + "', " + options.rowId + ");\" class=\"ui-pg-div ui-inline-del\" "
            + "style=\"float: left; margin-left: 5px; display: block;\" title=\"Delete selected row\">"
            + "<span class=\"ui-icon ui-icon-trash\"></span></div>";
}
/*===============================================================================================*/
function saveButtonHtml(options, refreshAfterSave) {

    if (refreshAfterSave)
        saveR = "actionRowSaveExt";
    else
        saveR = "actionRowSave";

    saveR = "<div id=\"" + options.gid + "_" + options.rowId + "_saveR\" onclick=\"" + saveR + "(this, '"
            + options.gid + "', " + options.rowId + "); \" class=\"ui-pg-div ui-inline-save\" "
            + "style=\"float: left; margin-left: 5px; display: none;\" title=\"Save\">"
            + "<span class=\"ui-icon ui-icon-disk\"></span></div>";

    return saveR;
}
/*===============================================================================================*/
function cancelButtonHtml(options) {
    return "<div id=\"" + options.gid + "_" + options.rowId + "_cancelR\" onclick=\"actionRowCancel(this, '"
            + options.gid + "', " + options.rowId + ");\" class=\"ui-pg-div ui-inline-cancel\" "
            + "style=\"float: left; margin-left: 5px; display: none;\" title=\"Cancel\"><span "
            + "class=\"ui-icon ui-icon-cancel\"></span></div>";
}
/*===============================================================================================*/
function actionRowFormatterBase(cellvalue, options, rowObject, showDel, refreshAfterSave, 
extraButtons) {
   
    editR = editButtonHtml(options);
    delR = deleteButtonHtml(options);
    saveR = saveButtonHtml(options, refreshAfterSave);
    cancelR = cancelButtonHtml(options);

    var s = "<div class=\"actionWrapper\">" + editR;

    if (showDel)
        s = s + delR;

    s = s + saveR + cancelR;

    if (extraButtons != null && extraButtons != undefined)
        s = s + extraButtons;

    s = s + "</div>";

    return s;
}
/*===============================================================================================*/
function actionRowFormatterNoEdit(cellvalue, options, rowObject, showDel, refreshAfterSave,
extraButtons) {

    editR = "<div id=\"" + options.gid + "_" + options.rowId + "_editR\" onclick=\"actionRowEdit(this, '"
                + options.gid + "', " + options.rowId
                + ");\" class=\"ui-pg-div ui-inline-edit\" style=\"float: left; margin-left: 5px; "
                + "display: none;\" title=\"Edit selected row\"><span class=\"ui-icon ui-icon-pencil\"></span></div>";
    delR = deleteButtonHtml(options);
    saveR = saveButtonHtml(options, refreshAfterSave);
    cancelR = cancelButtonHtml(options);

    var s = "<div class=\"actionWrapper\">" + editR;

    if (showDel)
        s = s + delR;

    s = s + saveR + cancelR;

    if (extraButtons != null && extraButtons != undefined)
        s = s + extraButtons;

    s = s + "</div>";

    return s;
}
/*===============================================================================================*/
//DOES NOT include delete button; refreshes the grid after saving
actionRowDeleteOnlyFormatter = function (cellvalue, options, rowObject) {
    return deleteButtonHtml(options);
}
/*===============================================================================================*/
//Includes delete button; DOES NOT refresh the grid after saving
actionRowFormatter = function (cellvalue, options, rowObject) {
    return actionRowFormatterBase(cellvalue, options, rowObject, true, false);
}
/*===============================================================================================*/
//DOES NOT include delete button; refreshes the grid after saving
actionRowNoDelFormatter = function (cellvalue, options, rowObject) {
    return actionRowFormatterBase(cellvalue, options, rowObject, false, true);
}
/*===============================================================================================*/
//DOES NOT include delete or edit button; refreshes the grid after saving - used on the Claims: Notes grid
actionRowNoDelNoEditFormatter = function (cellvalue, options, rowObject) {
    return actionRowFormatterNoEdit(cellvalue, options, rowObject, false, true);
}
/*===============================================================================================*/
//Includes delete button; refreshes the grid after saving
actionRowFormatterUsingSaveExt = function (cellvalue, options, rowObject) {
    return actionRowFormatterBase(cellvalue, options, rowObject, true, true);
}

/*===============================================================================================*/
function actionRowAdd(el, gridId, rowData) {
    $('#' + gridId).addRowData(-1, rowData, "last");

    var newEl = document.getElementById(gridId + '_-1_editR');

    actionRowEdit(newEl, gridId, -1);
}
/*===============================================================================================*/
function actionRowCancel(el, gridId, index) {

    CancelButtonClicked = true;

    if (index != -1) {
        $('#' + gridId).jqGrid('restoreRow', index);
    }
    else
        $('#' + gridId).delRowData(index);

    gridSelectedIndexArray[gridId] = null;

    /*- Show/hide action buttons ----------------------------------------------------------------*/
    $(el).parent().find('.ui-inline-edit').show();
    $(el).parent().find('.ui-inline-del').show();
    $(el).parent().find('.addRates').show();
    $(el).parent().find('.ui-inline-save').hide();
    $(el).parent().find('.ui-inline-cancel').hide();

}
/*===============================================================================================*/
function actionRowDelete(el, gridId, index) {
    DeleteButtonClicked = true;
    
    $('#' + gridId).delGridRow(index);
}
/*===============================================================================================*/
function actionRowEdit(el, gridId, newIndex) {
    EditButtonClicked = true;
    
    var oldIndex = gridSelectedIndexArray[gridId];

    if (oldIndex != null && oldIndex != '' && oldIndex != newIndex) {
        var actionCancelElement = document.getElementById(gridId + '_' + oldIndex + '_cancelR');
        actionRowCancel(actionCancelElement, gridId, oldIndex);
    }

    gridSelectedIndexArray[gridId] = newIndex;

    // Hide EDIT and DELETE icons
    $(el).parent().find('.ui-inline-edit').hide();
    $(el).parent().find('.ui-inline-del').hide();
    $(el).parent().find('.addRates').hide();

    // Show SAVE and CANCEL icons
    $(el).parent().find('.ui-inline-save').show();
    $(el).parent().find('.ui-inline-cancel').show();

    $('#' + gridId).editRow(newIndex);
}
/*===============================================================================================*/
function actionRowSave(el, gridId, index) {

    SaveButtonClicked = true;
    
    $('#' + gridId).saveRow(index);

    actionRowCancel(el, gridId, index);

    if (index == -1) {
        $('#' + gridId).trigger('reloadGrid');
    }
}
/*===============================================================================================*/
function actionRowSaveExt(el, gridId, index) {

    SaveButtonClicked = true;

    $('#' + gridId).saveRow(index, function (data) { $('#' + gridId).trigger('reloadGrid'); });

    if (gridid = "gridRoomCharges") {
        $("#gridDeposits").trigger('reloadGrid');
    }    

    actionRowCancel(el, gridId, index);
}
/*===============================================================================================*/
function onGridRowSelect(gridId, rowId) {

    if (!EditButtonClicked && !CancelButtonClicked && !SaveButtonClicked && !DeleteButtonClicked) {

        var selector = '';

        // "Click" the cancel icon for the last row selected(?)
        if (gridSelectedIndexArray[gridId] !== undefined && gridSelectedIndexArray[gridId] !== null) {
            selector = '#' + gridId + '_' + gridSelectedIndexArray[gridId] + '_editR';
            actionRowSaveExt(selector, gridId, gridSelectedIndexArray[gridId]);
        }

        // "Click" the edit icon for the row
        selector = '#' + gridId + '_' + rowId + '_editR';
        actionRowEdit(selector, gridId, rowId);

    }
    EditButtonClicked = false;
    CancelButtonClicked = false;
    SaveButtonClicked = false;
    DeleteButtonClicked = false;
}

ScrewTurn Wiki version 3.0.1.400. Some of the icons created by FamFamFam. Except where noted, all contents Copyright © 1999-2024, Patrick Jasinski.