I'm using grails 3.2.4 version, the scaffolding plugin uses the fields plugin. Add the plugin in the file build.gradle as follows.
dependencies {
...
compile "org.grails.plugins:scaffolding"
...
}
Install the templates with the command
grails install-form-fields-templates
The following files are created int the folder src/main/templates/scaffolding
- AsyncController.groovy
- AsyncSpec.groovy
- Controller.groovy
- create.gsp
- edit.gsp
- index.gsp
- ScaffoldedController.groovy
- show.gsp
- Spec.groovy
I have customized all the views to looks like twitter boostrap. To render the fields it uses the fields plugin. In the index.gsm view I want to customize the table rendering, replacing the g:table tag with my custom code.
This is the generated part for table rendering.
<f:table collection="\${${propertyName}List}" />
This is my custom code, based on the previos scaffolding code on previos grails version and adapted to run on grails 3.2.4 version.
<table class="table table-striped">
<thead>
<tr>
<%
def grailsApplication = grails.util.Holders.grailsApplication
domainObjetc = grailsApplication.domainClasses.find { it.clazz.simpleName == className }.clazz.newInstance()
domainClass= grailsApplication.getDomainClass(domainObjetc.class.name)
excludedProps = grails.persistence.Event.allEvents.toList() << 'id' << 'version'
allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
props = domainClass.properties.findAll { allowedNames.contains(it.name) && !excludedProps.contains(it.name) && it.type != null && !Collection.isAssignableFrom(it.type) }
comparator = new org.grails.validation.DomainClassPropertyComparator(domainClass)
Collections.sort(props, comparator)
props.eachWithIndex { p, i ->
if (i < 6) {
if (p.isAssociation()) {
%><th class="header"><g:message code="${domainClass.propertyName}.${p.name}.label" default="${p.naturalName}" /></th><%
} else {
%><g:sortableColumn property="${p.name}" title="\${message(code: '${domainClass.propertyName}.${p.name}.label', default: '${p.naturalName}')}" /><%
}
}
}%>
<th></th>
</tr>
</thead>
<tbody>
<g:each in="\${${propertyName}List}" var="${propertyName}">
<tr>
<% props.eachWithIndex { p, i ->
if (i < 6) {
if (p.type == Boolean || p.type == boolean) { %>
<td><g:formatBoolean boolean="\${${propertyName}.${p.name}}" /></td>
<% } else if (p.type == Date || p.type == java.sql.Date || p.type == java.sql.Time || p.type == Calendar) { %>
<td><g:formatDate date="\${${propertyName}.${p.name}}" /></td>
<% } else { %>
<td>\${fieldValue(bean: ${propertyName}, field: "${p.name}")}</td>
<% } } } %>
<td class="link">
<div class="btn-group btn-group-xs">
<g:link action="show" id="\${${propertyName}.id}" class="btn btn-primary btn-sm" role="button">
<span class="glyphicon glyphicon-eye-open"></span>
<g:message code="default.button.show.label" default="Show" />
</g:link>
<g:link action="edit" id="\${${propertyName}.id}" class="btn btn-primary btn-sm" role="button">
<span class="glyphicon glyphicon-pencil"></span>
<g:message code="default.button.edit.label" default="Edit" />
</g:link>
</div>
</td>
</tr>
</g:each>
</tbody>
</table>
The scaffolding plugin in grails 3, uses the GStringTemplateEngine to render the view, imports are not allowed in the template, skip to use them and use the full package location, for example to retrieve the grailsApplication in the Holders class inside the template you have to access them with grails.util.Holders.grailsApplication.
Sorting the properties need to be implemented, in the code above is commented as TODO task.
Update:
Sorting properties can be done using DomainClassPropertyComparator class, tested with grails 3.1
I hope to be useful for you.