Draw pie chart using spring boot, thymeleaf, js, highchart but can’t

I’m new with js and spring, now i want to create a html dashboard and this page will have a small div with pie chart. But i can’t create pie chart. I try some tutorial in youtube but now i want to pass value to ajax or something like that to get the pie chart. Here is my Code: admin_homepage.html:

<div class="col-xl-4 col-lg-5">
                    <div class="card shadow mb-4">
                        <!-- Thay chart vào thẻ div này -->
                        <div class="card-body">
                            <div class="chart-pie pt-4 pb-2">
                                <div id="chartContainer" style="height: 370px; width: 100%;"></div> 
                            </div>
                        </div>
                    </div>
                </div>

<script type="text/javascript">
    $.ajax({
        /* for pie chart */
        url: "admin_home",

        success: function(result){
            /* pie chart starts here */
            var series = [];
            var data = [];

            for(var i = 0; i < result.length; i++){
                var object = {};
                object.name = result[i].catName.toUpperCase();
                object.y = result[i].catCount;
                data.push(object);
            }
            var seriesObject = {
                name: 'Course By Category',
                colorByPoint: true,
                data: data
            };
            series.push(seriesObject);
            drawPieChart(series);

            /* pie chart ends here */
        }
    });

    /* for pie chart */
    function drawPieChart(series){
        Highcharts.chart('chartContainer', {
            chart: {
                plotBackgroundColor: null,
                plotBorderWidth: null,
                plotShadow: false,
                type: 'pie'
            },
            title: {
                text: 'Browser market shares in January, 2018'
            },
            tooltip: {
                formatter: function() {
                    return '<strong>'+this.key+': </strong>'+ this.y;
                }
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: 'pointer',
                    dataLabels: {
                        enabled: true,
                        format: '<b>{point.name}</b>: {point.y}'
                    }
                }
            },
            series: series
        });
    }

</script>

My Controller

@GetMapping("/admin_home")
public String viewHomePage(){
 // Get list of course and count
        List<CountCourse> pieChart = dashBoardRepository.countCourseByCategory();

        model.addAttribute("pieChart",pieChart);
        return "Admin_Homepage";
}

All i want is pass value of catName, catCount to pie chart but i can’t Any one help me. Many thanks.

Answer

Because you are using a Thymeleaf template, you are not required to use $.ajax({...}) to retrieve the pie chart data. Instead you can provide the data directly to the Thymeleaf template.

(Alternatively, you can continue to use an Ajax call – in which case, The Thymeleaf template will be rendered to HTML – and then as a separate step, the Ajax call will fetch the pie chart data.)

The following assumes the first approach (no Ajax needed):

No Ajax Needed

I took your Thymeleaf template in the question and made some changes to the script:

  1. I removed the Ajax call.

  2. I added a Thymeleaf variable to hold the chart data.

Here is the updated script:

<script th:inline="javascript">

    // this simply wraps the code in a function
    // that waits for the DOM to be ready:
    (function () {

        // this is populated by Thymeleaf:
        var pieChartData = /*[[${pieChartData}]]*/ [];

        var series = [];
        var data = [];

        for (var i = 0; i < pieChartData.length; i++) {
            var object = {};
            object.name = pieChartData[i].catName.toUpperCase();
            object.y = pieChartData[i].catCount;
            data.push(object);
        }
        var seriesObject = {
            name: 'Course By Category',
            colorByPoint: true,
            data: data
        };
        series.push(seriesObject);
        drawPieChart(series);

        // draw the pie chart:
        function drawPieChart(series) {
            Highcharts.chart('chartContainer', {
                chart: {
                    plotBackgroundColor: null,
                    plotBorderWidth: null,
                    plotShadow: false,
                    type: 'pie'
                },
                title: {
                    text: 'Your Heading Goes Here'
                },
                tooltip: {
                    formatter: function () {
                        return '<strong>' + this.key + ': </strong>' + this.y;
                    }
                },
                plotOptions: {
                    pie: {
                        allowPointSelect: true,
                        cursor: 'pointer',
                        dataLabels: {
                            enabled: true,
                            format: '<b>{point.name}</b>: {point.y}'
                        }
                    }
                },
                // use the series data defined earlier:
                series: series
            });
        }

    })();

</script>

The key points about this script are:

  1. The script tag looks like this:
<script th:inline="javascript">

This tells Thymeleaf that the script will contain one or more Thymeleaf expressions.

In our case we have one expression – here it is:

var pieChartData = /*[[${pieChartData}]]*/ [];

This syntax will cause Thymeleaf to replace the pieChartData variable with the data structure provided by the Java controller.

Here is that piece from the controller:

List<CountCourse> pieChartData = dashBoardRepository.countCourseByCategory();
model.addAttribute("pieChartData", pieChartData);
return "admin_homepage";

This assumes you have a CountCourse object which contains String catName and int catCount.

Thymeleaf will take the List<CountCourse> pieChartData data and generate the following JavaScript for you (using my test data):

var pieChartData = [
  {"catName":"Humanities","catCount":123},
  {"catName":"Sciences","catCount":145},
  {"catName":"Other","catCount":67}
];

After that, I use the same logic as you have in your Ajax success function to convert this raw data into HightCharts pie chart data.

The end result is the following HTML page:

enter image description here

With Ajax

If you want to use your Ajax approach instead of this, then you need to build a separate end point which will return the pie chart data directly to the Ajax handler in your JavaScript code.

When you take this approach, you no longer need to use the Thymeleaf attribute:

var pieChartData = /*[[${pieChartData}]]*/ []; // NO LONGER NEEDED

And you no longer need to pass this data to your model in the controller:

model.addAttribute("pieChartData", pieChartData); // NO LONGER NEEDED

Instead, you need to continue using your $.ajax code and you need to build a separate end-point which returns the pieChartData as JSON for that Ajax call:

$.ajax({
  /* for pie chart */
  url: "piechart_data_json", // some new URL for your JSON pie chart data
  ...
});

Given you are using Thymeleaf already, I think there is no need for this approach.


Update

Just to explain the following syntax a bit more:

var pieChartData = /*[[${pieChartData}]]*/ [];

It looks like an empty JavaScript array []. But in fact, there is more to it.

The Thymeleaf variable ${pieChartData} receives the data from the controller.

Because the variable is in a <script> tag, it’s not sufficient just to use the standard Thymeleaf ${pieChartData} expression. You also have to surround that expression with [[ and ]]. This is because ${pieChartData} is actually valid JavaScript – for example, as used in string interpolation.

That gives us this:

var pieChartData = [[${pieChartData}]];

This is all you need. This will work.

The problem here is, it’s not valid JavaScript, so your IDE may highlight it as having a syntax error.

To work around this, you can take one extra step. You can “hide” the expression in a JavaScript comment – and then provide a valid value (the empty array). This keeps the JavaScript syntax checker happy in your IDE.

Thymeleaf will locate the variable inside that comment and remove it – and also remove the placeholder [] value.

That is how Thymeleaf pushes the Java model data into the template in this case.