The GUI dataTable is the most complicated tag to set up, because there is a lot of data configuration required. You have to correctly set up the column definitions in the tag, and you have to set up the controller to serve up the proper data.
Let’s start from scratch with the basic book / author demo.
class Book {
String name
String isbn
static belongsTo = [author:Author]
Date publishDate
}We want a to create a dataTable tag that will display all the Books I loaded up in Bootstrap.groovy. First off, since we know we’ll be using a dataTable GUI tag in our page, we need to declare this to the resources tag in the page head element. The resources tag will include all the YUI CSS and JavaScript files it knows it will need to build a dataTable.
<gui:resources components="['dataTable']"/>Now for the dataTable tag. We’ll set up something basic first.
<div class="yui-skin-sam">
<gui:dataTable
controller="book" action="dataTableJSON"
columnDefs="[
[key:'id', label:'ID'],
[key:'name', label:'Name'],
[key:'isbn', label:'ISBN'],
[key:'author', label:'Author'],
[key:'publishDate', label:'Published on']
]"
/>
</div>So our dataTable is now expecting at least five fields for each row of data it receives: id, name, isbn, author, and publishDate. We need to set up a controller action that will serve this data in JSON format for us. In the dataTable tag, I’ve already decided the action will be called ‘dataTableJSON’, so let’s code it.
def dataTableJSON = {
def books = Book.list(params)
def data = [
totalRecords: Book.count(),
results: books
]
render data as JSON
}The dataTable will be calling this method to populate itself with data, so the params will be the dataTable’s filtering information, like how to sort, where to start, etc. The GUI dataTable knows only to send params that can be understood by the Grails list domain class function, so we can just send the params directly into Book.list(params).
The totalRecords must be the total count of records that might be displayed in the table, not the total count of records that is being passed back by the action.
In the data map above, the ‘results’ key is where the dataTable will look by default for its data. This can be changed in the dataTable tag. For example, if you added resultsList=”myResults” in the dataTable tag, and changed the controller action to return myResults: list instead of results: list, things would work fine.
At this point, we should have something that looks very much like this:
Not bad, but that date looks nasty because it is handled as a String (there is no type formatting in the dataTable yet). And I would really rather see the Author name rather than id. Also, we don’t really want to see the Book id in the table either. So we’re going to have to dig into our controller and take a firmer grip on how the JSON is being created from our domain class.
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
]
}
def data = [
totalRecords: Book.count(),
results: formattedBooks
]
render data as JSON
}At this point, we are in total control of the JSON being produced. Grails isn’t converting our domain objects into JSON anymore. We added a custom date format, and we drilled down into the Author to get a name. We also omitted the id field, so we’ll have to go into our tag and omit the id field in the column definitions. If the id field was left in the tag’s columnDefs, there would be an empty column in the table. While we are at it, let’s make all the columns sortable, resizeable, and draggable.
<div class="yui-skin-sam">
<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"
/>
</div>Notice that the id column definition is removed, sortable and resizeable were added to each column def, draggableColumns is on, and we added a sortedBy attribute.
Now our table looks like this:
But there is no mention anywhere of how many total records there are! Sure, you can see how many pages, how many rows per page, and do the math, but who wants to do that? So now lets say we want to display 12 rows per page, and show the total amount of records. Well, this doesn’t have anything to do with our data, so the controller action that provides the data will be fine as it is. But we do need to specify some more information to the tag on how to render the paginator.
<div class="yui-skin-sam">
<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'
]"
/>
</div>So we’ve added rowsPerPage, and a paginatorConfig. The rowsPerPage is really a paginatorConfig value, but for the convenience of not having to define the paginatorConfig only for setting rowsPerPage, it was opened up as a main attribute. All the paginatorConfig values are passed through to the dataTable’s YUI Paginator object upon creation. More information on Paginator templates can be found on the YUI Paginator page.
Now we should have a 12-row table with total records displayed:
And there you go. Sortable, resizeable, draggable columns, with a total record count and customized data formatting. If you want to have a hands-on look, here is the source code. Just unzip it, then run:
grails install-plugin yui
grails install-plugin bubbling
grails install-plugin grails-ui
grails run-app



34 Comments
Excellent tutorial for an excellent Grails plugin!
But “just unzip and run” won’t work, because you need to install the needed plugins first:
grails install-plugin yui
grails install-plugin bubbling
grails install-plugin grails-ui
Make sure you get the order right …
@Michael Thanks for the note, I’ve updated the post.
How about inline cell-editing? Does the Grails UI plugin support that out of the box?
@Roshan No it doesn’t, but that is an excellent feature request. I have not even looked into doing something like that yet, so it may even be possible using the config pass-through parameters. But I would guess not. I logged a JIRA issue for this to get into 1.1.
Really useful. I initially had left totalRecords out but the reason it was not working was that my domain was in a package - as a result I logged a Jira - http://jira.codehaus.org/browse/GRAILSPLUGINS-628
@boardtc Thanks for logging that JIRA. I’ve assigned it to 1.0.2. :)
How would you add a column that is not in the JSON, say a column that displays an icon, such as a “trash” icon to delete a record?
@Roshan There is really no easy way to do that currently. The best way would be for you to modify your JSON to include all the data you want displayed. You can easily add the some simple markup within the JSON that identifies the row (a span with class and id for example), to which you can add an icon too via CSS. Then in the view, attach listeners to all the spans with that class, which will call the remove URL.
I have an error, “Data error”, I have not rows in table, why?
Thanks
@valmanar In order to help you out, you’ll have to give me more information. Can you paste the tag and the controller method that is populating it?
Hi,
I cannot seem to get pagination working. I have stripped the table down to one id field and i still cant get it to work :( My Tag is:
My Controller action is:
def dataTest = [[id:1],[id:2],[id:3],[id:4],[id:5],[id:6],[id:7],[id:8],[id:9],[id:10],[id:11]] def data = [ totalRecords: dataTest.size(), results: dataTest ] //println data println data as JSON render data as JSONCan anyone help??
Thanks!
The problem is that your “totalRecords” field tells the paginator how many total items can possibly be displayed, but you are telling it how many you are sending back to the table.
For example, if you have 1000 records, but you want to display them 10 at a time, you’ll send a list of ten items in your JSON data, but the totalRecords will say ‘1000′.
Ah i see, i would of never of figured it calls the controller each time. Much appreciated!!!
Is it possible to conditionally highlight a row in the table, say if a column value goes over a certain value then highlight the row red??? If so, what do you think would be the best way to do this?? Thanks again!
I’m sorry I don’t have time to give a more detailed explanation, but I’m packing to leave the country today. I can offer you this advice, however. Take a look at this YUI example of conditional row coloring. You’ll have to create a javascript row formatter like this:
// Define a custom row formatter function var myRowFormatter = function(elTr, oRecord) { if (oRecord.getData('Quantity') < 40) { Dom.addClass(elTr, 'mark'); } return true; };… within script tags, and get the logic right for the condition. You’ll also have to add the CSS for the ‘mark’ class to format the row properly, but you can look at the example and see all that. Create this formatter before you declare your gui:dataTable, then pass it in as an attribute, like formatRow=’myRowFormatter’. This will include your formatter as a config parameter when the data table is created.
Let me know how that works for you!
Thanks Matthew, my Javascript isn’t too great but i will give it a try. Have a safe trip and Happy New Year!
This doesn’t seem to work because when the tagLib renders the property it is rendered as ‘formatRow’: ‘myRowFormatter’ , and the quotes around myRowFormatter probably makes the browser think this is a String rather than a variable (i think). Just to test, I hardcoded ‘formatRow’: myRowFormatter into the tag library and it worked (after i added a reference to the yahoo DOM, “var Dom = YAHOO.util.Dom;” that is).
Could this be considered a bug when adding properties that are JavaScript variables or have I messed up in some way?
Thanks,
John
Seems like a bug to me. There may be a workaround by trying:
YAHOO.util.Event.onDomReady(function() { GRAILSUI.myTableId.formatRow = myRowFormatter; });After the gui:dataTable tag. But I’m not sure if it will work.
I logged the posible bug under http://jira.codehaus.org/browse/GRAILS-3806. Its my first bug report in JIRA, so please let me know if i made a hash of it! Thanks for your help!
Hi,
Try to do it this way:
YAHOO.util.Event.onDOMReady(function() {
GRAILSUI.myTableId.set(”formatRow”, myFormatter);
});
Works pretty fine to me :)
The “formatRow” is not a property, but a “config attribute” - these are set by the set() method of YUI widgets.
Hope that helps!
I do not want to display the ID for a particular row but when I edit the row inline, I need the ID of that particular row so that I can update the correct row in the DB. Is there a way to add a column to the DataSource but not display it so that it populates the RecordSet? I tried removing the column from the ColumnDef but then it does not add that data to the underlying RecordSet.
@Prashant Add hidden=”true” to your columnDefs for id. See the YUI DataTable docs for a full list of applicable columnDef attributes.
Matthew, the problem with hidden=”true” is that it hides the column by reducing the breadth of the column. The table still has that column but it too narrow to display data.The table looks a little odd with such a narrow column.
The way this would work is to provide a column in the DataSource for the table but not provide it in the columnDef. Is there a way to achieve this using GrailsUI?
@Prashant You need to do it this way:
GRAILSUI.myDataTable.getDataSource().responseSchema.fields.push(’id’);
});
It’ll make the responseSchema be aware of the new field (’id’), but it won’t be rendered to the dataTable itself because it’s not declared in the columnDefs.
Of course, your controller has to export the ‘id’ field as well as other fields.
Good luck!
Thanks xis , that works.
Great solution, @xis. So do you think there is a way to better incorporate this requirement into Grails-UI?
Hi all,
A question from a beginner: I used GrailsUI before and it worked. Now when I access the controller, it doesn’t render the data but opens a pop up in Firefox (or IE) and tells me I am trying to open an object which is application/json from http://localhost:8080. If I open this with Firefox it displays the correct data. I can see a text looking like (not complete here):
{”totalRecords”:4,”results”:[{"id":1,"class":"Patient","dob":new Date(160556400000),"firstName":"John","lastName":"Doe","sex":"Male"}, ...
My code is fairly simple:
import grails.converters.JSON
def foo = {
def mPatients = Patient.list()
//return [ patients : patients ]
def data = [
totalRecords: mPatients.size(),
results: mPatients
]
render data as JSON
}
GSP:
Simple GSP page
FYI: Grails 1.0.4 and GrailsUI 1.0.2.
Any idea? Thanks!
Phil
@philcms1 Somehow I lost part of your comment. To find an answer to your question, your best bet is to send a message to the grails user mailing list. More info here.
Hi,
very nice tutorial, thank you very much.
I have the following question, I have a domain class with string properties and they have a maxSize:1000.
The load to the dataTable is ok, but the they are displayed on a single line. I tried to find some settings to apply on the component to make the text (wrap like in Excel) ,but could not find anything related. Maybe making the columns not autoresizeable could do it, but i don’t know how.
could you please suggest a hint on how to do this.
thanks
@alertgn You can find all the options for a DataTable at the YUI docs page. Take a look at the maxAutoWidth column definition option.
Matthew,
Thanks for the response. I already read the documentation and have tried the maxAutoWidth property. This property works for the container, the component itself, but has no effect on the contents. I didn’t find anything that does it automatically, maybe it is in some javascript functions, but I am not good at using javascript.
I achieved the desired effect using string manipulations and formatting in the controller, but I find such solution very ugly and complicated. especially with many datatables and “unpaternable” contents.
Thanks again, I’ll keep digging
@ albertgn You don’t know javascript very well? If you are a UI developer and you trying to do something this complicated with the GUI, I think I have to defer you to a previous post of mine. ;)
@albertgn I had the same issue, large data column that was restricted to one long line. I figured out all the css uses white-space: nowrap, when we want white-space:normal. It’s amazing how many places the css is specified in all the plugins, but I finally found one that expanded the row height based on text. Under \web-app\plugins\grails-ui-1.0.4\css\grailsui\datatable.css, change the following from ‘nowrap’ to ‘normal’.
.yui-skin-sam .yui-dt-liner { white-space:normal; }
Matthew - Excellent article. While working on the text display issue, I tried setting maxAutoWidth. That seems to cause a bug in my table, such that if maxAutoWidthis set on just one column, it adds blank padding to all columns. No matter the width or text inside any column, 60% of the field is blank. Even if I make them resizable, it will always make the columns wider and keep about 60% blank. Happens in Firefox3 and IE7. Not sure if it’s a bug in GrailsUI or YUI, but thought I’d mention it.
One Trackback
[...] issue that is likely of interest to anyone using the YUI CSS foundation (Reset, Fonts, and Grids).Using GrailsUI DataTable Tag: Matt Taylor writes: “The GUI dataTable is the most complicated tag to set up, because there [...]