skip to Main Content

I’m trying to create an application in Spring in which the User designs a quiz. They create a quiz by submitting questions using a Thymeleaf form. ‘Question’ is a class with a few different attributes, but for simplicity I am just focusing on the adding the question itself i.e. a String called ‘quesitionPrompt’. Once the form is submitted each question should save in the next available index of questionList (an ArrayListtype attribute in the Quiz class) .

The number of questions added to the form is up to the User. Therefore, I’ve created a J query method where a new question can be generated by the user. When the ‘clone and append’ button is clicked, a new textbox is generated for the next question and the label and id are updated to be unique. This method works by cloning and appending a question template to an empty div.

The issue I’m facing is that I can’t successfully update th:field. Updating this field would allow me to save each question separately in the next available index in questionList. Instead, on form submission, the text in each individual text box is concatenated and saved as a single Question in the same index. E.g. Do you like long movies?,Do you like horror?

I assume my issue is related to J-Query being unable to properly work with Thymeleaf attributes, but I can’t figure out how to fix it. Any help would be much appreciated!

HTML:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Create Quiz</title>

    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <script>
        $(document).ready(function () {
            var questionCounter = 1;
            var originalExpression = "*{quiz.quizQuestion[0].prompt}";

            // When the document is ready, execute the following code
            $("#cloneButton").click(function () {
                // When the button with id "cloneButton" is clicked, execute the following code
                var originalDiv = $("#questionContainer");
                // .clone() clones the contents of #cloneContainer instead of the container itself.
                var clonedDiv = $("#cloneContainer").clone();
                var newId = "cloneContainer" + questionCounter;
                // var newLabel ="Question: " + questionCounter;
                clonedDiv.attr("id", newId);

                var newLabel = "Question: " + (questionCounter + 1);
                clonedDiv.find("label").text(newLabel);
                // clonedDiv.find("input").attr("th:field", "*{quiz.quizQuestion[" + questionCounter + "].prompt}");

                var targetElement = clonedDiv.find(":contains('" + originalExpression + "')");
                var newExpression = "*{quiz.quizQuestion[" + questionCounter + "].prompt}";
                alert(newExpression);
                clonedDiv.html(clonedDiv.html().replace(originalExpression, newExpression));
                clonedDiv.find("input").val("*{quiz.quizQuestion[" + questionCounter + "].prompt}");

                originalDiv.append(clonedDiv);
                questionCounter++;
            });
        });
    </script>
</head>

<body>
<form th:action="@{/api/v1/quiz/registerQuiz}" method="post">

    <div id="cloneContainer">
        <label> Question: 1 </label> <input type="text" th:field="*{quiz.quizQuestion[0].prompt}" />
    </div>
    <div id="questionContainer"></div>

    <div>
        <button type="button" id="cloneButton">Clone and Append</button>
    </div>
    <div>
        <input type="submit" value="Submit">
    </div>
</form>
</body>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    I seems the issue I was encountering was related to how Thymeleaf processes form fields. When I dynamically added fields using JavaScript, Thymeleaf wasn't aware of these changes on the client side and didn't process them during server-side rendering.

    The solution was to use a more generic name attribute for the input fields, and then processed them on the server side without relying on Thymeleaf's specific syntax.

    My working code:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Create Quiz</title>
    
        <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
        <script>
            $(document).ready(function () {
                var questionCounter = 1;
            
                // When the document is ready, execute the following code
                $("#cloneButton").click(function () {
                    
                    // When the button with id "cloneButton" is clicked, execute the following code
                    var originalDiv = $("#questionContainer");
                    
                    // .clone() clones the contents of #cloneContainer instead of the container itself.
                    var clonedDiv = $("#cloneContainer").clone();
                    
                    //Upate name and Id fields for each clone
                    var newId = "cloneContainer" + questionCounter;
                   
                   clonedDiv.attr("id", newId);
               
                    //Upate label fields for each clone
              
                   var newLabel = "Question: " + (questionCounter + 1);
                   clonedDiv.find("label").text(newLabel);
                   
                    clonedDiv.find('input').prop('name', `quizQuestion[${questionCounter}].prompt`);
                    
                    originalDiv.append(clonedDiv);
                    questionCounter++;
                });
            });
        </script>
    </head>
    
    <body>
    <form th:action="@{/api/v1/quiz/registerQuiz}" method="post" th:object = ${quiz}>
    
         <div>
            <label> Title </label> <input type="text" th:field="*{quizTitle}" />
        </div>
        
        <div id="cloneContainer">
            <label> Question: 1 </label> <input type="text" name = "quizQuestion[0].prompt" />
        </div>
        <div id="questionContainer"></div>
    
        <div>
            <button type="button" id="cloneButton">Clone and Append</button>
        </div>
        <div>
            <input type="submit" value="Submit">
        </div>
    </form>
    </body>
    </html>
    

  2. Set the appropriate name attribute for each input.

    clonedDiv.find('input').prop('name', `quizQuestion[${questionCounter}].prompt`);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search