For whatever reason one of the 2s is put as NaN? What is the issue here? [1]: Best practice for creating a dynamic horizontal bar chart using Spring, Thymeleaf and Javascript [2]: https://stackoverflow.com/users/40064/wim-deblauwe [3]: https://i.sstatic.net/Blf1C.png [4]: https://i.sstatic.net/zQ7eT.png
For whatever reason one of the 2s is put as NaN? What is the issue here?
This is a continuation of this[this][1] question so right now I can get the data and it displays how many rounds a user has played and all their scores but as for the horizontal bar chart it only displays the bar chart in the first iteration in the thymeleaf th:each. So Wim Deblauwe[Wim Deblauwe][2] was nice enough to tell me I needed to use a javascript fetch() method and direct me to his website and a 40min lecture he gave about htmx. But that is all still beyond me.
[![enter image description here][3]][3]
Using yourNew chart like thisand accordion:
console.log('Rounds: ' + JSON.stringify(rounds)); var acc = document.getElementsByClassName("accordion"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } var datasets = []; var labelconst =charts []; for(var= i=0;i<roundsdocument.length;i++querySelectorAll('[data-counts]') {; var datasetcharts.forEach(chart ==> []{ for(var j=0;j<rounds[i].scores.length;j++) { // Get the data-counts attribute value and split it into an dataset.push(rounds[i].scores[j].score)array } const countsTest console= chart.loggetAttribute(dataset'data-counts').split(','); const counts = {}; // Loop over each value in the array and count occurrences for (constlet numi of= dataset0; i < countsTest.length; i++) { const num = parseInt(countsTest[i]); counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(countscountsTest); // Log the countsTest array label console.pushlog("Scorecounts); Round// "Log +the counts object // Destroy any existing chart instance for the canvas element const oldChart = chart.chart; if (i+1)oldChart) { datasets oldChart.pushdestroy(counts); } var newdatasets = []; // Create a new chart instance for the canvas element var keys const myChart = Object.keysnew Chart(datasets[0])chart, { for(j=0;j<keys.length;j++) { type: 'bar', newdatasets.push( options: { data responsive: []true, key maintainAspectRatio: keys[j]false, label: "Score " + keys[j] indexAxis: 'y', }); } for(i=0;i<newdatasets.length;i++) scales: { for(j=0;j<datasets.length;j++) x: { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key])stacked: true, } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx,display: {false type: 'bar' }, data y: { labels stacked: labeltrue, datasets display: newdatasetsfalse } }, options plugins: { responsive legend: true,{ maintainAspectRatio display: false, indexAxis: 'y' } } }, scales data: { x labels: ["Score"], datasets: [{ stacked data: true[counts[2] || 0], display backgroundColor: false"#77ACD8" },{ y data: [counts[3] || 0] },{ stacked data: true[counts[4] || 0], display backgroundColor: false"#FDC26A" },{ } data: [counts[5] || 0], plugins backgroundColor: "#FCAE37" },{ legend data: {[counts[6] || 0, counts[7] || 0, counts[8] || 0, counts[9] || 0, counts[10] || 0], display backgroundColor: false"#FCAE37" }] }, }); chart.chart = myChart; }); <th:block th:each="round : ${roundCourse.rounds}"> ... <div class="container-fluid"> <canvas th:id="myChart"><data-counts="${round.barChartArray}" th:id="'myChart-' + ${round.roundId}"></canvas> </div> </th:block> ... <script th:inline="javascript"> let rounds = /*[[${roundsJsonNode}]]*/ {}; </script> So by the screen shot, all the data is there, I wasnt sure how to send the list of Thymeleaf ${round.barChartArray} to the javascript, so (as you can see aboveget a barchart in each round now, the controller I created a list of rounds and passed it directly toproblem is the js usingdata in the chart is now wrong. It's always missing by one or has one too many. The console.log shows with this score array: let[2', rounds' =4', /*[[${roundsJsonNode}]]*/' {};4', ' 2', ' 3', ' 2', ' 3', ' 3', ' 3] The problem is all barcharts are in one round now. The order is correct the data I get this
2:2, 3:4, 4:2, NaN: 1 What is there using either model attributes, I just don't know how to put each bar chart on each round. Check the original question for complete html etc.issue here? [1]: Best practice for creating a dynamic horizontal bar chart using Spring, Thymeleaf and Javascript [2]: https://stackoverflow.com/users/40064/wim-deblauwe [3]: https://i.sstatic.net/Blf1C.png [4]:
https://i.sstatic.net/zQ7eT.png
This is a continuation of this question so right now I can get the data and it displays how many rounds a user has played and all their scores but as for the horizontal bar chart it only displays the bar chart in the first iteration in the thymeleaf th:each. So Wim Deblauwe was nice enough to tell me I needed to use a javascript fetch() method and direct me to his website and a 40min lecture he gave about htmx. But that is all still beyond me.
Using your chart like this:
console.log('Rounds: ' + JSON.stringify(rounds)); var acc = document.getElementsByClassName("accordion"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } var datasets = []; var label = []; for(var i=0;i<rounds.length;i++) { var dataset = [] for(var j=0;j<rounds[i].scores.length;j++) { dataset.push(rounds[i].scores[j].score) } console.log(dataset) const counts = {}; for (const num of dataset) { counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(counts) label.push("Score Round " + (i+1)) datasets.push(counts) } var newdatasets = []; var keys = Object.keys(datasets[0]) for(j=0;j<keys.length;j++) { newdatasets.push({ data: [], key: keys[j], label: "Score " + keys[j] }); } for(i=0;i<newdatasets.length;i++) { for(j=0;j<datasets.length;j++) { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key]) } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: label, datasets: newdatasets }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } }, } }); <th:block th:each="round : ${roundCourse.rounds}"> ... <div class="container-fluid"> <canvas th:id="myChart"></canvas> </div> </th:block> ... <script th:inline="javascript"> let rounds = /*[[${roundsJsonNode}]]*/ {}; </script> So by the screen shot, all the data is there, I wasnt sure how to send the list of Thymeleaf ${round.barChartArray} to the javascript, so (as you can see above in the controller I created a list of rounds and passed it directly to the js using the let rounds = /*[[${roundsJsonNode}]]*/ {}; The problem is all barcharts are in one round now. The order is correct the data is there using either model attributes, I just don't know how to put each bar chart on each round. Check the original question for complete html etc. 
This is a continuation of [this][1] question so right now I can get the data and it displays how many rounds a user has played and all their scores but as for the horizontal bar chart it only displays the bar chart in the first iteration in the thymeleaf th:each. So [Wim Deblauwe][2] was nice enough to tell me I needed to use a javascript fetch() method and direct me to his website and a 40min lecture he gave about htmx. But that is all still beyond me.
[![enter image description here][3]][3]
New chart and accordion:
var acc = document.getElementsByClassName("accordion"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } const charts = document.querySelectorAll('[data-counts]'); charts.forEach(chart => { // Get the data-counts attribute value and split it into an array const countsTest = chart.getAttribute('data-counts').split(','); const counts = {}; // Loop over each value in the array and count occurrences for (let i = 0; i < countsTest.length; i++) { const num = parseInt(countsTest[i]); counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(countsTest); // Log the countsTest array console.log(counts); // Log the counts object // Destroy any existing chart instance for the canvas element const oldChart = chart.chart; if (oldChart) { oldChart.destroy(); } // Create a new chart instance for the canvas element const myChart = new Chart(chart, { type: 'bar', options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } } }, data: { labels: ["Score"], datasets: [{ data: [counts[2] || 0], backgroundColor: "#77ACD8" },{ data: [counts[3] || 0] },{ data: [counts[4] || 0], backgroundColor: "#FDC26A" },{ data: [counts[5] || 0], backgroundColor: "#FCAE37" },{ data: [counts[6] || 0, counts[7] || 0, counts[8] || 0, counts[9] || 0, counts[10] || 0], backgroundColor: "#FCAE37" }] } }); chart.chart = myChart; }); <th:block th:each="round : ${roundCourse.rounds}"> ... <div class="container-fluid"> <canvas th:data-counts="${round.barChartArray}" th:id="'myChart-' + ${round.roundId}"></canvas> </div> </th:block> ... <script th:inline="javascript"> let rounds = /*[[${roundsJsonNode}]]*/ {}; </script> So I can get a barchart in each round now, the problem is the data in the chart is now wrong. It's always missing by one or has one too many. The console.log shows with this score array: [2', ' 4', ' 4', ' 2', ' 3', ' 2', ' 3', ' 3', ' 3] I get this
2:2, 3:4, 4:2, NaN: 1 What is the issue here? [1]: Best practice for creating a dynamic horizontal bar chart using Spring, Thymeleaf and Javascript [2]: https://stackoverflow.com/users/40064/wim-deblauwe [3]: https://i.sstatic.net/Blf1C.png [4]: https://i.sstatic.net/zQ7eT.png
So Ive create (I guess) a DTO CourseByRound object that looks like this is the my js now:
console.log(rounds); var acc = document.getElementsByClassName("accordion"); for (var i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } var datasets = []; var label = []; for(var i=0;i<rounds.length;i++) { var dataset = [] for(var j=0;j<rounds[i].scores.length;j++) { dataset.push(rounds[i].scores[j].score) } console.log(dataset) const counts = {}; for (const num of dataset) { counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(counts) label.push("Score Round " + (i+1)) datasets.push(counts) } var newdatasets = []; var keys = Object.keys(datasets[0]) for(j=0;j<keys.length;j++) { newdatasets.push({ data: [], key: keys[j], label: "Score " + keys[j] }); } for(i=0;i<newdatasets.length;i++) { for(j=0;j<datasets.length;j++) { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key]) } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: label, datasets: newdatasets }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } }, } }); My Controller
@GetMapping("/rounds/{id}") public String roundsHome(@PathVariable(value = "id") Long id, Model model) { List<Course> courses = courseService.getAllCourses(); List<Round> rounds = userService.getUserById(id).getRounds(); rounds.sort(Comparator.comparing(Round::getRoundDate).reversed()); Map<Course, List<Round>> mapRoundsByCourse = rounds.stream().collect(Collectors.groupingBy(Round::getCourse)); try { ObjectMapper mapper = new ObjectMapper(); JsonNode json = mapper.readTree(mapper.writeValueAsString(mapRoundsByCourse)); model.addAttribute("roundsJson", json); } catch (IOException e) { e.printStackTrace(); } model.addAttribute("userId", id); model.addAttribute("roundService", roundService); model.addAttribute("courses", courses); model.addAttribute("rounds", mapRoundsByCourse); return "/discgolf/round/rounds"; } And updated script
<script th:inline="javascript"> let rounds = /*[[${roundsJson}]]*/ {}; </script> The 'let rounds' is null? Screen shot, line 27 is for(var i=0;i<rounds.length;i++) { 
Models Course
@Entity @Table(name = "course") @Builder public class Course { @Id @Column(name = "course_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false)courseId; private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "course_id", referencedColumnName = "course_id")courseName; private List<Hole> holes = new ArrayList<>(); @Column(name = "course_par", nullable =int false)coursePar; private int par; courseRecord; @Column(name = "record", nullableprivate =double false)courseAverage; private int record; timesPlayed; @Column(name = "course_average", nullable = false) private doubleList<Round> courseAverage;rounds; //Constructorconstructor, getters and setters. } HoleUsing your chart like this:
@Entityconsole.log('Rounds: ' + JSON.stringify(rounds)); @Table var acc = document.getElementsByClassName(name"accordion"); var i; for (i = "hole"0; i < acc.length; i++) { public class Hole acc[i].addEventListener("click", function() { this.classList.toggle("active"); @Id var panel = this.nextElementSibling; @Column if (namepanel.style.maxHeight) { panel.style.maxHeight = "hole_id"null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); @GeneratedValue} var datasets = []; var label = []; for(strategyvar i=0;i<rounds.length;i++) { var dataset = GenerationType[] for(var j=0;j<rounds[i].IDENTITYscores.length;j++) { private Long holeId; dataset.push(rounds[i].scores[j].score) } @Column console.log(namedataset) const counts = "name"{}; for (const num of dataset) { private int number; counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(counts) @Column label.push(name"Score Round " + (i+1)) datasets.push(counts) } var newdatasets = "par"[]; var keys = Object.keys(datasets[0]) for(j=0;j<keys.length;j++) { private int par;newdatasets.push({ //Constructor data: [], getters and setters key: keys[j], label: "Score " + keys[j] }); } for(i=0;i<newdatasets.length;i++) { for(j=0;j<datasets.length;j++) { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key]) } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: label, datasets: newdatasets }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } }, } }); ScoreMy html
@Entity @Table(name = "score") public<th:block classth:each="round Score: ${ @Id @Column(name = "score_id")roundCourse.rounds}"> @GeneratedValue(strategy = GenerationType.IDENTITY).. private Long<div scoreId; class="container-fluid"> @Column(name =<canvas "score")th:id="myChart"></canvas> private int score;</div> </th:block> @Column(name = "hole_par")... private int<script holePar; th:inline="javascript"> private String name; let privaterounds String= color; /*[[${roundsJsonNode}]]*/Constructor, getters and setters.{}; </script> RoundInside the controller getCourseByRound(id) just gets a list of CourseByRound by a userId
@Entity @Table(nameList<CourseByRound> courseByRounds = "round"getCourseByRound(id); public class Round { @Id List<Round> @Column(namejsonRounds = "round_id") new @GeneratedValueArrayList<>(strategy = GenerationType.IDENTITY); private Long roundId; for @JsonIgnore (CourseByRound courseByRound : courseByRounds) @ManyToOne{ @JoinColumn(name = "course_id") private Course course; for @OneToMany(cascade = CascadeType.ALL,Round orphanRemovalround =: truecourseByRound.getRounds()) { @JoinColumn(name = "round_id", referencedColumnName = "round_id") private List<Score> scores = new ArrayList<>jsonRounds.add(round); @JsonDeserialize(using = LocalDateTimeDeserializer.class) @Column(name = "round_date")} @DateTimeFormat(pattern = "dd/MM/yyyy") private Date roundDate;} rounds.sort(Comparator.comparing(Round::getRoundDate).reversed()); ObjectMapper mapper = new @ColumnObjectMapper(name = "round_total"); private intmapper.registerModule(new total;JavaTimeModule()); //Constructormodel.addAttribute("roundsJsonNode", getters and settersjsonRounds); model.addAttribute("courseByRounds", courseByRounds); Json DataSo by the screen shot, all the data is there, I wasnt sure how to send the list of Thymeleaf ${round.barChartArray} to the javascript, so (as you can see above in the controller I created a list of rounds and passed it directly to the js using the let rounds = /*[[${roundsJsonNode}]]*/ {}; The problem is all barcharts are in one round now. The order is correct the data is there using either model attributes, I just don't know how to put each bar chart on each round. Check the original question for complete html etc. 

So this is the my js now:
console.log(rounds); var acc = document.getElementsByClassName("accordion"); for (var i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } var datasets = []; var label = []; for(var i=0;i<rounds.length;i++) { var dataset = [] for(var j=0;j<rounds[i].scores.length;j++) { dataset.push(rounds[i].scores[j].score) } console.log(dataset) const counts = {}; for (const num of dataset) { counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(counts) label.push("Score Round " + (i+1)) datasets.push(counts) } var newdatasets = []; var keys = Object.keys(datasets[0]) for(j=0;j<keys.length;j++) { newdatasets.push({ data: [], key: keys[j], label: "Score " + keys[j] }); } for(i=0;i<newdatasets.length;i++) { for(j=0;j<datasets.length;j++) { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key]) } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: label, datasets: newdatasets }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } }, } }); My Controller
@GetMapping("/rounds/{id}") public String roundsHome(@PathVariable(value = "id") Long id, Model model) { List<Course> courses = courseService.getAllCourses(); List<Round> rounds = userService.getUserById(id).getRounds(); rounds.sort(Comparator.comparing(Round::getRoundDate).reversed()); Map<Course, List<Round>> mapRoundsByCourse = rounds.stream().collect(Collectors.groupingBy(Round::getCourse)); try { ObjectMapper mapper = new ObjectMapper(); JsonNode json = mapper.readTree(mapper.writeValueAsString(mapRoundsByCourse)); model.addAttribute("roundsJson", json); } catch (IOException e) { e.printStackTrace(); } model.addAttribute("userId", id); model.addAttribute("roundService", roundService); model.addAttribute("courses", courses); model.addAttribute("rounds", mapRoundsByCourse); return "/discgolf/round/rounds"; } And updated script
<script th:inline="javascript"> let rounds = /*[[${roundsJson}]]*/ {}; </script> The 'let rounds' is null? Screen shot, line 27 is for(var i=0;i<rounds.length;i++) { 
Models Course
@Entity @Table(name = "course") @Builder public class Course { @Id @Column(name = "course_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "course_id", referencedColumnName = "course_id") private List<Hole> holes = new ArrayList<>(); @Column(name = "course_par", nullable = false) private int par; @Column(name = "record", nullable = false) private int record; @Column(name = "course_average", nullable = false) private double courseAverage; //Constructor, getters and setters. Hole
@Entity @Table(name = "hole") public class Hole { @Id @Column(name = "hole_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long holeId; @Column(name = "name") private int number; @Column(name = "par") private int par; //Constructor, getters and setters. Score
@Entity @Table(name = "score") public class Score { @Id @Column(name = "score_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long scoreId; @Column(name = "score") private int score; @Column(name = "hole_par") private int holePar; private String name; private String color; //Constructor, getters and setters. Round
@Entity @Table(name = "round") public class Round { @Id @Column(name = "round_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long roundId; @JsonIgnore @ManyToOne @JoinColumn(name = "course_id") private Course course; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "round_id", referencedColumnName = "round_id") private List<Score> scores = new ArrayList<>(); @JsonDeserialize(using = LocalDateTimeDeserializer.class) @Column(name = "round_date") @DateTimeFormat(pattern = "dd/MM/yyyy") private Date roundDate; @Column(name = "round_total") private int total; //Constructor, getters and setters. So Ive create (I guess) a DTO CourseByRound object that looks like this:
private Long courseId; private String courseName; private int coursePar; private int courseRecord; private double courseAverage; private int timesPlayed; private List<Round> rounds; //constructor, getters and setters } Using your chart like this:
console.log('Rounds: ' + JSON.stringify(rounds)); var acc = document.getElementsByClassName("accordion"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); } var datasets = []; var label = []; for(var i=0;i<rounds.length;i++) { var dataset = [] for(var j=0;j<rounds[i].scores.length;j++) { dataset.push(rounds[i].scores[j].score) } console.log(dataset) const counts = {}; for (const num of dataset) { counts[num] = counts[num] ? counts[num] + 1 : 1; } console.log(counts) label.push("Score Round " + (i+1)) datasets.push(counts) } var newdatasets = []; var keys = Object.keys(datasets[0]) for(j=0;j<keys.length;j++) { newdatasets.push({ data: [], key: keys[j], label: "Score " + keys[j] }); } for(i=0;i<newdatasets.length;i++) { for(j=0;j<datasets.length;j++) { console.log(datasets[j][newdatasets[i].key]); newdatasets[i].data.push(datasets[j][newdatasets[i].key]) } } console.log(newdatasets) var ctx = document.getElementById("myChart").getContext("2d"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: label, datasets: newdatasets }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { stacked: true, display: false }, y: { stacked: true, display: false } }, plugins: { legend: { display: false } }, } }); My html
<th:block th:each="round : ${roundCourse.rounds}"> ... <div class="container-fluid"> <canvas th:id="myChart"></canvas> </div> </th:block> ... <script th:inline="javascript"> let rounds = /*[[${roundsJsonNode}]]*/ {}; </script> Inside the controller getCourseByRound(id) just gets a list of CourseByRound by a userId
List<CourseByRound> courseByRounds = getCourseByRound(id); List<Round> jsonRounds = new ArrayList<>(); for (CourseByRound courseByRound : courseByRounds) { for (Round round : courseByRound.getRounds()) { jsonRounds.add(round); } } rounds.sort(Comparator.comparing(Round::getRoundDate).reversed()); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); model.addAttribute("roundsJsonNode", jsonRounds); model.addAttribute("courseByRounds", courseByRounds); So by the screen shot, all the data is there, I wasnt sure how to send the list of Thymeleaf ${round.barChartArray} to the javascript, so (as you can see above in the controller I created a list of rounds and passed it directly to the js using the let rounds = /*[[${roundsJsonNode}]]*/ {}; The problem is all barcharts are in one round now. The order is correct the data is there using either model attributes, I just don't know how to put each bar chart on each round. Check the original question for complete html etc. 