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 = ["HabitName1"];
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.
JsonArray? I assume it's ajavax.json.JsonArray- but which JSON implementation are you using?habitNamesJsonArray, then there is no use of Gson in your question. It's all just plain Java. And when you passArrayList<String> habitNamesto your Thymeleaf template, that will automatically be converted to a JavaScript array (for examplevar 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.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"];.