Datatable Scroller (Server Side) Part:3

本文介绍如何在datatable.js v1.10中实现滚动加载,并通过ajax显示服务器端数据。通过设置scrollY和scroller属性,可以提升用户体验并优化大数据量场景下的性能。

假设您有数百万条记录,你不需要分页,然后您可以使用数据表滚动条。 我认为滚动条比分页更好的用户体验,因为几乎你可以一次查看整个表。 今天我将展示如何在datatable.js v1.10中实现scroller并通过ajax显示服务器端数据。 要在datatable中实现scroller,您必须添加js库或扩展dataTables.scroller.js。 您可以直接从这里下载。

<script type="text/javascript" language="javascript" src="js/dataTables.scroller.js"></script>

Datatable将根据您的视图(如表格)获取一些额外的记录,以实现平滑渲染。 它将根据滚动位置向服务器请求新记录。
让我初始化数据表:

$(document).ready(function() {
   var dataTable =  $('#employee-grid').DataTable( {
	serverSide: true,
	ajax:{
			url :"employee-grid-data.php", // json datasource
			type: "post",  // method  , by default get
			error: function(){  // error handling
				$(".employee-grid-error").html("");
				$("#employee-grid").append('<tbody class="employee-grid-error"><tr><th colspan="3">No data found in the server</th></tr></tbody>');
				$("#employee-grid_processing").css("display","none");
			}
		},
	dom: "frtiS",
	scrollY: 200,
	deferRender: true,
	scroller: {
	    loadingIndicator: true
	}
    } );
} );

这里有三个选项很重要::

  • dom: “frtiS”表格的Dom元素,这里“f”表示过滤,“r”用于处理,“t”用于表,“i”用于表的信息,“S”用于表滚动
  • scrollY: 200这是网格高度。
  • deferRender: true仅当您滚动时,它才会从服务器渲染大量数据(表行)中的块数据。 它将显着提高性能。

根据“scrollY”中提到的表高度,滚动条将触发带有起始行号和限制条数的ajax。假设您的数据库中有200条记录。在只显示5行的表视图中,默认情况下,scroller将获取10条记录以进行平滑渲染。 在下一个滚动触发器中,ajax将请求更多10条记录。此长度按数据表滚动条本身的“scrollY”计算。
datatable scroller viewport

在我的第一篇文章中,我讨论了服务器端处理。 根据您的搜索参数,顺序和限制条数,这是服务器端代码:

//Database connections
$servername = "localhost";
$username = "root";
$password = "password";
$dbname = "test";
$conn = mysqli_connect($servername, $username, $password, $dbname) or die("Connection failed: ".mysqli_connect_error());

// storing  request (ie, get/post) global array to a variable
$requestData = $_REQUEST;

$columns = array(
	// datatable column index  => database column name
	0 => 'employee_name',
	1 => 'employee_salary',
	2 => 'employee_age'
);

// getting total number records without any search
$sql = "SELECT employee_name, employee_salary, employee_age ";
$sql. = " FROM employee";
$query = mysqli_query($conn, $sql) or die("employee-grid-data.php: get employees");
$totalData = mysqli_num_rows($query);
$totalFiltered = $totalData; // when there is no search parameter then total number rows = total number filtered rows.

$sql = "SELECT employee_name, employee_salary, employee_age ";
$sql. = " FROM employee WHERE 1=1";
if (!empty($requestData['search']['value'])) { // if there is a search parameter, $requestData['search']['value'] contains search parameter
	$sql. = " AND ( employee_name LIKE '".$requestData['search']['value'].
	"%' ";
	$sql. = " OR employee_salary LIKE '".$requestData['search']['value'].
	"%' ";
	$sql. = " OR employee_age LIKE '".$requestData['search']['value'].
	"%' )";
}
$query = mysqli_query($conn, $sql) or die("employee-grid-data.php: get employees");
$totalFiltered = mysqli_num_rows($query); // when there is a search parameter then we have to modify total number filtered rows as per search result.
$sql. = " ORDER BY ".$columns[$requestData['order'][0]['column']].
"   ".$requestData['order'][0]['dir'].
"   LIMIT ".$requestData['start'].
" ,".$requestData['length'].
"   ";
/* $requestData['order'][0]['column'] contains colmun index, $requestData['order'][0]['dir'] contains order such as asc/desc , $requestData['start'] contains start row number ,$requestData['length'] contains limit length. */
$query = mysqli_query($conn, $sql) or die("employee-grid-data.php: get employees");

$data = array();
while ($row = mysqli_fetch_array($query)) { // preparing an array
	$nestedData = array();

	$nestedData[] = $row["employee_name"];
	$nestedData[] = $row["employee_salary"];
	$nestedData[] = $row["employee_age"];

	$data[] = $nestedData;
}

$json_data = array(
	"draw" => intval($requestData['draw']), // for every request/draw by clientside , they send a number as a parameter, when they recieve a response/data they first check the draw number, so we are sending same number in draw.
	"recordsTotal" => intval($totalData), // total number of records
	"recordsFiltered" => intval($totalFiltered), // total number of records after searching, if there is no searching then totalFiltered = totalData
	"data" => $data // total data array
);

echo json_encode($json_data); // send data as json format

Datatable实现滚动加载

一般来说datatable都是默认使用“paging”:true,这一属性来控制全局的表格翻页。效果大致如下图:
在这里插入图片描述

但是有时候你也会用到滚动条式的翻页方式,类似下面这种:
在这里插入图片描述
这时,你只需要加上以下两个属性即可。

    scrollY: 1000,
    scroller: {
          loadingIndicator: true
            },

其中scrollY表示:定义一个高度,当列表内容超过这个高度时,显示垂直滚动条。

scroller表示开启滚动翻页。

下面是官网论坛上给出的一个demo

    $(document).ready(function() {
       var dataTable =  $('#employee-grid').DataTable( {
        serverSide: true,
        ajax:{
                url :"employee-grid-data.php", // json datasource
                type: "post",  // method  , by default get
                error: function(){  // error handling
                    $(".employee-grid-error").html("");
                    $("#employee-grid").append('<tbody class="employee-grid-error"><tr><th colspan="3">No data found in the server</th></tr></tbody>');
                    $("#employee-grid_processing").css("display","none");
                }
            },
        dom: "frtiS",
        scrollY: 200,
        deferRender: true,
        scroller: {
            loadingIndicator: true
        }
        } );
    } );
<think>我们有两个问题需要解决: 1. 前端使用DataTables的AJAX请求,其中包含分页参数(如draw, start, length等) 2. 后端(C#)根据这些参数对DataTable进行分页处理,并返回符合DataTables要求格式的JSON数据 根据引用[4],前端DataTables在服务器端分页模式下会发送如下关键参数: - draw: 请求的标识符,每次请求递增,服务器需要原样返回 - start: 起始记录索引(从0开始) - length: 每页记录数(即pageSize) 引用[2]和[3]提供了C#中对DataTable进行分页的方法,我们可以参考这些方法。 步骤: 1. 前端配置DataTables使用服务器端分页(serverSide: true),并指定AJAX请求的URL。 2. 后端(C#)创建一个控制器方法,接收draw, start, length等参数。 3. 在控制器方法中,获取总记录数(recordsTotal)和过滤后的总记录数(recordsFiltered,如果没有任何过滤条件,则与recordsTotal相同)。 4. 对原始DataTable进行分页处理(使用引用[2]或[3]的方法)。 5. 将分页后的数据转换为JSON格式,并按照DataTables要求的格式返回(包含draw, recordsTotal, recordsFiltered, data)。 注意:在实际应用中,我们通常不会在服务器端一次性加载所有数据到DataTable(因为数据量可能很大),而是通过数据库查询进行分页。但用户要求是在C#中对DataTable进行分页,所以我们假设已经将数据加载到内存的DataTable中。 下面我们分别实现: 前端示例(DataTables配置): ```javascript $(document).ready(function() { $(&#39;#example&#39;).DataTable({ "processing": true, "serverSide": true, "ajax": { "url": "/YourController/GetPagedData", "type": "POST" }, "columns": [ { "data": "id" }, { "data": "name" }, { "data": "created" } // 其他列... ] }); }); ``` 后端C#代码(控制器方法): 首先,我们需要一个方法对DataTable进行分页(参考引用[2]和[3]): ```csharp public static DataTable GetPagedTable(DataTable dt, int start, int length) { // 如果start超过总行数,则返回空表 if (start >= dt.Rows.Count) return dt.Clone(); // 返回结构相同但无数据的表 // 计算实际要取的行数(避免超出范围) int end = start + length; if (end > dt.Rows.Count) end = dt.Rows.Count; // 创建分页后的表 DataTable pagedDt = dt.Clone(); for (int i = start; i < end; i++) { pagedDt.ImportRow(dt.Rows[i]); } return pagedDt; } ``` 然后,在控制器中处理请求: ```csharp [HttpPost] public IActionResult GetPagedData() { // 获取DataTables请求参数 var draw = Request.Form["draw"].FirstOrDefault(); var start = Request.Form["start"].FirstOrDefault(); var length = Request.Form["length"].FirstOrDefault(); // 转换为整数 int pageStart = string.IsNullOrEmpty(start) ? 0 : Convert.ToInt32(start); int pageLength = string.IsNullOrEmpty(length) ? 10 : Convert.ToInt32(length); // 假设我们有一个方法获取原始DataTable(这里仅作示例) DataTable dt = GetYourDataTable(); // 从数据库或其他来源获取数据 // 分页处理 DataTable pagedData = GetPagedTable(dt, pageStart, pageLength); // 将DataTable转换为JSON格式的列表(假设每行是一个字典,或者可以转换为对象列表) var data = new List<Dictionary<string, object>>(); foreach (DataRow row in pagedData.Rows) { var dict = new Dictionary<string, object>(); foreach (DataColumn col in pagedData.Columns) { dict[col.ColumnName] = row[col]; } data.Add(dict); } // 返回DataTables要求的JSON格式 return Json(new { draw = draw, // 必须原样返回 recordsTotal = dt.Rows.Count, // 总记录数(无过滤) recordsFiltered = dt.Rows.Count, // 这里假设没有过滤,所以与总记录数相同 data = data }); } ``` 注意:以上代码假设没有进行过滤(搜索、排序等)。如果需要进行过滤和排序,则需要在分页前对DataTable进行过滤和排序,并计算过滤后的记录数(recordsFiltered)。但根据引用[4],前端会传递搜索和排序参数,因此我们需要处理这些参数。由于问题主要关注分页,这里只实现了分页,过滤和排序可以后续添加。 如果数据量很大,建议不要将全部数据加载到内存的DataTable中,而应该使用数据库分页(如SQL的OFFSET FETCH)。但用户要求是对DataTable进行分页,所以这里使用内存分页。 总结步骤: 1. 获取请求中的draw, start, length参数。 2. 获取原始数据(DataTable)。 3. 计算分页后的数据(使用GetPagedTable方法)。 4. 将分页后的数据转换为JSON格式。 5. 返回JSON结果,包括draw, recordsTotal, recordsFiltered, data。 注意:recordsFiltered在无过滤时等于recordsTotal,有过滤时等于过滤后的记录总数(在分页前进行过滤)。 相关问题: 1. 如何在DataTables服务器端分页中实现搜索过滤? 2. 如何对DataTable进行动态排序? 3. 如何优化大数据量下的DataTable分页性能? 4. 如何将数据库查询与DataTables服务器端分页结合?</think>在 C# 中实现 DataTable 的后端分页功能,需要结合前端 DataTables 的 AJAX 请求参数和后端数据处理逻辑。以下是完整实现方案: ### 1. 前端配置(DataTables AJAX) ```javascript $(&#39;#dataTable&#39;).DataTable({ processing: true, serverSide: true, // 启用服务器端分页 ajax: { url: &#39;/api/GetPagedData&#39;, type: &#39;POST&#39;, data: function(d) { // 添加额外参数(如搜索条件) d.customFilter = $("#searchInput").val(); } }, columns: [ { data: &#39;id&#39; }, { data: &#39;name&#39; }, { data: &#39;createdDate&#39; } ], lengthMenu: [10, 25, 50, 100], // 分页大小选项 pageLength: 10 // 默认分页大小 }); ``` ### 2. 后端 C# 分页处理(核心代码) ```csharp // 分页方法(参考引用[2][3]优化版) public DataTable GetPagedData(DataTable dt, int start, int length) { DataTable pagedDt = dt.Clone(); // 计算实际分页范围 int pageSize = length; int pageStart = Math.Min(start, dt.Rows.Count); int pageEnd = Math.Min(start + length, dt.Rows.Count); // 分页处理 for (int i = pageStart; i < pageEnd; i++) { pagedDt.ImportRow(dt.Rows[i]); } return pagedDt; } // API 控制器方法 [HttpPost] public IActionResult GetPagedData() { // 获取 DataTables 请求参数 var draw = Request.Form["draw"].FirstOrDefault(); int start = Convert.ToInt32(Request.Form["start"].FirstOrDefault()); int length = Convert.ToInt32(Request.Form["length"].FirstOrDefault()); // 获取原始数据(从数据库或缓存) DataTable fullData = GetSourceData(); // 应用分页(参考引用[2][3]) DataTable pagedData = GetPagedData(fullData, start, length); // 转换为 JSON 格式 var result = new { draw = Convert.ToInt32(draw), recordsTotal = fullData.Rows.Count, recordsFiltered = fullData.Rows.Count, // 无过滤时等于总记录数 data = pagedData }; return Json(result); } ``` ### 3. 关键参数说明(参考引用[4]) | **参数** | **来源** | **作用** | **示例值** | |-------------|---------------|-----------------------------------|--------------| | `draw` | 前端自动生成 | 请求/响应匹配标识(必须原样返回) | 3 | | `start` | 前端分页控件 | 当前页起始记录索引 | 20 | | `length` | 前端分页控件 | 每页显示记录数 | 10 | | `search[value]` | 搜索框输入 | 全局搜索关键词 | "2023" | ### 4. 高级功能实现 #### 4.1 搜索过滤(结合引用[4]) ```csharp // 在分页前添加过滤逻辑 DataTable filteredData = fullData.Clone(); string searchValue = Request.Form["search[value]"].FirstOrDefault().ToLower(); if (!string.IsNullOrEmpty(searchValue)) { foreach (DataRow row in fullData.Rows) { if (row["name"].ToString().ToLower().Contains(searchValue) || row["id"].ToString().Contains(searchValue)) { filteredData.ImportRow(row); } } } else { filteredData = fullData.Copy(); } // 更新分页和返回参数 recordsFiltered = filteredData.Rows.Count; DataTable pagedData = GetPagedData(filteredData, start, length); ``` #### 4.2 排序处理(参考引用[4]) ```csharp // 获取排序参数 int sortColIndex = Convert.ToInt32(Request.Form["order[0][column]"].FirstOrDefault()); string sortDir = Request.Form["order[0][dir]"].FirstOrDefault(); string sortColName = Request.Form[$"columns[{sortColIndex}][data]"].FirstOrDefault(); // 应用排序 filteredData.DefaultView.Sort = $"{sortColName} {sortDir}"; filteredData = filteredData.DefaultView.ToTable(); ``` ### 5. 性能优化建议 1. **数据库分页优先**: 对于大数据集,应在数据库层面分页(使用 `OFFSET-FETCH`)而非内存分页[^1] ```sql SELECT * FROM Table ORDER BY Id OFFSET @start ROWS FETCH NEXT @length ROWS ONLY ``` 2. **分页缓存策略**: ```csharp // 使用 MemoryCache 减少数据加载 var cacheKey = $"DataTableCache_{DateTime.Today:yyyyMMdd}"; if (!_memoryCache.TryGetValue(cacheKey, out DataTable fullData)) { fullData = GetSourceData(); _memoryCache.Set(cacheKey, fullData, TimeSpan.FromMinutes(30)); } ``` 3. **流式处理大文件**: 当处理 CSV/Excel 等大文件时: ```csharp using var reader = new StreamReader(filePath); while (!reader.EndOfStream && pagedDt.Rows.Count < length) { if (currentIndex >= start) { // 添加行到分页表 } currentIndex++; } ``` ### 注意事项 1. `draw` 参数必须原样返回,否则 DataTables 会报错 2. `recordsFiltered` 必须包含过滤后的总记录数(非当前页数量) 3. 当数据超过 10,000 行时,内存分页性能会显著下降,建议使用数据库分页[^1] 4. 使用 `DataTable.ImportRow()` 比逐列复制性能更优[^2]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值