I've been using tags in my projects. I was browsing the custom tags on grails.org to find some new tags for my library.
http://www.grails.org/Contribute+a+Tag
I was wondering if people in the StackOverflow community have a favorite custom tag that they would like to share.
I have a remote paginate tab, that helps me paginate results via ajax. Its an improvement over the default tab and takes in custom arguments.
Here's 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'))
}
}
}
I find the DecimalFormat class (and Grails's formatNumber tag by extension) a bit opaque for certain use cases, and I still haven't found a reasonable way to do some pretty basic formatting with it without some ugly pre-processing to generate an appropriate format string. I threw together a simple number formatting tag several months ago which essentially constructs a format string and does some minimal processing to the number itself.
It's not as generic or elegant as I'd like (it's all we needed at the time - it's super basic, but it still keeps some ugly processing out of GSPs), but it should be easy to read, and it's obvious where it could be trivially improved (ie making the scaling iterative instead of naive if-elseif slop, allowing the user to pass in custom scaling markers, allowing for a custom 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
}
}
I have a "fmt:relDate" tag that gives you Twitter-like relative dates "3 days ago", "less than 30 seconds ago", etc., with the real time as a tooltip.
The current implementation is basically a gigantic chain of if/then statements with the boundaries that I like. A binary-search based algorithm would be better (in the sense of "more efficient"), and the current implementation has my personal preferences encoded into it, so I'm reluctant to share the tag.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.