grails 怎么使用_将Grails与jQuery和DataTables结合使用

grails 怎么使用

我是Grails的忠实粉丝。 当然,我主要是一个数据人员,喜欢使用命令行工具来探索和分析数据。 但是,即使是数据人,有时也需要查看数据,有时使用数据意味着拥有出色的数据浏览器。 使用Grails, jQueryDataTables jQuery插件 ,我们可以制作出非常漂亮的表格数据浏览器。

DataTables网站提供了许多不错的“食谱样式”文档,这些文档显示了如何将一些优秀的示例应用程序组合在一起,并且其中包括必要JavaScript,HTML和偶尔的PHP,以完成一些漂亮的工作。 但是对于那些更愿意将Grails用作后端的人来说,需要一些解释。 此外,使用的示例应用程序数据是虚构公司的单个员工平表,因此处理表关系的复杂性是读者的练习。

Groovy定义的Java Hibernate标准。 我已将应用程序的代码放在GitHub上 ,因此本文旨在说明代码的细微差别。

对于先决条件,您将需要设置Java,Groovy和Grails环境。 对于Grails,我倾向于使用终端窗口和Vim ,所以这里使用的就是这种方式。 为了获得现代Java,我建议下载并安装由Linux发行版(应该是Java 8、9、10或11)提供的Open Java Development Kit (OpenJDK);在撰写本文时,我正在使用Java 8。 )。 从我的角度来看,获取最新Groovy和Grails的最佳方法是使用SDKMAN!

从未尝试过Grails的读者可能需要做一些背景阅读。 首先,我建议创建您的First Grails应用程序

获取员工浏览器应用程序

如上所述,我已将此示例员工浏览器应用程序的源代码放在GitHub上 。 为了进一步说明,应用程序嵌入是在Linux终端窗口中使用以下命令构建的:


   
   
cd Projects
grails create - app com. nuevaconsulting . embrow

域类和单元测试创​​建如下:


   
   
cd embrow
grails create - domain - class com. nuevaconsulting . embrow . Position
grails create - domain - class com. nuevaconsulting . embrow . Office
grails create - domain - class com. nuevaconsulting . embrow . Employee

用这种方法构建的域类没有属性,因此必须按如下方式进行编辑:

Position域类:


   
   
package com. nuevaconsulting . embrow
 
class Position {

    String name
    int starting

    static constraints = {
        name nullable : false , blank : false
        starting nullable : false
    }
}

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
    }
}

和Employee域类:


   
   
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
        position nullable : 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实例的表的引用或外键。

现在,您可以生成控制器,视图和其他各种测试组件:


   
   
grails generate - all com. nuevaconsulting . embrow . Position
grails generate - all com. nuevaconsulting . embrow . Office
grails generate - all com. nuevaconsulting . embrow . Employee

至此,您已经可以使用基本的创建-读取-更新-删除(CRUD)应用程序。 我已经在grails-app / init / com / nuevaconsulting / BootStrap.groovy中添加了一些基本数据来填充表格。

如果使用以下命令运行应用程序:

 grails run - app 

您将在浏览器的http:// localhost:8080 /上看到以下屏幕

Embrow home screen

Embrow应用程序主屏幕

单击OfficeController的链接,将显示一个类似于以下内容的屏幕:

Office list

办公室清单

请注意,此列表由OfficeController索引方法生成,并由视图office/index.gsp

类似地,单击EmployeeController会显示一个屏幕,如下所示:

Employee controller

员工控制人

好的,这很丑陋-Position和Office链接有什么用?

好了,上面的generate-all命令generate-all的视图将创建一个索引。 使用Grails <f:table />标记的gsp文件,默认情况下显示类名称( com.nuevaconsulting.embrow.Position )和持久性实例标识符( 30 )。 可以自定义此行为以产生更好看的外观,并且具有一些自动生成的链接,自动生成的分页和自动生成的可排序列的漂亮东西。

但是,即使完全清理完毕,此员工浏览器也提供了有限的功能。 例如,如果要查找其职位包括文本“ dev”的所有员工该怎么办? 如果要合并列进行排序,以便主排序键是姓氏,辅助排序键是办公室名怎么办? 或者,如果您要将已排序的子集导出到电子表格或PDF中,以电子邮件发送给无法访问浏览器的人,该怎么办?

jQuery DataTables插件提供了这种额外的功能,并允许您创建完整的表格数据浏览器。

创建员工浏览器视图和控制器方法

为了创建基于jQuery DataTables的员工浏览器,您必须完成两个任务:

  1. 创建一个Grails视图,其中包含启用DataTables所需HTML和JavaScript

  2. 向Grails控制器添加方法以处理新视图

员工浏览器视图

在目录embrow / grails-app / views / employee中 ,首先制作索引的副本 gsp文件,将其命名为browser.gsp


   
   
cd Projects
cd embrow / grails - app / views / employee
cp index . gsp browser . gsp

此时,您要自定义新的浏览器。 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 ( ) {

下面的代码用于调整表格列底部的过滤器框的大小:


   
   
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 + '?" />' ) ;
} ) ;

接下来,定义表模型。 这是提供所有表选项的地方,包括滚动而不是分页的界面性质,根据dom字符串提供的隐秘修饰,将数据导出为CSV和其他格式的能力以及与服务器的Ajax连接已建立。 请注意,该URL是通过对Grails createLink()方法的Groovy GString调用创建的,引用了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的屏幕截图:

UI view screenshot

这是另一个截图,显示了工作中的过滤和多列排序(查找职位中包含字符“ dev”的员工,首先按办公室,然后按姓氏排序):

Grails UI sorted view

这是另一个屏幕截图,显示了单击CSV按钮时发生的情况:

Click on CSV button

最后,这是一个截图,显示了在LibreOffice中打开的CSV数据:

CSV data in LibreOffice

好的,因此视图部分看起来非常简单。 因此,控制器动作必须完成所有繁重的工作,对吗? 让我们来看看…

员工控制器browserLister操作

回想一下,我们看到了这个字符串

 "${createLink(controller: 'employee', action: 'browserLister')}" 

作为用于DataTables表模型的Ajax调用的URL。 createLink()是 Grails标记后面的方法 ,该标记用于在Grails服务器上对HTML进行预处理时动态生成链接。 最终生成到EmployeeController的链接,该链接位于

 embrow / grails - app / controllers / com / nuevaconsulting / embrow / EmployeeController. groovy 

特别是控制器方法browserLister() 。 我在代码中保留了一些打印语句,以便可以在运行应用程序的终端窗口中看到中间结果。


   
   
    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检索的所有列名称的列表,以及在视图中应如何对这些列进行排序的对应列表,分别称为allColumnListorderList的 Groovy列表:


   
   
        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标准的实现来实际执行要显示元素的选择以及它们的排序和分页。 标准要求过滤器关闭; 在大多数示例中,这是在创建条件实例本身的过程中给出的,但是在此我们预先定义过滤器关闭。 请注意,在这种情况下,“租用日期”过滤器的解释相对复杂,该过滤器被视为一年并用于建立日期范围,并使用createAlias来使我们进入相关的类职位和职位:


   
   
        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 )
        }

完全清楚地说,JTables中的分页代码管理三个计数:数据集中的记录总数,应用过滤器后的结果数量以及要在页面上显示的数量(显示是滚动还是分页) )。 该排序应用于所有过滤的记录,而分页应用于这些过滤的记录的块,以用于显示。

接下来,处理订购者返回的结果,在每一行中创建指向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 DataTables要求的JSON送回。


   
   
        def result = [ draw : jqdtParams. draw , recordsTotal : recordsTotal , recordsFiltered : recordsFiltered , data : employees ]
        render ( result as JSON )
    }

而已。

如果您熟悉Grails,这似乎比您原先想像的要多,但是这里没有火箭科学,只有很多活动部件。 但是,如果您没有太多接触Grails(或Groovy)的知识,那么有很多新的东西需要理解-闭包,委托和构建器,等等。

在这种情况下,从哪里开始? 最好的地方是了解Groovy本身,尤其是Groovy闭包以及Groovy委托和构建器 。 然后回到上面建议的有关Grails和Hibernate条件查询的内容。

结论

jQuery DataTables为Grails制作了很棒的表格数据浏览器。 对视图进行编码并不是很棘手,但是DataTables文档中提供PHP示例仅帮助您了解到这一点。 特别是,它们并不是在考虑Grails程序员的情况下编写的,也不是在探索使用引用其他类的元素(本质上是查找表)的更详细的信息。

我已经使用这种方法制作了两个数据浏览器,使用户可以选择要查看和累积记录计数的列,或者只是浏览数据。 即使在相对适中的VPS上的百万行表中,性能也很好。

一个警告:我偶然发现了Grails中公开的各种Hibernate标准机制的一些问题(请参阅我的其他GitHub存储库),因此需要谨慎和试验。 如果所有其他方法均失败,则另一种方法是动态构建SQL字符串并改为执行它们。 在撰写本文时,我更喜欢使用Grails准则,除非我陷入混乱的子查询,但这可能只是反映出我相对缺乏在Hibernate中使用子查询的经验。

希望您那里的Grails程序员觉得这很有趣。 请随时在下面留下评论或建议。

翻译自: https://opensource.com/article/18/9/using-grails-jquery-and-datatables

grails 怎么使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值