Search code examples
javaspringdatehighchartsthymeleaf

How do I use dates from a LinkedHashMap for the x-axis on a Highcharts line graph (using Thymeleaf)?


I am new to using Highcharts, so this may be a trivial issue. However, I have not had much success through my own googling about this.

I am trying to get a line graph to display strength progress over time, but every time I plug in the keySet() (of type java.util.Date) to be what is on the X-axis, the graph disappears from the web page.

I want the record of an event being done and the date it was done on to be a point on the graph (i.e. the (x,y) would be (action, date)).

Java stuff:

List<StrengthProgressPoint> records = getAllStrengthLogsForExercise(session, exerciseName);
Map<Date, Double> strengthGraph = new LinkedHashMap<>();
for(StrengthProgressPoint p : records) { 
    strengthGraph.put(p.getDate(), p.getWeight()); 
}

Highcharts stuff:

<script>
$(function () {
$('#progressGraphContainer').highcharts({
        title: {
            text: ''
        },

        subtitle: {
            text: ''
        },

        yAxis: {
            min: 0,
            title: {
                text: 'Weight Lifted'
            }
        },

        xAxis: {
            categories: [[${strengthGraph.keySet()}]],
        },

        legend: {
            enabled: false
        },

        plotOptions: {
            series: {
                label: {
                    connectorAllowed: false,
                    connectNulls: true
                },
            }
        },

        series: [{
            name: 'Weight',
            data: [[${strengthGraph.values()}]]
        }],

        responsive: {
            rules: [{
                condition: {
                    maxWidth: 500
                },
                chartOptions: {
                    legend: {
                        enabled: false
                    }
                }
            }]
        }
});
});
</script>

I did get a graph to display on my web page a few times, but never what I wanted. I have seemingly narrowed it down to the 'categories: [[${strengthGraph.keySet()}]]' being the culprit of causing the graph to just not show up on the web page. I just want the dates in the HashMap to display on the x-axis (and correspond to the appropriate values of course).


Solution

  • Because you are using Thymeleaf with JavaScript, you need to indicate this in your <script> tag:

    <script th:inline="javascript">
    

    Without this, the rendering from your Java objects to the equivalent JavaScript objects will not take place.

    I assume you have already included the Thymeleaf namespace in the page's <html> tag:

    <html xmlns:th="http://www.thymeleaf.org">
    

    Dates will be displayed on the x-axis - but the format may be a bit cumbersome - for example:

    2021-03-15T00:00:00.000-04:00
    

    So, you can add a label formatter. There are many ways to format a date, and I expect HighCharts has built-in ways to do this, but I am not familiar with them - so here is a plain JavaScript way:

    xAxis: {
        categories: [[${strengthGraph.keySet()}]],
        labels: {
            formatter: function () {
                var d = Date.parse(this.value);
                const ye = new Intl.DateTimeFormat('en', {year: 'numeric'}).format(d);
                const mo = new Intl.DateTimeFormat('en', {month: 'short'}).format(d);
                const da = new Intl.DateTimeFormat('en', {day: '2-digit'}).format(d);
                return `${da}-${mo}-${ye}`;
            }
        }
    
    },
    ...
    

    Now the axis will use labels such as this:

    15-Mar-2021
    

    I took this formatter code from this question, where there are other approaches also.


    Worth mentioning: When you place Thymeleaf expressions in JavaScript, you can suppress JavaScript syntax errors by placing the Thymeleaf expression inside comments - and by also providing a default value in its place:

    categories: /*[[${strengthGraph.keySet()}]]*/ []
    

    In this case the [] is the default value. When Thymeleaf renders the expression, it will remove the /* and */ comment delimiters, and also remove the default expression.