Spring - Thymeleaf Date type - spring

I'm new at Spring and thymeleaf,so sorry for the silly question.
I have a form that must filter some data based on a date that the user choose.
I have this class:
public class RequestFilterEntity {
private Date requestedAt;
private Date dateScheduled;
...
}
I have this endpoint in the controller:
#RequestMapping(value = "/requests", method = POST)
public String filterRequests(RequestFilterEntity requestFilter, Model model) {
model.addAttribute("requestFilter", new RequestFilterEntity());
return "admin/reporting/filter_requests";
}
and this View:
<form method = "post" th:object="${requestFilter}" th:action="#{|/admin/reporting/requests|}">
<div class="form-group">
<label for="requested">Requested at </label>
<input id="requested" type="date" class="form-control" th:field="*{requestedAt}"/>
</div>
I want that the object requestFilter that the Controller pass to the View will have the Date requestedAt set with the date chosen by the user (note that I am using input type="date")
Does it make sense? I have an error into the view. Can someone help me out? Where is my mistake?

There are few problems.
You are passing an empty object from your controller and expecting it to show up in your views.
model.addAttribute("requestFilter", new RequestFilterEntity());
You are not formatting your date object. Please add this initbinder to your controller
InitBinder for custom date editor
#InitBinder
public void initBinder(WebDataBinder binder) {
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true);
binder.registerCustomEditor(Date.class, editor);
}

Related

Spring - Failed to convert property value of type java.lang.String to required type java.util.Date

I have read some topics about this, but none of them helped me.
My problem is that the conversion is working well, but today I received a user's complaint using ios 12 and Safari. Immediately I tested on Iphone with ios 12 and I couldn't reproduce the error.
Could it be some device specific configuration or regional settings?
Please help!
Thanks.
Controller
#Controller
public class SaleController {
#InitBinder
public void setPropertyBinder(WebDataBinder dataBinder) {
//The date format to parse or output your dates
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
//Create a new CustomDateEditor
CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
//Register it as custom editor for the Date type
dataBinder.registerCustomEditor(Date.class, editor);
}
#RequestMapping(value="/venda3", method=RequestMethod.POST)
public String venda3(#Valid #ModelAttribute("anuncioImovel") AnuncioImovel anuncioImovel, BindingResult resultImovel, Model model, Principal principal, HttpServletRequest request) {
}
}
Model
#Entity
public class AnuncioImovel {
#Column(nullable=true)
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern="yyyy-MM-dd")
#Past
private Date inicioLocacao;
#Column(nullable=true)
#Temporal(TemporalType.DATE)
#DateTimeFormat(pattern="yyyy-MM-dd")
#Future
private Date terminoLocacao;
public void setInicioLocacao(Date inicioLocacao) {
this.inicioLocacao = inicioLocacao;
}
public void setTerminoLocacao(Date terminoLocacao) {
this.terminoLocacao = terminoLocacao;
}
}
Thymeleaf Template
<!-- Início Locação -->
<div class="form-group">
<label for="frmInicioLocacao" class="form-control-label text-primary"><strong>Início Locação</strong></label>
<input type="date" class="form-control" id="frmInicioLocacao" name="inicioLocacao"
th:value="${anuncioImovel.inicioLocacao} ? ${#dates.format(anuncioImovel.inicioLocacao, 'yyyy-MM-dd')}"
th:classappend="${#fields.hasErrors('anuncioImovel.inicioLocacao')} ? 'is-invalid' : ''"
oninput="javascript: this.value = this.value.slice(0, 10);"/>
<div class="invalid-feedback">
<span th:errors="*{anuncioImovel.inicioLocacao}"></span>
</div>
</div>
<!-- Término Locação -->
<div class="form-group">
<label for="frmTerminoLocacao" class="form-control-label text-primary"><strong>Término Locação</strong></label>
<input type="date" class="form-control" id="frmTerminoLocacao" name="terminoLocacao"
th:value="${anuncioImovel.terminoLocacao} ? ${#dates.format(anuncioImovel.terminoLocacao, 'yyyy-MM-dd')}"
th:classappend="${#fields.hasErrors('anuncioImovel.terminoLocacao')} ? 'is-invalid' : ''"
oninput="javascript: this.value = this.value.slice(0, 10);"/>
<div class="invalid-feedback">
<span th:errors="*{anuncioImovel.terminoLocacao}"></span>
</div>
</div>
Print error sent by the user
click here
My test
In my test on Iphone the dates are shown this way, and the conversion works (I don't know how the date is shown on user's device):
Click here
Your error states that it can't parse the date format dd/MM/yyyy.
In your code, you are using the format yyyy-MM-dd.
Are you sure your are sending the right date format?

Form validation in spring with thymeleaf

I'm skilling in form validation with spring boot and thymeleaf and i have problem: i can't do validation in form with two #ModelAttribute fields. The example like form validation om spring official site works correctly, but when I added two #model Attribute in post i get only error at webpage and no hints at form like in spring example.
Controller class:
#Controller
public class MyController {
#Autowired
InstructorRepository instructorRepository;
#Autowired
DetailRepository detailRepository;
#GetMapping("/index")
public String mainController(){
return "index";
}
#GetMapping("/add")
public String addInstructorForm(Model model){
model.addAttribute("instructor", new Instructor());
model.addAttribute("detail", new InstructorDetail());
return "addInstructor";
}
#PostMapping("/add")
public String submitForm(#Valid #ModelAttribute Instructor instructor, #ModelAttribute InstructorDetail instructorDetail, BindingResult bindingResult1){
/* if (bindingResult.hasErrors()) {
return "instructorsList";
}
instructor.setInstructorDetail(instructorDetail);
instructorRepository.save(instructor);*/
if (bindingResult1.hasErrors()) {
return "addInstructor";
}
return "redirect:/instructorsList";
}
#GetMapping("/instructorsList")
public String getList(Model model){
Map map = new HashMap<>();
List list = new ArrayList<Instructor>();
list = instructorRepository.findAll();
List resultList = new ArrayList();
for (int i = 0; i < list.size(); i++) {
Instructor instructor = (Instructor)list.get(i);
InstructorDetail detail = detailRepository.getInstructorDetailById(instructor.getId());
InstructorAndDetail iid = new InstructorAndDetail(instructor, detail);
resultList.add(iid);
}
model.addAttribute("instructors", resultList);
return "instructorsList";
}
}
html form snippet:
<form action="#" data-th-action="#{/add}" data-th-object="${instructor}" method="post">
<div class="form-group">
<label for="1">First name</label>
<input class="form-control" id="1" type="text" data-th-field="${instructor.firstName}" placeholder="John"/>
<div data-th-if="${#fields.hasErrors('firstName')}" data-th-errors="${instructor.firstName}">name error</div>
</div>
There was next problem: then I add entity to thymeleaf form I passed only 2 fields (or one field after), but there was 3 fields with
#NotNull
#Size(min=2, max=30)
So when I commented them in my code the single field validation begin to work :).
If you stucked at the same problem check that all you fields in class that marked #Valid annotation are mirrored in your form.
(or have default valid values? UPD: dont work with valid defaults if they have no form mirroring)

Persist radio checked value in Thymeleaf Spring

When searching, if we select a given field to search within and submit, our choice is forgotten. How could someone modify the view template to keep the previous search field selected when displaying results?
I have read many other SO questions and the thymeleaf documentation here but have not found a suitable answer yet.
One can hard code a string (like employer) with the following:
search.html snippet
<span th:each="column : ${columns}">
<input
type="radio"
name="searchType"
th:id="${column.key}"
th:value="${column.key}"
th:checked="${column.key == 'employer'}"/>
<label th:for="${column.key}" th:text="${column.value}"></label>
</span>
SearchController.html snippet
#RequestMapping(value = "")
public String search(Model model) {
model.addAttribute("columns", columnChoices);
return "search";
}
How can I persist the user selected radio value upon POST in Thymeleaf Spring?
(and default to the first, value on the GET)
http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#creating-a-form
Command object.
public class Search {
// default "employer"
private String type = "employer";
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Controller
#GetMapping
public String search(Model model) {
model.addAttribute("columns", columnChoices);
model.addAttribute("search", new Search());
return "search";
}
#PostMapping
public String search(#ModelAttribute Search search) {
// Search's attributes match form selections.
}
With search as your command object, a radio buttons should look like this:
<form th:object="${search}">
<th:block th:each="column: ${columns}">
<input type="radio" th:value="${column.key}" th:id="${column.key}" th:field="*{type}" />
<label th:for="${column.key}" th:text="${column.value}" />
</th:block>
</form>

Adding an attribute to a Controller form at #GetMapping using Spring Boot

Validating the overlapping of date intervals, in the implementation of the Validator class. I get a list from a Posgresql database, which I would like to show, next to the error message.
I've tried to insert it, without succeeding, with this line of code:
model.addAttribute("dateOverlaps", pricesValidator.getDBIntervals());
That's the complete code:
#Controller
public class PricesController {
#Autowired
private RateRepository rateRepository;
#Autowired
private PricesValidator pricesValidator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(pricesValidator);
}
//Enter new price form
#GetMapping("/admin/rates/priceform")
public String priceForm(Model model){
model.addAttribute("rate", new Rate());
model.addAttribute("dateOverlaps", pricesValidator.getDBIntervals());
return "/admin/rates/priceform";
}
#PostMapping("/admin/rates/priceform")
public String priceSubmit(#ModelAttribute #Valid Rate price, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/admin/rates/priceform";
}
rateRepository.addRate(price);
return "redirect:/admin/rates/prices";
}
}
I use Thymeleaf, but I take for granted that the problem is not with the viewer.
This is the html view:
<!--Global validation results-->
<div th:if="${#fields.hasErrors('global')}">
<div class="alert alert-danger" role="alert"
th:each="err : ${#fields.errors('global')}">
<div th:switch="${err}">
<p th:case="error.fromAfterTo" th:text="#error.fromAfterTo}"></p>
<p th:case="error.overlaps" th:text="#{error.overlaps}"></p>
<ul>
<li th:text="#{from} + ' - ' + #{to}"></li>
<li th:each="interval : ${dateOverlaps}"
th:text="${#temporals.format(interval.datefrom, 'dd/MM/yyyy')} + '-' +
${#temporals.format(interval.dateto, 'dd/MM/yyyy')}">Intervals</li>
</ul>
</div>
</div>
</div><!--Global validation results-->
Thank you in advance for your help.
The problem is that pricesValidator.getDBIntervals() at #GetMapping:
model.addAttribute("dateOverlaps", pricesValidator.getDBIntervals());
is calling the list that I want to show with the message error, which is empty until the form has been submitted. Because its values, are picked up from the database, with the validation process.
That raises a new question:
How can I set this model attribute in the #PostMapping section?
The answer to this question is simple, in the #PostMapping section, you can set a Model, as well as in the #GetMapping one. In my case, this was the solution:
#PostMapping("/admin/rates/priceform")
public String priceSubmit(#ModelAttribute #Valid Rate price, BindingResult bindingResult, Model model){
if(bindingResult.hasErrors()){
model.addAttribute("dateOverlaps", pricesValidator.getDBIntervals());
return "/admin/rates/priceform";
}
rateRepository.addRate(price);
return "redirect:/admin/rates/prices";
}

Annotated Spring MVC #ModelAttribute automapping not working with associated objects

I am using Spring MVC with Annotations. Here's a quick outline of my problem.
My Domain:
public class Restaurant {
private String name;
private Address address = new Address();
//Get and set....
}
public class Address{
private String street;
//Get and set....
}
My Controller:
//Configure and show restaurant form.
public ModelAndView showAction() {
ModelAndView mav = new ModelAndView("/restaurant/showRestaurant");
restaurant = new Restaurant();
mav.addObject("restaurant", restaurant);
return mav;
}
//Save restaurant
public ModelAndView saveAction(#ModelAttribute(value="restaurant") Restaurant restaurant,BindingResult result) {
restaurant.getName();//<- Not is null
restaurant.getAddress().getStreet(); //<- is null
}
My View:
<form>
<span class="full addr1">
<label for="Nome">Name<span class="req">*</span></label>
<h:inputText class="field text large" value="#{restaurant.name}"
id="name" forceId="true" styleClass="field text addr"/>
</span>
<span class="full addr1">
<label for="Nome">Street <span class="req">*</span></label>
<h:inputText class="field text large" value="#{restaurant.address.street}"
id="street" forceId="true" styleClass="field text addr"/>
</span>
</form>
My problem is, when I fill the name and the street to call the method "saveAction" when I try to get the restaurant filled happens that the name comes from the street but did not.
I'm not all that familliar with jsf, but for binding in spring you generally need the full path, i.e. name="address.street", in order to get the street name bound properly
Try binding using the spring form tags http://static.springsource.org/spring/docs/2.0.x/reference/spring-form.tld.html. Its pretty easy.

Resources