Warning: Creating default object from empty value in /home/kellyrob/kellyrob99.com/blog/wp-content/plugins/search-unleashed/models/incoming-search.php on line 27

The Kaptain on … stuff

25 Oct, 2009

Grails-UI DataTable using XML for a model

Posted by: Kelly Robinson In: Development

I had a great chance to play with some new stuff this weekend, namely Grails 1.2-M3, the new Grails REST plugin and Groovy in general. Awhile back I wrote some Groovy code to explore the various Jira, Fisheye and Crucible REST api’s using Apache HttpClient directly, and I wanted to see what it would like abstracted behind HTTPBuilder. With the help of the Grails-UI and Google-charts plugins, I got some pretty nice views of the data as well.

First off, dealing with a RESTful call was about as simple as I’ve ever seen. This is the entire code that deals with creating the connection, authenticating, executing a GET, and parsing the returned XML into a ‘data’ property on the response.

    private withFisheye(String url, Map args) {
        withRest(uri: url) {
            auth.basic userName, password
            return get(query: args)
        }
    }

Getting XML using Andres’ plugin is extremely simple and we can achieve some pretty nice visualizations of the resulting data very easily as well. In particular I wanted to share how to use an XML response as a backing model for DataTable. The excellent examples provided by Matthew Taylor on his blog seemed to concentrate on using GORM to load and sort domain objects to utilize as data models. When using XML as an alternative way to represent the data, and using RESTful calls to populate that data, the available sorting options on a DataTable weren’t able to function properly without a little extra help.

Consider an list of XML elements like this, which represents a single review in Crucible.

<reviewdata>
    <allowreviewerstojoin>false</allowreviewerstojoin>
    <author>
        <displayname>Kelly Robinson</displayname>
        <username>TheKaptain</username>
    </author>
    <closedate>20010101T00::00.0000000</closedate>
    <createdate>20010101T00::00.0000000</createdate>
    <creator>
        <displayname>Kelly Robinson</displayname>
        <username>TheKaptain</username>
    </creator>
    <description>
        Test review
    </description>
    <moderator>
        <displayname>Kelly Robinson</displayname>
        <username>TheKaptain</username>
    </moderator>
    <name>
        Test review
    </name>
    <permaid>
        <id>CR1</id>
    </permaid>
    <projectkey>CR</projectkey>
    <state>Closed</state>
    <summary>
      Test review
    </summary>
</reviewdata>

You can leverage this list as a DataTable model by extracting specific elements from the XML, and providing a sorting mechanism based on dynamic map value lookups.

    private def createReviewDataJson(HttpServletResponse response, Map params, reviews) {
        def list = []
        response.setHeader("Cache-Control", "no-store")
       //build a List of Maps, one for each row in the DataTable
        reviews.reviewData.each {
            list << [
                    id: it.permaId.id.text(),
                    author: it.author.displayName.text(),
                    creator: it.creator.displayName.text(),
                    moderator: it.moderator.displayName.text(),
                    description: it.description.text(),
                    state: it.state.text()
            ]
        }
       //sort by the 'order' and 'sort' parameters passed in by DataTable
        switch (params.order) {
            case 'asc':
                list.sort { it."${params.sort}" } //sort by value where key == params.sort
                break
            case 'desc':
                list.sort {a, b -> b."${params.sort}" <=> a."${params.sort}" } //reverse sort by value
                break
        }
       //the entire model
        def data = [totalRecords: list.size(), results: list]
        render data as JSON
    }

This model can then be rendered using the tag. Note that this is pretty much a direct copy of a provided example, nothing fancy here.

<gui:datatable id="statDataTable" draggablecolumns="true" columndefs="[
        [id:'id', sortable:true, resizeable:true],
        [author:'author', sortable:true, resizeable:true],
        [creator:'creator', sortable:true, resizeable:true],
        [moderator:'moderator',  sortable:true, resizeable:true],
        [description:'description', sortable:true, resizeable:true, formatter:'textarea'],
        [state:'state', sortable:true, resizeable:true]
    ]" paginatorconfig="[
        template:'{PreviousPageLink} {PageLinks} {NextPageLink} {CurrentPageReport}',
        pageReportTemplate:'{totalRecords} total records'
    ]" sortedby="id" controller="fisheye" action="${action}" resultslist="results" rowsperpage="10">

So for very little work you get a sortable, paginated DataTable, complete with column resizing and dragging. The DataTable is also being rendered using a template, allowing it to be used arbitrarily by controller methods that populate the model with different reviews. Pretty cool if you ask me. I also used the same backing XML model to drive some PieCharts, but that’s perhaps a topic for another post.

:)

Share

About

Tales of development, life and the folly that goes along with both.

Tags

profile for TheKaptain at Stack Overflow, Q&A for professional and enthusiast programmers
Get Adobe Flash player