Thymeleaf seems to prefer toString over initbinder customEditor - java

I want to pass a LocalDateTime object to thymeleaf with a specific format (yyyy/MM/dd HH:mm) and later receive it back into my controller class.
I want to use an customEditor / initbinder to do the convertion.
/**
* Custom Initbinder makes LocalDateTime working with javascript
*/
#InitBinder
public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(LocalDateTime.class, "reservationtime", new LocalDateTimeEditor());
}
public class LocalDateTimeEditor extends PropertyEditorSupport {
// Converts a String to a LocalDateTime (when submitting form)
#Override
public void setAsText(String text) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
LocalDateTime localDateTime = LocalDateTime.parse(text, formatter);
this.setValue(localDateTime);
}
// Converts a LocalDateTime to a String (when displaying form)
#Override
public String getAsText() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
String time = ((LocalDateTime)getValue()).format(formatter);
return time;
}
}
While spring uses my initbinder when it receives the data from the form, thymeleaf though seems to prefer the .toString() method over my initbinder and my getAsText() method never gets called.
My view:
<input type="text" th:name="${reservationtime}" id="reservationtime" class="form-control"
th:value="${reservationtime}"/>
I find the initbinder "way" quite good in terms of code readability. So I would like to keep using the initbinder. Is it possible to tell thymeleaf to use my initbinder or any other good workaround?

remove the parameter"reservationtime", may resolve the issue :
binder.registerCustomEditor(LocalDateTime.class, new LocalDateTimeEditor());
And Then, the converter will be used for ALL LocalDateTime fields

Related

How to get Calendar field from REST request in RestController in SpringBoot?

I am getting date in POST request in #RestController with Json like this:
{ "date":"2016-01-28" }
How can I get object with type java.util.Calendar?
Usually I use java.util.Date, but almost all methods are deprecated.
What Jackson annotation possible to use to get Calendar?
I tried to use
public class Test {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
Calendar date;
}
but get null
You can use annotation #DateTimeFormat. According to Spring docs it's applicable to java.util.Calendar.
#PostMapping("/somemapping")
public void date(#RequestParam("date") #DateTimeFormat(pattern = "yyyy-MM-dd") Calendar date) {
//some other processing code
}
{
"calendar": 1558347802873
}
Sample json request
just pass date in number format
use below object
import java.util.Calendar;
public class TestCal {
private Calendar calendar;
public Calendar getCalendar() {
return calendar;
}
public void setCalendar(Calendar calendar) {
this.calendar = calendar;
}
}
sample spring Controller
#PostMapping("/testCal")
public void testCal(#RequestBody TestCal test) {
// do your stuff
test.getCalendar();
}
I would suggest using a java.util.Date instead of a Calendar. At least in your repository object. If you need the additional functionality of a Calendar you can create an instance as demonstrated by #Hamza.
Calendar is a pretty heavy-weight class. No need to create a new instance of it considering the tiny amount of data you are storing.
You can pick out the date from another class that operates on it to provide you the additional functionality as required.

Use DateFormat.SHORT in Thymeleaf

I want to format an Instant using a predefined format of Java. I can do this in Java:
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, request.getLocale());
// this will hopefully format a Date to 12.09.2018 02:10
And I want this to be done in Thymeleaf using my Instant type:
<div th:text="${#temporals.format(work.indexTime)}"></div>
<!-- this will print "2018-09-12T02:10:06Z" -->
But how can I tell Thymeleaf to use the DateFormat.SHORT settings?
EDIT:
My current workaround is this:
Controller:
DateTimeFormatter dateFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(request.getLocale())
.withZone(ZoneId.systemDefault());
Template:
<div th:text="${dateFormatter.format(work.indexTime)}"></div>
You can simply specify SHORT as the format.
<div th:text="${#temporals.format(work.indexTime, 'SHORT')}"></div>
From the README:
/*
* Format date with the specified pattern
* SHORT, MEDIUM, LONG and FULL can also be specified to used the default java.time.format.FormatStyle patterns
* Also works with arrays, lists or sets
*/
Yeah, you can set that up in thymeleaf, but it's pretty verbose... this works for me:
<th:block th:with="clazz=${T(java.time.format.DateTimeFormatter)},
style=${T(java.time.format.FormatStyle).SHORT},
zone=${T(java.time.ZoneId).systemDefault()},
formatter=${clazz.ofLocalizedDateTime(style).withLocale(#locale).withZone(zone)}">
<span th:text="${formatter.format(work.indexTime)}" />
</th:block>
You could also add a default converter from Instant to String and use the double bracket syntax when outputting an Instant:
Context:
public class Context extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry r) {
DateTimeFormatter dateFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(LocaleContextHolder.getLocale())
.withZone(ZoneId.systemDefault());
r.addConverter(new Converter<Instant, String>() {
#Override
public String convert(Instant s) {
return s != null ? dateFormatter.format(s) : "";
}
});
}
}
HTML:
<div th:text="${{work.indexTime}}" />

Method to handle invalid request in a Java Web Application?

I have to get two dates in a request parameter lets say "from=jan 1 2016" and "to= feb 1 2016". "from" should always come before "to".
My controller methods return Map in response if "from" is before "to", but if "to=jan 1 2016" value comes before "from=feb 1 2016", how do I handle the response to send a message?
The proper way would be to throw an exception if anything happens that shouldn't happen. If you're using Java 8 time API (or something like Joda time), you can easily achieve this by using isBefore():
if (to.isBefore(from)) {
// Write your own exception class
throw new InvalidParameterException("To cannot be before from");
}
Now you can use #ExceptionHandler to do anything you want if an exception is thrown. For example:
#ExceptionHandler(InvalidParameterException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ErrorMessageDTO handleInvalidParameter(InvalidParameterException ex) {
// Write your own DTO to return an exception
return new ErrorMessageDTO(ex.getMessage());
}
If you want to use dates as request parameters, you might want to use a Formatter<LocalDate> to properly do this:
#Component
public class LocalDateStringFormatter implements Formatter<LocalDate> {
// Or use a custom formatter with a custom pattern
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
#Override
public LocalDate parse(String isoDateString, Locale locale) throws ParseException {
return LocalDate.parse(text, FORMATTER);
}
#Override
public String print(LocalDate date, Locale locale) {
retun date.format(FORMATTER);
}
}
This way you can map #RequestParams of type LocalDate.

How spring InitBinder works?

I read about InitBinder on net but not very clear how it works. As per my understanding it can be used to perform cross cutting
concern like setting validator, conversion of request parameter to some custom object etc
Came across below example on net
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
Handler method is
public void handlerMethod(#RequestParam("date") Date date) {
}
The advantage is before DispatcherServlet calls the handlerMethod it converts the request parameter in to Date object (otherwise
developer has to do it handleMethod). Right?
My question how spring knows which request parameter needs to be converted to Date object?
Say my request string is /someHandler/name?user=Brian&userCreatedDate=2011-01-01&code=aaaa-bb-cc
So how spring knows it has to convert userCreatedDate not other two parameters i.e code/user?
It knows which request parameters to apply the conversion to based on their datatype.
By doing this:
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
You are registering the editor for the Date type.
So if you have
#RequestMapping("/foo")
public String foo(#RequestParam("date") Date date,
#RequestParam("name") String name) {
// ...
}
Then the editor will be applied only to the first parameter, because the second one is String not Date.

How to work with Spring based form tag Date field?

In my Spring application have jsp and form.
demo.jsp have one field <form:input path="fromDate"/>
And in my DemoForm have field private Date fromDate;,
When we store the value Null value storing...
My Question Is their any direct tag for Store the date in my spring supplied jsp tag.
other wise give me other alternate way..
You need to define the custom editor for the Date in your controller. Please try below code.
#InitBinder
public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
df.setLenient(false);
CustomDateEditor editor = new CustomDateEditor(df, true); // second argument 'allowEmpty' is set to true to allow null/empty values.
binder.registerCustomEditor(Date.class, editor);
}

Categories