` 部分配置。
<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 - 代码之家 
  ©