2

I am working on a portfolio project using the following technologies: Java, Spring, Hibernate, JavaScript, Thymeleaf and Highcharts. When I try to render a data visualization chart with JavaScript, the string array model does not get defined properly in JS.

This class generates the arrays in question. convertToJsonArray() is commented out since I'm testing both java arrays and json arrays.

@Controller
public class GraphController {
    private ArrayList<Integer> habitRatingsData = new ArrayList<>();
    private ArrayList<String> habitNames = new ArrayList<>();
    private JsonArray habitNamesJsonArray = new JsonArray();

    @Autowired
    SessionService sessionService;

    @GetMapping("/graph")
    public String graph(Model model) {
        User user = sessionService.getAllSessions().stream().findFirst().orElse(null).getSiteUser();

        for (int i = 0; i < user.getHabits().size(); i++) {
            habitNames.add(user.getHabits().get(i).getName());
            habitRatingsData.add(user.getHabits().get(i).getRating());
        }

        //convertToJsonArray();
        model.addAttribute("habitList", habitNames);
        //model.addAttribute("habitList", habitNamesJsonArray);
        model.addAttribute("habitDataList", habitRatingsData);
        return "graph";
    }

    private void convertToJsonArray() {
        for (int i = 0; i < habitNames.size(); i++) {
        habitNamesJsonArray.add(habitNames.get(i));
        }
    }

Here is the graph.html file that is using Thymeleaf variables to render:

<!DOCTYPE html>

<html>

<head>
    <title>Healthy Habits - Graph</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>

    <div class ="container">

        <nav class="navbar navbar-expand-lg navbar-light bg-light">
            <a class="navbar-brand" href="http://localhost:8082/">HealthyHabits.com</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="http://localhost:8082/">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="http://localhost:8082/login">Login</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="http://localhost:8082/logout">Logout</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="http://localhost:8082/register">Register</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="http://localhost:8082/habits">Habits</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="http://localhost:8082/graph">Graph</a>
                    </li>
                </ul>
            </div>
        </nav>

        <div class="jumbotron">
            <h1 class="display-4">Visualize your habits.</h1>
        </div>

    </div>

    <body>

    <div class="container" align="center">
        <div id="container"
             style="width: 900px; height: 600px; margin: 0 auto"></div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://code.highcharts.com/highcharts.js"></script>
    <script src="https://code.highcharts.com/modules/exporting.js"></script>
    <script th-inline="javascript">
    /*<![CDATA[*/
    var habitList = [[${habitList}]];
    var habitDataList = [[${habitDataList}]];
    var habitListLength = habitList.length;

    seriesArray = [];

    for (var i = 0; i < habitListLength; i++) {
        var habitNameObject = {
            "name": habitList[i],
            "data": [habitDataList[i]]
        };
        seriesArray.push(habitNameObject);
    }

    console.log(habitList);
    console.log(seriesArray);

    $(function(){
    Highcharts.chart('container', {
        chart: {
            type: 'line'
        },
        title: {
            text: ''
        },
        subtitle: {
            text: ''
        },
        xAxis: {
             type: 'datetime',
             dateTimeLabelFormats: {
                day: '%m/%d'
             }
        },
        yAxis: {
            min: 0,
            max: 10,
            title: {
                text: 'Habit Severity'
            }
        },
        tooltip: {
            headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
            pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
            '<td style="padding:0"><b>{point.y}/10</b></td></tr>',
            footerFormat: '</table>',
            shared: true,
            useHTML: true
        },
        plotOptions: {
            column: {
                pointPadding: 0.2,
                borderWidth: 0
            }
        },

        legend: {
        layout: 'vertical',
        align: 'right',
        verticalAlign: 'top'

      },

        series: seriesArray
    /*]]>*/
        });
    });
</script>

    </body>
</div>

</html>

This is the JS variable I'm having issues with (using Thymeleaf for placeholder):

 <script th-inline="javascript">
    /*<![CDATA[*/
    var habitList = [[${habitList}]];

    ...etc...
    /*]]>*/
        });
    });
</script>

Here's how JS seems to be reading this variable in live use (passing as JsonArray):

 <script th-inline="javascript">
    /*<![CDATA[*/
    var habitList = [&quot;HabitName1&quot;];

The data object in Java is a JsonArray. I have tried passing a normal string and it causes a syntax issue as well since there are no quotes:

 <script th-inline="javascript">
    /*<![CDATA[*/
    var habitList = [HabitName1];

Html Errors

1.) The web console gives this error when I pass habitList as a JsonArray:

  • Uncaught SyntaxError: Unexpected token '&'

2.) The web console gives this error when I pass habitList as an ArrayList of strings:

  • Uncaught ReferenceError: HabitName1 is not defined

The problem to me seems that the strings inside the array need to be in real quotes, but it's in unicode? Any help would be appreciated. If you need more information to properly answer, let me know. I feel like the answer lies in something simple in JS like using String.parse or Json.parse on the variable, both of which I have already tried but with no luck.

6
  • Can you edit your question to show the Java code which creates the JsonArray? I assume it's a javax.json.JsonArray - but which JSON implementation are you using? Commented Dec 4, 2020 at 21:44
  • I'm using com.google.gson.JsonArray actually. I added the requested code, thanks for the comment. Commented Dec 5, 2020 at 1:23
  • Thank you for the updates. I am not able to recreate your errors. If we ignore the commented-out habitNamesJsonArray, then there is no use of Gson in your question. It's all just plain Java. And when you pass ArrayList<String> habitNames to your Thymeleaf template, that will automatically be converted to a JavaScript array (for example var habitList = [[${habitList}]];). That all works as expected, for me, in my test code. This suggests maybe there is something else going on, not shown in the question. Commented Dec 5, 2020 at 2:58
  • Hm, is it clear that the bottom two JS code blocks are from inspect element while running the app? The placeholder itself works as in it passes the habit name(s) to the placeholder, the placeholder just doesn't get recognized in JS as an array of strings since there's no quotes. If that's already understood then I will add some other bits that will be useful. Commented Dec 5, 2020 at 3:41
  • Yes, understood re. using the inspector. I am seeing a valid JS array. I also see it in the "view source" - since that is what Thymeleaf actually generates when it renders the template. It's a little cramped writing code in a comment, but here is the Java input: List<String> arrayList = new ArrayList<>(); arrayList.add("foo"); arrayList.add("bar");. And after this is passed into the Thymeleaf template, and then rendered, I get the following JavaScript in the HTML page: var jsArrayList = ["foo","bar"];. Commented Dec 5, 2020 at 14:23

1 Answer 1

1

You should change this:

<script th-inline="javascript">

to this:

<script th:inline="javascript">

So, change the - to :.

But also I noticed that you have your opening /*<![CDATA[*/ mis-aligned with your closing /*]]>*/ - so that may also have some unwanted effects.

There is also a closing </div> after your closing </body>. If you run the HTML through an HTML checker such as this one, you may find some other items which could be tidied up.

Sign up to request clarification or add additional context in comments.

1 Comment

Sweet, it works now! Thank you so much for the help. I also had to add this at the top of the document, because without it, it said the Thymeleaf namespace was unknown. <html xmlns:th="w3.org/1999/xhtml">

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.