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
Exactly what I was looking for in terms of integrating Thymeleaf and D3.js – thanks for the post.
Great to hear! Thanks for your feedback.
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.
You need to use the correct script type:
https://gist.github.com/michael-simons/46d69881e04665935bb3b7520a48be61
Why is your Java Type not capitalized?
For instance,
var model = genresModel();
The data type var does not exist in Java.
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.
Great post. I am new to Thymeleaf, It is exactly what I am seeking for.
Hello xiaoshan, I am glad it is helpful to you!
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.
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
solved
Post a Comment