I'm trying to do validation on 1 field of the form as you guys can see:
<div id ="EditModal" class="modal fade" role="dialog">
<form class="modal-dialog" th:action="#{/Edit}" th:object="${person1}" method="POST">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<input type="hidden" id="edit-modal-person-id" name="id" value=""/>
<input type="text" placeholder="Name" id="edit-modal-person-name" name="name"/>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:class="'error'">something</p>
<div>
<a class="btn btn-default pull-right" id="PhoneNumberEdit">Edit Phone Number</a>
</div>
</div>
<div class="modal-footer">
<button type="submit" id="SubmitEdit" class="btn btn-default" >Submit</button>
<button type="button" class = "btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</form>
</div>
the attribute th:if="${#fields.hasErrors('name')}" always causes the 500 error for me.
my controller:
#RequestMapping(value = "/Edit", method = RequestMethod.POST)
public String editPerson(#Valid #ModelAttribute(value="person1") PersonRequest person, BindingResult result) {
if(result.hasErrors()) {
return "redirect:All";
}
else {
personService.update(person.getId(), person.getName());
return "redirect:All";
}
}
my entity:
public class PersonRequest {
#NotNull
private long id;
#NotEmpty
#Name
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PersonRequest() {
super();
}
}
the console return this following error:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'person1' available as request attribute.
But I think that doesnt have anything to do with this because if I remove p tag it runs normally.
The problem is that you make a redirect, (you make a "redirect:All").
Because of the redirect, you do not pass object person1 thus the error "java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'person1' available as request attribute".
If you want post the code of the /all requestMapping that you have
Probably you want something like this
#RequestMapping(value = "/Edit", method = RequestMethod.POST)
public String editPerson(#Valid #ModelAttribute(value="person1") PersonRequest person, BindingResult result) {
if(result.hasErrors()) {
return "edit";//change it to the name of the html page that you want to go
}
else {
//probably here you return in the page with all the persons
personService.update(person.getId(), person.getName());
return "redirect:All";
}
}
Related
I started to work with thymeleaf in a basic project. When I try to put th:field="*{name}" in form, comes error with thymeleaf parsing. Only with th:object="${reservationBinding}" without any th:fields works correctly. I can't understand why doesn't work with th:filed.
Here is my controller where I set the message:
#Controller
public class ReservationController {
#GetMapping("/reservation")
public String checkFOrReservationGet(Model model){
model.addAttribute ("reservationBinding",new ReservationBinding ());
return "index";
}
#PostMapping("/reservation")
public String checkForReservation(#Valid #ModelAttribute() ReservationBinding reservationBinding,
Model model,
BindingResult bindingResult,
RedirectAttributes redirectAttributes,
HttpSession httpSession){
// model.addAttribute ("reservationBinding",new ReservationBinding ());
if(bindingResult.hasErrors ()){
redirectAttributes.addFlashAttribute ("reservationBinding",reservationBinding);
redirectAttributes.addFlashAttribute ("org.springframework.validation.BindingResult.reservationBinding",bindingResult);
return "redirect:#book-a-table";
}
return "redirect:#book-a-table";
}
Here is my html form
<form action="#book-a-table" th:action="#{/reservation}" method="post" th:object="${reservationBinding}" >
<div class="row">
<div class="col-lg-4 col-md-6 form-group" >
<input type="text" name="name" class="form-control" id="name"
placeholder="name" data-rule="minlen:4"
th:field="*{name}"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3 mt-md-0">
<input type="email" class="form-control" name="email" id="email" placeholder="Your Email" data-rule="email"
data-msg="Please enter a valid email">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3 mt-md-0">
<input type="text" class="form-control" name="phone" id="phone" placeholder="Your Phone" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="text" name="date" class="form-control" id="date" placeholder="Date" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="text" class="form-control" name="time" id="time" placeholder="Time" data-rule="minlen:4"
data-msg="Please enter at least 4 chars">
<div class="validate"></div>
</div>
<div class="col-lg-4 col-md-6 form-group mt-3">
<input type="number" class="form-control" name="people" id="people" placeholder="# of people"
data-rule="minlen:1" data-msg="Please enter at least 1 chars">
<div class="validate"></div>
</div>
</div>
<div class="form-group mt-3">
<textarea class="form-control" name="message" rows="5"
placeholder="Message"></textarea>
<div class="validate"></div>
</div>
<div class="mb-3">
<div class="loading">Loading</div>
<div class="error-message"></div>
<div class="sent-message">Your booking request was sent. We will call back or send an Email to confirm your reservation. Thank you!</div>
</div>
<div class="text-center"><button type="submit" >Send Message</button></div>
</form>
This is my bindingModel
public class ReservationBinding {
private String name;
private String email;
private String phone;
private String date;
private String time;
private int People;
private String message;
public ReservationBinding(){
}
// #Length(min = 2, message = "Username length must be minimum two characters!.")
public String getName(){
return name;
}
public ReservationBinding setName(String name){
this.name = name;
return this;
}
#Column(name = "email", nullable = false)
#Email(message = "Enter valid email address.")
public String getEmail(){
return email;
}
public ReservationBinding setEmail(String email){
this.email = email;
return this;
}
#Column(name = "phone", nullable = false)
#Length(min = 9, message = "Enter valid phone number.")
public String getPhone(){
return phone;
}
public ReservationBinding setPhone(String phone){
this.phone = phone;
return this;
}
#Column(name = "date", nullable = false)
public String getDate(){
return date;
}
public ReservationBinding setDate(String date){
this.date = date;
return this;
}
#Column(name = "time")
#Length(min = 2, message = "Enter a valid time for check in.")
public String getTime(){
return time;
}
public ReservationBinding setTime(String time){
this.time = time;
return this;
}
#Min(value = 2, message = "What is the number of the guests")
public int getPeople(){
return People;
}
public ReservationBinding setPeople(int people){
People = people;
return this;
}
public String getMessage(){
return message;
}
public ReservationBinding setMessage(String message){
this.message = message;
return this;
}
}
Do you have any idea, why alone th:object map the properties and when I put th:field doesn't work and comes
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing
(template: "class path resource [templates/index.html]"
Error during execution of processor
'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor'
(template: "index" - line 564, col 17)
I've got ENTITY Country with all getters and setters + Constructor with no args. Getters and setter are all written by IDEA, not Lombok.
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String code;
private String capital;
private String description;
private String nationality;
private String continent;
#OneToMany(mappedBy="country")
private List<State> states;
public Country() {
}`
Here goes CountryRepository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Integer> {}
Here goes CountryService:
#Service
public class CountryService {
#Autowired
private CountryRepository countryRepository;
public List<Country> getListOfCountries() {
return countryRepository.findAll();
}
public void addNewCountry(Country country) {
countryRepository.save(country);
}
}
and CountryController:
#Controller
public class CountryController {
#Autowired
private CountryService countryService;
#GetMapping("/countries")
private String getListOfCountries(Model model) {
List<Country> listOfCountries = countryService.getListOfCountries();
model.addAttribute("countries", listOfCountries);
return "countries";
}
#PostMapping("/countries/addNew")
private String addNewCountry(#RequestParam String code,
#RequestParam String capital,
#RequestParam String description,
#RequestParam String nationality,
#RequestParam String continent) {
Country country = new Country();
country.setCode(code);
country.setCapital(capital);
country.setDescription(description);
country.setNationality(nationality);
country.setContinent(continent);
countryService.addNewCountry(country);
return "redirect:/countries";
}
}
and here is the form that's supposed to take input, but unfortunately nothing happens(
<div class="modal-body">
<form th:action="#{/countries/addNew}" method="post">
<div class="form-group">
<label for="recipient-name" class="col-form-label">Code:</label>
<input type="text" class="form-control" id="recipient-name" name="code">
</div>
<div class="form-group">
<label for="recipient-name1" class="col-form-label">Capital:</label>
<input type="text" class="form-control" id="recipient-name1" name="capital">
</div>
<div class="form-group">
<label for="recipient-name2" class="col-form-label">Description:</label>
<input type="text" class="form-control" id="recipient-name2" name="description">
</div>
<div class="form-group">
<label for="recipient-name3" class="col-form-label">Nationality:</label>
<input type="text" class="form-control" id="recipient-name3" name="nationality">
</div>
<div class="form-group">
<label for="recipient-name4" class="col-form-label">Continent:</label>
<input type="text" class="form-control" id="recipient-name4" name="continent">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" data-dismiss="modal">Add</button>
</div>
</form>
</div>
If I add data manually to the DB everything works fine, but when using this form nothing happens. At the same time I didn't get to errors or whatever. Server runs OK. Instead of JPARepository I tried using CRUDRepository. Also doblechecked the names of the form so they have the same values as Country fields. Realy lost here...
I have this controller:
#RequestMapping(value="admin/departamento", method = RequestMethod.POST)
public ModelAndView departamentoGuardar(
#Valid Departamento departamento ,
BindingResult br
){
ModelAndView mav = new ModelAndView();
mav.setViewName("costos/departamento");
return mav;
}
...and it is working.
But, if I build a empty repository class:
#Repository("departamentoRepository")
public interface DepartamentoRepository extends JpaRepository<Departamento, Integer>{
}
My controller throws this when a put a String and only accepts int:
org.springframework.beans.TypeMismatchException: Failed to convert
value of type 'java.lang.String' to required type
'com.carrduci.gestionycontrol.model.Departamento'; nested exception is
org.springframework.core.convert.ConversionFailedException: Failed to
convert from type [java.lang.String] to type [java.lang.Integer] for
value 'asdf'; nested exception is java.lang.NumberFormatException: For
input string: "asdf" at
org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:80)
~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE] at
org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:52)
~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE] at
org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:692)
~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE] at
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttributeFromRequestValue(ServletModelAttributeMethodProcessor.java:141)
~[spring-webmvc-5.0.4.RELEASE.jar:5.0.4.RELEASE
My Class:
#Entity
#Table(name = "departamento")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Departamento.findAll", query = "SELECT d FROM Departamento d")})
public class Departamento implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "id_departamento")
private Integer idDepartamento;
#Size(max = 20)
#Column(name = "departamento")
private String departamento;
public Departamento() {
}
public Departamento(Integer idDepartamento) {
this.idDepartamento = idDepartamento;
}
public Integer getIdDepartamento() {
return idDepartamento;
}
public void setIdDepartamento(Integer idDepartamento) {
this.idDepartamento = idDepartamento;
}
public String getDepartamento() {
return departamento;
}
public void setDepartamento(String departamento) {
this.departamento = departamento;
}
#Override
public int hashCode() {
int hash = 0;
hash += (idDepartamento != null ? idDepartamento.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Departamento)) {
return false;
}
Departamento other = (Departamento) object;
if ((this.idDepartamento == null && other.idDepartamento != null) || (this.idDepartamento != null && !this.idDepartamento.equals(other.idDepartamento))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.carrduci.gestionycontrol.model.Departamento[ idDepartamento=" + idDepartamento + " ]";
}
}
UPDATE
<th:block th:include="fragments/encabezados :: encabezados"></th:block>
<div class="container">
<div class="row">
<div class="col-md-offset-4 col-sm-offset-0">
<div class="row">
<div class="col-sm-12"> <h4>Departamento</h4></div>
</div>
<div class="row">
<div class="col-sm-12 col-md-8 ">
<form th:action="${departamento.idDepartamento>0}?
#{/costos/admin/departamento/modificar}:
#{/costos/admin/departamento}"
method="post" th:object="${departamento}">
<div class="row">
<div class="form-group col-xs-12">
<input type="hidden" th:field="*{idDepartamento}">
<div class="input-group">
<span class="input-group-addon">Nombre</i></span>
<input class="form-control input-lg" type="text" th:field="*{departamento}">
<div class="alert alert-danger" th:if="${#fields.hasErrors('departamento')}" th:errors="*{departamento}" ></div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-right">
<a type="button" class="btn btn-danger BOTON_LIMPIAR" href="/index"> <span class=" glyphicon glyphicon-remove"></span> Cancelar</a>
<button type="submit" class="btn btn-success">
<span class="glyphicon glyphicon-floppy-save"></span>
<span th:text="${departamento.idDepartamento>0}?
#{Modificar}:
#{Guardar}">
</span>
</button>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-8 ">
<hr>
<th:block th:include="fragments/tabla :: tabla"></th:block>
</div>
</div>
</div>
</div>
</div>
<th:block th:include="fragments/pieDePagina :: pieDePagina"></th:block>
I am trying to create a task creation page. There is a form where the user can enter the name, description and task status and save it. However, when I go to the page it shows me this error
There was an unexpected error (type=Internal Server Error, status=500).
Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (index)
I also have a delete task in my program and that seems to work perfectly fine.
This is what my mainController looks like
#RequestMapping(value = "/save-task")
public String saveTask(#ModelAttribute("task") Task task, BindingResult bindingResult, Model model) {
task.setDateCreated(new Date());
taskService.save(task);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("task", new Task());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
#RequestMapping( value = "/delete-task")
public String deleteTask(#RequestParam (required = false) int id, Model model){
taskService.delete(id);
model.addAttribute("tasks", taskService.findAll());
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
Here is the form
<div class="container text-center">
<h3>Manage Task</h3>
<hr/>
<form class="form-horizontal" method="POST" th:action="#{/save-task}" th:object="${task}">
<input type="hidden" name="id" th:field="*{id}"/>
<div class="form-group">
<label class="control-label col-md-3">Name</label>
<div class="col-md-7">
<input type="text" class="form-control" name="name" th:field="*{name}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Description</label>
<div class="col-md-7">
<input type="text" class="form-control" name="description" th:field="*{description}"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Finished</label>
<div class="col-md-7">
<input type="radio" class="col-sm-1" th:name="finished" value="true"/>
<div class="col-sm-1">Yes</div>
<input type="radio" class="col-sm-1" th:name="finished" value="false"/>
<div class="col-sm-1">No</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Save"/>
</div>
</div>
</form>
</div>
</div>
Delete Task html
<tr th:each="task: ${tasks}">
<td th:text="${task.id}"></td>
<td th:text="${task.name}"></td>
<td th:text="${task.description}"></td>
<td th:text="${task.dateCreated}"></td>
<td th:text="${task.finished}"></td>
<td><a th:href="#{'delete-task?id=' + ${task.id}}"><span class="glyphicon glyphicon-trash"></span></a> </td>
</tr>
Here is a part of my Task entity
#Entity
#Table(name = "T_TASKS")
public class Task implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "DATE_CREATED")
#Temporal(TemporalType.TIMESTAMP)
private Date dateCreated;
#Column(name = "FINISHED")
private boolean finished;
Here is the error in the slack trace
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'task' available as request attribute
Change your controller mapping to accept GET and POST requests. You're getting the error because Spring can't bind a bean to the form. So add a bean when GET is called.
#GetMapping("/save-task")
public String addTask(Model model) {
Task task = new Task();
task.setDateCreated(new Date();
model.addAttribute("task", task);
return "save-task"; //or whatever the page is called
}
Then do your processing:
#PostMapping("/save-task")
public String saveTask(#ModelAttribute("task") Task task,
BindingResult bindingResult,
Model model) {
taskService.save(task);
model.addAttribute("mode", "MODE_TASKS");
return "index";
}
Your bean looks fine. I don't see that you use tasks in your HTML snippet, but that would go in the method mapped with #GetMapping if you're looking to just display all tasks.
Add the following to your controller class:
#ModelAttribute("task")
public Task newTaskObject() {
return new Task();
}
It will solve the binding problem. Also you need a post controller to handle the form submission.
When validating object controller just refreshes the page, instead showing errors via form:errors, can you tell me where is the problem. I guest it needs to get error from binding result and insert it into the page, instead controller is just refreshing page
Controller :
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createProduct(MultipartFile image, Model model,#ModelAttribute #Valid ProductDto product, BindingResult bindingResult) throws IOException {
productValidator.validate(product,bindingResult);
if (bindingResult.hasErrors()){
return "admin/create";
} else {
return "index";
}
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String createProduct(Model model) {
model.addAttribute("product", new ProductDto());
model.addAttribute("categories", categoryService.findAll());
return "admin/create";
}
Validator :
#Override
public boolean supports(Class<?> aClass) {
return ProductDto.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
ProductDto product = (ProductDto) o;
if (product.getTitle().isEmpty() || product.getTitle() == null) {
errors.rejectValue("title", "product.title", "Product title cant be empty");
}
if (product.getDescription().isEmpty() || product.getDescription() == null) {
errors.rejectValue("description", "product.description", "Product description cant be empty");
}
if (product.getPrice() == null || product.getPrice()<=0) {
errors.rejectValue("price", "product.price", "Product price is not valid");
}
if (product.getCategoryId()==null) {
errors.rejectValue("category", "product.category", "Product category is not valid");
}
}
jstl page :
<spring:url value="/product/create" var="formUrl"/>
<form:form modelAttribute="product" action="${formUrl }" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<form:input id="title"
cssClass="form-control" path="title"/>
<form:errors path="title"/>
</div>
<div class="form-group">
<label>Description</label>
<form:textarea id="description" rows="10"
cssClass="form-control" path="description"/>
</div>
<div class="form-group">
<label>Price</label>
<form:input id="price" type="number"
cssClass="form-control" path="price"/>
<form:errors path="description"/>
</div>
<div class="form-group">
<label for="sel1">Select category</label>
<form:select id="sel1" cssClass="form-control" path="categoryId">
<form:options items="${categories}" itemValue="id" itemLabel="title"/>
</form:select>
</div>
<label class="btn btn-default btn-file">
Image <input type="file" multiple accept='image/*' ng-file-select="onFileSelect($files)" name="image" style="display: block;">
</label>
<br><br>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-success text-center"><span
class="fa fa-check"></span> Submit
</button>
<a href="/" class="btn btn-danger btn-lg text-center"><span
class="fa fa-times"></span> Cancel
</a>
</div>
</form:form>
I think your product model attribute has different name in your POST-handling method.
According to documentation:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List<mypackage.OrderAddress>".
So your ProductDto product attribute will have name productDto in resulting model. But you referring the product in your template.
Try to explicitly set the name attribute of #ModelAttribute annotation
public String createProduct(MultipartFile image, Model model, #ModelAttribute(name = "product") #Valid ProductDto product, BindingResult bindingResult)
And to make sure that bidning and validation actually works, try to log it:
if (bindingResult.hasErrors()) {
System.err.println("Form has errors!");
}