Wednesday, July 28, 2010

preventing grails-ui tooltip from disappearing

Today I tried to prevent the grails-ui tooltip from disappearing after 5 seconds when the mouse is not moved. From the YUI doc I knew I had to alter a parameter 'autodismissdelay', but I haven't found a way to pass this parameter with the taglib. Unfortunately the only way I succeeded was to alter the ToolTip.js in grails-ui-1.2-SNAPSHOT\web-app\js\grailsui where I changed the value from 5000 to -1.

Tuesday, July 20, 2010

making export plugin work with filterpane

The export plugin and the filterpane plugin for themselves work like a charm. If you try to export your filtered list however, you run into problems. The root of this problem has been recognized during an earlier discussion on the mailing list.

The problem: You have to extend the filter action to deal with the export. Therefore you have to apply the filterService.filter method with the same filter params as when you applied the actual filter.

The workaround (not a perfect solution): To solve this problem I save the filter params in the session. When export is applied on the filter action the filter params are retrieved from the session. That works well although I'm not very happy with storing so much information in the session permanently.

The Code:

Here I have modified the example from the plugin page. The export-part of the list action is extracted and gets its own export closure, which can then be used by the filter action as well. I also do not explicitly name the parameters as I am using this code in a scaffolded controller. Therefore a list is fetched of all properties, excluded those that are not really representative.

def export = {attrs ->
  
  def response = attrs.response
  println attrs.exportList
  
  def excluded = grails.persistence.Event.allEvents.toList() + ["mapping", "lastModifierId", "hasMany", "class", "belongsTo", "constraints", "searchable", "attached", "errors", "metaClass", "log", "object", "version", "beforeInsert", "beforeUpdate", "mappedBy", "springSecurityService", "type", "typeLabel"]
  List fields = ${className}.newInstance().properties.keySet().toList().findAll { !excluded.contains(it) && !isId(it)}

  response.contentType = org.codehaus.groovy.grails.commons.ConfigurationHolder.config.grails.mime.types[attrs.format]
  response.setHeader("Content-disposition", "attachment; filename=${className}.\${attrs.extension}")
  
  exportService.export(attrs.format, response.outputStream, attrs.exportList, fields, [:], [:], [:])
 }
 
 def isId(def label)
 {
  label.reverse().startsWith("dI")
 }

 def filter = {  
  if(!params.max) params.max = 10
  println "out:" +params
  if(params?.format && params.format != "html" && session.filterParams)
  {
   def exportList = filterService.filter( session.filterParams, ${className} )
   println "list:" + exportList
   export(response: response, extension: params.extension, format: params.format, exportList: exportList)
  }
  
  session.filterParams = params
  
  render( view:'list',
   model:[ ${propertyName}List: filterService.filter( params, ${className} ),
   ${propertyName}Total: filterService.count( params, ${className} ),
   filterParams: com.zeddware.grails.plugins.filterpane.FilterUtils.extractFilterParams(params),
   params:params ] )
 }

EDIT: Steve made me aware that it is easy to extract the necessary parameters with a helper method. One should therefore rather modify the export taglib to pass the correct parameters to the filter action. Also see Steve's comment below.

EDIT: Please forget about most of the crazy stuff I have done here :-) You can pass the filterParams with the export tag like this:


Thursday, July 15, 2010

just some notes about working with scaffolding code

When I modify scaffolding templates I always wonder how to obtain different properties like e.g. the class name or the id of the current instance. A general remark: What confused me most in the beginning was notation like \${${...}}. What you have to know is that \$ is escaped and is therefore what you can normally achieve in GSPs with a $. The unescaped $ within that clause is for the template. When actual code is generated from the template those $ are resolved immediately. You can therefore only use properties that are kown at this stage. Here are a few examples.

\${${className}}
This resolves the long version of the current class name

You often see that properties of an instance are iterated. The variable name of a property is then 'p':

${p.name}
This is the name of the property as it is used in the current instance. You need this value if you want to access that property in a controller (similar to this: domainClassInstance."${params.propertyName}").

${p.referencedDomainClass?.propertyName}
This one gives you the propertyName of the referenced domain class, thus the domain class of the property p. The propertyName is equal to the controller name if you stick to the convention.

${p.referencedDomainClass?.fullName}
Sometimes you need to have the full class name with the whole package notation. This can be useful for domain class instantiation (see my last post).

${p.referencedDomainClass?.shortName}
This is the actual class name omitting the package notation.

how to get a domain class instance from a class string (e.g. via params)

I had the problem that I could pass the class name from my gsp to my controller, but I didn't know how to work with that being a String. Luckily I stumpled about the solution in a code example somewhere on the web:

def grailsApplication
grailsApplication.getDomainClass(params.className).newInstance()

Wednesday, July 7, 2010

creating custom grails artefacts

I wanted to create my own kind of artefacts, in order to make easy extension of my application easier. I already had an interface called "Module" that has been implemented by classes that add new content to my sites. There were two problems that artefacts could solve for me:
  1. Dynamic reloading on code change won't work in non-artefact classes :-(
  2. Whenever I introduce a new module, I have to manually register it with a service handler.
As an artefact a module is automagically added and corresponding classes are hot-swapped if changed. I followed the great instructions on this page to get started.

The only thing I have done differently was in the last step, when I access the modules like this:

def grailsApplication

def modules = grailsApplication.getModuleClasses().collect { 
 it.referenceInstance
}

The thing is, getModuleClasses() returns 'GrailsClasses' and not java.lang.classes. At first I took the wrong road from there, trying to use it.class, which is then the DefaultModuleClass that you had to define with no abilities. I wondered how to obtain the underlying 'real' module class. Looking into the properties of the GrailsClass I found out that you can get the class of the module by accessing 'class'. But you can do even better: A instance is already attached and can be accessed by it.referenceInstance.

Happy artefacting!

closures in bootstrap.groovy not working

Just a note: Closures are currently (I believe since 1.2.2) not working in Bootstrap.groovy. This is also documented in JIRA. What you can do however, is use methods instead.

Monday, July 5, 2010

richui:treeView, expand and select

I extended the richui:treeView component a little bit as I wanted to be able to have a little control about extending and selecting nodes depending on what was happening on the rest of my page. I therefore followed the instructions on:

http://6by9.wordpress.com/2009/06/18/62/

Additionally to the extended attribute, I also added the selected attribute to the code:

builder.yieldUnescaped "    function createNode(text, id, icon, pnode, expand, select){\n"
builder.yieldUnescaped "        var n = new YAHOO.widget.TextNode(text, pnode, false);\n"
builder.yieldUnescaped "        n.labelStyle=icon;\n"
builder.yieldUnescaped " if(expand == \"true\"){\n"
builder.yieldUnescaped "  n.expanded = true;\n"
builder.yieldUnescaped " }\n"
builder.yieldUnescaped " if(select == \"true\"){\n"
builder.yieldUnescaped "  n.selected = true;\n"
builder.yieldUnescaped " }\n"

and a few lines later this one had to be modified twice:

builder.yieldUnescaped "    createNode(\"" + it.@name + "\", \"" + it?.@id + "\", \"" + it.@icon + "\", $parent, \"" + it.@expanded + "\", \"" + it.@selected + "\");\n"

Afterwards I was able to set the expanded and the selected property during XML generation as node properties. The expanded attribute is immediately accepted by the YUI widget, but selected is not. I found out that you have to look for the selected property yourself and then use the focus method to apply your selection to the actual node:

var selectedNode = YAHOO.widget.TreeView.getTree('tree').getNodeByProperty('selected',true);
if (selectedNode){
 selectedNode.focus();
}

With this, I was able to create the YUI treeView with nodes expanded and selected as I wanted.