Let's say I have a Domain Class (let's call it Person) with various String properties, like the following:
class Person {
String favoriteColor
String favoriteAnimal
static constraints = {
favoriteColor(nullable: true)
favoriteAnimal(nullable: true)
}
}
I am trying to use dynamic scaffolding only for now to see how far it will take me with this great rapid application development ability I've heard so much about...but that stops right here with this class.
The Problem
The problem is that the default behavior in Grails dynamic scaffolding is that the index page will show the first X amount of entries, with the first field of each entry turned into a hyper-link that allows you to view/edit that specific item.
That's really great, but sadly when the first field can be null the index page lists the entry - but there is no link to click to be able to view/edit the item in detail.
The easy solution, of course, is to make a non-nullable field the first item...but now I have a class which has properties where all individual fields can be null, with custom validation to ensure at least one field contains something. So that won't work in this annoying edge case (yet which is a business requirement, there is no single field I can guarantee will never be null here).
What is the Grails way to fix this issue, without just completely throwing out scaffolding?
If I need only to alter the index function of this one controller to fix the issue, I'd like to do that with minimal code and not have to ditch dynamic scaffolding for the rest of the controller. I really like the rapid prototype ability of scaffolding (and Grails), and want to make the most of this feature set.
The Solution
Using Dónal's answer suggesting install-templates, I simply ran the grails install-templates
command and then went to my projects src->templates->scaffolding->index.gsp file. While it is full of scriptlets and such that make it somewhat tricky to understand, the solution proved remarkably easy.
In the html for the table section, there is a bit of code similar to this:
<table>
<thead>
<tr>
<% excludedProps = Event.allEvents.toList() << 'id' << 'version'
...
By simply changing it to the following, you get an extra header row for an edit function:
<table>
<thead>
<tr>
<th> </th>
<% excludedProps = Event.allEvents.toList() << 'id' << 'version'
...
Then lower you have code such as:
<g:each in="\${${propertyName}List}" status="i" var="${propertyName}">
<tr class="\${(i % 2) == 0 ? 'even' : 'odd'}">
<% props.eachWithIndex { p, i ->
if (i == 0) { %>
<td><g:link action="show" id="\${${propertyName}.id}">\${fieldValue(bean: ${propertyName}, field: "${p.name}")}</g:link></td>
With a simple change to get rid of the link on the first property name column, and instead add your own Edit link to the first column, the problem is fixed:
<g:each in="\${${propertyName}List}" status="i" var="${propertyName}">
<tr class="\${(i % 2) == 0 ? 'even' : 'odd'}">
<% props.eachWithIndex { p, i ->
if (i == 0) { %>
<td><g:link action="show" id="\${${propertyName}.id}">Edit</g:link></td>
<td>\${fieldValue(bean: ${propertyName}, field: "${p.name}")}</td>
So with one line changed and two lines added to a single file (after a Grails command) you have changed default scaffold behavior to suit your fancy and support nullable fields. Problem solved!
The templates that are used to generate the scaffolded views can be modified. Before you can modify them you must first run the install-templates command. After you've installed the templates, modify the template that generates the list view such that it adds a view link (or buttons) to each row, rather than linking the value of a particular property. Then you will always be able to view the domain instance, regardless of which properties are null.