0

I am working on my scaffolding templates, more specifically the create.gsp file. I would like to get the properties defined in my class. I saw many posts online on how to do this but none of them seem to work.

Tried the following (grails templates - scaffolding controller):

<%
    domainClass.properties.each {
        println "    ${it.type} ${it.name}"
    }
%>

Getting the following error when using generate-all:

Error occurred running Grails CLI: No such property: domainClass for class: groovy.lang.Binding

Also tried this approach :

<% import grails.persistence.Event %>

<%  
excludedProps = Event.allEvents.toList() << 'version' << 'dateCreated' << 'lastUpdated'
persistentPropNames = domainClass.persistentProperties*.name

props = domainClass.properties.findAll { persistentPropNames.contains(it.name) && !excludedProps.contains(it.name) && (domainClass.constrainedProperties[it.name] ? domainClass.constrainedProperties[it.name].display : true) }
Collections.sort(props, comparator.constructors[0].newInstance([domainClass] as Object[]))

for (p in props) {  %>

<g:message code="${domainClass.propertyName}.${prefix}${p.name}.label" default="${p.naturalName}" />

<% } %>

Getting the following error when using generate-all:

Error occurred running Grails CLI: Failed to parse template script (your template may contain an error or be trying to use expressions not currently supported): startup failed:
GStringTemplateScript4.groovy: 2: Unknown type: IMPORT at line: 2 column: 54. File: GStringTemplateScript4.groovy @ line 2, column 54.
   turn { out -> out << """""";  import gra

Am I missing something or is the approach different for Grails 3?

Using Grails Version 3.0.11

I appreciate any help!

Community
  • 1
  • 1
NBabineau
  • 19
  • 3

2 Answers2

2

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.

Ibrahim.H
  • 1,062
  • 1
  • 13
  • 22
  • tried on Grails 3.3.10 and got No such property: grails for class: groovy.lang.Binding, happens on accessing grails.* packages. Also DomainClassPropertyComparator is deprecated in 3.3.10. Check https://github.com/grails/grails-core/issues/10156#issuecomment-469381767 – Pablo Pazos May 13 '20 at 01:37
  • Thats is correct, DomainClassPropertyComparator is deprecated in 3.3.10. – Jose Luis Bracamonte Amavizca May 14 '20 at 05:52
0

In Grails 4, in my case, Grails 4.0.10 to obtain the domains list, the procedure must be changed to this:

MappingContext mappingContext = grailsApplication.getMappingContext()
def entities = mappingContext.getPersistentEntities()

And to obtain an entity specifically:

def entity = mappingContext.getPersistentEntity(fullEntityName) 

To obtain the persistent properties of the entity:

def persistentProperties = entity.getPersistentProperties()

And to get the excluded prpoerties:

def excludedProps = Event.allEvents.toList() << 'id' << 'version'

The libraries that need to be imported are as follows:

import grails.artefact.DomainClass
import grails.core.GrailsApplication
import grails.persistence.Event
import org.grails.datastore.mapping.model.MappingContext

And so we can follow the same logic suggested in previous posts!