Thymeleaf th:每次循环渲染一个图表js条形图

问题描述 投票:0回答:0

这是我最后一次尝试。我已经遇到这个问题好几个星期了,但没有运气。我有一个带有 thymeleaf 的 spring crud 应用程序,用于圆盘高尔夫应用程序。您可以添加课程,然后为登录用户添加回合(类似于 udisc),一切都很好。用户可以在手风琴中按课程查看所有回合。我使用 Java Map 并循环

Map<Course, List<Round>>
并使用 thymeleaf/html 显示它。您单击课程(手风琴),然后它会打开该课程的所有回合。鉴于我有限的编程技能,我对到目前为止所做的事情印象深刻。

我的问题是(这是我第三次尝试提出这个问题)在手风琴内部的每一轮中都有一个条形图。使用下面的代码,我在第一个 th:each 中得到了所需的水平条形图,但之后的每一轮都没有。当我刷新页面时,因为 Map 的顺序永远不会相同,手风琴中的第一道菜总是有一个循环中第一轮的条形图,但之后就没有了。那么如何在按

Map<Course, List<Round>>
排序的地图中获得每一轮的条形图?

如果我以错误的方式这样做,请提出建议,我愿意接受所有建议。我对 javascript 的了解远远少于 Java,所以将我视为初学者,但我假设有一个 fetch() 函数或必须再次发生才能使其工作的东西。之前问过here哪里有一些屏幕截图。提前致谢。

课程

@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;
//getter setters etc

@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;
//getter setters etc

控制器

@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));

        model.addAttribute("userId", id);
        model.addAttribute("roundService", roundService);
        model.addAttribute("courses", courses);
        model.addAttribute("rounds", mapRoundsByCourse);
        return "/discgolf/round/rounds";
    }

html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link th:href="@{/css/fontawesome/css/all.css}" rel="stylesheet">
    <link rel="stylesheet" type="text/css" th:href="@{/css/bootstrap/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css}">
    <link rel="stylesheet" type="text/css" th:href="@{/css/discgolf/round.css}">

    <title>Rounds</title>


</head>
<body>
<div th:replace="fragments/navbars/libraryNavbar :: navbar"></div>
<div class="container">
    <div class="subContainer">
        <h1>Rounds</h1>
        <a class="col-sm-4" th:href="@{/discgolf}">Disc Golf Home</a>
        <div class="row" id="username">
            <b class="col-sm-8">Username: <span sec:authentication="principal.username"></span></b>
        </div>
    </div>
    <a>Add Round</a>
    <form action="#" th:action="@{/discgolf/newRound}" th:object="${course}"
          method="GET">
        <div class="form-group">
            <div class="form-group blu-margin">
                <select th:name="course" class="form-control" onchange="this.form.submit()">
                    <option th:value="0" th:text="${'Please Select'}"></option>
                    <option th:each="course : ${courses}"
                            th:text="${course.name}" >

                    </option>
                </select>
            </div>
        </div>
    </form>
</div>
<div class="container">
    <div>
        <a>Rounds Played</a>
    </div>
    <div >
        <div th:each="roundCourse : ${rounds}" class="card">
            <button class="accordion">
                <div class="row">
                    <span class="col" th:text="${roundCourse.key.name}"></span>
                    <div class="col">
                        <label>Course par:</label>
                        <span th:text="${roundCourse.key.par}"></span>
                    </div>
                    <div class="col">
                        <label>Course average:</label>
                        <span th:text="${#numbers.formatDecimal(roundCourse.key.courseAverage, 0, 2)}"></span>
                    </div>
                </div>
            </button>
            <div class="panel">
                <div class="row">
                    <div class="col-3">
                        <label>Record: </label>
                        <label th:if="${roundCourse.key.record > 0}" th:text="${'+' + roundCourse.key.record + ' (' + (roundCourse.key.par + roundCourse.key.record) + ')'}"></label>
                        <label th:if="${roundCourse.key.record < 0}" th:text="${roundCourse.key.record + ' (' + (roundCourse.key.record + roundCourse.key.par) + ')'}"></label>
                        <label th:if="${roundCourse.key.record == 0}" th:text="${'E (' + (roundCourse.key.record + roundCourse.key.par) + ')'}"></label>
                    </div>
                    <div class="col-3">
                        <label>Times played: </label>
                        <label th:text="${#lists.size(roundCourse.value)}"></label>
                    </div>
                    <div class="col-3">
                        <label>My best:</label>
                        <label th:if="${(roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par) == 0}" th:text="${'E'}"></label>
                        <label th:if="${(roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par) < 0}"
                               th:text="${roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) - roundCourse.key.par}"></label>
                        <label th:text="${'(' + roundService.getBestRoundScoreByCourseId(userId, roundCourse.key.id) + ')'}"></label>
                    </div>
                    <div class="col-3">
                        <label>My average: </label>
                        <label th:text="${#numbers.formatDecimal(roundService.getAverageScoreByCourse(userId, roundCourse.key.id), 0, 2)}"></label>
                    </div>
                </div>
                <hr>
                <th:block th:each="round : ${roundCourse.value}">
                <div id="cardBody" class="card-body">
                    <div class="row">
                        <div class="col-3">
                            <label>Date: </label>
                            <label th:text="${#dates.format(round.roundDate, 'dd-MMM-yyyy')}"></label>
                        </div>
                        <div class="col-3">
                            <label>Score: </label>
                            <label th:if="${round.total - round.course.par == 0}" th:text="${'E'}"></label>
                            <label th:if="${round.total - round.course.par > 0}" th:text="${'+' + (round.total - round.course.par)}"></label>
                            <label th:text="${'(' + round.total + ')'}"></label>
                        </div>
                        <div class="col-6">
                            <div class="container-fluid">
                                <canvas th:attr="data-counts=${roundService.getListOfScoresByRoundId(round.roundId)}" id="myChart"></canvas>
<!--                                <canvas id="myChart"></canvas>-->
                            </div>
                        </div>
                    </div>
                    <br>
                    <div >
                        <table id="courseInfo" class="table table-bordered w-auto">
                            <th:block th:each="course : ${round.course}">
                                <tr>
                                    <th th:text="${'Hole'}"></th>
                                    <th th:each="hole : ${course.holes}" th:text="${hole.number}"></th>
                                    <th th:text="${'Total'}"></th>
                                </tr>
                                <tr>
                                    <td th:text="${'Par'}"></td>
                                    <td th:each="par : ${course.holes}" th:text="${par.par}"></td>
                                    <td th:text="${course.par}"></td>
                                </tr>
                                <tr>
                                    <td th:text="${'Score'}"></td>
                                    <th:block th:each="score : ${round.scores}">
                                        <td th:text="${score.score}" th:style="'background-color: ' + ${score.color}">
                                        </td>
                                    </th:block>
                                    <td th:text="${round.total}"></td>
                                </tr>
                            </th:block>
                        </table>
                        <br>
                        <a th:href="@{/discgolf/deleteRound/{id}(id=${round.roundId})}" title="Remove Course"
                           data-target="#deleteRoundModal" class="table-link danger" id="deleteRoundButton" >
                            <span id="deleteRound" class="fa-stack">
                                <i class="fa fa-square fa-stack-2x"></i>
                                <i class="fa fa-trash-o fa-stack-1x fa-inverse" title="Delete this round"></i>
                            </span>
                        </a>
                    </div>
                </div>
                <hr>
                    <div class="modal modal-danger" tabindex="-1" role="dialog" id="deleteRoundModal">
                        <div class="modal-dialog" role="document">
                            <div class="modal-content">
                                <div class="modal-header">
                                    <h5 class="modal-title">Confirm Delete</h5>
                                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                        <span aria-hidden="true">&times;</span>
                                    </button>
                                </div>
                                <div class="modal-body">
                                    <p>Are you sure you want to delete this round?</p>
                                </div>
                                <div class="modal-footer">
                                    <a href="" class="btn btn-primary" id="delRound">Confirm</a>
                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                                </div>
                            </div>
                        </div>
                    </div>
                </th:block>
            </div>
        </div>
    </div>
</div>

<script type="text/javascript" src="/js/jquery-3.6.0.js"></script>
<script type="text/javascript" src="/js/bootstrap/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script th:src="@{/js/discgolf/userRounds.js}"></script>
</body>
</html>

Javascript

const countsTest = document.getElementById('myChart').getAttribute('data-counts');
const counts = {};

for (const num of countsTest) {
  counts[num] = counts[num] ? counts[num] + 1 : 1;
}

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";
    }
  });
}


  new Chart(document.getElementById('myChart'),{
      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]],
          backgroundColor: "#77ACD8"
        },{
          data: [counts[3]]
        },{
          data: [counts[4]],
          backgroundColor: "#FDD79C"
        },{
           data: [counts[5]],
           backgroundColor: "#FDC26A"
         },{
             data: [counts[6], counts[7], counts[8], counts[9], counts[10]],
             backgroundColor: "#FCAE37"
           }]
      }
    }
  );
javascript html spring-boot chart.js thymeleaf
© www.soinside.com 2019 - 2024. All rights reserved.