I have this project at work – it’s a monitoring tool I built in Grails.  It’s a web application that runs a series of tests every hour. The results are stored in a database and there’s a UI layer that reports the results.

Recently our Director of Dev/Ops at work wanted to put my UI on a 60″ TV in the Tech team area.  This way people would have a monitor to see live events going on our production site.   However my UI wasn’t made for a TV.  So I worked on changing the UI to a resolution fit of 1920×1080.

The Director liked it, but he noticed I had set the data in the page manually.  He showed me a trick to dynamically create columns.  This way the data comes back and is dynamically displayed and columns/rows are dynamically created.

Listing Data in Grails

In Grails, you can list all results from a database table like this:


Basically the example domain here is nameLookup (a table of names for example) and the .list() method lists that data out.  Grails has a variety of list options such as listing by a order, in this case .listOrderByName()

Initially I was looping through the results and presenting their pass/fail results on screen:

<g:each in="${NameLookup.listOrderByName()}">
<g:render template="nameRow" model='[cName:"${it.cName}",cNumber:"${it.cNumber}"]'></g:render>

I had that loop going over the data, and passing fields from the database into a template that would spill out a list on the page.

The template looked like this:


def lastResult = InboundC.executeQuery("....[I had a select statement here for my database query to find the last result])[0]


<td style="padding-right: 5px;">
<a href="http://[link to our pcap files]">${cNumber}</a>
<g:if test="${lastResult == 'PASS'}">
<td bgcolor="#7fff00" >
</g:if><g:elseif test="${lastResult == 'FAIL'}">
<td bgcolor="#dc143c" >
<font color="#ffd700">

Basically the index.gsp page loops over that rendered template, passing in the name for each test, and the template looks up the pass/fail result for each name being passed in.  The only downside, is that the code renders one big long column.  Since we’re using a TV now, we needed to make use of the widescreen space.

Multiple Column Solution

What I initially did, was make a table by hand, with 4 columns and put 10 render clauses in each td tag.  So I was manually displaying the results.  But, in our case we add and remove entries all the time.  That would mean each time we add or remove an entry to the test, we would have to update the page manually.

The director at our company showed me this little trick using mod.

In the code below we have a table, but we put in a variable “counter” and set it to 0.  We also define a variable for inboundCLookup to pull all results from the database using the .list method.

We start the loop with the g:each call, pointing to the inboundCLookup results we get back and below that we increment the counter + 1.  This means that each result we get back, we add 1 to the counter.  Then we get to our column logic..

An if condition checks if this is the first item (counter == 1) if so, we create the TR and then we render a TD with the call to the template.  Below that we check for the last item in the list… by saying let’s grab the size of the results coming back (size in groovy is the count.)  So we have a conditional statement here saying if the counter == inboundCLookup.size()  then we end the row.

However, if the counter.mod(3) == 0 then we end the current row and open another.  The value 3, is the columns I want. I want 3 columns.  This will display results from left to right, when it gets to the third column, it starts a new row… like this:

Item 1    Item 2    Item  3
Item 4   Item 5


def counter = 0
def inboundCLookup = InboundCLookup.listOrderByCName()
<g:each in="${inboundCLookup}">
counter = counter +1

<g:if test="${counter == 1}">

<g:render template="nameRow" model='[cName:"${it.cName}",cNumber:"${it.cNumber}"]'></g:render>
<g:if test="${counter == inboundCLookup.size()  }">
</g:if><g:elseif test="${counter.mod(3) == 0 }">
</tr> <tr>



No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *