本文转自:http://www.cnblogs.com/think8848/archive/2011/07/15/2107828.html
前些天写了一篇有关jqGrid的文章,因为是公司项目的原因一直没有提供源代码,今天又要用jqGrid,顺便做一个示例,以便有需要的人参考。源代码下载
为了使用方便起见,将jqGrid的一些选项放在了_Layout.cshtml中,这样就不用在每一个页面中重复输入了,见代码:
<!DOCTYPE html> <html> <head> <title>@ViewBag.Title</title>
<link href= "@Url.Content(" ~/Content/themes/ base /site.css ")" rel= "Stylesheet" type= "text/css" />
<link href= "@Url.Content(" ~/Content/themes/ base /jquery.ui.css ")" rel= "Stylesheet" type= "text/css" />
<link href= "@Url.Content(" ~/Content/themes/ base /jquery.jqgrid.css ")" rel= "Stylesheet" type= "text/css" />
<script src= "@Url.Content(" ~/Scripts/jquery.js ")" type= "text/javascript" ></script>
<script src= "@Url.Content(" ~/Scripts/jquery.ui.js ")" type= "text/javascript" ></script>
<script src= "@Url.Content(" ~/Scripts/i18n/grid.locale-cn.js ")" type= "text/javascript" ></script>
<script src= "@Url.Content(" ~/Scripts/jquery.jqgrid.js ")" type= "text/javascript" ></script>
<script type= "text/javascript" >
var gridContainerId = '#gridview' , gridId = '#list' ;
$(function () {
$.extend($.jgrid.defaults, {
ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
datatype: 'json' ,
mtype: 'GET' ,
hidegrid: false ,
rownumbers: true ,
rowNum: 10,
rowList: [10, 15, 20],
sortorder: 'asc' ,
viewrecords: true ,
pager: $( '#pager' ),
height: 'auto' ,
editfunc: function (id, data) { return false ; },
delfunc: function (id, data) { return false ; },
cleverjqgridactions: 'clever_jqgrid_actions' ,
colActionsTitle: '操作'
});
});
function cleverActions(cellvalue, options, rowObject) {
return '<a href="javascript:void(0)" id="' + options.rowId + '" class="ui-corner-all clever-jqgrid-action clever-jqgrid-edit" style="float: left; margin-left: 5px; padding: 0px;" title="' + $.jgrid.nav.edittitle + '" onclick="actionClick(this,event)" onmouseover="actionMouseover(this,event)" onmouseout="actionMouseout(this,event)" ><span class="ui-icon ui-icon-pencil"></span></a>'
+ '<a href="javascript:void(0)" id="' + options.rowId + '" class="ui-corner-all clever-jqgrid-action clever-jqgrid-del" style="float: left; margin-left: 5px; padding: 0px;" title="' + $.jgrid.nav.deltitle + '" onclick="actionClick(this,event)" onmouseover="actionMouseover(this,event)" onmouseout="actionMouseout(this,event)" ><span class="ui-icon ui-icon-trash"></span></a>' ;
}
function actionMouseover(el, ev) {
$(el).addClass( 'ui-state-hover' );
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
}
function actionMouseout(el, ev) {
$(el).removeClass( 'ui-state-hover' );
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
}
function actionClick(el, ev) {
var id = $(el).attr( 'id' );
var data = $(gridId).jqGrid( 'getRowData' , id);
if (data.clever_jqgrid_actions) {
delete data.clever_jqgrid_actions;
}
if ($(el).hasClass( 'clever-jqgrid-edit' )) {
$(gridId).getGridParam( 'editfunc' )(id, data);
} else if ($(el).hasClass( 'clever-jqgrid-del' )) {
if (confirm($.jgrid.del.msg)) {
$(gridId).getGridParam( 'delfunc' )(id, data);
}
}
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
}
</script>
@RenderSection( "header" , false )
<script type= "text/javascript" >
$(function () {
$(window).bind( 'resize' , function () {
var width = $(gridContainerId).width();
if (width == null || width < 1) {
width = $(gridContainerId).attr( 'offsetWidth' );
}
width = width - 2;
if (width > 0 && Math.abs(width - $(gridId).width()) > 5) {
$(gridId).setGridWidth(width);
}
}).trigger( 'resize' );
});
</script>
</head> <body> @RenderBody()
</body> </html> |
为了使jqGrid能随着浏览器的缩放而自适应宽度,因此我在视图页面执行完javascript后又为jqGrid指定了resize事件。
在这里值的一提的是,要为jqGrid指定默认选项,需要使用$.jgrid.defaults,方法嘛当然是继承了,这里我把部分默认选项的说明给出来:
//指定请求数据时的ContentType,我使用了json
ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
//返回数据格式为json
datatype: 'json',
//Http方法为GET
mtype: 'GET',
//隐藏Grid的头
hidegrid: false,
//是否显示行号
rownumbers: true,
//默认显示数据行的个数
rowNum: 10,
//允许显示数据行个数的选项
rowList: [10, 15, 20],
//默认排序策略(升序)
sortorder: 'asc',
//是否显示表脚,即翻页所在的那行
viewrecords: true,
//页脚的容器
pager: $('#pager'),
//Grid高度自动调整
hieght: 'auto',
//这是我自定义的一个功能,后面会提到
editfunc: function (id, data) { return false; },
//自定义功能,同上
delfunc: function (id, data) { return false; },
//自定义功能,同上
cleverjqgridactions: 'clever_jqgrid_actions',
//自定义功能,同上
colActionsTitle: '操作'
一般情况下,在管理系统中,需要尽可能的减少用户的操作步骤,因此,把"编辑","删除"按钮直接放在数据行中,这样用户点一下就可以了,而不需要先选中一行,然后再去点击编辑/删除按钮,因此,使用自定义Formatter的方式,使每行增加一个"编辑","删除"按钮,方法如下:
在colModel中添加一个列:
{ name: $.jgrid.defaults.cleverjqgridactions, width: 80, fixed: true , sortable: false , resize: false , formatter: cleverActions }
|
上文中$.jgrid.defaults.cleverjqgridactions就是这个用途,为这个列提供一个不大可能重复的固定列定,以标示这个列,重点是formatter,为这个选项提供一个方法便是,这个方法的签名如下: |
function (cellvalue, options, rowObject){
} |
cellValue不用理会,因为它本来是指json为这个列提从的值,因为我们这个项不是json提供的,所以它肯定为undefined; |
options里提供了这个列的定义(colModel中列的定义选项),该字段所在行的主键值(当然,得是你指定了某行为主键),以及jqGrid的id; |
rowObject是一个数组,为截止目前,所在行的json数据,为啥是截止目前呢?因为当你这一列定义成功后,本列也成为行数据的一员了,在实际应用中,你肯定不希望这列的数据(其实就是你定义的按钮的HTML字符串)作为正式数据传入到业务逻辑里吧,所以使用 delete 方法要删除行数据中的这个字段值,那如何获取这个字段的名称呢?当..当..当,还记得$.jgrid.defaults.cleverjqgridactions吗?当然是使用它了(貌似是一个锉方法哦?)。
|
ok,知道了这些,我们来定义一个自定义的formatter: |
function cleverActions(cellvalue, options, rowObject) {
return '<a href="javascript:void(0)" id="' + options.rowId + '" class="ui-corner-all clever-jqgrid-action clever-jqgrid-edit" style="float: left; margin-left: 5px; padding: 0px;" title="' + $.jgrid.nav.edittitle + '" onclick="actionClick(this,event)" onmouseover="actionMouseover(this,event)" onmouseout="actionMouseout(this,event)" ><span class="ui-icon ui-icon-pencil"></span></a>'
+ '<a href="javascript:void(0)" id="' + options.rowId + '" class="ui-corner-all clever-jqgrid-action clever-jqgrid-del" style="float: left; margin-left: 5px; padding: 0px;" title="' + $.jgrid.nav.deltitle + '" onclick="actionClick(this,event)" onmouseover="actionMouseover(this,event)" onmouseout="actionMouseout(this,event)" ><span class="ui-icon ui-icon-trash"></span></a>' ;
} |
使用$.jgrid.nav.edittitle和$.grid.nav.deltitle能获取到本地化的 "编辑" , "删除" 字符串,这样当用户把鼠标移动上按钮上时,就可以知道这两个按钮是什么用途了,所以将它哥俩的值指定给了a的title。
|
另外,我希望在我点击了 "编辑" 或是 "删除" 按钮时,就不要再触发行选中事件了,因此,我中止了事件冒泡,同样,我也中止了按钮的mouseover和mouseout事件冒泡:
|
function actionMouseover(el, ev) {
$(el).addClass( 'ui-state-hover' );
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
} function actionMouseout(el, ev) {
$(el).removeClass( 'ui-state-hover' );
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
} function actionClick(el, ev) {
var id = $(el).attr( 'id' );
var data = $(gridId).jqGrid( 'getRowData' , id);
if (data.clever_jqgrid_actions) {
delete data.clever_jqgrid_actions;
}
if ($(el).hasClass( 'clever-jqgrid-edit' )) {
$(gridId).getGridParam( 'editfunc' )(id, data);
} else if ($(el).hasClass( 'clever-jqgrid-del' )) {
if (confirm($.jgrid.del.msg)) {
$(gridId).getGridParam( 'delfunc' )(id, data);
}
}
if ($.browser.msie) {
ev.cancelBubble = true ;
} else {
ev.stopPropagation();
}
} |
代码看到这里,我直是讨厌IE,就是整得和别人不一样。好好的就使用stopPropagation嘛,非得整啥cancelBubble呢? |
var id = $(el).attr( 'id' );
var data = $(gridId).jqGrid( 'getRowData' , id);
|
鉴于在点击按钮后再获取行的主键值比较困难,我在生成铵钮时就将行主键值存在a标签的id中了,因此此时取出来,再使用getRowData方法获取该主键所在的行的数据对象,getRowData方法,据官方文档中说,性能不高,使用需谨慎,不要使用在 for 或是 while 中。
|
if (data.clever_jqgrid_actions) {
delete data.clever_jqgrid_actions;
} |
如果行数据包含了按钮HTML文本,则删除数据的这个属性 |
$(gridId).getGridParam( 'editfunc' )(id, data);
|
使用getGridParam方法调用当点击了编辑按钮时的回调方法。 |
ok,至此,在_Layout.cshtml中的故事就讲完了,接下来我们来看看在实际使用的页面中是如何编码的: |
@{ ViewBag.Title = "Index";
} @section header{ < script type="text/javascript">
$(function () {
$('#list').jqGrid({
url: '@Url.Action("Index", "User")',
colNames: ['Primary Key', 'User Name', 'Email', $.jgrid.defaults.colActionsTitle],
colModel: [
{ name: 'ID', index: 'ID', width: 1, hidden: true, key: true },
{ name: 'Name', index: 'Name', width: 100, align: 'left' },
{ name: 'Mail', index: 'Mail', width: 100, align: 'left' },
{ name: $.jgrid.defaults.cleverjqgridactions, width: 80, fixed: true, sortable: false, resize: false, formatter: cleverActions }
],
sortname: 'Name',
multiselect: true,
editfunc: function (id, data) {
alert(id);
},
delfunc: function (id, data) {
alert(id);
}
});
});
</ script >
} < div id="gridview">
< table id="list">
</ table >
< div id="pager">
</ div >
</ div >
|
有了_Layout.cshtml中的代码,这个页面上的代码就少多了,至少看起来不那么吓人了,照例来讲讲参数吧: |
//地球人都知道,这是请求数据的url |
url: '@Url.Action("Index", "User")' ,
|
//显示用的表头数据,$.jgrid.defaults.colActionsTitle本来的用途是显示"操作"两个字,但是不见得我在每个视图的资源文件中都定义吧,所以还是定义在_Layout.cshtml中,这样使用js的方式调用好可 |
colNames: ['Primary Key', 'User Name', 'Email', $.jgrid.defaults.colActionsTitle], //定义数据列 |
colModel: [{ |
//列名 |
name: 'ID', |
//排序时的键 |
index: 'ID', |
//宽度,应该是px单位 |
width: 1, |
//隐藏 |
hidden: true, |
//标示主键列 |
key: true }, { name: 'Name', index: 'Name', width: 100, align: 'left' },
{ name: 'Mail', index: 'Mail', width: 100, align: 'left' },
{ name: $.jgrid.defaults.cleverjqgridactions, width: 80, fixed: true, sortable: false, resize: false, formatter: cleverActions }
],
//默认使用Name列排序 |
sortname: 'Name', //是否允许多选,这个选项为true,则前面会加上一个全是checkbox的列,作用不用我多说了吧 |
multiselect: true, //自定义formatter中编辑按钮的回调方法,id是本行的主键值,data是本行的所有数据 |
editfunc: function (id, data) { alert(ids);
},
|
//自定义formatter中删除按钮的回调方法 delfunc: function (id, data) { alert(ids);
}
|
有了这些差不多jqGrid就能很好的工作起来了,但是服务端的数据处理起来也够麻烦的,think8848一向作人厚道,因此专门写了两个助手类,专为jqGrid提供格式化的数据: |
/// <summary> /// 分页排序助手类 /// </summary> public static class DataPagingHelper
{ public static IQueryable<T> GetQueryable<T>( this IList<T> list, string sidx, string sord, int page, int rows)
{
return GetQueryable<T>(list.AsQueryable<T>(), sidx, sord, page, rows);
}
public static IQueryable<T> GetQueryable<T>( this IQueryable<T> queriable, string sidx, string sord, int page, int rows)
{
var data = ApplyOrder<T>(queriable, sidx, sord.ToLower() == "asc" ? true : false );
return data.Skip<T>((page - 1) * rows).Take<T>(rows);
}
private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> queriable, string property, bool isASC)
{
PropertyInfo pi = typeof (T).GetProperty(property);
ParameterExpression arg = Expression.Parameter( typeof (T), "x" );
Expression expr = Expression.Property(arg, pi);
Type delegateType = typeof (Func<,>).MakeGenericType( typeof (T), pi.PropertyType);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
string methodName = isASC ? "OrderBy" : "OrderByDescending" ;
object result = typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod( typeof (T), pi.PropertyType)
.Invoke( null , new object [] { queriable, lambda });
return (IOrderedQueryable<T>)result;
}
} /// <summary> /// jqGrid数据处理助手类 /// </summary> public static class JqGridHelper
{ public static JsonResult GetJson<T>( this IList<T> datas, string sidx, string sord, int page, int rows, JsonRequestBehavior behavior, params string [] fields)
{
return GetJson<T>(datas.AsQueryable<T>(), sidx, sord, page, rows, behavior, fields);
}
public static JsonResult GetJson<T>( this IQueryable<T> queriable, string sidx, string sord, int page, int rows, JsonRequestBehavior behavior, params string [] fields)
{
var data = queriable.GetQueryable<T>(sidx, sord, page, rows);
var json = new JsonResult();
json.JsonRequestBehavior = behavior;
if (rows != 0)
{
var totalpages = ( decimal )queriable.Count<T>() / ( decimal )rows;
totalpages = (totalpages == ( int )totalpages) ? totalpages : ( int )totalpages + 1;
var rowsData = GetJsonData<T>(data, fields);
json.Data = new
{
page,
records = rows,
total = ( int )totalpages,
rows = rowsData
};
}
return json;
}
private static object [] GetJsonData<T>(IQueryable<T> queriable, params string [] fields)
{
var properties = typeof (T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
T[] datas = queriable.ToArray<T>();
object [] result = new object [datas.Length];
if (fields.Length == 0)
{
fields = Array.ConvertAll<PropertyInfo, string >(properties.Where<PropertyInfo>
(x => x.GetCustomAttributes( typeof (InternalAttribute), false ).Length == 0)
.ToArray<PropertyInfo>()
, delegate (PropertyInfo p)
{
return p.Name;
});
}
for ( int i = 0; i < datas.Length; i++)
{
object [] values = new object [fields.Length];
for ( int j = 0; j < fields.Length; j++)
{
var pi = properties.First<PropertyInfo>(x => x.Name == fields[j]);
var value = pi.GetValue(datas[i], null );
values[j] = value != null ? value.ToString() : "" ;
}
result[i] = new { id = i, cell = values };
}
return result;
}
} /// <summary> /// 本人项目中使用的,不用深究 /// </summary> [AttributeUsage(AttributeTargets.Property)] public class InternalAttribute : Attribute { }
|
有了这两个助手类,我们再来看提供数据的服务是啥样的: |
public class UserController : Controller
{ //
// GET: /User/
public ActionResult Index( string sidx, string sord, int page, int rows)
{
var users = new List<User>();
users.Add( new User() { ID = Guid.NewGuid(), Name = "think8848" , Mail = "think8848@csdn.net" , Password = "abcdefg" });
users.Add( new User() { ID = Guid.NewGuid(), Name = "a" , Mail = "a@csdn.net" , Password = "abcdefg" });
users.Add( new User() { ID = Guid.NewGuid(), Name = "b" , Mail = "b@csdn.net" , Password = "abcdefg" });
//...
users.Add( new User() { ID = Guid.NewGuid(), Name = "y" , Mail = "y@csdn.net" , Password = "abcdefg" });
users.Add( new User() { ID = Guid.NewGuid(), Name = "z" , Mail = "z@csdn.net" , Password = "abcdefg" });
return users.GetJson<User>(sidx, sord, page, rows, JsonRequestBehavior.AllowGet, new string [] { "ID" , "Name" , "Mail" });
}
} |
User实体定义: |
public class User
{ public Guid ID { get ; set ; }
public string Name { get ; set ; }
public string Password { get ; set ; }
public string Mail { get ; set ; }
} |
必须得完结了,我的脚被蚊子要抬走了... |
效果图: |