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}" />
Related
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
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.
I am using spring boot as back-end and angular 2 as front-end.
My problem consist how to bind spring validators with angular 2.
For example, i implemented crud functionality with spring boot.
i set the attribute quantite not empty with annotation #notEmpty(message="the quantite field should not be empty").
In the form, if the quantite field is empty and when i click on submit button, i want that spring validates the inputs.
Produit.class
public class Produit implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
private String designation;
private double prix;
#NotEmpty(message="the quantite field should not be empty")
private int quantite;
...
ProduitRestService.class
#RequestMapping(value="/produits/{id}",method=RequestMethod.PUT)
public Produit update(#RequestBody #valid Produit p,#PathVariable Long id)
{
p.setId(id); // ??????
return produitRepository.saveAndFlush(p);
}
angular 2
add.component.html:
<form #addForm="ngForm" (ngSubmit)="onSubmit(addForm.value)" >
<div class="md-form">
<input type="text" id="form4" class="form-control" name="des" [(ngModel)]="des" >
<label for="form4">designation!!!!!</label>
</div>
<div class="md-form">
<input type="number" id="form4" class="form-control" name="prix" [(ngModel)]="prix" >
<label for="form4">prix</label>
</div>
<div class="md-form">
<input type="number" id="form4" class="form-control" #quantiteref="ngModel" name="quantite" [(ngModel)]="quantite" >
<label for="form4">quantité</label>
<div [hidden]="quantiteref.valid" class="alert alert danger" > this field must not be empty</div> // i know this validation in angular 2, but i need the work to be in spring dev
</div>
<div class="text-center">
<button class="btn btn-deep-purple" (click)="addProduct()" >ADD</button>
</div>
</form>
You can add a BindingResult object as a parameter of your update method.
Thus, you'll be able to check if there is any error like this :
public Produit update(#RequestBody #Valid Produit p, BindingResult result, #PathVariable Long id) {
if (result.hasErrors()) {
// handle error
} else {
// good to go
}
}
I'm doing an application using Spring MVC and I have a problem when I try to register an bank account to a specific person in my application. In this case, one Person has many accounts:
There are two classes in this relationship:
Sumarizing:
#Entity
public class Pessoa {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int idPessoa;
#OneToMany(mappedBy = "pessoa", targetEntity = ContaCorretora.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ContaCorretora> contaCorretora;
#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;
Controller Class:
#RequestMapping("/pessoa")
#Controller
public class ContaCorretoraController {
#Autowired
private ContaCorretoraDAO contaCorretoraDao;
#Autowired
private PessoaDAO pessoaDao;
#RequestMapping("/pessoacorretora/{id}")
public ModelAndView pessoaCorretora(#PathVariable("id") int id, ContaCorretora contaCorretora ) {
contaCorretora = new ContaCorretora();
Map<String, Object> model = new HashMap<String, Object>();
Pessoa pessoa = pessoaDao.find(id);
model.put("contaCorretora", contaCorretora);
model.put("pessoa", pessoa);
model.put("tipoConta", TipoConta.values());
model.put("tipoRisco", TipoRisco.values());
return new ModelAndView("pessoa/contacorretora", "model", model);
}
#RequestMapping(value="/pessoacorretora/finalizar", method=RequestMethod.POST)
public ModelAndView gravar(ContaCorretora contaCorretora) {
contaCorretoraDao.gravar(contaCorretora);
return new ModelAndView("redirect:pessoa/contacorretora");
}
I really don't know if the controller class is correct, but probably the problem is there. Nevertheless, I'll paste the JSP code as well:
<form:form action="${s:mvcUrl('CCC#gravar').build() }" method="post" commandName="pessoa" 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>
<button type="submit" class="btn btn-primary">Cadastrar</button>
</form:form>
When I try to open the website (..../pessoa/pessoacorretora/6) - 6 is a valid ID, this is the error:
HTTP Status 500 - java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'pessoa' available as request attribute
type Exception report
message java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'pessoa' available as request attribute
description The server encountered an internal error that prevented it from fulfilling this request.
Actually I really don't know how to handle with a relationship of two classes when I want to do CRUD actions.
The problem is that spring can't bind the commandName="pessoa" in your JSP form.
Are you sure that the value of Pessoa returnd by :
Pessoa pessoa = pessoaDao.find(id); // is not Null ??
try this as a test :
model.put("pessoa", pessoa == null ? new Pessoa() : pessoa );
another way to solve this is to add it through Model Attribute annotation on you controller:
#ModelAttribute("pessoa ")
public Pessoa defaultInstance() {
return new Pessoa();
}
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}