编译自: https://opensource.com/article/18/9/using-grails-jquery-and-datatables
作者: Chris Hermansen
译者: jrg

本文先容如何构建一个基于 Grails 的数据浏览器来可视化繁芜的表格数据。
我是 Grails 的虔诚粉丝。当然,我紧张是热衷于利用命令行工具来探索和剖析数据的数据从业职员。数据从业职员常常须要查看数据,这也意味着他们常日拥有精良的数据浏览器。利用 Grails、 jQuery ,以及 DataTables jQuery 插件 ,我们可以制作出非常友好的表格数据浏览器。
DataTables 网站 供应了许多“食谱式”的教程文档,展示了如何组合一些精良的示例运用程序,这些程序包含了完成一些非常俊秀的东西所必要的 JavaScript、HTML,以及偶尔涌现的 PHP 。但对付那些甘心利用 Grails 作为后真个人来说,有必要进行一些解释示教。此外,样本程序中利用的数据是一个虚构公司的员工的单个平面表格数据,因此处理这些繁芜的表关系可以作为读者的一个练习项目。
本文中,我们将创建具有略微繁芜的数据构造和 DataTables 浏览器的 Grails 运用程序。我们将先容 Grails 标准,它是 Groovy 式的 Java Hibernate 标准。我已将代码托管在 GitHub 上方便大家访问,因此本文紧张是对代码细节的解读。
首先,你须要配置 Java、Groovy、Grails 的利用环境。对付 Grails,我方向于利用终端窗口和 Vim ,本文也利用它们。为得到当代的 Java 环境,建议下载并安装 Linux 发行版供应的 Open Java Development Kit (OpenJDK)(该当是 Java 8、9、10 或 11 之一,撰写本文时,我正在利用 Java 8)。从我的角度来看,获取最新的 Groovy 和 Grails 的最佳方法是利用 SDKMAN! 。
从未考试测验过 Grails 的读者可能须要做一些背景资料阅读。作为初学者,推举文章 创建你的第一个 Grails 运用程序 。
获取员工信息浏览器运用程序正如上文所提,我将本文中员工信息浏览器的源代码托管在 GitHub 上。进一步讲,运用程序 embrow 是在 Linux 终端中用如下命令构建的:
cd Projects
grails create-app com.nuevaconsulting.embrow
域类和单元测试创建如下:
grails create-domain-class com.nuevaconsulting.embrow.Position
grails create-domain-class com.nuevaconsulting.embrow.Office
grails create-domain-class com.nuevaconsulting.embrow.Employeecd embrowgrails createdomaincom.grails createdomaincom.grails createdomaincom.
这种办法构建的域类没有属性,因此必须按如下办法编辑它们:
Position 域类:
package com.nuevaconsulting.embrow
class Position {
String name
int starting
static constraints = {
name nullable: false, blank: false
starting nullable: false
}
}com.Stringint startingstatic constraintsnullableblankstarting nullable
Office 域类:
package com.nuevaconsulting.embrow
class Office {
String name
String address
String city
String country
static constraints = {
name nullable: false, blank: false
address nullable: false, blank: false
city nullable: false, blank: false
country nullable: false, blank: false
}
}
Enployee 域类:
package com.nuevaconsulting.embrow
class Employee {
String surname
String givenNames
Position position
Office office
int extension
Date hired
int salary
static constraints = {
surname nullable: false, blank: false
givenNames nullable: false, blank: false
: false
office nullable: false
extension nullable: false
hired nullable: false
salary nullable: false
}
}
请把稳,虽然 Position 和 Office 域类利用了预定义的 Groovy 类型 String 以及 int,但 Employee 域类定义了 Position 和 Office 字段(以及预定义的 Date)。这会导致创建数据库表,个中存储的 Employee 实例中包含了指向存储 Position 和 Office 实例表的引用或者外键。
现在你可以天生掌握器,视图,以及其他各种测试组件:
-all com.nuevaconsulting.embrow.Position
grails generate-all com.nuevaconsulting.embrow.Office
grails generate-all com.nuevaconsulting.embrow.Employeegrails generateall com.grails generateall com.grails generateall com.
此时,你已经准备好了一个基本的增编削查(CRUD)运用程序。我在 grails-app/init/com/nuevaconsulting/BootStrap.groovy 中包含了一些根本数据来添补表格。
如果你用如下命令来启动运用程序:
grails run-app
在浏览器输入 http://localhost:8080/,你将会看到如下界面:
Embrow 运用程序主界面。
单击 “OfficeController” 链接,会跳转到如下界面:
Office 列表
把稳,此表由 OfficeController 的 index 办法天生,并由视图 office/index.gsp 显示。
同样,单击 “EmployeeController” 链接 跳转到如下界面:
employee 掌握器
好吧,这很丑陋: Position 和 Office 链接是什么?
上面的命令 generate-all 天生的视图创建了一个叫 index.gsp 的文件,它利用 Grails <f:table/> 标签,该标签默认会显示类名(com.nuevaconsulting.embrow.Position)和持久化示例标识符(30)。这个操作可以自定义用来产生更好看的东西,并且自动天生链接,自动天生分页以及自动天生可排序列的一些非常简洁直不雅观的东西。
但该员工信息浏览器功能也是有限的。例如,如果想查找 “position” 信息中包含 “dev” 的员工该怎么办?如果要组合排序,以姓氏为主排序关键字,“office” 为赞助排序关键字,该怎么办?或者,你须要将已排序的数据导出到电子表格或 PDF 文档以便通过电子邮件发送给无法访问浏览器的人,该怎么办?
jQuery DataTables 插件供应了这些所需的功能。许可你创建一个完成的表格数据浏览器。
创建员工信息浏览器视图和掌握器的方法要基于 jQuery DataTables 创建员工信息浏览器,你必须先完成以下两个任务:
创建 Grails 视图,个中包含启用 DataTable 所需的 HTML 和 JavaScript给 Grails 掌握器增加一个方法来掌握新视图。员工信息浏览器视图在目录 embrow/grails-app/views/employee 中,首先复制 index.gsp 文件,重命名为 browser.gsp:
cd Projects
cd embrow/grails-app/views/employee
cp gsp browser.gsp
此刻,你自定义新的 browser.gsp 文件来添加干系的 jQuery DataTables 代码。
常日,在可能的时候,我喜好从内容供应商处得到 JavaScript 和 CSS;不才面这行后面:
<title><g:message code=\公众default.list.label\"大众 args=\公众[entityName]\"大众 /></title>
插入如下代码:
<script src=\"大众https://code.jquery.com/jquery-2.2.4.min.js\"大众 integrity=\"大众sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=\"大众 crossorigin=\公众anonymous\"大众></script>
<link rel=\公众stylesheet\公众 type=\"大众text/css\"大众 href=\公众https://cdn.datatables.net/1.10.16/css/jquery.dataTables.css\公众>
<script type=\"大众text/javascript\"大众 charset=\"大众utf8\"大众 src=\"大众https://cdn.datatables.net/1.10.16/js/jquery.dataTables.js\公众></script>
<link rel=\公众stylesheet\"大众 type=\公众text/css\"大众 href=\"大众https://cdn.datatables.net/scroller/1.4.4/css/scroller.dataTables.min.css\"大众>
<script type=\"大众text/javascript\"大众 charset=\"大众utf8\公众 src=\"大众https://cdn.datatables.net/scroller/1.4.4/js/dataTables.scroller.min.js\"大众></script>
<script type=\"大众text/javascript\"大众 charset=\公众utf8\公众 src=\"大众https://cdn.datatables.net/buttons/1.5.1/js/dataTables.buttons.min.js\"大众></script>
<script type=\"大众text/javascript\"大众 charset=\"大众utf8\"大众 src=\"大众https://cdn.datatables.net/buttons/1.5.1/js/buttons.flash.min.js\"大众></script>
<script type=\"大众text/javascript\"大众 charset=\"大众utf8\"大众 src=\公众https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js\"大众></script>
<script type=\公众text/javascript\"大众 charset=\"大众utf8\"大众 src=\公众https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/pdfmake.min.js\"大众></script>
<script type=\公众text/javascript\"大众 charset=\公众utf8\"大众 src=\"大众https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js\"大众></script>
<script type=\"大众text/javascript\公众 charset=\公众utf8\"大众 src=\"大众https://cdn.datatables.net/buttons/1.5.1/js/buttons.html5.min.js\公众></script>
<script type=\"大众text/javascript\公众 charset=\"大众utf8\公众 src=\公众https://cdn.datatables.net/buttons/1.5.1/js/buttons.print.min.js \"大众></script>
然后删除 index.gsp 中供应数据分页的代码:
<div id=\"大众list-employee\公众 class=\"大众content scaffold-list\"大众 role=\"大众main\"大众>
<h1><g:message code=\公众default.list.label\"大众 args=\"大众[entityName]\公众 /></h1>
<g:if test=\公众${flash.message}\"大众>
<div class=\公众message\"大众 role=\"大众status\公众>${flash.message}</div>
</g:if>
<f:table collection=\"大众${employeeList}\公众 />
<div class=\"大众pagination\"大众>
<g:paginate total=\公众${employeeCount ?: 0}\"大众 />
</div>
</div>
并插入实现 jQuery DataTables 的代码。
要插入的第一部分是 HTML,它将创建浏览器的基本表格构造。DataTables 与后端通信的运用程序来说,它们只供应表格页眉和页脚;DataTables JavaScript 则卖力表中内容。
<div id=\"大众employee-browser\"大众 class=\"大众content\"大众 role=\"大众main\公众>
<h1>Employee Browser</h1>
<table id=\"大众employee_dt\"大众 class=\公众display compact\公众 style=\"大众width:99%;\"大众>
<thead>
<tr>
<th>Surname</th>
<th>Given name(s)</th>
<th>Position</th>
<th>Office</th>
<th>Extension</th>
<th>Hired</th>
<th>Salary</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Surname</th>
<th>Given name(s)</th>
<th>Position</th>
<th>Office</th>
<th>Extension</th>
<th>Hired</th>
<th>Salary</th>
</tr>
</tfoot>
</table>
</div>
接下来,插入一个 JavaScript 块,它紧张供应三个功能:它设置页脚中显示的文本框的大小,以进行列过滤,建立 DataTables 表模型,并创建一个处理程序来进行列过滤。
<g:javascript>
$('#employee_dt tfoot th').each( function() {javascript
下面的代码处理表格列底部的过滤器框的大小:
var title = $(this).text();
if (title == 'Extension' || title == 'Hired')
$(this).html('<input type=\公众text\公众 size=\公众5\公众 placeholder=\公众' + title + '?\"大众 />');
else
$(this).html('<input type=\公众text\"大众 size=\"大众15\"大众 placeholder=\公众' + title + '?\公众 />');
});titletitletitletitletitle
接下来,定义表模型。这是供应所有表选项的地方,包括界面的滚动,而不是分页,根据 DOM 字符串供应的装饰,将数据导出为 CSV 和其他格式的能力,以及建立与做事器的 AJAX 连接。 请把稳,利用 Groovy GString 调用 Grails createLink() 的方法创建 URL,在 EmployeeController 中指向 browserLister 操作。同样有趣的是表格列的定义。此信息将发送到后端,后端查询数据库并返回相应的记录。
var table = $('#employee_dt').DataTable( {
\"大众scrollY\公众: 500,
\"大众deferRender\公众: true,
\"大众scroller\"大众: true,
\"大众dom\"大众: \"大众Brtip\"大众,
\"大众buttons\"大众: [ 'copy', 'csv', 'excel', 'pdf', 'print' ],
\公众processing\公众: true,
\"大众serverSide\公众: true,
\公众ajax\公众: {
\"大众url\公众: \"大众${createLink(controller: 'employee', action: 'browserLister')}\"大众,
\公众type\"大众: \"大众POST\"大众,
},
\"大众columns\"大众: [
{ \"大众data\公众: \"大众surname\"大众 },
{ \"大众data\公众: \"大众givenNames\"大众 },
{ \公众data\"大众: \公众position\公众 },
{ \"大众data\公众: \公众office\公众 },
{ \公众data\"大众: \"大众extension\公众 },
{ \"大众data\公众: \"大众hired\"大众 },
{ \"大众data\"大众: \"大众salary\"大众 }
]
});
末了,监视过滤器列以进行变动,并利用它们来运用过滤器。
table.columns().every(function() {
var that = this;
$('input', this.footer()).on('keyup change', function(e) {
if (that.search() != this.value && 8 < e.keyCode && e.keyCode < 32)
that.search(this.value).draw();
});
这便是 JavaScript,这样就完成了对视图代码的变动。
});
</g:javascript>
以下是此视图创建的UI的屏幕截图:
这是另一个屏幕截图,显示了过滤和多列排序(探求 “position” 包括字符 “dev” 的员工,先按 “office” 排序,然后按姓氏排序):
这是另一个屏幕截图,显示单击 CSV 按钮时会发生什么:
末了,这是一个截图,显示在 LibreOffice 中打开的 CSV 数据:
好的,视图部分看起来非常大略;因此,掌握器必须做所有繁重的事情,对吧? 让我们来看看……
掌握器 browserLister 操作回忆一下,我们看到过这个字符串:
\公众${createLink(controller: 'employee', action: 'browserLister')}\公众
对付从 DataTables 模型中调用 AJAX 的 URL,是在 Grails 做事器上动态创建 HTML 链接,其 Grails 标记背后通过调用 createLink() 的方法实现的。这会终极产生一个指向 EmployeeController 的链接,位于:
embrow/grails-app/controllers/com/nuevaconsulting/embrow/EmployeeController.groovy
特殊是掌握器方法 browserLister()。我在代码中留了一些 print 语句,以便在运行时能够在终端看到中间结果。
def browserLister() {
// Applies filters and sorting to return a list of desired employees
首先,打印出通报给 browserLister() 的参数。我常日利用此代码开始构建掌握器方法,以便我完备清楚我的掌握器正在吸收什么。
println \"大众employee browserLister params $params\"大众
println()
接下来,处理这些参数以使它们更加有用。首先,jQuery DataTables 参数,一个名为 jqdtParams 的 Groovy 映射:
def jqdtParams = [:]
params.each { key, value ->
def keyFields = key.replace(']','').split(/\[/)
def table = jqdtParams
for (int f = 0; f < keyFields.size() - 1; f++) {
def keyField = keyFields[f]
if (!table.containsKey(keyField))
table[keyField] = [:]
table = table[keyField]
}
table[keyFields[-1]] = value
}
println \"大众employee dataTableParams $jqdtParams\"大众
println()
接下来,列数据,一个名为 columnMap 的 Groovy 映射:
def columnMap = jqdtParams.columns.collectEntries { k, v ->
def whereTerm = null
switch (v.data) {
case 'extension':
case 'hired':
case 'salary':
if (v.search.value ==~ /\d+(,\d+)/)
whereTerm = v.search.value.split(',').collect { it as Integer }
break
default:
if (v.search.value ==~ /[A-Za-z0-9 ]+/)
whereTerm = \"大众%${v.search.value}%\"大众 as String
break
}
[(v.data): [where: whereTerm]]
}
println \"大众employee columnMap $columnMap\"大众
println()
接下来,从 columnMap 中检索的所有列表,以及在视图中应如何排序这些列表,Groovy 列表分别称为 allColumnList 和 orderList :
def allColumnList = columnMap.keySet() as List
println \"大众employee allColumnList $allColumnList\"大众
def orderList = jqdtParams.order.collect { k, v -> [allColumnList[v.column as Integer], v.dir] }
println \"大众employee orderList $orderList\"大众
我们将利用 Grails 的 Hibernate 标准实现来实际选择要显示的元素以及它们的排序和分页。标准哀求过滤器关闭;在大多数示例中,这是作为标准实例本身的创建的一部分给出的,但是在这里我们预先定义过滤器闭包。请把稳,在这种情形下,“date hired” 过滤器的相对繁芜的阐明被视为一年并运用于建立日期范围,并利用 createAlias 以许可我们进入干系种别 Position 和 Office:
def filterer = {
createAlias 'position', 'p'
createAlias 'office', 'o'
if (columnMap.surname.where) ilike 'surname', columnMap.surname.where
if (columnMap.givenNames.where) ilike 'givenNames', columnMap.givenNames.where
if (columnMap.position.where) ilike 'p.name', columnMap.position.where
if (columnMap.office.where) ilike 'o.name', columnMap.office.where
if (columnMap.extension.where) inList 'extension', columnMap.extension.where
if (columnMap.salary.where) inList 'salary', columnMap.salary.where
if (columnMap.hired.where) {
if (columnMap.hired.where.size() > 1) {
or {
columnMap.hired.where.each {
between 'hired', Date.parse('yyyy/MM/dd',\公众${it}/01/01\"大众 as String),
Date.parse('yyyy/MM/dd',\"大众${it}/12/31\"大众 as String)
}
}
} else {
between 'hired', Date.parse('yyyy/MM/dd',\公众${columnMap.hired.where[0]}/01/01\公众 as String),
Date.parse('yyyy/MM/dd',\"大众${columnMap.hired.where[0]}/12/31\"大众 as String)
}
}
}
是时候运用上述内容了。第一步是获取分页代码所需的所有 Employee 实例的总数:
def recordsTotal = Employee.count()
println \"大众employee recordsTotal $recordsTotal\"大众
接下来,将过滤器运用于 Employee 实例以获取过滤结果的计数,该结果将始终小于或即是总数(同样,这是针对分页代码):
def c = Employee.createCriteria()
def recordsFiltered = c.count {
filterer.delegate = delegate
filterer()
}
println \公众employee recordsFiltered $recordsFiltered\"大众
得到这两个计数后,你还可以利用分页和排序信息获取实际过滤的实例。
def orderer = Employee.withCriteria {
filterer.delegate = delegate
filterer()
orderList.each { oi ->
switch (oi[0]) {
case 'surname': order 'surname', oi[1]; break
case 'givenNames': order 'givenNames', oi[1]; break
case 'position': order 'p.name', oi[1]; break
case 'office': order 'o.name', oi[1]; break
case 'extension': order 'extension', oi[1]; break
case 'hired': order 'hired', oi[1]; break
case 'salary': order 'salary', oi[1]; break
}
}
maxResults (jqdtParams.length as Integer)
firstResult (jqdtParams.start as Integer)
}
要完备清楚,JTable 中的分页代码管理三个计数:数据集中的记录总数,运用过滤器后得到的数字,以及要在页面上显示的数字(显示是滚动还是分页)。 排序运用于所有过滤的记录,并且分页运用于那些过滤的记录的块以用于显示目的。
接下来,处理命令返回的结果,在每行中创建指向 Employee、Position 和 Office 实例的链接,以便用户可以单击这些链接以获取干系实例的所有详细信息:
def dollarFormatter = new DecimalFormat('$##,###.##')
def employees = orderer.collect { employee ->
['surname': \"大众<a href='${createLink(controller: 'employee', action: 'show', id: employee.id)}'>${employee.surname}</a>\"大众,
'givenNames': employee.givenNames,
'position': \"大众<a href='${createLink(controller: 'position', action: 'show', id: employee.position?.id)}'>${employee.position?.name}</a>\公众,
'office': \公众<a href='${createLink(controller: 'office', action: 'show', id: employee.office?.id)}'>${employee.office?.name}</a>\"大众,
'extension': employee.extension,
'hired': employee.hired.format('yyyy/MM/dd'),
'salary': dollarFormatter.format(employee.salary)]
}
末了,创建要返回的结果并将其作为 JSON 返回,这是 jQuery DataTables 所须要的。
def result = [draw: jqdtParams.draw, recordsTotal: recordsTotal, recordsFiltered: recordsFiltered, data: employees]
render(result as JSON)
}
大功告成。
如果你熟习 Grails,这可能看起来比你原来想象的要多,但这里没有火箭式的一步到位方法,只是很多分散的操作步骤。但是,如果你没有太多打仗 Grails(或 Groovy),那么须要理解很多新东西 - 闭包,代理和构建器等等。
在那种情形下,从哪里开始? 最好的地方是理解 Groovy 本身,尤其是 Groovy closures 和 Groovy delegates and builders 。然后再去阅读上面关于 Grails 和 Hibernate 条件查询的建议阅读文章。
结语jQuery DataTables 为 Grails 制作了很棒的表格数据浏览器。对视图进行编码并不是太棘手,但 DataTables 文档中供应的 PHP 示例供应的功能仅到此位置。特殊是,它们不是用 Grails 程序员编写的,也不包含探索利用引用其他类(本色上是查找表)的元素的更风雅的细节。
我利用这种方法制作了几个数据浏览器,许可用户选择要查看和累积记录计数的列,或者只是浏览数据。纵然在相对适度的 VPS 上的百万行表中,性能也很好。
一个警告:我有时创造了 Grails 中暴露的各种 Hibernate 标准机制的一些问题(请参阅我的其他 GitHub 代码库),因此须要谨慎和实验。如果所有其他方法都失落败了,另一种方法是动态构建 SQL 字符串并实行它们。在撰写本文时,我更喜好利用 Grails 标准,除非我碰着凌乱的子查询,但这可能只反响了我在 Hibernate 中对子查询的相对缺少履历。
我希望 Grails 程序员创造本文的有趣性。请随时不才面留下评论或建议。
via: https://opensource.com/article/18/9/using-grails-jquery-and-datatables
作者: Chris Hermansen 选题: lujun9972 译者: jrg 校正: wxy
本文由 LCTT 原创编译, Linux中国 名誉推出
点击“理解更多”可访问文内链接