` 部分配置。
<table id="table"
data-unique-id="Code"
data-sort-name="Code"
data-sort-order="asc"
data-classes="table table-condensed table-hover table-striped"
data-toggle="table"
data-side-pagination="server"
data-url="Load"
data-pagination="true"
data-search="true"
data-show-refresh="true"
data-toolbar="#toolbar"
data-page-size="20"
data-page-list="[5,10,20,50,100,All]">
<thead>
<tr>
<th data-field="ISO2" data-sortable="false"
data-halign="center" data-align="center"
data-formatter="flagFormatter">Flag</th>
<th data-field="Code" data-sortable="true"
data-halign="center" data-align="center">Code</th>
<th data-field="ISO3" data-sortable="true">ISO 3</th>
<th data-field="Name" data-sortable="true">Name</th>
</tr>
</thead>
</table>
可以通过属性配置列设置,例如对齐方式、宽度、日期和数字格式。`data-unique-id="Code"` 设置主键列。在渲染过程中,每一行表格都会获得一个 `data-uniqueid` 属性,其中包含其键值。
<tr data-uniqueid="4" data-index="0">
...
</tr>
此键属性对于 CRUD(创建、检索、更新和删除)操作至关重要。使用 jQuery,您可以轻松地从行中检索键值。如果 `data-unique-id` 为空或未设置,表格仍会渲染。`data-sort-name` 和 `data-sort-order` 属性用于设置初始排序列和排序顺序。这些值在数据请求期间会传递给控制器。
自定义单元格渲染
Bootstrap Table 通过 `data-formatter` 属性支持自定义单元格渲染。如果标准配置选项不够用,这提供了最大的灵活性。
function flagFormatter(value, row) {
return '<img src="/images/flags/' + value.toLowerCase() + '.png" >';
}
`value` 参数是列值,`row` 参数包含所有行值。
服务器端分页、排序和过滤
只需几个属性即可设置服务器端分页、排序和过滤。
data-side-pagination="server"
data-url="Load"
data-pagination="true"
data-search="true"
`data-url="Load"` 属性指定控制器类有一个 `public Load` 函数。`Load` 函数处理输入参数并返回一个 JSON 文档。
[HttpGet]
public virtual ActionResult Load(String sort, String order, String search, Int32 limit, Int32 offset)
{
// Get entity fieldnames
List<String> columnNames = typeof(Country).GetProperties(BindingFlags.Public |
BindingFlags.Instance).Select(p => p.Name).ToList();
// Create a separate list for searchable field names
List<String> searchFields = new List<String>(columnNames);
// Exclude field Iso2 for filtering
searchFields.Remove("ISO2");
// Perform filtering
IQueryable items = SearchItems(countries.AsQueryable(), search, searchFields);
// Sort the filtered items and apply paging
return Content(ItemsToJson
(items, columnNames, sort, order, limit, offset), "application/json");
}
输入参数
`sort`: 排序列名
`order`: 排序方向,asc 或 desc
`search`: 用户输入的搜索参数
`limit`: 每页记录数
`offset`: 在获取数据页之前要跳过的记录数
JSON 输出
`total`: 过滤后可用的记录数
`rows`: 包含国家记录的数组
数组的容量等于或小于 `limit` 页大小。
"total": 193,
"rows": [
{
"Code": 4,
"ISO2": "AF",
"ISO3": "AFG",
"Name": "Afghanistan"
},
{
"Code": 8,
"ISO2": "AL",
"ISO3": "ALB",
"Name": "Albania"
},
...
排除过滤字段
ISO2 字段用于渲染国旗图像,代码本身对用户不可见。在此 GUI 设计中,搜索参数仅适用于可见数据。这意味着 `ISO2` 属性必须从可搜索字段中排除。
// Get entity fieldnames
List<String> columnNames = typeof(Country).GetProperties(BindingFlags.Public |
BindingFlags.Instance).Select(p => p.Name).ToList();
// Create a separate list for searchable field names
List<String> searchFields = new List<String>(columnNames);
// Exclude field Iso2 for filtering
searchFields.Remove("ISO2");
// Perform filtering
IQueryable items = SearchItems(countries.AsQueryable(), search, searchFields);
使用 Dynamic Linq 进行可重用过滤
Linq 是一项了不起的创新。它提供了对可枚举集合执行查询的能力。与 SQL 不同,Linq 具有编译时语法检查。这在大多数情况下都很有帮助。它在编译时而不是运行时检测错误。如果我想创建一个可重用的过滤方法,编译时语法会成为障碍。不同的实体具有不同的字段名,那么如何将不同的字段名传递给一个可重用的方法?运行时解析而不是编译时解析是解决方案。Dynamic Linq Core 包正是做到了这一点。它能与 Linq 无缝集成,并为 `where` 子句、排序和其他操作提供额外的重载函数。Dynamic Linq 用于 `SearchItems` 以在运行时创建 `searchExpression`。
protected virtual IQueryable SearchItems(IQueryable items, String search, List<String> columnNames)
{
// Apply filtering to all visible column names
if (search != null && search.Length > 0)
{
StringBuilder sb = new StringBuilder();
// create dynamic Linq expression
foreach (String fieldName in columnNames)
sb.AppendFormat("({0} == null ?
false : {0}.ToString().IndexOf(@0, @1) >=0) or {1}",
fieldName, Environment.NewLine);
String searchExpression = sb.ToString();
// remove last "or" occurrence
searchExpression = searchExpression.Substring
(0, searchExpression.LastIndexOf("or"));
// Apply filtering,
items = items.Where
(searchExpression, search, StringComparison.OrdinalIgnoreCase);
}
return items;
}
由 `SearchItems` 生成的 `Country searchExpression`
(Code == null ? false : Code.ToString().IndexOf(@0, @1) >=0) or
(ISO3 == null ? false : ISO3.ToString().IndexOf(@0, @1) >=0) or
(Name == null ? false : Name.ToString().IndexOf(@0, @1) >=0)
请注意,`ISO2` 字段不在 `searchExpression` 中,这符合预期。在本演示中,`SearchItems` 的实现相当直接。如果需要更复杂的过滤,可以覆盖 `SearchItems` 以满足新需求。
生成 JSON 文档
`ItemsToJson` 函数创建 Bootstrap Table 所使用的 JSON 文档。
protected String ItemsToJson(IQueryable items, List<String> columnNames,
String sort, String order, Int32 limit, Int32 offset)
{
try
{
// where clause is set, count total records
Int32 count = items.Count();
// Skip requires sorting, so make sure there is always sorting
String sortExpression = "";
if (sort != null && sort.Length > 0)
sortExpression += String.Format("{0} {1}", sort, order);
// show all records if limit is not set
if (limit == 0)
limit = count;
// Prepare json structure
var result = new
{
total = count,
rows = items.OrderBy(sortExpression).Skip(offset).Take(limit).Select
("new (" + String.Join(",", columnNames) + ")")
};
return JsonConvert.SerializeObject(result, Formatting.None, new JsonSerializerSettings()
{ MetadataPropertyHandling = MetadataPropertyHandling.Ignore });
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
输入参数
`items`: 未排序、已过滤的实体集
`columnNames`: 包含在 JSON 文档中的字段
`sort`: 排序列名
`order`: 排序方向,asc 或 desc
`search`: 用户输入的搜索参数
`limit`: 每页记录数
`offset`: 在获取数据页之前要跳过的记录数
`columnNames` 变量限制了在 JSON 文档中公开的属性数量。如果您不想出于性能或安全原因显示所有可用的实体属性,这会很有用。分页需要排序,而排序由 Dynamic Linq 提供。分页是通过标准的 Linq `Skip` 和 `Take` 函数实现的。`Formatting.None` 选项会减小 JSON 文档的大小,提高性能,但使其更难阅读。我只在调试时使用 `Formatting.Indented` 选项。
高亮显示选定行
Bootstrap Table 具有多种行选择选项。它甚至可以记住上一页中选定的行,我认为这非常酷。您可以阅读文档了解如何实现这一点。在本演示应用程序中,我使用了 jQuery 和 CSS。在论坛上,关于这个话题有很多问题,所以让我们在这里多加关注。首先,我修改了 CSS 样式,使选定行更加醒目。我可以覆盖 Bootstrap 的 CSS 文件,但在更新时所有工作都会丢失。在 `site.css` 文件中设置新样式可以避免这种风险。
/* selected row style */
.table-striped tbody .highlight td {
background-color: #b5b5b5;
}
下一步是将 Bootstrap Table 的行点击事件附加到 `highLightRow` 函数。
// register row-click event
$('#table').on('click-row.bs.table', function ($element, row, $tr) {
highLightRow($tr);
});
`highLightRow` 函数将高亮 CSS 类应用于选定的行,并从所有其他行中移除 CSS 类。这确保了一次只有一个行被选中。
function highLightRow($tr) {
$tr.addClass('highlight').siblings().removeClass('highlight');
}
自定义工具栏
使用 Bootstrap Table,您可以使用纯 HTML 自定义工具栏。在某些其他包中,您需要了解很多关于网格的细节及其 API。Bootstrap Table 非常简单。在具有 `id` 的 `div` 中创建您的工具栏按钮和其他控件。将此 `id` 分配给 `data-toolbar` 属性,即可完成!
<div id="toolbar">
<button id="btninfo" title="Show selected row info" class="btn btn-default" type="button">
<i class="glyphicon glyphicon-info-sign"></i> row info</button>
</div>>
<table id="table"
...
data-toolbar="#toolbar"
...
附加 Load 参数
有时,GUI 要求将附加参数发送到控制器。这只需要几个简单的步骤。首先,使用 `data-query-params` 属性设置注入额外参数的函数。
<table id="table"
...
data-query-params="extraServerParams"
...
在本演示中,附加参数是固定的,通常您会使用输入控件的值。
function extraServerParams(params) {
params.ExtraParam = 2;
return params;
}
最后一步是修改控制器上的 Load 函数以处理附加参数。
结论
在许多应用程序中,在网格中显示数据是一项重要需求。Bootstrap Table 在此方面做得非常出色。它易于设置和使用,并且与 Bootstrap 和 Dot Net Core 配合良好。Dynamic Linq 使解决方案具有高度的可重用性。我添加了演示应用程序,以便您可以进行尝试。如果您有任何意见或问题,请告诉我。
延伸阅读
.NET Core Datagrid - CodeProject - 代码之家
© . All rights reserved.