How to pass an object inside another object in Thymeleaf? - java

I rendered a page with an object called "agency". It is coming from the controller that called the page.
Like this:
Agency agency = agencyService.findAgencyByName(name);
agencyOrSchool.addObject("agency", agency);
In the page (html) I also have a form that is the object "comment".
Like this:
<form th:action="#{/saveComment}" method="post">
<textarea name="comment" class="form-control" placeholder="Escreva seu comentário..." rows="3"></textarea>
<br>
<button type="Submit" class="btn btn-info float-right">Post</button>
</form>
"comment is an attribute of the object 'Comment' (my Model in Java) that I want to save.
Here is the main part of the model:
public class Comments {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#NotNull #NotEmpty
private String comment;
#NotNull #ManyToOne #JoinColumn (name = "userId")
private User user;
#NotNull #ManyToOne #JoinColumn (name = "agencyId")
private Agency agency;
This is right and could work, however, inside the object 'Comment' in my Java model I have the object 'Agency'. That means in the post I have to pass the 'agency' object (already in the page) inside the object 'Comment'.
How can I do it? I tried a lot of things, including this:
<form th:action="#{/saveComment}" method="post">
<textarea name="comment" class="form-control" placeholder="Escreva seu comentário..." rows="3"></textarea>
<br>
<input type="hidden" id="user" name="agency" th:object="${agency}"></input>
<button type="Submit" class="btn btn-info float-right">Post</button>
</form>
But it didn't work.

Related

How to bind an object that contains a list of an object from thymeleaf to contoller

I'm currently making a recipe application.
Every recipe will be containing several ingredients obviously.
So I made Recipe and Ingredient as OneToMany relationship. So that I can have multiple ingredients in a single recipe.
But the problem is when I try to add recipe using thymeleaf webpage.
I want to add ingredients dynamically, I mean I want to add ingredients as much as I need for each recipe using javascript to make the input boxes for ingredients increase or decrease dynamically.
Here is my Ingredient Entity Class:
public class Ingredient {
#Id
#GeneratedValue
private int id;
private String name;
private String amount;
}
This is Recipe Entity Table
public class Recipe {
#Id
#GeneratedValue
private int id;
private String name;
private String description;
private String instruction;
private Timestamp createdTime;
#OneToMany(cascade=CascadeType.ALL)
private List<Ingredient> ingredients = new ArrayList<>();
}
These are methods for adding recipe:
#GetMapping("/addRecipe")
public String addRecipeGet(Model model) {
model.addAttribute("recipe", new Recipe());
return "add_recipe";
}
#PostMapping("/addRecipe")
public String addRecipePost(Recipe recipe) {
recipeService.createRecipe(recipe);
return "home";
}
And I tried to bind the recipe object with thymeleaf like this:
<body>
<form method="POST" th:action="#{/addRecipe}" th:object="${recipe}" class="form-signup">
<label class="form-label pt-3" for="name">Recipe Name</label>
<input id="name" name="name" type="text" class="form-control mb-3" th:field="*{name}" placeholder="Recipe Name" />
<label class="form-label pt-3" for="description">Description</label>
<textarea id="description" name="description" type="text" class="form-control mb-3" th:field="*{description}" placeholder="Description"></textarea>
<label class="form-label pt-3" for="instruction">Instruction</label>
<textarea id="instruction" name="instruction" type="text" class="form-control mb-3" th:field="*{instruction}" placeholder="Instruction"></textarea>
<ul>
<li>
<div>
<input type="text" class="form-control mb-3" th:name="???" placeholder="ex) Onion"></input>
<input type="text" class="form-control mb-3" th:name="???" placeholder="ex) 300g"></input>
</div>
</li>
</ul>
<div class="d-grid col-8 mx-auto">
<button type="submit" class="btn btn-dark">Submit</button>
</div>
</form>
</body>
I just tried to bind only one ingredient for now but it's not working obviously.. I don't know what to put in the th:field or th:name
I have no idea how to post a list of data inside an object
Is even there a way to solve this problem??
You can bind list items using th:field="*{ingredients[i].name}" or name in the same format.
See https://www.baeldung.com/thymeleaf-list for a full guide on how to handle list binding in thymeleaf

Send object in JSP as foreign key using Spring MVC and Hibernate

I want to create a bank account register related to a specific Person in a OneToMany relationship.
I have the clas Pessoa (Person):
#Entity
public class Pessoa {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idPessoa;
private String nome;
#OneToMany(mappedBy = "pessoa", targetEntity = ContaCorretora.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<ContaCorretora> contaCorretora;
...and the class ContaCorretora (Bank account):
#Entity
public class ContaCorretora {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idConta;
private TipoConta tipoConta;
private TipoRisco tipoRisco;
private String login;
private String senha;
private BigDecimal valorAtual;
#ManyToOne
#JoinColumn(name="idPessoa")
private Pessoa pessoa;
I'm using this method in Controller for start the process of registration:
#RequestMapping(value = "pessoacorretora/{id}")
public ModelAndView pessoaCorretora(#PathVariable("id") int id, ContaCorretora contaCorretora ) {
Map<String, Object> model = new HashMap<String, Object>();
Pessoa pessoa = pessoaDao.find(id);
model.put("pessoa", pessoa);
model.put("tipoConta", TipoConta.values());
model.put("tipoRisco", TipoRisco.values());
return new ModelAndView("corretora/contacorretora", "model", model);
}
Sumarizining, I have a specific page for recording bank accounts. So, I created this form:
<form:form action="${s:mvcUrl('CC#gravar').build() }" method="post" commandName="contaCorretora" enctype="multipart/form-data" >
<div class="form-group" >
<label>Conta</label>
<select name="tipoConta">
<c:forEach items="${model.tipoConta}" var="tipoConta">
<option value=${tipoConta}>${tipoConta}</option>
</c:forEach>
</select>
</div>
<div class="form-group" >
<label>Risco</label>
<select name="tipoRisco">
<c:forEach items="${model.tipoRisco}" var="tipoRisco">
<option value=${tipoRisco}>${tipoRisco}</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<label>Login</label>
<form:input path="login" cssClass="form-control" />
</div>
<div class="form-group">
<label>Senha</label>
<form:input path="senha" cssClass="form-control" />
</div>
<div class="form-group">
<label>Valor Atual</label>
<form:input path="valorAtual" cssClass="form-control" />
</div>
<form:hidden path="pessoa" cssClass="form-control" value="${pessoa}"/>
<button type="submit" class="btn btn-primary">Cadastrar</button>
</form:form>
When I use the form in this way, I receive the error "description The request sent by the client was syntactically incorrect." I figured out that the problem is in this line, because when I delete, the form post ok:
<form:hidden path="pessoa" cssClass="form-control" value="${pessoa}"/>
Nevertheless, if I delete this line, the program doesn't save the idPessoa as a foreign key, this field is null. I would like to know how to pass an entire object in my JSP form. The post method is:
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("pessoa/listageral");
}
all the DAO's methods are okay.
You only need to send the primary key of pessoa to the form.
Change the form attribute
<form:hidden path="pessoa.idPessoa" cssClass="form-control" value="${model.pessoa.idPessoa}"/>
Before persisting the ContaCorretora, make sure you get the Pessoa object from db.
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretora.setPessoa(pessoaDao.find(contaCorretora.getPessoa().getIdPessoa()));
//I escaped null check and not found exceptions, you should apply some logic to take care of that
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("pessoa/listageral");
}
Using Entities as form model is not a good approach. Persistance layer should not be on the MVC layer.
In order to pass Java object from your Controller
#RequestMapping(method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
ModelAndView mav = new ModelAndView("pessoa/listageral");
// retrieve object from DAO
Object myObj = Dao.find(id);
// Put object into model map
mav.addObject("myObj", myObj);
// return model and view
return mav;
}
In JSP you can refer to object using ExpressionLanguage syntax
${myObj}

Why is my redirectAttributes.addFlashAttribute and data Re-populating not working after validation fails?

I have spent the whole day and night trying to find why my form data disappears when validation fails. I also added a redirectAttributes.addFlashAttribute which is suppose to indicate that an error occurred and also survive one redirect. So far none of these are working. I have done my research on stack over and other forums and i seem to be doing the right thing but it is not working for me.
I am not getting an error so i can not even debug to find what is wrong.
<div class="form-group" th:if="${exams.size() lt 6}">
<form method="post" th:object="${newExam}" th:action="#{/exams}" class="inline new-item">
<div th:classappend="${#fields.hasErrors('indexNumber')}? 'error' : ''">
<input type="text" th:field="*{indexNumber}" placeholder="Index Number" />
<div class="error-message" th:if="${#fields.hasErrors('indexNumber')}" th:errors="*{indexNumber}"></div>
</div>
<div th:classappend="${#fields.hasErrors('grade')}? 'error' : ''">
<select th:field="*{grade}" class="form-control input-lg">
<option value="">[Select Grade]</option>
<option th:each="grade : ${grades}" th:value="${grade.values}" th:text="${grade.name}">Grade
</option>
</select>
<div class="error-message" th:if="${#fields.hasErrors('grade')}" th:errors="*{grade}"></div>
</div>
<div th:classappend="${#fields.hasErrors('courseOffered')}? 'error' : ''">
<input type="text" th:field="*{courseOffered}" placeholder="CourseOffered" />
<div class="error-message" th:if="${#fields.hasErrors('courseOffered')}" th:errors="*{courseOffered}"></div>
</div>
<div th:classappend="${#fields.hasErrors('examType')}? 'error' : ''">
<input type="text" th:field="*{examType}" placeholder="ExamType" />
<div class="error-message" th:if="${#fields.hasErrors('examType')}" th:errors="*{examType}"></div>
</div>
<div th:classappend="${#fields.hasErrors('subject')}? 'error' : ''">
<input type="text" th:field="*{subject}" placeholder="Subject" />
<div class="error-message" th:if="${#fields.hasErrors('subject')}" th:errors="*{subject}"></div>
</div>
<div th:classappend="${#fields.hasErrors('gradeYear')}?'error' : ''">
<input type="text" th:field="*{gradeYear}" placeholder="ExamYear" />
<div class="error-message" th:if="${#fields.hasErrors('gradeYear')}" th:errors="*{gradeYear}"></div>
</div>
<button type="submit" class="btn btn-primary">Add</button>
</form>
</div>
Controller
#RequestMapping(value = "/cert_prog", method = RequestMethod.GET)
public String examsList(Model model){
Iterable<Exams> exams = examService.findAll();
if(!model.containsAttribute("newExam")){
model.addAttribute("newExam", new Exams()); }
model.addAttribute("grades", Grade.values());
model.addAttribute("regions", Region.values()); model.addAttribute("schools",schools);
if(!model.containsAttribute("newSchool")){
model.addAttribute("newSchool",new School()); }
model.addAttribute("regions", Region.values());
return "cert_prog"; }
#RequestMapping(value = "/exams", method = RequestMethod.POST) public String addTask(#Valid
#ModelAttribute("newExam") Exams exams, BindingResult result, RedirectAttributes redirectAttributes, Principal principal){
User user = (User)((UsernamePasswordAuthenticationToken)principal).getPrincipal(); exams.setUser(user);
if(result.hasErrors()){
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.exams", result);
redirectAttributes.addFlashAttribute("exams",exams); return "redirect:/cert_prog"; }
examService.save(exams);
return "redirect:/cert_prog"; }
Model
#Entity
public class Exams {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull(message = "The above field must not be blank.")
private String courseOffered;
#NotNull(message = "The above field must not be blank.")
private String examType;
#NotNull(message = "The above field must not be blank.")
private String subject;
#NotNull(message = "The above field must not be blank.")
private String grade;
#NotNull(message = "The above field must not be blank.")
private Long indexNumber;
#NotNull(message = "The above field must not be blank.")
private Long gradeYear;
private boolean isComplete;
In your addTask controller method you have
redirectAttributes.addFlashAttribute("exams",exams)
attribute name as "exams" and you are checking as "newExam" in examsList controller method. This might be the problem.
try this,
redirectAttributes.addFlashAttribute("newExam",exams)

How to bind an object list to checkbox in thymeleaf?

I am having a lot of difficulty with binding checkbox inputs with a object list.Issue is when I add th:field="*{userRole}" on input field type checkbox then I am getting Bad request as response on web browser.
Here is my code:
User Model class:
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private boolean enabled;
private List<UserRole> userRole = new ArrayList<UserRole>(0);
}
UserRole Model class:
public class UserRole implements Serializable {
#Id
#GeneratedValue
private Integer userRoleId;
#ManyToMany(mappedBy = "userRole")
private List<User> users;
#Column(name = "role", nullable = false, length = 45)
private String role;
}
Model Attribute:
#ModelAttribute(value = "user")
public User userModel() {
return new User();
}
GET Method:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Map<String, Object> model) {
List<UserRole> roles = roleService.getAllRoles();
model.put("roles", roles);
return "index";
}
My POST method:
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addUser(#ModelAttribute("user") User userModel) {
}
Index page Form :
<form role="form" action="#" th:action="#{/add}" th:object="${user}" method="post">
<div class="form-group">
<label for="email">User Name:</label>
<input type="text" class="form-control" id="username" th:field="*{username}" required autofocus>
</div>
<ul>
<li th:each="item : ${roles}">
<input type="checkbox" th:field="*{userRole}" th:value="${item}" />
<label th:text="${item.role}">Test</label>
</li>
</ul>
<input type="submit" value="Submit" />
</form>
when I click submit, browser shows as a Bad request, But without th:field="*{userRole}" I can submit the form. Any idea to resolve this issue?
---Update---
issue was Thymeleaf unable to bind object directly. So added new List of String for binding.
private List<String> userRoles = new ArrayList<>(0);
and then changed the form as #Roel mentioned.
<li th:each="item, stat : ${roles}">
<label th:text="${stat.index}">Test</label>
<input type="checkbox" th:field="*{userRoles[__${stat.index}__]}" th:value="${item.userRoleId}" />
<label th:text="${item.role}">Test</label>
</li>
Thanks.
You are trying to add item as the value to a List. It's like trying to say ArrayList<Integer> list = 5;
What you need is to add the item to the list:
li th:each="item, stat : ${roles}">
<input type="checkbox" th:field="*{userRole[__${stat.index}__]}" th:value="${item}" />
<label th:text="${item.role}">Test</label>
</li>
I'm unsure if this will directly solve your problem as thymeleaf has issues with simply "adding" an object to a list. Thymeleaf generally works with basic types like strings and integers. But at least I pointed out where your problem lies. Try fiffling around with this a little bit. Try using this line instead, at least this will definitely work:
<input type="checkbox" th:field="*{userRole[__${stat.index}__].role}" th:value="${item.role}" />

How to save multiple identical entities from one form in Spring Controller?

I have a question, how to save multiple identical entities from one form in Spring Controller?
If I have the following code in html:
<form method="post" action="/dictionary/save">
<table>
... BEGIN jsp foreach function ...
<tr>
<td><input type=hidden name="id" value="${entity.id}"></td>
<td><input type=text name="en" value="${entity.en}"></td>
<td><input type=text name="lv" value="${entity.lv}"></td>
<td><input type=text name="ru" value="${entity.ru}"></td>
</tr>
... END jsp foreach function ...
</table>
<input type=submit value="Save">
</form>
In JSP listing can be till 50 entities. How to save its all in one request?
Create a modelAttribute of a domain object say .. dictionary which would have a list of some element (that you say can be 50 in the JSP)
in the JSP, use the modelAttribute in the form:form tag
and instead of input type use:
In Spring
class Dictionary{
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(unique = true, nullable = false)
private String code;
#Column
private String ru;
#Column
private String lv;
#Column
private String en;
}
In Controller
List<Dictionary> diction=new ArrayList<Dictionary>();
model.addattribute("dictionary",diction);
In JSP
<form:form method="post" action="/dictionary/save" modelAttribute="dictionary">
<table><tr>
<td>
<form:input path="diction["+rowNum+"].code" />
<form:input path="diction["+rowNum+"].ru" />
<form:input path="diction["+rowNum+"].lv" />
<form:input path="diction["+rowNum+"].en" />
</td>
//code to add next td (either through javascript or jquery)
</form:form>
*Please see that
1. tags wont work in javascript or jquery and you can have simple input tags as *
<input type="text" name="code"/>
This name input could be as many as you want

Categories