Is there a favorite Grails custom tag?

I use tags in my projects. I was looking through user tags at grails.org to find new tags for my library.

http://www.grails.org/Contribute+a+Tag

I was wondering if the people in the StackOverflow community have their favorite tag that they would like to share.

+6
grails
source share
3 answers

I found that the DecimalFormat class (and the Grails formatNumber format extension tag) is a bit opaque for certain use cases, and I still haven't found a reasonable way to make it fairly simple formatting without some ugly preprocessing to generate the appropriate format string. A few months ago, I put together a simple number formatting tag that essentially creates a format string and does some minimal processing for the number itself.

It's not as generic or elegant as I would like (all we need at the time is super basic, but it still holds some ugly processing from GSP), but it should be easy to read, and this is obvious where you can it would be trivial to improve (i.e. make scaling iterative rather than naive if-elseif slop, allowing the user to pass custom scaling tokens, allowing you to use your own number validator as a parameter, etc.).

// Formats a number to 3 significant digits, appending appropriate scale marker // (k, m, b, t, etc.). Defining var allows you to use a string representation // of the formatted number anywhere you need it within the tag body, and // provides the scale as well (in case highlighting or other special formatting // based upon scale is desired). def formatNumberScaled = {attrs, body -> // number, prefix, suffix, invalid, var Double number String numberString String scale try { number = attrs.'number'.toDouble() } catch (Exception e) { number = Double.NaN } if (number.isNaN() || number.isInfinite()) { numberString = scale = attrs.'invalid' ?: "N/A" } else { Boolean negative = number < 0d number = negative ? -number : number if (number < 1000d) { scale = '' } else if (number < 1000000d) { scale = 'k' number /= 1000d } else if (number < 1000000000d) { scale = 'm' number /= 1000000d } else if (number < 1000000000000d) { scale = 'b' number /= 1000000000d } else if (number < 1000000000000000d) { scale = 't' number /= 1000000000000d } String format if (number < 10d) { format = '#.00' } else if (number < 100d) { format = '##.0' } else { format = '###' } format = "'${attrs.'prefix' ?: ''}'${format}'${scale} ${attrs.'suffix' ?: ''}'" numberString = g.formatNumber('number': negative ? -number : number, 'format': format) } // Now, either print the number or output the tag body with // the appropriate variables set if (attrs.'var') { out << body((attrs.'var'): numberString, 'scale': scale) } else { out << numberString } } 
+1
source share

I have a tag “fmt: relDate” that gives you relative Twitter-3 dates “3 days ago”, “less than 30 seconds ago”, etc., in real time as a tooltip.

The current implementation is basically a giant chain of if / then statements with those boundaries that I like. A binary search-based algorithm would be better (in the sense of "more efficient"), and in the current implementation my personal preferences are encoded in it, so I don’t want to share the tag.

+1
source share

I have a remote page tab that helps me paginate pages on ajax. This improves the default tab and accepts custom arguments.

Here is the code:

 class CustomRemotePaginateTagLib { static namespace = 'myTagLib' /** * Creates next/previous links to support pagination for the current controller * * <g:paginate total="$ { Account.count() } " /> */ def remotePaginate = {attrs -> def writer = out if (attrs.total == null) throwTagError("Tag [remotePaginate] is missing required attribute [total]") if (attrs.update == null) throwTagError("Tag [remotePaginate] is missing required attribute [update]") def locale = RequestContextUtils.getLocale(request) def total = attrs.total.toInteger() def update = attrs.update def action = (attrs.action ? attrs.action : (params.action ? params.action : "list")) def controller = (attrs.controller ? attrs.controller : params.controller) def offset = params.offset?.toInteger() def max = params.max?.toInteger() def maxsteps = (attrs.maxsteps ? attrs.maxsteps.toInteger() : 10) if (!offset) offset = (attrs.offset ? attrs.offset.toInteger() : 0) if (!max) max = (attrs.max ? attrs.max.toInteger() : 10) def linkParams = [offset: offset - max, max: max] if (params.sort) linkParams.sort = params.sort if (params.order) linkParams.order = params.order if (attrs.params) linkParams.putAll(attrs.params) linkParams['action'] = action linkParams['controller'] = controller def linkTagAttrs = [url: "#"] if (attrs.controller) { linkTagAttrs.controller = attrs.controller } if (attrs.id != null) { linkTagAttrs.id = attrs.id } // determine paging variables def steps = maxsteps > 0 int currentstep = (offset / max) + 1 int firststep = 1 int laststep = Math.round(Math.ceil(total / max)) // display previous link when not on firststep if (currentstep > firststep) { linkTagAttrs.class = 'prevLink' def prevOffset = linkParams.offset def params = attrs.params ?: [] params.'offset' = prevOffset linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params) writer << link(linkTagAttrs.clone()) { (attrs.prev ? attrs.prev : g.message(code: 'default.paginate.prev', default: 'Previous')) } } // display steps when steps are enabled and laststep is not firststep if (steps && laststep > firststep) { linkTagAttrs.class = 'step' // determine begin and endstep paging variables int beginstep = currentstep - Math.round(maxsteps / 2) + (maxsteps % 2) int endstep = currentstep + Math.round(maxsteps / 2) - 1 if (beginstep < firststep) { beginstep = firststep endstep = maxsteps } if (endstep > laststep) { beginstep = laststep - maxsteps + 1 if (beginstep < firststep) { beginstep = firststep } endstep = laststep } // display firststep link when beginstep is not firststep if (beginstep > firststep) { linkParams.offset = 0 def params = attrs.params ?: [] params['offset'] = linkParams.offset linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params) writer << link(linkTagAttrs.clone()) { firststep.toString() } writer << '<span class="step">..</span>' } // display paginate steps (beginstep..endstep).each {i -> if (currentstep == i) { writer << "<span class=\"currentStep\">${i}</span>" } else { linkParams.offset = (i - 1) * max def params = attrs.params ?: [] params['offset'] = linkParams.offset linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params) writer << link(linkTagAttrs.clone()) { i.toString() } } } // display laststep link when endstep is not laststep if (endstep < laststep) { writer << '<span class="step">..</span>' linkParams.offset = (laststep - 1) * max def params = attrs.params ?: [] params['offset'] = linkParams.offset linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params) writer << link(linkTagAttrs.clone()) { laststep.toString() } } } // display next link when not on laststep if (currentstep < laststep) { linkTagAttrs.class = 'nextLink' linkParams.offset = offset + max def params = attrs.params ?: [] params['offset'] = linkParams.offset linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params) writer << link(linkTagAttrs.clone()) { (attrs.next ? attrs.next : g.message(code: 'default.paginate.next', default: 'Next')) } } } 
+1
source share

All Articles