java - 使用JS在Thymeleaf中生成输入字段的id和name属性无效
问题描述
我遇到了一个非常奇怪的问题,我找不到原因。简而言之,当我尝试在 update.html 中的应用程序中更新一些先前给出的答案时,某些东西会为我的一个输入字段生成随机 id 和随机名称值,因此这些特定数据不会发送到后端。
这是我的 update.html 的控制器方法
@GetMapping("/update/{questionFormId}/{appUserId}")
public String updateAnswerFormCreatedByUser(@PathVariable long questionFormId, @PathVariable long appUserId, Model model) {
log.info("GET answer-form/update/" + questionFormId + "/" + appUserId + " started");
try {
model.addAttribute("answerForm", answerFormService.createAnswerFormToUpdate(questionFormId, appUserId));
log.info("GET answer-form/update/" + questionFormId + "/" + appUserId + " finished");
return "answerform/update";
这是我发送到 HTML 的对象的类
public class CreateAnswerFormDTO {
private long questionFormId;
private long answerFormId;
private long appUserId;
private List<Question> questions = new ArrayList<>();
private List<Answer> answers = new ArrayList<>();
public CreateAnswerFormDTO() {
}
// with other constructors and getters/setters
问题类
// I have four different types of questions (text, checkbox, radio and scale(radio but with numbers)
//and I store them in the same table and they have some different class variables as well)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Question_Type")
@Where(clause="deleted=0")
public class Question implements Comparable<Question> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@NotNull
private String questionText;
private int listPosition;
private boolean deleted;
@Transient
private List<String> answersNotFilledOutByUser = new ArrayList<>();
@ManyToOne(cascade = {CascadeType.MERGE})
@JsonBackReference(value = "questionsQuestionForm")
private QuestionForm questionForm;
@OneToMany(mappedBy = "question", cascade = {CascadeType.MERGE})
@JsonManagedReference("answersQuestion")
private List<Answer> answers = new ArrayList<>();
// with constructors, getters and setters
我的答案课
public class Answer {
// I store the concrete answertexts provided by the users in the ActualAnswerText class,
// because f.e. text questions or checkbox questions can have multiple answer texts"
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean deleted;
@OneToMany(mappedBy = "answer", cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JsonManagedReference(value = "answersActualAnswerTexts")
private List<ActualAnswerText> actualAnswerTexts = new ArrayList<>();
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JsonBackReference(value = "answersAnswerform")
private AnswerForm answerForm;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JsonBackReference("answersQuestion")
private Question question;
// with constructors, getters and setters
我的更新html
<head>
<meta charset="UTF-8">
<title>Update Answerform</title>
<link th:replace="fragments/navbar.html :: bootstrap-link">
<link th:replace="fragments/navbar.html :: css">
</head>
<body>
<script th:src="@{/js/renderanswerform.js}" type="text/javascript"></script>
<header th:replace="fragments/navbar.html :: navbar"></header>
<div th:replace="fragments/messages.html :: error"></div>
<h1>Update your answerform</h1>
<form id="answerform" method="post"
th:action="@{|/answer-form/update/${answerForm.answerFormId}/${answerForm.appUserId}|}">
<tr th:each="question, iterStat : ${answerForm.questions}">
<p th:text="${question.questionText}"></p>
<!--Rendering the question's text-->
<div>
<th:block th:switch="${question.getDiscriminatorValue()}">
<!--checking what questiontype the current question belongs to-->
<div th:case="'CheckBoxQuestion'">
<span th:each="questionTextPossibility, list: ${question.questionTextPossibilities}">
<label th:text="${questionTextPossibility.answerText}"></label> <!--Displaying the possible answertext value-->
<!-- I use th:field to prefill-->
<!-- I use th:name to make it available on the backend-->
<!-- I use th:value to set what value will be sent to the backend when clicking on this checkbox-->
<input
th:field="${answerForm.answers[__${iterStat.index}__].actualAnswerTexts[__${list.index}__].answerText}"
th:name="answers[__${iterStat.index}__].actualAnswerTexts[__${list.index}__].answerText"
th:value="${questionTextPossibility.answerText}"
type="checkbox">
</span>
</div>
<div th:case="'RadioButtonQuestion'">
<span th:each="questionTextPossibility, list: ${question.questionTextPossibilities}">
<label th:text="${questionTextPossibility.answerText}"></label>
<input th:field="${answerForm.answers[__${iterStat.index}__].actualAnswerTexts[0].answerText}"
th:name="answers[__${iterStat.index}__].actualAnswerTexts[0].answerText"
th:value="${questionTextPossibility.answerText}"
type="radio">
</span>
</div>
<div th:case="'ScaleQuestion'">
<th:block th:each="i: ${#numbers.sequence(1, question.scale)}">
<!-- Scale question generates radio button with numbers until the scale attribute of ScaleQuestion class-->
<label th:text="${i}"></label>
<input th:field="${answerForm.questions[__${iterStat.index}__].answers[0].actualAnswerTexts[0].answerText}"
th:name="answers[__${iterStat.index}__].actualAnswerTexts[0].answerText"
th:value="${i}"
type="radio">
</th:block>
</div>
<div th:case="'TextQuestion'" th:id="|textquestioncreate[__${iterStat.index}__]|">
<!--With TextQuestion users can provided more than one answer.
f.e What are the best movies? They can generate more than one input fields to provide answers -->
<span th:id="|answers[__${iterStat.index}__]|">
<span
th:each="actualAnswerText, list: ${answerForm.answers[__${iterStat.index}__].actualAnswerTexts}">
<input
th:field="${answerForm.answers[__${iterStat.index}__].actualAnswerTexts[__${list.index}__].answerText}"
th:name="answers[__${iterStat.index}__].actualAnswerTexts[__${list.index}__].answerText"
type="text">
</span>
</span>
<script th:inline="javascript">
<!-- This js code creates the buttons that enable the adding of new input fields,
finishing given question and re-enabling modification-->
/*<![CDATA[*/
renderQuestionText(/*[[${iterStat.index}]]*/)
/*]]>*/
/*<![CDATA[*/
resetForCreatingTextQuestion()
/*]]>*/
</script>
</div>
</th:block>
</div>
</tr>
</table>
</form>
最后,我的 renderanswerform.js 文件
// I haven't listed all the functions because some are just for creating
// finish, submit, reset and re-enable buttons
var actualAnswerIndexForTextQuestion = 0;
var buttonIndexToEnable = 0;
var currentTextAnswerIndex = 0;
var numberOfInputFieldsCreated = 0;
var hasInputfieldsCreated = false;
window.onload = function WindowLoad(event) {
createValidateResetAndSubmitButtons();
}
function renderQuestionText(index) {
if (!hasInputfieldsCreated) {
let container = document.getElementById("textquestioncreate[" + index + "]");
container.appendChild(createTextQuestionInput(index));
hasInputfieldsCreated = true;
}
}
function renderQuestionTextForCreate(index) {
let container = document.getElementById("textquestioncreate[" + index + "]");
container.appendChild(createTextQuestionInput(index));
hasInputfieldsCreated = true;
}
function createTextQuestionInput(index) {
let container = document.getElementById("answers[" + index + "]");
let anotherInputButton = createAnotherInputButton(index);
anotherInputButton.disabled = true;
anotherInputButton.id = "textanswerbutton" + currentTextAnswerIndex;
let finishButton = createFinishButtonForTextAnswer(currentTextAnswerIndex);
let reEnableButton = createReenableButton(currentTextAnswerIndex);
container.appendChild(anotherInputButton);
container.appendChild(finishButton);
container.appendChild(reEnableButton);
enableDisableFinishAndAddAnotherTextFieldButtons();
currentTextAnswerIndex++;
return container;
}
function createAnotherInputButton(index) {
let anotherInputButton = document.createElement("BUTTON");
anotherInputButton.id = "anotherinputbutton" + index;
anotherInputButton.textContent = "Create another input field";
anotherInputButton.className = "buttonsToDisableEnable";
anotherInputButton.type = "button";
anotherInputButton.addEventListener("click", () => createAnotherInputField(index));
return anotherInputButton;
}
function createAnotherInputField(index) {
let elementId = "answers[" + index + "]";
let container = document.getElementById(elementId);
let numberOfInputFieldsInContainer = container.getElementsByTagName('INPUT').length
let input = document.createElement("INPUT");
numberOfInputFieldsCreated++;
input.id = "textanswer" + currentTextAnswerIndex;
actualAnswerIndexForTextQuestion++;
input.name = "answers" + "[" + index + "].actualAnswerTexts[" + numberOfInputFieldsInContainer + "].answerText";
container.insertBefore(input, document.getElementById("textanswerbutton" + buttonIndexToEnable));
}
在 ScaleQuestion 输入中没有正确的名称属性,而是在浏览器中检查它时看到以下内容。我在代码中的任何地方都没有提供任何 id,并且 name 属性的开头不应包含“question0.answers0”,只能包含 answers[0]。启动应用程序时 HTML 中的 ScaleQuestion(不正确)
<div>
<label>1</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="1" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText1">
<label>2</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="2" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText2">
<label>3</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="3" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText3">
<label>4</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="4" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText4">
<label>5</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="5" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText5">
<label>6</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="6" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText6">
<label>7</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="7" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText7">
<label>8</label>
<input name="questions[2].answers[0].actualAnswerTexts[0].answerText" value="8" type="radio" id="questions2.answers0.actualAnswerTexts0.answerText8">
</div>
单选按钮问题(正确):
<span>
<label>radio1</label>
<input name="answers[0].actualAnswerTexts[0].answerText" value="radio1" type="radio" id="answers0.actualAnswerTexts0.answerText1">
</span>
到目前为止我已经尝试过:
- 使用无效缓存重新启动 Intellij
- 从浏览器中删除缓存
- 使用不同的浏览器
- 将内容复制到不同的 html 文件并从控制器引用该文件
- 哭
我的猜测是 js 代码的某些先前状态会以某种方式干扰(因为在此之前所有输入字段都是在 js 中生成的),但我看不到如何或在哪里。
提前感谢您的帮助!
解决方案
推荐阅读
- android - android中的多个按钮处理
- r - 源代码中的 uniroot() 函数在修改后不起作用;无法找出错误
- api - Telegram Bots:我现在可以开始聊天吗?
- powershell - 使用 maven 部署 zip 文件
- awk - 用于解析文件并以字母格式列出输出的 AWK 命令,包括每个项目的计数
- bash - 计算连续字符数的代码
- android - AppCenter:必须引用包含私钥和相应公钥证书链的有效 KeyStore 密钥条目
- ruby-on-rails - 如果对象在 Rails 6 中以嵌套形式不存在,如何创建对象?
- mysql - 每次在 MySQL 的第一列中找到值时增加其值的列
- python - 如何从 Pexels API 下载视频?