One of the coolest features of the GrailsUI dataTable is its ability to turn on row expansion. In order to have enough data to do this, we need to know a URL for the dataTable to load on row click. We also need to turn the rowExpansion feature on in the tag. Let’s continue using the example I set up in my last post about using the GrailsUI dataTable tag. We’ll expand on it and I’ll link the new source code at the bottom of this post.
First off, turning on rowExpansion in the tag is easy enough.
<gui:dataTable
controller="book" action="dataTableJSON"
columnDefs="[
[key:'name', label:'Name', sortable: true, resizeable: true],
[key:'isbn', label:'ISBN', sortable: true, resizeable: true],
[key:'author', label:'Author', sortable: true, resizeable: true],
[key:'publishDate', label:'Published on', sortable: true, resizeable: true]
]"
sortedBy="name"
draggableColumns="true"
rowsPerPage="12"
paginatorConfig="[
template:'{PreviousPageLink} {PageLinks} {NextPageLink} {CurrentPageReport}',
pageReportTemplate:'{totalRecords} total records'
]"
rowExpansion="true"
/>The real work is in setting up and passing the dataUrl in the controller action that serves the dataTable:
def dataTableJSON = {
def books = Book.list(params)
// let's convert our book list into an array of maps so we can format
// each value exactly as we want...
def formattedBooks = books.collect {
[
name: it.name,
isbn: it.isbn,
publishDate: new java.text.SimpleDateFormat("MMM dd, yyyy").format(it.publishDate),
author: it.author.name,
dataUrl: g.createLink(action:'bookDetail', id:it.id)
]
}
def data = [
totalRecords: Book.count(),
results: formattedBooks
]
render data as JSON
}The dataTable will only enable row expansion if it finds a ‘dataUrl’ field in the JSON data that populates it. Our controller is now creating a URL that will be called by the dataTable on row click to populate the new expanded element.
Only problem is that we don’t have a bookDetail action yet. We want to add a bookDetail action that will serve up the HTML to display in the row expansion. So let’s take our ’show’ page and break out a template to show the book detail.
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">Id:</td>
<td valign="top" class="value">${fieldValue(bean:book, field:'id')}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Author:</td>
<td valign="top" class="value"><g:link controller="author" action="show"
id="${book?.author?.id}">${book?.author?.encodeAsHTML()}</g:link></td>
</tr>
<tr class="prop">
<td valign="top" class="name">Isbn:</td>
<td valign="top" class="value">${fieldValue(bean:book, field:'isbn')}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Name:</td>
<td valign="top" class="value">${fieldValue(bean:book, field:'name')}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">Publish Date:</td>
<td valign="top" class="value">${fieldValue(bean:book, field:'publishDate')}</td>
</tr>
</tbody>
</table>And now we can render the book to this template from our bookDetail controller action:
def bookDetail = {
render(template:"bookDetail", model:[book:Book.get(params.id)])
}Add a little CSS to change the row expansion background and text colors in list.gsp:
<style>
tr.ymod-expandedData div.ymod-expandedDataContent td {
background: #FFF;
color: #000;
}
</style>And we have dataTable with a rowExpansion that calls a controller method that renders detailed info in a template. Click on any row, and it will expand to show whatever is rendered in the bookDetail action. Click the row again to hide the expansion. Any sorting or pagination will force the expansion to close.
Here is the row expansion demo source code. Just unzip it, then run:
grails install-plugin yui
grails install-plugin bubbling
grails install-plugin grails-ui
grails run-app


10 Comments
I can’t get dataTable to render a list I’m returning as JSON. The column names are rendered, then ‘loading…’ appears under the headers and it stays there.
{"totalRecords":3,
"results":[
{"name":"MyName",
"description":"A new text",
"isActive":true,
"dataUrl":"/myLifeV2/blogEntryItem/edit/1"},
{"name":"A subject",
"description":"A short description",
"isActive":true,
"dataUrl":"/myLifeV2/blogEntryItem/edit/2"},
{"name":"asdf",
"description":"asdf",
"isActive":true,
"dataUrl":"/myLifeV2/blogEntryItem/edit/3"}
]}
gsp looks like this:
<div class="yui-skin-sam">
<gui:dataTable
controller="blogEntryItem" action="blogEntriesListAsJSON"
columnDefs="[
[key:'name' , label:'Name'],
[key:'description' , label:'Description'],
[key:'isActive' , label:'Active']
]"
/>
</div>
and the controller:
def blogEntriesListAsJSON = {
def list = []
def demoList = BlogEntryItem.list(params)
response.setHeader(”Cache-Control”, “no-store”)
demoList.each {
list
I see the problem. Take a look at my previous blog entry and search for ’sortedBy’:
Hi Matthew,
I just thought I’d post a thank you for this series of articles, I hope there are more to come.
We’re currently in the process of implementing our first project in Grails and have used the RichUI plugin for a couple of components. I think that we’ll be changing that for GrailsUI shortly.
Keep up the good work.
r.
Hi Matthew,
Great!!! I tried it, it is really nice.
If RowExpansion window edits fields of row, does dataTable automatically updated?
Nice article indeed! Keep up the good work.
cheers
Vinay
@Vinay If you want to edit the row within the row expansion, I assume you’ll have either a form/submit within the expansion, or some ajaxy stuff within it, right?
Either way, you’ll have to hit another URL to manipulate any view data. And the only way for the datatable to be updated is for it to be sorted or paginated, because those are the only reasons it would request new data apart from a page reload.
If you do this via ajax, the server might update it’s data, but the datatable will not have the updated data until the page is refreshed or it is given a command to paginate or sort. I would suggest that if you modify the table structure within the row expansion via ajax, you should do a page refresh, or a least refresh the div that contains the dataTable to force it to re-render itself with the new data.
I had not considered the possibility of calling a tag (g.createLink) in a controller before!
I have nested GrailsUI datatable in expanded row. It works in FF3.x, Chrome and Opera, however it does not work (nothing is shown although AJAX call is made and JSON data returned) in IE 6 , 7 or 8.
Any suggestions?
@Pedja My only suggestion is to create a JIRA issue for your problem.
How would one render an image inside a cell using this yui table and json?
@Simon: In the controller where you are setting up your JSON data, just include any String HTML markup you want within the data map that is being converted into JSON.
One Trackback
[...] a newly expanded element on row click, using a ‘dataUrl’ JSON field in the table data (more on row expansion here). It should be easy to set up the dataTable tag to navigate to the URL provided in [...]