Using Thymeleaf inline JavaScript with D3.js

The fancy data visualization for the lazy backend developer
January 31, 2019 by Michael

I gave this talk several times now and every time I got asked how I created this visualization, clustering the genres I’m listening to by decade and country:



I just did a super spontaneous video for that. Key take away: Just use server side rendered HTML through Thymeleaf, obviously running on Spring Boot, render the content model as inlined Javascript and be good to go. Want an API later on?

Structure your Spring MVC controller and your service or repository layer in a way that makes it actually reusable.

Anyway, here’s the short clip and below the relevant code snippets:

Spring MVC controller, GenresController.java, either returning a String, denoting the template to be rendered, or the Model as just the response body:

@GetMapping(value = { "", "/" }, produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView genres() {
 
	var model = genresModel();
	return new ModelAndView("genres", model);
}
 
@GetMapping(value = { "", "/" }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public Map<String, Object> genresModel() {
 
	var genres = this.genreRepository.findAll(Sort.by("name").ascending());
	var allSubgrenes = genreRepository.findAllSubgrenes();
	var top10Subgenres = allSubgrenes.stream().sorted(comparingLong(Subgenre::getFrequency).reversed()).limit(10).collect(toList());
	return Map.of(
			"genres", genres,
			"subgenres", allSubgrenes,
			"top10Subgenres", top10Subgenres
	);
}

The Thymeleaf template genres.html. The standard controller above renders it when / is requested, accepting text/html.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:th="http://www.thymeleaf.org"
      layout:decorate="~{layout}">
<head>
    <title th:text="#{genres}">Placeholder</title>
    <script th:inline="javascript">
        var subgenres = /*[[${subgenres}]]*/ {};
    </script>
</head>
<body>
<!-- Some more content -->
</body>
</html>

Calling this thing application.js is way too much. It’s a simple function, reading in the global subgenres variable and displays it as a nice D3.js chart.

Remember: This is also no silver bullet and I absolutely don’t have anything agains todays Javascript development. I just feel more at home near a database.

11 comments

  1. Minty wrote:

    Exactly what I was looking for in terms of integrating Thymeleaf and D3.js – thanks for the post.

    Posted on June 13, 2019 at 11:42 PM | Permalink
  2. Michael wrote:

    Great to hear! Thanks for your feedback.

    Posted on June 15, 2019 at 9:32 PM | Permalink
  3. jj wrote:

    Hi,
    Iam stuck with thymeleaf, nvd3 data issue, when i return a json object from the controller and setting it to the value.

    var data =
    [{
    key: “ExampleData”,
    values:/*[[${jsontest}]]*/{}
    }]

    the output of jsontest is executing with escaped double quotes
    [{\r\n \”label\”: \”This\”,\r\n \”value\”: 10\r\n },\r\n {\r\n \”label\”: \”Tutorial\”,\r\n \”value\”: 20\r\n },\r\n {\r\n \”label\”: \”Worked\”,\r\n \”value\”: 30\r\n },\r\n ]”
    }]
    Which is causing failure for graph to be displayed.

    Posted on September 9, 2019 at 2:43 PM | Permalink
  4. Michael wrote:

    You need to use the correct script type:

    https://gist.github.com/michael-simons/46d69881e04665935bb3b7520a48be61

    Posted on September 10, 2019 at 9:46 AM | Permalink
  5. Alvyn wrote:

    Why is your Java Type not capitalized?
    For instance,

    var model = genresModel();

    The data type var does not exist in Java.

    Posted on February 21, 2020 at 9:26 AM | Permalink
  6. Michael wrote:

    This is a Java 10+ project, using Java’s type inference.

    By using the var keyword, you let the Java compiler infer the type.

    The thing is still type safe.

    Posted on February 21, 2020 at 10:13 AM | Permalink
  7. xiaoshan wrote:

    Great post. I am new to Thymeleaf, It is exactly what I am seeking for.

    Posted on June 21, 2022 at 9:19 AM | Permalink
  8. Michael wrote:

    Hello xiaoshan, I am glad it is helpful to you!

    Posted on June 23, 2022 at 6:52 PM | Permalink
  9. xiaoshan wrote:

    My graph failed to be displayed.

    1. I got my data in controller.java as below:
    model.addAttribute(“data”, JSON.parseArray(nodes));
    model.addAttribute(“link”, JSON.parseArray(links));

    2. my code in js file is as below:
    var force = d3.layout.force()
    .nodes(data)
    .links(link)
    .size([1000, 700])
    .linkDistance(400)
    .charge([-400]);

    force.start();

    then I got an error:
    Uncaught TypeError: Cannot read properties of undefined (reading ‘weight’) at force.start (d3.v3.js:6395:13)

    3. then I use console.log(nodes) on javascript, the nodes can be displayed as:
    nodes:
    [{‘id’:0,’instanceOfNodeId’:0,’name’:’a’,’ontologyNode’:false},
    {‘id’:1,’instanceOfNodeId’:0,’name’:’b’,’ontologyNode’:false}
    ]
    but while I tried console.log(nodes[0]), it displays nothing.

    It seems that the format for data and link is wrong. But I donot know how to correct it.

    Posted on July 20, 2022 at 10:36 AM | Permalink
  10. xiaoshan wrote:

    I solved the above problem by add the following to js file:
    var data = JSON.parse(nodes)

    But while I run my application, I still got the same error:
    Uncaught TypeError: Cannot read properties of undefined (reading ‘weight’)

    Could you please help?
    Thanks

    Posted on July 20, 2022 at 10:49 AM | Permalink
  11. xiaoshan wrote:

    solved

    Posted on July 20, 2022 at 11:12 AM | Permalink
Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *