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

创建可展开的主详细信息表 (jQuery DataTables 和 ASP.NET MVC 集成 - 第 IV 部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (54投票s)

2011 年 5 月 1 日

CPOL

9分钟阅读

viewsIcon

402332

downloadIcon

15986

使用 jQuery DataTables 插件在 ASP.NET MVC 中实现可展开的主详细信息表

引言

如果您使用 jQuery DataTables 插件,那么创建具有分页、排序和过滤功能的完整表格将是一项轻松的任务。该插件允许您仅用一行代码即可为普通的 HTML 表格添加所有这些功能,如下面的示例所示。

("#myTable").dataTables();

这一行代码会查找一个 ID 为“myTable”的 HTML 表格,并添加分页、排序和关键字过滤功能。一个普通的 HTML 表格将被转换为一个功能齐全的表格,如下图所示。

Expandabe-DataTable-MVC/datatables.png

在本文中,我们将了解如何将标准的 DataTable 扩展为可展开的主详细信息表。本文的目的是展示如何显示公司列表,其中包含一个展开按钮,该按钮将显示所选公司的员工列表,如下图所示。

Expandabe-DataTable-MVC/expandableDataTables-table.png

在此示例中,表格中的每个记录(在此示例中为公司)都有一个展开按钮,点击此按钮时会打开详细信息。详细信息可以是子记录(例如,在此示例中为该公司工作的员工)、关于公司的其他详细信息(例如,标志、描述以及未在表格行中显示的其他信息),或两者的组合。

背景

当存在相关实体时,可展开表格通常是一个常见的需求。如果您有拥有员工的公司,有学生老师,有包含销售订单项的销售订单,那么您可能需要实现主记录列表(公司、老师、销售订单)的功能,并能够在展开主记录行时显示与之相关的记录。在其他情况下,您可能需要可展开的表格,即只在表格中显示最少数量的列,并且当用户点击一行时,会在展开的行中显示更多详细信息。这些需求可以使用 jQuery DataTables 插件轻松实现——本文将介绍如何配置它,添加哪些客户端逻辑,以及服务器端需要实现什么来支持客户端的可展开表格。

当实现可展开表格时,会使用以下场景:

  1. 在表格中添加一个额外的列,其中包含展开/折叠按钮,使用户能够展开选定的表格行。
  2. 当用户点击展开按钮时,会向服务器发送一个 AJAX 调用,并返回详细信息的 HTML。
  3. 返回的 HTML 使用 DataTablesfnOpen 函数作为子行注入,并且展开按钮会转换为折叠按钮。
  4. 当用户点击折叠按钮时,会使用 DataTablesfnClose 函数关闭子行,并且折叠按钮会转换回展开按钮。

本文将介绍如何使用 jQuery DataTables 插件实现此场景。

Using the Code

此示例展示了如何使用 jQuery DataTables 插件在 ASP.NET MVC 中实现可展开的主详细信息表。项目需要实现三个部分:

  1. 定义将要显示的数据结构的 Model。
  2. Controller 类,用于响应 DataTables 插件发送的请求。
  3. View,它是渲染引擎,并且包含需要实现的客户端逻辑。

以下各节将对此进行描述。

模型

Model 表示为两个通过一对多关系关联的类。此示例使用公司作为主信息,员工作为详细信息。Model 类的源代码如下所示。

public class Company 
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
} 

public class Employee 
{ 
    public int EmployeeID { get; set; }
    public string Name { get; set; }
    public string Position { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
    public int CompanyID { get; set; }
}

员工与其公司之间的关系通过 Employee 类中的 CompanyID 属性建立。

控制器 (Controller)

Controller 类响应用户操作并返回响应。客户端会发送一个按公司 ID 获取 employees 列表的请求,当按下展开按钮时会发送此请求。此请求将包含显示在展开行中的 employees 列表的公司 ID。控制器中的 Action 方法返回按公司 ID 查找的员工,如下所示。

public class HomeController : Controller
{
    public ActionResult CompanyEmployees(int? CompanyID)
    {
       var employees = DataRepository.GetEmployees();
       var companyEmployees = (from e in employees
                               where (CompanyID == null || e.CompanyID == CompanyID)
                               select e).ToList();
       return View(companyEmployees);
    }
}

此 Action 方法获取公司的 ID,并查找所有具有该公司 ID 的 employees。有一个用于格式化返回的公司列表的辅助 Partial View,如下所示。

@model IEnumerable<JQueryDataTables.Models.Employee>

<table cellpadding="4" cellspacing="0" 
           border="0" style="padding-left:50px;">
    <tr>
        <th>Employee</th>
        <th>Position</th>
        <th>Phone</th>
        <th>Email</th>
    </tr>
@foreach (var item in Model) {
    <tr>
        <td>@item.Name</td>
        <td>@item.Position</td>
        <td>@item.Phone</td>
        <td>@item.Email</td>
    </tr>
}
</table>

此 View 使用由控制器过滤的员工列表生成 HTML 表格。此 HTML 代码作为 AJAX 响应发送回客户端。下图显示了在 Firebug 中跟踪的 Home/CompanyEmployees 调用的 AJAX 响应。

Expandabe-DataTable-MVC/expandableDataTables-details.png

视图

View 页面用于显示表格并包含客户端 JavaScript。此示例使用了三个 View 页面:

  1. 静态 View 页面,其中公司表格是静态 HTML 表格。
  2. 服务器端 View,其中公司表格是在服务器端生成的。
  3. AJAX View 页面,其中公司表格是通过 AJAX 调用加载的。

然而,所有这些 View 都使用相同的客户端逻辑来显示详细信息(员工列表)——一个 AJAX 调用被发送到上面描述的控制器 Action /Home/CompanyEmployees,并且控制器返回一个 HTML 响应,该响应被注入到表格中。

所有三个 View 都使用相同的布局页面,其中包含它们所需的所有通用元素。该布局页面显示在以下列表中。

<html>
    <head>
        <title>Implementation of Master-Details tables using a JQuery DataTables plugin
        </title>
        <link href="@Url.Content("~/Content/dataTables/demo_page.css")"
                                 rel="stylesheet" type="text/css" />
        <link href="@Url.Content("~/Content/dataTables/demo_table.css")" 
                                 rel="stylesheet" type="text/css" />
        <link href="@Url.Content("~/Content/dataTables/demo_table_jui.css")" 
                                 rel="stylesheet" type="text/css" />
        <link href="@Url.Content("~/Content/themes/base/jquery-ui.css")" 
                                 rel="stylesheet" type="text/css" media="all" />
        <link href="@Url.Content("~/Content/themes/smoothness/jquery-ui-1.7.2.custom.css")"
                                 rel="stylesheet" type="text/css" media="all" />
        <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript">
        </script>
        <script src="@Url.Content("~/Scripts/jquery.dataTables.min.js")" 
                                  type="text/javascript"></script>
        <script language="javascript" type="text/javascript">
        $(document).ready(function () {
            @RenderSection("onReady", required: false)
            });
        </script>
    </head>
    <body id="dt_example">
        <div id="container">
            <a href="/Home/Index">Static table</a> | 
            <a href="/Home/ServerSide">Server-side generated table</a> | 
            <a href="/Home/Ajax">Ajax-loaded table</a>
            @RenderBody()
        </div>
    </body>
</html> 

布局页面包括所有必要的 CSS/JavaScript 文件、定义的页面通用结构以及将在特定页面上定义的两个左侧部分。这些部分是:

  1. onReady 部分,用于包含将在文档就绪事件上执行的自定义 JavaScript。
  2. Body 部分,用于放置每个页面的 HTML 代码。

在以下示例中,将描述三种不同的可展开表格使用场景。

静态 View

在静态 View 页面中,主公司表格被生成为一个普通的 HTML 表格。静态表格的 HTML 源代码显示在以下列表中。

<div id="demo">
    <table id="companies" class="display">
        <thead>
            <tr>
                <th></th>
                <th>Company name</th>
                <th>Address</th>
                <th>Town</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td><img src="/Content/images/details_open.png" 
                    rel="0" alt="expand/collapse"></td>
                <td>Emkay Entertainments</td>
                <td>Nobel House, Regent Centre</td>
                <td>Lothian</td>
            </tr>
            <tr>
                <td><img src="/Content/images/details_open.png" 
                   rel="0" alt="expand/collapse"></td>
                <td>The Empire</td>
                <td>Milton Keynes Leisure Plaza</td>
                <td>Buckinghamshire</td>
            </tr>
            ...
        </tbody>
    </table>
</div>

此代码放置在 View 的 body 部分。在第一列中,放置了一个 rel 属性等于公司 ID 的图像。当用户点击此图像时,将向服务器端控制器发送一个 AJAX 调用,并以展开行的形式显示返回的 HTML。执行此操作的 JavaScript 代码位于 View 的 head 部分,如下所示。

var oTable;
$('#companies tbody td img').click(function () {
    var nTr = this.parentNode.parentNode;
    if (this.src.match('details_close')) {
        /* This row is already open - close it */
        this.src = "/Content/images/details_open.png";
        oTable.fnClose(nTr);
    }
    else {
        /* Open this row */
        this.src = "/Content/images/details_close.png";
        var companyid = $(this).attr("rel");
        $.get("CompanyEmployees?CompanyID=" + companyid, function (employees) {
            oTable.fnOpen(nTr, employees, 'details');
        });
    }
});

在上面的 JavaScript 中,为第一列中的每个图像添加了一个点击处理程序。当用户点击图像时,此代码会检查图像是否处于关闭状态。如果图像处于关闭状态,则会向服务器发送一个 AJAX 调用;返回的 HTML 使用 fnOpen 函数注入,并且图像被更改;否则,将使用 fnClose 函数关闭详细信息行。oTable 变量是对以下代码中初始化的 DataTable 对象的引用。

您需要添加 DataTables 初始化代码来初始化公司表格。这是使用 fnOpenfnClose 函数所必需的。DataTable 初始化代码的示例显示如下。唯一的特定设置是使用 jQueryUI 进行样式设置,并使第一列(包含图像)不可排序且不可搜索。

/* Initialize table and make first column non-sortable*/
oTable = $('#companies').dataTable({  "bJQueryUI": true,
                                        "aoColumns": [
                                        {   "bSortable": false,
                                            "bSearchable": false },
                                        null,
                                        null,
                                        null
                                ]
    });

服务器端生成的 View

在本文的第二个示例中,company 表格是在服务器端动态生成的。View 页面的主体显示在以下列表中。

<div id="demo">
    <table id="companies" class="display">
        <thead>
            <tr>
                <th></th>
                <th>Company Name</th>
                <th>Address</th>
                <th>Town</th>
            </tr>
        </thead>
    @foreach (var item in Model) {
        <tr>
            <td><img src="/Content/images/details_open.png" 
                alt="expand/collapse" rel="@item.ID"/></td>
            <td>@item.Name</td>
            <td>@item.Address</td>
            <td>@item.Town</td>
        </tr>
    }
    </table>
</div>

如您所见,没有太大的区别——View 动态生成与上一个示例相同的结构。因此,onready 部分与上一个 View 相同。

请注意,此 View 使用公司列表来生成表格,因此 Action 方法应传递应用程序中所有公司的列表。用于此示例以将模型传递给 View 的 action 方法的示例显示在以下列表中。

public class HomeController : Controller
{
    public ActionResult ServerSide()
    {
        return View(DataRepository.GetCompanies());
    }
}

AJAX View 页面

本文的最后一个示例是一个通过 AJAX 调用动态加载公司的 View 页面。这是前一种情况的性能改进,如果您预计表格中的项目数量会很多,并且一次加载所有项目可能会导致性能问题,那么应该使用此方法。View 页面的 body 仅表示表格的结构,如下所示。

<div id="demo">
    <table id="companies" class="display">
        <thead>
            <tr>
                <th></th>
                <th>Company name</th>
                <th>Address</th>
                <th>Town</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
</div>

包含初始化代码的 onready 部分稍作修改,如下所示。

var oTable;
$('#companies tbody td img').live('click', function () {
    var nTr = this.parentNode.parentNode;
    if (this.src.match('details_close')) {
        /* This row is already open - close it */
        this.src = "/Content/images/details_open.png";
        oTable.fnClose(nTr);
    }
    else {
        /* Open this row */
        this.src = "/Content/images/details_close.png";
        var companyid = $(this).attr("rel");
        $.get("CompanyEmployees?CompanyID=" + companyid, function (employees) {
            oTable.fnOpen(nTr, employees, 'details');
        });
    }
});

区别在于使用了 live 函数而不是直接的点击处理程序,因为事件应该添加到动态添加到每次重新加载的元素上。如果没有 live 函数,您将需要在每次从服务器端重新加载表格时应用点击处理程序。

DataTables 初始化调用也已更改——您需要将 bServerSide 参数设置为 true,表示公司将从 AJAX 源加载,并且服务器端页面将提供表格数据。初始化调用显示在以下列表中。

/* Initialize table and make first column non-sortable*/
oTable = $('#companies').dataTable({
                            "bProcessing": true,
                            "bServerSide": true,
                            "sAjaxSource": 'AjaxHandler',
                            "bJQueryUI": true,
                            "aoColumns": [
                      { "bSortable": false,
                                    "bSearchable": false,
                        "fnRender": function (oObj) {
                              return '/Content/images/details_open.png" ' + 
                                     'alt="expand/collapse" rel="' + 
                                     oObj.aData[0] + '"/>';
                                      } 
                       },
                        null,
                        null,
                        null
                                ]
});

请注意,在第一列中,使用 fnRender 函数生成图像并将公司 ID 放在展开/折叠图像的 rel 属性中。在 ASP.NET MVC 中设置服务器端处理超出了本文的范围,因此我建议您查看 将 DataTables 集成到 ASP.NET MVC 应用程序中 这篇文章(这是该系列文章的第一篇)。

结论

本文介绍了如何使用 jQuery DataTables 插件和 ASP.NET MVC 创建可展开的主详细信息表。这是我撰写的关于将 jQuery DataTables 插件集成到 ASP.NET MVC 应用程序中的系列文章中的第四篇。其他可能让您感兴趣的文章有:

  1. 将 jQuery DataTables 插件集成到 ASP.NET MVC 应用程序中,描述了如何在 ASP.NET 中实现服务器端处理,以便使用 jQuery DataTables 插件实现高性能的服务器端表格。
  2. 在 ASP.NET MVC 中创建可编辑的 DataTables,描述了如何在数据表中实现添加、编辑和删除功能。
  3. 在 ASP.NET MVC 中创建表格之间的父子关系 - 一篇与本文类似的论文,解释了如何以父子关系连接两个表格。

该系列文章可能会让您能够使用 jQuery DataTables 插件在 ASP.NET MVC 中创建功能齐全且有效的表格。

历史

  • 2011 年 5 月 1 日:初始版本
© . All rights reserved.