Grails Programmer : How to integrate Chart.js to load Dynamic JSON data in a Grails 3 application?

This is a translation of the post Intégrer Chart.js avec des données dynamiques JSON dans une application Grails 3 by Bertrand Goetzmann

This Gist Responsive Chart.js Example with AJAX callback inspired this article. A HTML snippet which includes JavaScript. It demonstrates how to display a graph of the library Chart.js with JSON data obtained asynchronously using jQuery.

Let’s see how to integrate such a graph in a Grails 3 application. We review the different ways to produce in the server the JSON payload needed by the graph.

grails-chart-json-views

The next snippets are part of a Grails 3 application available in the  odelia-grails-chart repository.

The application includes a ChartController and its associated default GSP View(index.gsp). Together they generate an HTML page that displays Chart.js graphs of type doughnut.

Each graph is displayed with a button that triggers the execution of a JavaScript function defined in the file doughtnut.js. For example, use the following snippet for the first graph:

<div class="panel panel-default">
    <div class="panel-heading">Doughnut chart using displayChart() and data_1 controller action</div>
    <div class="panel-body">
        <button type="button" class="btn"
          onClick="displayChart('/chart/data_1', '#invoice_status_chart_1')">Display Chart</button>
        <div id="content" role="main" style="padding: 20px">
            <canvas id="invoice_status_chart_1" width="200" height="200"></canvas>
        </div>
    </div>
</div>

DisplayChart JavaScript function is defined in the file doughtnut.js. In the grails-app/assets/javascripts with Chart.min.js. It is integrated as a dependency of application.js which itself is added by Grails layout main.

displayChart has two arguments:

– URL used to retrieve the JSON data
– jQuery selector which identifies the canvas element in which the graph will be displayed.

doughtnut.js contains two functions displayChart displayChart1. Both functions call the CreateChart which paints the graph.

function createChart(selector, data) {
    var ctx = jQuery(selector).get(0).getContext("2d");                  
    new Chart(ctx).Doughnut(data);
}

function displayChart(urldata, selector) {
    jQuery.get(urldata, function(data) {
        var invoice_status_data = [
                {
                    value: data.status_temp,
                    color: "#26292C",
                    highlight: "#363B3F",
                    label: "Temp"
                },
                {
                    value: data.status_pending,
                    color: "#FFA500",
                    highlight: "#FAD694",
                    label: "Pending"
                },
                {
                    value: data.status_partial,
                    color: "#E14D43",
                    highlight: "#FF5A5E",
                    label: "Partial"
                },
                {
                    value: data.status_complete,
                    color: "#76AB48",
                    highlight: "#86BC4A",
                    label: "Complete"
                }
            ];
            createChart(selector, invoice_status_data);
        }
    )
}

function displayChart1(urldata, selector) {
    jQuery.get(urldata, function(data) {
            createChart(selector, data);
        }
    )
}

Lets see different ways to generate the JSON data.

Method 1: Use Grails JSON converter

The easiest way to generate a JSON document, which defines four values for our graph, is to use Grails grails.converters.JSON converter to convert a Groovy Map into a JSON payload.

def data_1() {
    render([status_temp: 25, status_pending: 25, status_partial: 25, status_complete: 25] as JSON)
}

Invoking the URL /chart/data_1 mapped to action data_1 produces the JSON document:

{"status_temp":25,"status_pending":25,"status_partial":25,"status_complete":25}

We can can call the javascript function displayChart(‘/chart/DATA_1’, ‘# invoice_status_chart_1’) when clicking the button to load the graph. Note, the javascript function will call the controller’s action we previously defined.

Method 2: Use a JSON builder

Grails’ render method accepts a more declarative approach to define an internal JSON builder.

Action data_2 snippet displayed below produces the same output as the action data_1.

def data_2() {
    render(contentType: 'application/json') {            
        status_temp 10
        status_pending 35
        status_partial 30
        status_complete 20
    }
}

We can can call the javascript function displayChart(‘/chart/data_2’, ‘# invoice_status_chart_2’) when clicking the button to load the graph. Note, the javascript function will call the controller’s action we previously defined.

Method 3: use JSON Views

The third graph renders by calling the javascript method: displayChart(‘/chart/data_3_1’, ‘#invoice_status_chart_3_1’)

In Grails 3 we can use additional views (JSON and XML at the time of writing this article), not just GSP. As a GSP view, JSON views is defined in a file created in grails-app/views/chart. chart matches our controller name. We use .gson extension.

We create a new controller action data_3_1:

def data_3_1() { }

Following convention, Grails looks for a file named grails-app/views/chart/data_3_1.gson.

To generate an identical payload as in the previously described methods we can have a file such as:

json {
    status_temp 10
    status_pending 35
    status_partial 30
    status_complete 20
}

The above file is indeed a Groovy script, which uses the json reference type StreamingJsonBuilder> allowing to build a JSON response.

By default, and for performance reasons, JSON views are compiled statically.

So far, in the example, we have transmitted just the graph values. However, we can transmit the full definition of the graph (values, colours, labels) used in displayChart javascript function. Moreover, in the previous example values are hardcoded. We should be able to pass the values in a Model object.

model {
    Map data
}
json([
    [
        value: data.status_temp,
        color: "#26292C",
        highlight: "#363B3F",
        label: "Temp"
    ],
    [
        value: data.status_pending,
        color:"#FFA500",
        highlight: "#FAD694",
        label: "Pending"
    ],
    [
        value: data.status_partial,
        color:"#E14D43",
        highlight: "#FF5A5E",
        label: "Partial"
    ],
    [
        value: data.status_complete,
        color: "#76AB48",
        highlight: "#86BC4A",
        label: "Complete"
    ]
])

Our model object will be passed by controller’s action:

def data_3_2() {
    [data: [status_temp: 70, status_pending: 10, status_partial: 10, status_complete: 10]]
}

You probably have noticed that we defined the type Map of the variable data. JSON Views are compiled by Grails and thus we need to use types to avoid compilation failure.

Now that our server responds a complete JSON definition, we can use the displayChart1 JavaScript function. A much simpler function than the displayChart function we have used so far.

The fourth graph in odelia-grails-chart project invokes the javascript method displayChart1(‘/chart/data_3_2’, ‘#invoice_status_chart_3_2’.

Future improvements

The graph uses hardcoded English labels (“Temp”, “Pending”, “Partial” and “Complete”). That should be avoid in multilingual application.

To achieve this, we can use in the view the g.message method. That is part of Grails’ i18n support.
Grails retrieves a message translated into the current Locale, according to a code (e.g. g.message(code: ‘chart.status.temp’)).

Another solution is to create different localised JSON view files. Just add the suffix of Locale to the file name. For example for French data_3_2_fr.gson or data_3_2_en.gson for English. Grails will automatically select the appropriate view depending on the language.

You can test this by adding the desired Locale as a request parameter. For example: http://localhost:8080/chart?lang=fr

Don’t hesitate to checkout Grails Views documentation for more information on JSON or XML Views.

This is a translation of the post Intégrer Chart.js avec des données dynamiques JSON dans une application Grails 3 by Bertrand Goetzmann

Leave a Reply

Your email address will not be published. Required fields are marked *