tags:

views:

691

answers:

2

I am banging my head up against the wall about what I think would be a very simple problem to resolve in Grails:

Say that I have shopping-cart-like model; so a class Cart that hasMany items, and each item belongsTo the cart. In general, I don't care about the order of the items in the cart - I don't care what order they're stored in, calculated in, etc. HOWEVER, I do want to DISPLAY them in the same order. It seems to me that this logic should be able to exist ENTIRELY in the view layer, but the only solutions I've been able to find tell me to declare items as a SortedSet in the model layer. This also affects my controller layer, as simple List operations such as .collect{} now require extra syntactic jumping around to keep the type conversions correct and preserve my sorting.

To me, this is nuts, so I must be missing something simple! Is there any way, for example, to do something like <g:each in="${cart.items.sort{it.name}}"> or something similar, so that I can enforce a consistent display order ONLY at the output / view layer? EDIT - See Matt's answer below; a version of this does actually work.

Thank you for any advice or pointers!

A: 

This 3rd party tag looks like it will do what you need. If not, you can always make your own tag. The Tag class could do the sorting like this

class SortTagLib {

    static namespace = 'sort'

    def sort = { attrs ->

        // A closure that does the sorting can be passed as an attribute to the tag.
        // If it is not provided the default sort order is used instead
        def sorter = attrs.sorter ?: {item1, item2 -> item1 <=> item2}
        sorter = sorter as Comparator        

        // The collection to be sorted should be passed into the tag as a parameter
        Collections.sort(attrs.items, sorter)
    }
}

This tag could then be used to sort a collection of objects by their name property like this:

<sort:sort items="someCollection" sorter="${someComparatorClosure}"/>

The collection referred to by someCollection will be sorted in-place when the tag is executed.

Don
That is great. I eventually stumbled on a similar solution along the same lines using a ProxyGenerator and the Comparator interface, but yours is much cleaner, and definitely the kind of solution I was looking for. thanks!
bprotas
+2  A: 

You can also use the sort methods available for Collections / Maps as defined here: http://groovy.codehaus.org/api/org/codehaus/groovy/runtime/DefaultGroovyMethods.html

I personally have found it fairly easy to do this in conjunction with a <g:each/> tag in my GSP:

<!-- Books sorted by title -->
<g:each in="${ books.sort{a,b-> a.title.compareTo(b.title)} }">
    <p>Title: ${it.title}</p>
    <p>Author: ${it.author}</p>
</g:each>

For more ways you can manipulate Collections and Maps, I recommend this page and this page respectively.

Matt Lachman
Thanks Matt...Your solution works as well, and is also very clean, though I could SWEAR that I tried this earlier and it failed...my brain must have been muddy. Thanks!
bprotas