Thymeleaf form submit with model having associations
Asked Answered
D

1

0

The application is web-application using spring boot and thymeleaf. The application has a questionnaire along with some fields that needs to filled to submit the form. The model object (here answeredQuestionnaire) in template th:object="${answeredQuestionnaire}" has nested objects with as shown below.

When I submit the form, I need to assign the questionId and possibleAnswerId to answers (Set) in answeredQuestionnaire. How can I assign this in thymeleaf template. Please provide some suggestions.

AnsweredQuestionnaire .java

    public class AnsweredQuestionnaire {
    private UUID id;
    private Boolean isRirelevant;
    private Boolean isITrelevant;
    private String descriptionProcess;
    private int classCalculated;
    private LocalDateTime createdDateAndTime;
    private Questionnaire questionnaire;//separate model class
    private Process process;//separate model class
    private Set<Answer> answers =  new HashSet<>();// Set of separate model class 
}

Answer.java

public class Answer {
    private UUID id;
    private Question question;// separate class
    private PossibleAnswer selectedPossibleAnswer;// separate class
    private String answerComment;
    private AnsweredQuestionnaire answeredQuestionnaire;
}

The controller handler to display the questionnaire page is as shown below.

  @GetMapping("/review/process/{id}")
    public String reviewProcess(@PathVariable UUID id, Model model){


        Process byId = processService.findById(id);


        if(byId != null){
            if(byId.getAnsweredQuestionnaires().size() > 0){

                Set<AnsweredQuestionnaire> answeredQuestionnaires = byId.getAnsweredQuestionnaires();
                AnsweredQuestionnaire lastUpdatedAnsweredQuestionnaire = answeredQuestionnaires.stream().sorted(Comparator.comparing(AnsweredQuestionnaire::getCreatedDateAndTime).reversed())
                        .collect(Collectors.toList()).get(0);
                System.out.println("AnsweredQuestionnaire for a given process :"+lastUpdatedAnsweredQuestionnaire);
                model.addAttribute("answeredQuestionnaire", lastUpdatedAnsweredQuestionnaire);
                model.addAttribute("isIT_SiG_relevant", lastUpdatedAnsweredQuestionnaire.getIsIT_SiG_relevant());
                model.addAttribute("isRiDARrelevant", lastUpdatedAnsweredQuestionnaire.getIsRiDARrelevant());

                model.addAttribute("possibleAnswersIds", getAllSelectedPossibleAnswersWithId(lastUpdatedAnsweredQuestionnaire.getAnswers()) );
  
            }else{
                System.out.println("Process without answeredQuestionnaire");
                Questionnaire byIsActive = questionnaireService.findByIsActive(true);
                AnsweredQuestionnaire emptyAnsweredQuestionnaire = new AnsweredQuestionnaire();
                emptyAnsweredQuestionnaire.addQuestionnaireForAnsweredQuestionnaire(byIsActive);
                System.out.println("Questionnaire found"+byIsActive);
                model.addAttribute("answeredQuestionnaire", emptyAnsweredQuestionnaire);
                model.addAttribute("isIT_relevant", false);
                model.addAttribute("isRi_relevant", false);
//                model.addAttribute("questionnaire", byIsActive);
                model.addAttribute("possibleAnswersIds", getAllSelectedPossibleAnswersWithId(emptyAnsweredQuestionnaire.getAnswers()) );
            }
        }
   

review.html

        <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Review page</title>
</head>
<body>
<h1>This is the review page</h1>

<form action="#" th:action="@{/review_questionnaire}" th:object="${answeredQuestionnaire}" method="post">

    <label>
        isRirelevant :
    </label>

    <select th:field="*{isRirelevant}">
        <option  th:value="false" th:text="no" th:selected="${false==isRirelevant}"></option>
        <option  th:value="true" th:text="yes" th:selected="${true==isRirelevant}"></option>
    </select>
    <br>
    <select th:field="*{isITrelevant}">
        
        <option  th:value="false" th:text="no" th:selected="${false==isITrelevant}"></option>
        <option  th:value="true" th:text="yes" th:selected="${true==isITrelevant}"></option>
    </select>

    <table>
        <thead>
        <tr>
            <th>Question</th>
            <th>Answer</th>
            <th>Comment</th>
        </tr>
        </thead>
        <tbody>
            <tr th:each="question : ${answeredQuestionnaire.questionnaire.getQuestions()}">
            <td th:text="${question.text}"  th:field="*{#lists.toList(answers)}">Question</td>
            <td>
                <select>
                    <option value="0" >Select Answer</option>

                    <option th:each="possibleAnswer : ${question.getPossibleAnswers()}" th:value="${possibleAnswer.id}" th:text="${possibleAnswer.text}" ></option>

                </select>
            </td>
        </tr>
        </tbody>
    </table>
    <br>
    <input type="submit" />
    <br>
    <label >Class calculated : </label>
    <input type="number"  th:field="*{classCalculated}" th:value="${answeredQuestionnaire.classCalculated}" readonly  >
</form>
</body>
</html>

Update

I separated the answers (Set) with th:if conditional, but still I cannot submit the form.

<div th:if="${answeredQuestionnaire.answers.size()>0}">
    <table >
        <thead>
        <tr>
            <th>Question</th>
            <th>Answer</th>
            <th>Comment</th>
        </tr>
        </thead>
        <tbody th:field="*{answers}" >
        <tr th:each="answer,iStat : ${answeredQuestionnaire.answers}" >
            <td>
                <input  th:text="${answer.question.text}" th:value="${answer.question.id}" th:field="*{answers[__${iStat.index}__].question.id}"/>
            </td>
            <td>
                <select th:field="*{answers[__${iStat.index}__].selectedPossibleAnswer.id}">
                    <option th:value="0" >Select Answer</option>
                    <option th:each="possibleAnswer : ${answer.question.getPossibleAnswers()}" th:value="${possibleAnswer.id}" th:text="${possibleAnswer.text}" th:selected="${possibleAnswer.id==answer.getSelectedPossibleAnswer().id}" ></option>
                </select>
            </td>
                             <td>
                                 <input  th:text="${answer.getAnswerComment()}" th:field="*{answers[__${iStat.index}__].answerComment}" >
                             </td>
        </tr>
        </tbody>
    </table>
    </div>
Devisee answered 2/9, 2022 at 11:47 Comment(0)
I
1

Some suggestions :

1/ You should use Command objects or DTO and not Entity from your model directly to bind datas from your web forms.

2/ You should use a List to bind datamodel relations, it works with Thymeleaf but Set will not work.

3/ You can manage relations with DTO which are containing subsets of your model objects, and simplify the business process a lot and the wireload to your services and database(s). It depends how your model are related too btw.

4/ Disable OpenSessionInView and make a service layer with Transactional annotated methods

5/ Never use an Entity inside a Controller. It's an architectural conception's nonsense.

Ilonailonka answered 5/9, 2022 at 9:50 Comment(2)
Hi,1. I did not use any entities in controller or thymeleaf template. These are just the model classes. 2. Sets can be converted to lists using #lists in thymeleaf. 3. Please eloborate this point. All this code is from the Frontend which is java application using HttpClient to communicate with the backend.Devisee
I can display values from the backend, the main issue is while submitting the form answers (Set ) with questions and possibleAnswer id need to reach the controller.Devisee

© 2022 - 2024 — McMap. All rights reserved.