在 ASP.NET MVC 中使用 jqGrid 的搜索工具栏和多重过滤器






4.78/5 (67投票s)
本文介绍了如何在 ASP.NET MVC 中使用 jqGrid 的搜索工具栏和多重过滤器。
引言
很多时候,我们需要以表格形式显示数据并对其进行操作。在传统的 ASP.NET 中,内置控件是可用的。在 ASP.NET MVC 中,使用自定义脚本来解决这个问题总是更好的选择。jqGrid 就是其中一个解决方案。
背景
jqGrid
jqGrid 是一个 jQuery 插件,它允许您以表格形式显示数据,并具有更丰富的功能。其功能包括:
- 支持表格嵌套
- 支持数据编辑
- 支持树形结构显示数据
- 支持 jQuery UI 的主题
- 记录搜索、每列过滤、按列排序、分页导航等。
许可证
“jqGrid 在 GPL 和 MIT 许可下发布。此许可政策使软件免费提供给所有人(如同免费啤酒),您可以不受任何限制地将其用于商业或开源项目(上述自由)。当然,创建此软件花费了一些时间,而维护它将花费更多。我们用于增强 jqGrid 的所有时间都是您在自己的项目中节省的时间。考虑到您通过简单地下载源文件并尝试为其定价而节省的所有时间:这就是您应该考虑付给我们以继续我们良好工作的金额。”
官方网站:trirand.com。目前可用的版本是 3.6.3。注意:我使用的是一个旧版本。它在 IE 中存在一些 bug,但在 Firefox 中运行正常。
使用代码
使用 jqGrid
考虑我们需要包含哪些文件才能使用此插件。
<link href="../../Scripts/jqGrid/themes/smoothness/jquery-ui-1.7.2.custom.css"
rel="stylesheet" type="text/css" />
<link href="../../Scripts/jqGrid/themes/ui.jqGrid.css"
rel="stylesheet" type="text/css" />
<script language="javascript" type="text/javascript"
src="<%= Url.Content("/Scripts/jquery-1.3.2.js") %>"></script>
<script language="javascript" type="text/javascript"
src="<%= Url.Content("/Scripts/jquery-ui-1.7.2.custom.min.js") %>">
很明显,我们需要包含 jQuery 和 JQuery UI 脚本,jqGrid 插件,以及包含网格声明的 jqGridHomeIndex.js 文件。此外,在 \Scripts\jqGrid 目录中还有一些辅助文件。我们必须设置 jqGrid 的设置,以帮助插件根据我们的需求正确配置自身。在其中,我们定义了表头、列、使用的数据格式、表格的大小等。
$('#grid').jqGrid({
colNames: ['Online', 'Computer', 'IP', 'User'],
colModel: [
{ name: 'IsOnline', width: 100, index: 'IsOnline',
searchoptions: { sopt: ['eq', 'ne']} },
{ name: 'Name', index: 'Name',
searchoptions: { sopt: ['eq', 'ne', 'cn']} },
{ name: 'IP', index: 'IP',
searchoptions: { sopt: ['eq', 'ne', 'cn']} },
{ name: 'User', index: 'User',
searchoptions: { sopt: ['eq', 'ne', 'cn']} }
],
sortname: 'Name',
rowNum: 10,
rowList: [10, 20, 50],
sortorder: "asc",
datatype: 'json',
caption: 'Result of scanning',
viewrecords: true,
mtype: 'GET',
jsonReader: {
root: "rows",
page: "page",
total: "total",
records: "records",
repeatitems: false,
userdata: "userdata"
},
url: "/Home/GetData"
})
您可以在互联网上找到关于此插件的大量信息,因此我将不再赘述其设置。
搜索
jqGrid 具有设置过滤器以仅搜索必要数据的能力。为此,在其导航器面板中,应提供以下搜索选项:
.navGrid('#pager', { view: false, del: false, add: false, edit: false },
{}, // default settings for edit
{}, // default settings for add
{}, // delete
{closeOnEscape: true, multipleSearch: true,
closeAfterSearch: true }, // search options
{}
);
multipleSearch
- 允许按多重条件搜索closeOnEscape
- 允许通过按 Escape 键关闭搜索工具栏closeAfterSearch
- 允许在搜索后关闭搜索工具栏
但是,我们还必须确定对表中特定列可用的搜索操作。这在 colModel
部分的 searchoptions
参数中完成。
colModel: [
{ name: 'IsOnline', width: 100, index: 'IsOnline',
searchoptions: { sopt: ['eq', 'ne']} },
{ name: 'Name', index: 'Name', searchoptions: { sopt: ['eq', 'ne', 'cn']} },
{ name: 'IP', index: 'IP', searchoptions: { sopt: ['eq', 'ne', 'cn']} },
{ name: 'User', index: 'User', searchoptions: { sopt: ['eq', 'ne', 'cn']}
}],
此处为每个字段定义了一组操作。
eq
- 等于ne
- 不等于cn
- 包含
设置这些选项后,将出现一个弹出搜索窗口。
“匹配”下拉列表用于确定这些设置如何相互关联 - 通过 AND 或 OR 运算符。"+" 和 "-" 用于添加或删除新的过滤选项。按“查找”按钮后,服务器将发送一个带有以下参数的异步请求:
_search = true
filters {
"groupOp":"AND",
"rules":[
{"field":"IsOnline","op":"eq","data":"True"},
{"field":"Name","op":"cn","data":"asdasd"}
]
}
nd = 1265873327560
page = 1
rows = 10
sidx = Name
sord = asc
_search
- 确定是否使用了过滤filters
- 如果使用了过滤,它将以 JSON 格式提供有关其参数的信息groupOp
-应用于规则组的运算符,Rules
(AND
和OR
)rules
- 一组规则,其中field
- 进行过滤的字段op
- 用户选择的操作data
- 用户输入的过滤器page
- 页码rows
- 每页记录数sidx
- 执行排序的字段sord
- 排序方向(asc
或desc
)
请求通过在 jqGrid 中设置的 URL 发送。
mtype: 'GET',
url: "/Home/GetData"
现在,服务器必须处理请求并返回相关数据。
jqGrid 处理
处理此请求的方法声明如下:
public JsonResult GetData(GridSettings grid)
如所示,它接收一个强类型对象 GridSettings
,并返回 JSON 数据作为响应。为了将该对象传递给方法,我们使用了 ASP.NET MVC 的功能 ModelBinder,它允许 Action 方法将复杂类型作为其参数。
首先,我们定义几个将包含来自 jqGrid 的数据的类型:
GridSettings
- 存储 jqGrid 结构
public class GridSettings
{
public bool IsSearch { get; set; }
public int PageSize { get; set; }
public int PageIndex { get; set; }
public string SortColumn { get; set; }
public string SortOrder { get; set; }
public Filter Where { get; set; }
}
Filter
- 用户在搜索工具栏中定义的过滤器[DataContract]
public class Filter
{
[DataMember]
public string groupOp { get; set; }
[DataMember]
public Rule[] rules { get; set; }
public static Filter Create(string jsonData)
{
try
{
var serializer =
new DataContractJsonSerializer(typeof(Filter));
System.IO.StringReader reader =
new System.IO.StringReader(jsonData);
System.IO.MemoryStream ms =
new System.IO.MemoryStream(
Encoding.Default.GetBytes(jsonData));
return serializer.ReadObject(ms) as Filter;
}
catch
{
return null;
}
}
}
因子方法 Create
允许从序列化的 JSON 数据创建此类的对象。
Rule
- 代表过滤器中的一条规则。[DataContract]
public class Rule
{
[DataMember]
public string field { get; set; }
[DataMember]
public string op { get; set; }
[DataMember]
public string data { get; set; }
}
实际上,这些类型与 jqGrid 中的相应结构相同。接下来,我们创建一个名为 GridModelBinder
的类,它继承自 IModelBinder
并重写 BindModel
方法。在该方法中,我们期望 HttpContext.Request
对象将包含来自 jqGrid 的查询字符串。
public class GridModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
try
{
var request = controllerContext.HttpContext.Request;
return new GridSettings
{
IsSearch = bool.Parse(request["_search"] ?? "false"),
PageIndex = int.Parse(request["page"] ?? "1"),
PageSize = int.Parse(request["rows"] ?? "10"),
SortColumn = request["sidx"] ?? "",
SortOrder = request["sord"] ?? "asc",
Where = Filter.Create(request["filters"] ?? "")
};
}
catch
{
return null;
}
}
}
为了在访问 Home/GetData
时调用此方法,您必须将以下属性添加到 GridSettings
类中:
[ModelBinder(typeof(GridModelBinder))]
public class GridSettings
现在,Home
控制器的 GetData
方法中的网格对象将正确初始化。
服务器端过滤和排序
在了解了如何从 jqGrid 获取类型化的表单数据后,最好对其进行处理并应用于集合。反射和表达式树将在此处提供帮助。以下是有关表达式树的一些 MSDN 信息:
“表达式树以数据形式表示语言级别的代码。数据存储在树形结构中。表达式树中的每个节点都表示一个表达式,例如,方法调用或二元运算,如 x < y。下图显示了一个表达式及其在表达式树中的表示。表达式的不同部分已进行颜色编码,以匹配表达式树中相应的表达式树节点。表达式树节点的各种类型也已显示。”
在 LinqExtensions
类中,我编写了两个使用此方法的方法:
OrderBy
其中
OrderBy
方法用于排序。
public static IQueryable<T> OrderBy<T>(
this IQueryable<T> query, string sortColumn, string direction)
这是 IQueryable<T>
对象的扩展方法,具有以下参数:
sortColumn
- 要排序的列名direction
- 排序方向
现在让我们看看此方法中做了什么。
- 定义排序顺序。
string methodName = string.Format("OrderBy{0}",
direction.ToLower() == "asc" ? "" : "descending");
p => p.Name
(或 System.Linq.Expressions.Expression<func<t,tkey>>
)。我们创建参数 p
,其类型在 ElementType
中定义。ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
MemberExpression memberAccess = null;
foreach (var property in sortColumn.Split('.'))
memberAccess = MemberExpression.Property
(memberAccess ?? (parameter as Expression), property);
LambdaExpression orderByLambda = Expression.Lambda(memberAccess, parameter);
MethodCallExpression result = Expression.Call(
typeof(Queryable),
methodName,
new[] { query.ElementType, memberAccess.Type },
query.Expression,
Expression.Quote(orderByLambda));
IQuerable<T>
。return query.Provider.CreateQuery<T>(result);
Where
方法用于过滤。
public static IQueryable<T> Where<T>(this IQueryable<T> query,
string column, object value, WhereOperation operation)
column
- 列名value
- 过滤值operation
- 用于过滤的操作
WhereOperation
枚举声明如下:
public enum WhereOperation
{
[StringValue("eq")]
Equal,
[StringValue("ne")]
NotEqual,
[StringValue("cn")]
Contains
}
在这里,我使用了 StringValue
属性,它允许设置传输行的字符串值,这在我们的情况下非常方便。有关 StringValue
属性的更多信息可以在这里找到:StringValueAttribute by Matt Simner。
根据操作类型,我们必须构造一个适当的 lambda 表达式。为此,我们可以使用 Expression
类预定义的 Equal
和 NotEqual
方法,或者使用反射来访问另一个类的所需方法,就像我们在 Contains
操作的情况下所做的那样。
Expression condition = null;
LambdaExpression lambda = null;
switch (operation)
{
//equal ==
case WhereOperation.Equal:
condition = Expression.Equal(memberAccess, filter);
break;
//not equal !=
case WhereOperation.NotEqual:
condition = Expression.NotEqual(memberAccess, filter);
break;
//string.Contains()
case WhereOperation.Contains:
condition = Expression.Call(memberAccess,
typeof(string).GetMethod("Contains"),
Expression.Constant(value));
break;
}
lambda = Expression.Lambda(condition, parameter);
通过设置这些方法,我们可以利用它们。在过滤时,我们必须通过调用 Where
方法遍历整个规则集合。以下是使用 AND
运算符进行过滤的方法:
foreach (var rule in grid.Where.rules)
query = query.Where<computer>(rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
使用 OR
运算符进行过滤。
var temp = (new List<computer>()).AsQueryable();
foreach (var rule in grid.Where.rules)
{
var t = query.Where<computer>(
rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
temp = temp.Concat<computer>(t);
}
//remove repeating records
query = temp.Distinct<computer>();
排序更简单。
query = query.OrderBy<computer>(grid.SortColumn, grid.SortOrder);
返回数据到 jQuery
现在,当我们有了所需数据集合后,就可以将其发送到客户端了。让我们看看这是如何完成的:
- 计算满足条件的记录数。
var count = query.Count();
var data =
query.Skip((grid.PageIndex - 1) *grid.PageSize).Take(grid.PageSize).ToArray();
var result = new
{
total = (int)Math.Ceiling((double)count / grid.PageSize),
page = grid.PageIndex,
records = count,
rows = (from host in data
select new
{
IsOnline = host.IsOnline.ToString(),
Name = host.Name,
IP = host.IP,
User = host.User,
}).ToArray()
};
return Json(result, JsonRequestBehavior.AllowGet);
数据源
我们可以使用数据库作为数据源。为此,我们需要使用 ORM 工具,例如 NHibernate(您必须使用 LinqToNHibernate 扩展)和 LinqToSql,它们提供了 IQueryable
接口。
历史
- 2010 年 2 月 12 日 - 文章的第一个版本。