新聞中心
本文介紹如何構(gòu)建一個(gè)基于 Grails 的數(shù)據(jù)瀏覽器來可視化復(fù)雜的表格數(shù)據(jù)。
成都創(chuàng)新互聯(lián)公司專注于虎丘企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城建設(shè)。虎丘網(wǎng)站建設(shè)公司,為虎丘等地區(qū)提供建站服務(wù)。全流程按需定制設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
我是 Grails 的忠實(shí)粉絲。當(dāng)然,我主要是熱衷于利用命令行工具來探索和分析數(shù)據(jù)的數(shù)據(jù)從業(yè)人員。數(shù)據(jù)從業(yè)人員經(jīng)常需要查看數(shù)據(jù),這也意味著他們通常擁有優(yōu)秀的數(shù)據(jù)瀏覽器。利用 Grails、jQuery,以及 DataTables jQuery 插件,我們可以制作出非常友好的表格數(shù)據(jù)瀏覽器。
DataTables 網(wǎng)站提供了許多“食譜式”的教程文檔,展示了如何組合一些優(yōu)秀的示例應(yīng)用程序,這些程序包含了完成一些非常漂亮的東西所必要的 JavaScript、HTML,以及偶爾出現(xiàn)的 PHP。但對(duì)于那些寧愿使用 Grails 作為后端的人來說,有必要進(jìn)行一些說明示教。此外,樣本程序中使用的數(shù)據(jù)是一個(gè)虛構(gòu)公司的員工的單個(gè)平面表格數(shù)據(jù),因此處理這些復(fù)雜的表關(guān)系可以作為讀者的一個(gè)練習(xí)項(xiàng)目。
本文中,我們將創(chuàng)建具有略微復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和 DataTables 瀏覽器的 Grails 應(yīng)用程序。我們將介紹 Grails 標(biāo)準(zhǔn),它是 Groovy 式的 Java Hibernate 標(biāo)準(zhǔn)。我已將代碼托管在 GitHub 上方便大家訪問,因此本文主要是對(duì)代碼細(xì)節(jié)的解讀。
首先,你需要配置 Java、Groovy、Grails 的使用環(huán)境。對(duì)于 Grails,我傾向于使用終端窗口和 Vim,本文也使用它們。為獲得現(xiàn)代的 Java 環(huán)境,建議下載并安裝 Linux 發(fā)行版提供的 Open Java Development Kit (OpenJDK)(應(yīng)該是 Java 8、9、10 或 11 之一,撰寫本文時(shí),我正在使用 Java 8)。從我的角度來看,獲取***的 Groovy 和 Grails 的***方法是使用 SDKMAN!。
從未嘗試過 Grails 的讀者可能需要做一些背景資料閱讀。作為初學(xué)者,推薦文章 創(chuàng)建你的***個(gè) Grails 應(yīng)用程序。
獲取員工信息瀏覽器應(yīng)用程序
正如上文所提,我將本文中員工信息瀏覽器的源代碼托管在 GitHub上。進(jìn)一步講,應(yīng)用程序embrow是在 Linux 終端中用如下命令構(gòu)建的:
cd Projectsgrails create-app com.nuevaconsulting.embrow
域類和單元測(cè)試創(chuàng)建如下:
grails create-domain-class com.nuevaconsulting.embrow.Positiongrails create-domain-class com.nuevaconsulting.embrow.Officegrails create-domain-class com.nuevaconsulting.embrow.Employeecd embrowgrails createdomaincom.grails createdomaincom.grails createdomaincom.
這種方式構(gòu)建的域類沒有屬性,因此必須按如下方式編輯它們:
Position 域類:
package com.nuevaconsulting.embrowclass Position {String nameint startingstatic constraints = {name nullable: false, blank: falsestarting nullable: false}}com.Stringint startingstatic constraintsnullableblankstarting nullable
Office 域類:
package com.nuevaconsulting.embrowclass Office {String nameString addressString cityString countrystatic constraints = {name nullable: false, blank: falseaddress nullable: false, blank: falsecity nullable: false, blank: falsecountry nullable: false, blank: false}}
Enployee 域類:
package com.nuevaconsulting.embrowclass Employee {String surnameString givenNamesPosition positionOffice officeint extensionDate hiredint salarystatic constraints = {surname nullable: false, blank: falsegivenNames nullable: false, blank: false: falseoffice nullable: falseextension nullable: falsehired nullable: falsesalary nullable: false}}
請(qǐng)注意,雖然 Position 和 Office 域類使用了預(yù)定義的 Groovy 類型 String 以及 int,但 Employee 域類定義了 Position 和 Office 字段(以及預(yù)定義的 Date)。這會(huì)導(dǎo)致創(chuàng)建數(shù)據(jù)庫表,其中存儲(chǔ)的 Employee 實(shí)例中包含了指向存儲(chǔ) Position 和 Office 實(shí)例表的引用或者外鍵。
現(xiàn)在你可以生成控制器,視圖,以及其他各種測(cè)試組件:
-all com.nuevaconsulting.embrow.Positiongrails generate-all com.nuevaconsulting.embrow.Officegrails generate-all com.nuevaconsulting.embrow.Employeegrails generateall com.grails generateall com.grails generateall com.
此時(shí),你已經(jīng)準(zhǔn)備好了一個(gè)基本的增刪改查(CRUD)應(yīng)用程序。我在 grails-app/init/com/nuevaconsulting/BootStrap.groovy 中包含了一些基礎(chǔ)數(shù)據(jù)來填充表格。
如果你用如下命令來啟動(dòng)應(yīng)用程序:
grails run-app
在瀏覽器輸入 http://localhost:8080/,你將會(huì)看到如下界面:
Embrow 應(yīng)用程序主界面。
單擊 “OfficeController” 鏈接,會(huì)跳轉(zhuǎn)到如下界面:
Office 列表
注意,此表由 OfficeController 的 index 方式生成,并由視圖 office/index.gsp 顯示。
同樣,單擊 “EmployeeController” 鏈接 跳轉(zhuǎn)到如下界面:
employee 控制器
好吧,這很丑陋: Position 和 Office 鏈接是什么?
上面的命令 generate-all 生成的視圖創(chuàng)建了一個(gè)叫 index.gsp 的文件,它使用 Grails 標(biāo)簽,該標(biāo)簽?zāi)J(rèn)會(huì)顯示類名(com.nuevaconsulting.embrow.Position)和持久化示例標(biāo)識(shí)符(30)。這個(gè)操作可以自定義用來產(chǎn)生更好看的東西,并且自動(dòng)生成鏈接,自動(dòng)生成分頁以及自動(dòng)生成可排序列的一些非常簡(jiǎn)潔直觀的東西。
但該員工信息瀏覽器功能也是有限的。例如,如果想查找 “position” 信息中包含 “dev” 的員工該怎么辦?如果要組合排序,以姓氏為主排序關(guān)鍵字,“office” 為輔助排序關(guān)鍵字,該怎么辦?或者,你需要將已排序的數(shù)據(jù)導(dǎo)出到電子表格或 PDF 文檔以便通過電子郵件發(fā)送給無法訪問瀏覽器的人,該怎么辦?
jQuery DataTables 插件提供了這些所需的功能。允許你創(chuàng)建一個(gè)完成的表格數(shù)據(jù)瀏覽器。
創(chuàng)建員工信息瀏覽器視圖和控制器的方法
要基于 jQuery DataTables 創(chuàng)建員工信息瀏覽器,你必須先完成以下兩個(gè)任務(wù):
- 創(chuàng)建 Grails 視圖,其中包含啟用 DataTable 所需的 HTML 和 JavaScript
- 給 Grails 控制器增加一個(gè)方法來控制新視圖。
員工信息瀏覽器視圖
在目錄 embrow/grails-app/views/employee 中,首先復(fù)制 index.gsp 文件,重命名為 browser.gsp:
cd Projectscd embrow/grails-app/views/employeecp gsp browser.gsp
此刻,你自定義新的 browser.gsp 文件來添加相關(guān)的 jQuery DataTables 代碼。
通常,在可能的時(shí)候,我喜歡從內(nèi)容提供商處獲得 JavaScript 和 CSS;在下面這行后面:
插入如下代碼:
然后刪除 index.gsp 中提供數(shù)據(jù)分頁的代碼:
并插入實(shí)現(xiàn) jQuery DataTables 的代碼。
要插入的***部分是 HTML,它將創(chuàng)建瀏覽器的基本表格結(jié)構(gòu)。DataTables 與后端通信的應(yīng)用程序來說,它們只提供表格頁眉和頁腳;DataTables JavaScript 則負(fù)責(zé)表中內(nèi)容。
Employee Browser
Surname Given name(s) Position Office Extension Hired Salary Surname Given name(s) Position Office Extension Hired Salary
接下來,插入一個(gè) JavaScript 塊,它主要提供三個(gè)功能:它設(shè)置頁腳中顯示的文本框的大小,以進(jìn)行列過濾,建立 DataTables 表模型,并創(chuàng)建一個(gè)處理程序來進(jìn)行列過濾。
$('#employee_dt tfoot th').each( function() {javascript
下面的代碼處理表格列底部的過濾器框的大?。?/p>
var title = $(this).text();if (title == 'Extension' || title == 'Hired')$(this).html('');else$(this).html('');});titletitletitletitletitle
接下來,定義表模型。這是提供所有表選項(xiàng)的地方,包括界面的滾動(dòng),而不是分頁,根據(jù) DOM 字符串提供的裝飾,將數(shù)據(jù)導(dǎo)出為 CSV 和其他格式的能力,以及建立與服務(wù)器的 AJAX 連接。 請(qǐng)注意,使用 Groovy GString 調(diào)用 Grails createLink() 的方法創(chuàng)建 URL,在 EmployeeController 中指向 browserLister 操作。同樣有趣的是表格列的定義。此信息將發(fā)送到后端,后端查詢數(shù)據(jù)庫并返回相應(yīng)的記錄。
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" }]});
***,監(jiān)視過濾器列以進(jìn)行更改,并使用它們來應(yīng)用過濾器。
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,這樣就完成了對(duì)視圖代碼的更改。
});
以下是此視圖創(chuàng)建的UI的屏幕截圖:
這是另一個(gè)屏幕截圖,顯示了過濾和多列排序(尋找 “position” 包括字符 “dev” 的員工,先按 “office” 排序,然后按姓氏排序):
這是另一個(gè)屏幕截圖,顯示單擊 CSV 按鈕時(shí)會(huì)發(fā)生什么:
***,這是一個(gè)截圖,顯示在 LibreOffice 中打開的 CSV 數(shù)據(jù):
好的,視圖部分看起來非常簡(jiǎn)單;因此,控制器必須做所有繁重的工作,對(duì)吧? 讓我們來看看……
控制器 browserLister 操作
回想一下,我們看到過這個(gè)字符串:
"${createLink(controller: 'employee', action: 'browserLister')}"
對(duì)于從 DataTables 模型中調(diào)用 AJAX 的 URL,是在 Grails 服務(wù)器上動(dòng)態(tài)創(chuàng)建 HTML 鏈接,其 Grails 標(biāo)記背后通過調(diào)用 createLink() 的方法實(shí)現(xiàn)的。這會(huì)最終產(chǎn)生一個(gè)指向 EmployeeController 的鏈接,位于:
embrow/grails-app/controllers/com/nuevaconsulting/embrow/EmployeeController.groovy
特別是控制器方法 browserLister()。我在代碼中留了一些 print 語句,以便在運(yùn)行時(shí)能夠在終端看到中間結(jié)果。
def browserLister() {// Applies filters and sorting to return a list of desired employees
首先,打印出傳遞給 browserLister() 的參數(shù)。我通常使用此代碼開始構(gòu)建控制器方法,以便我完全清楚我的控制器正在接收什么。
println "employee browserLister params $params"println()
接下來,處理這些參數(shù)以使它們更加有用。首先,jQuery DataTables 參數(shù),一個(gè)名為 jqdtParams 的 Groovy 映射:
def jqdtParams = [:]params.each { key, value ->def keyFields = key.replace(']','').split(/\[/)def table = jqdtParamsfor (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()
接下來,列數(shù)據(jù),一個(gè)名為 columnMap 的 Groovy 映射:
def columnMap = jqdtParams.columns.collectEntries { k, v ->def whereTerm = nullswitch (v.data) {case 'extension':case 'hired':case 'salary':if (v.search.value ==~ /\d+(,\d+)*/)whereTerm = v.search.value.split(',').collect { it as Integer }breakdefault:if (v.search.value ==~ /[A-Za-z0-9 ]+/)whereTerm = "%${v.search.value}%" as Stringbreak}[(v.data): [where: whereTerm]]}println "employee columnMap $columnMap"println()
接下來,從 columnMap 中檢索的所有列表,以及在視圖中應(yīng)如何排序這些列表,Groovy 列表分別稱為 allColumnList 和 orderList :
def allColumnList = columnMap.keySet() as Listprintln "employee allColumnList $allColumnList"def orderList = jqdtParams.order.collect { k, v -> [allColumnList[v.column as Integer], v.dir] }println "employee orderList $orderList"
我們將使用 Grails 的 Hibernate 標(biāo)準(zhǔn)實(shí)現(xiàn)來實(shí)際選擇要顯示的元素以及它們的排序和分頁。標(biāo)準(zhǔn)要求過濾器關(guān)閉;在大多數(shù)示例中,這是作為標(biāo)準(zhǔn)實(shí)例本身的創(chuàng)建的一部分給出的,但是在這里我們預(yù)先定義過濾器閉包。請(qǐng)注意,在這種情況下,“date hired” 過濾器的相對(duì)復(fù)雜的解釋被視為一年并應(yīng)用于建立日期范圍,并使用 createAlias 以允許我們進(jìn)入相關(guān)類別 Position 和 Office:
def filterer = {createAlias 'position', 'p'createAlias 'office', 'o'if (columnMap.surname.where) ilike 'surname', columnMap.surname.whereif (columnMap.givenNames.where) ilike 'givenNames', columnMap.givenNames.whereif (columnMap.position.where) ilike 'p.name', columnMap.position.whereif (columnMap.office.where) ilike 'o.name', columnMap.office.whereif (columnMap.extension.where) inList 'extension', columnMap.extension.whereif (columnMap.salary.where) inList 'salary', columnMap.salary.whereif (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)}}}
是時(shí)候應(yīng)用上述內(nèi)容了。***步是獲取分頁代碼所需的所有 Employee 實(shí)例的總數(shù):
def recordsTotal = Employee.count()println "employee recordsTotal $recordsTotal"
接下來,將過濾器應(yīng)用于 Employee 實(shí)例以獲取過濾結(jié)果的計(jì)數(shù),該結(jié)果將始終小于或等于總數(shù)(同樣,這是針對(duì)分頁代碼):
def c = Employee.createCriteria()def recordsFiltered = c.count {filterer.delegate = delegatefilterer()}println "employee recordsFiltered $recordsFiltered"
獲得這兩個(gè)計(jì)數(shù)后,你還可以使用分頁和排序信息獲取實(shí)際過濾的實(shí)例。
def orderer = Employee.withCriteria {filterer.delegate = delegatefilterer()orderList.each { oi ->switch (oi[0]) {case 'surname': order 'surname', oi[1]; breakcase 'givenNames': order 'givenNames', oi[1]; breakcase 'position': order 'p.name', oi[1]; breakcase 'office': order 'o.name', oi[1]; breakcase 'extension': order 'extension', oi[1]; breakcase 'hired': order 'hired', oi[1]; breakcase 'salary': order 'salary', oi[1]; break}}maxResults (jqdtParams.length as Integer)firstResult (jqdtParams.start as Integer)}
要完全清楚,JTable 中的分頁代碼管理三個(gè)計(jì)數(shù):數(shù)據(jù)集中的記錄總數(shù),應(yīng)用過濾器后得到的數(shù)字,以及要在頁面上顯示的數(shù)字(顯示是滾動(dòng)還是分頁)。 排序應(yīng)用于所有過濾的記錄,并且分頁應(yīng)用于那些過濾的記錄的塊以用于顯示目的。
接下來,處理命令返回的結(jié)果,在每行中創(chuàng)建指向 Employee、Position 和 Office 實(shí)例的鏈接,以便用戶可以單擊這些鏈接以獲取相關(guān)實(shí)例的所有詳細(xì)信息:
def dollarFormatter = new DecimalFormat('$##,###.##')def employees = orderer.collect { employee ->['surname': "${employee.surname}",'givenNames': employee.givenNames,'position': "${employee.position?.name}",'office': "${employee.office?.name}",'extension': employee.extension,'hired': employee.hired.format('yyyy/MM/dd'),'salary': dollarFormatter.format(employee.salary)]}
***,創(chuàng)建要返回的結(jié)果并將其作為 JSON 返回,這是 jQuery DataTables 所需要的。
def result = [draw: jqdtParams.draw, recordsTotal: recordsTotal, recordsFiltered: recordsFiltered, data: employees]render(result as JSON)}
大功告成。
如果你熟悉 Grails,這可能看起來比你原先想象的要多,但這里沒有火箭式的一步到位方法,只是很多分散的操作步驟。但是,如果你沒有太多接觸 Grails(或 Groovy),那么需要了解很多新東西 - 閉包,代理和構(gòu)建器等等。
在那種情況下,從哪里開始? ***的地方是了解 Groovy 本身,尤其是 Groovy closures 和 Groovy delegates and builders。然后再去閱讀上面關(guān)于 Grails 和 Hibernate 條件查詢的建議閱讀文章。
結(jié)語
jQuery DataTables 為 Grails 制作了很棒的表格數(shù)據(jù)瀏覽器。對(duì)視圖進(jìn)行編碼并不是太棘手,但 DataTables 文檔中提供的 PHP 示例提供的功能僅到此位置。特別是,它們不是用 Grails 程序員編寫的,也不包含探索使用引用其他類(實(shí)質(zhì)上是查找表)的元素的更精細(xì)的細(xì)節(jié)。
我使用這種方法制作了幾個(gè)數(shù)據(jù)瀏覽器,允許用戶選擇要查看和累積記錄計(jì)數(shù)的列,或者只是瀏覽數(shù)據(jù)。即使在相對(duì)適度的 VPS 上的百萬行表中,性能也很好。
一個(gè)警告:我偶然發(fā)現(xiàn)了 Grails 中暴露的各種 Hibernate 標(biāo)準(zhǔn)機(jī)制的一些問題(請(qǐng)參閱我的其他 GitHub 代碼庫),因此需要謹(jǐn)慎和實(shí)驗(yàn)。如果所有其他方法都失敗了,另一種方法是動(dòng)態(tài)構(gòu)建 SQL 字符串并執(zhí)行它們。在撰寫本文時(shí),我更喜歡使用 Grails 標(biāo)準(zhǔn),除非我遇到雜亂的子查詢,但這可能只反映了我在 Hibernate 中對(duì)子查詢的相對(duì)缺乏經(jīng)驗(yàn)。
網(wǎng)站欄目:在Grails中使用jQuery和DataTables
標(biāo)題路徑:http://fisionsoft.com.cn/article/djieeds.html


咨詢
建站咨詢

