65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (67投票s)

2010年2月12日

CPOL

7分钟阅读

viewsIcon

367148

downloadIcon

10191

本文介绍了如何在 ASP.NET MVC 中使用 jqGrid 的搜索工具栏和多重过滤器。

MvcGrid_Sample

引言

很多时候,我们需要以表格形式显示数据并对其进行操作。在传统的 ASP.NET 中,内置控件是可用的。在 ASP.NET MVC 中,使用自定义脚本来解决这个问题总是更好的选择。jqGrid 就是其中一个解决方案。

背景

jqGrid

jqGrid 是一个 jQuery 插件,它允许您以表格形式显示数据,并具有更丰富的功能。其功能包括:

  • 支持表格嵌套
  • Demo1.PNG

  • 支持数据编辑
  • Demo2.PNG

  • 支持树形结构显示数据
  • 支持 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 - 包含

设置这些选项后,将出现一个弹出搜索窗口。

SearchToolbar.PNG

“匹配”下拉列表用于确定这些设置如何相互关联 - 通过 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 -应用于规则组的运算符,RulesANDOR
    • rules - 一组规则,其中
      • field - 进行过滤的字段
      • op - 用户选择的操作
      • data - 用户输入的过滤器
  • page - 页码
  • rows - 每页记录数
  • sidx - 执行排序的字段
  • sord - 排序方向(ascdesc

请求通过在 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。下图显示了一个表达式及其在表达式树中的表示。表达式的不同部分已进行颜色编码,以匹配表达式树中相应的表达式树节点。表达式树节点的各种类型也已显示。”

ExpressionTrees.PNG

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");
  • 然后,我们将 lambda 表达式构建为 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);
  • 通过创建表示方法调用的对象来完成 Lambda 表达式。
  • 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 类预定义的 EqualNotEqual 方法,或者使用反射来访问另一个类的所需方法,就像我们在 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();
  • 转换为 jqGrid 期望的数据格式。在这里,匿名类可以为我们提供很大的帮助。
  • 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()
    };
  • 序列化为 JSON 格式并发送到客户端。
  • return Json(result, JsonRequestBehavior.AllowGet);

数据源

我们可以使用数据库作为数据源。为此,我们需要使用 ORM 工具,例如 NHibernate(您必须使用 LinqToNHibernate 扩展)和 LinqToSql,它们提供了 IQueryable 接口。

历史

  • 2010 年 2 月 12 日 - 文章的第一个版本。
© . All rights reserved.