Spring with Thymeleaf binding date in html form - spring

I have a simple snippet of form like:
<form th:action="#{'/save'}" th:object="${account}" method="post">
<div class="form-group">
<label for="expirationDate">Expiration date</label>
<input type="datetime-local" class="form-control" id="expirationDate"
placeholder="Expiration date" th:field="*{expirationTime}"/>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I'm passing there a filled object account and expirationTime is a LocalDateTime field. The problem is that the expirationTime is not bind with the value passed to the form (object which is passed is 100% correct).
Any idea why?

Edit: Simply put, Spring/Thymeleaf doesn't format a Java 8 date correctly for the datetime-local input type. Tell Spring how to format the value correctly with #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm").
The annotation #DateTimeFormat tells Thymeleaf how to format the date string when parsing/formatting. That includes producing the value= annotation in the HTML input, and reading in POST data submitted by the form. The datetime-local input type expects a value formatted yyyy-MM-dd'T'HH:mm, so we use that as the formatter in the model class.
Model Class:
public class DateContainer {
private String name;
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
private LocalDateTime dateTime;
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Controller:
#RequestMapping("/dateTest")
public String dateTest(final DateContainer dateContainer) {
if (dateContainer.getDateTime() == null) {
dateContainer.setDateTime(LocalDateTime.now());
}
return "dateTest";
}
Template:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org/">
<head></head>
<body>
<h1>Hello World!</h1>
<form th:action="#{/dateTest}" th:object="${dateContainer}">
<label>Name: </label><input type="text" th:field="*{name}"/>
<label>Date Time: </label><input type="datetime-local" th:field="*{dateTime}"/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
HTML Produced:
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>Hello World!</h1>
<form action="/dateTest">
<label>Name: </label><input type="text" id="name" name="name" value="" />
<label>Date Time: </label><input type="datetime-local" id="dateTime" name="dateTime" value="2017-08-28T13:01" />
<input type="submit" value="Submit" />
<input type="hidden" name="_csrf" value="437b30dc-a103-44d0-b4e9-791d8de62986" /></form>
</body>
</html>
Screenshot:
Screenshot in Chrome 60
Comptibility:
The datetime-local input type is a new HTML5 type and isn't supported in all browsers. See compatibility: https://caniuse.com/#search=datetime-local
In non-compliant browsers, the datetime-local field will simply appear as a text field, and will contain the date and time with the T in between. That may lead to usability issues depending on your use case.

Make sure that you have this dependency:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
After that just add the org.thymeleaf.extras.java8time.dialect.Java8TimeDialect class to the list of dialects in your TemplateEngine implementation, and you will have the #temporals object available to be used in your templates.
More details can be found here.

Related

Spring: Mapping two POST fields to three RESULT fields

In the post "Hello World" phase of my Spring journey. Creating an app that POSTS two fields which RESULT in three fields.
My controller has three fields. Two are populated on POST, and all three should populate the RESULT fields.
The Controller:
#Slf4j
#Controller
public class GreetController {
#GetMapping("/greeting")
public String greetingForm(Model model) {
model.addAttribute("greeting", new Greeting());
}
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting, Model model) {
model.addAttribute("greeting", greeting);
}
}
The Model:
public class Greeting {
//fields correspond to greeting.html
private long id;
private String content;
private String numbah;
public String getNumbah() { return numbah; }
public void setNumbah() {
this.numbah = SomeFunctions.functionOne(this.getContent());
}
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public String getContent() { return content; }
public void setContent(String content) { this.content }
}
The View:
<!DOCTYPE HTML>
this is greeting.html
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Getting Started: Handling Form Submission</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Form</h1>
<!--th.fields correspond to fields in th.object (ie greeting.java)-->
<form action="#" th:action="#{/greeting}" th:object="${greeting}" method="post">
<p>Id: <input type="text" th:field="*{id}" /></p>
<p>Message: <input type="text" th:field="*{content}" /></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
<h1>Result</h1>
<p th:text="'id: ' + ${greeting.id}" />
<p th:text="'content: ' + ${greeting.content}" />
<p th:text="'numbah: ' + ${greeting.numbah}" />
Submit another message
</body>
</html>
Note:
The ${greeting.numbah} field, as configured above, is resulting in null. I had a log statement in that function that never returned anything, indicating that the function in the model is never firing. However, when the view was configured like this...
<body>
<h1>Form</h1>
<!--th.fields correspond to fields in th.object (ie greeting.java)-->
<form action="#" th:action="#{/greeting}" th:object="${greeting}" method="post">
<p>Id: <input type="text" th:field="*{id}" /></p>
<p>Message: <input type="text" th:field="*{content}" /></p>
<p>Some Number: <input type="text" th:field="*{numbah}"></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
<h1>Result</h1>
<p th:text="'id: ' + ${greeting.id}" />
<p th:text="'content: ' + ${greeting.content}" />
<p th:text="'numbah: ' + ${greeting.numbah}" />
Submit another message
</body>
...the field was populating, so the function itself....functions.
Long short, what I'm attempting to do here is take id and content, do someFunction(content)=numbah and return id, content, and numbah.
Thank you in advance.
It always gets down to something simple, doesn't it?
There was a typo in the setNumbah constructor in the model.
This is why I hardly ever post on sites like this: because you almost always figure out what you did wrong if you just sleep on it and have another look.
Thanks for checking this out and apologies that it's not a "high quality" problem/answer. Will delete if that's the protocol for a self-answered question.

Spring Web application tymeleaf : POST request directed to wrong address

I have post mapping for URL: "/bank/addnew" My controller looks like:
Upon submission the form is submitted to "http://127.0.0.1:8082/banks/%20/banks" However, I need it to go to "http://127.0.0.1:8082/banks/addnew". Thanks for the Help!
#Controller
public class BankController {
#Autowired private BankService bankService;
#GetMapping("/banks")
public String bankList() {
return "bank/bank_list";
}
#PostMapping(value="/banks/addnew")
public String addNew(Bank bank) {
bankService.save(bank);
return "redirect: /banks";
}
}
And my template:
<form method="POST" action="#" th:action="#{/banks/addnew}" >
<div class="form-group">
<label for="recipient-name" class="col-form-label">Bank Name:</label>
<input type="text" class="form-control" id="recipient-name" name="name">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
The Post Method was actually working okay. I checked it by printing the Model object inside the target controller. However, it was being submitted to the database a NULL value because I didn't initialize getters and setters for my Model. Finally, there was a space in the return string, thus I removed it.
The controller Should have a return statement with no space.
return "redirect:/banks";

How pass extra param from html to controller

Spring Boot 2.5, Thymeleaf
I need when click submit to pass object Product and additional extra param (quantity)
html template:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${appName}">Category template title</title>
<link th:href="#{/public/style.css}" rel="stylesheet"/>
<meta charset="UTF-8"/>
</head>
<body>
<div class="container">
<h3 th:text="*{title}"/>
<form method="post" action="#" th:object="${product}" th:action="#{/product}">
<input type="hidden" id="id" th:field="*{id}"/>
<input type="text" placeholder="Name" id="name" th:field="*{name}" th:disabled="${isView}"/>
<input type="hidden" id="created" th:field="*{created}"/>
<textarea placeholder="Description" rows="5" id="description"
th:field="*{description}" th:disabled="${isView}"></textarea>
<input type="number" placeholder="Price" id="price" th:field="*{price}" th:disabled="${isView}"/>
<input type="text" placeholder="Currency" id="currency" th:field="*{currency}" th:disabled="${isView}"/>
<input type="text" placeholder="Images URL(separate by comma)" id="images" th:field="*{images}" th:disabled="${isView}"/>
<input th:type="${isView} ? hidden : submit" value="Submit"/>
</form>
</div>
</body>
</html>
and here my controller:
#RequestMapping("cart/add")
public String addProduct(Model model) {
logger.info("addProduct");
model.addAttribute("isAdd", true);
model.addAttribute("product", new Product());
model.addAttribute("title", "Add Product");
model.addAttribute("viewMode", ViewMode.ADD);
return "product";
}
#PostMapping(value = "/product")
public String submitProduct(Product product, Model model) {
logger.info("submitProduct = " + product);
if (product.getId() == 0) { // add category
product.setCreated(new Date());
} else { // update category
product.setUpdated(new Date());
}
return "redirect:/cart";
}
So when click button Submit call submitProduct with fill object Product. But I need to pass extra param (as second param in method submitProduct) - quantity.
How I can pass this extra int param from html to controller?
One option is to access the value directly from the request parameters.
Assuming the quantity value is available in the form as an input field, with a name of quantity (looks like it is not there at the moment), then you can alter your controller to use this:
import org.springframework.web.bind.annotation.RequestParam;
And then change the relevant method signature to something like this:
public String submitProduct(Product product, Model model,
#RequestParam(name = "quantity") String quantity) {...}
(Field validation of some kind would also be needed, I assume.)

Pass a list of user defined object from input field using spring form handling

I have an Employee class that looks like this.
public class Employee {
private String Name;
private List<Address> address;
*****Getters and Setters****
}
And my Address class looks like this
public class Address {
private int addressid;
private Employee employee;
#NotNull(message="Field Cannot be Empty")
private String description;
*****Getters and Setters****
}
I want to bind List of addresses (Employee can have more than one address - 1:M) to the employee class with the data that is parsed through the form. Which looks like this.
I have 3 input address fields, one of the sample input fields look like this...
<div class="form-group">
<div class="row">
<label class="col-sm-3" for="exampleInputEmail1">Address
1</label>
<div class="col-sm-7">
<form:input class="form-control" placeholder="" path="" />
<form:errors path="" cssClass="error" />
</div>
</div>
</div>
Note that I have cut down many unnecessary form fields to demonstrate the problem more clearly. Please help me bind the list of input fields with the relevant class. Any advice for this design is also welcome.
You can use Thymeleaf for this as below.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>form|data</title>
</head>
<body>
<div class="starter-template">
<form th:action="#{employeeData}" method="post" th:object="${employee}">
<input th:field="*{name}" type="text" th:name="name" name="name" placeholder="name" class="input u-one-third js-get-form-data"/>
<th:block th:each="add,addStat:*{address}">
<input th:field="*{address[__${addStat.index}__].description}" type="text" th:name="add.description" name="description" placeholder="description" class="input u-one-third js-get-form-data"/>
</th:block>
<input type="submit" value="SEND"/>
</form>
</div>
</body>
</html>
Controller.
#RequestMapping(value = "/formData", method = RequestMethod.GET)
public String formData(Map<String, Object> model) {
Employee employee = new Employee();
Address address = new Address();
address.setDescription("TEST");
address.setEmployee(employee);
employee.getAddress().add(address);
model.put("employee",employee);
return "formData";
}
#RequestMapping(value = "/employeeData", method = RequestMethod.POST)
public void employeeData(#Valid Employee employeeData, BindingResult errors) {
System.out.println(employeeData.getName());
}
Please find the working commit here.

Controller Not receiving value from span in HTML using Spring boot and Thymeleaf

I have the following content in my HTML which is using Thymeleaf
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}" th:field="*{domain}">domain</span>
<input type="Submit" value="close" />
</form>
And I have the following in my Controller which is using Sprint Boot
#RequestMapping(value = "/shutDown", method = RequestMethod.POST)
public ModelAndView shutDownPage(ModelAndView modelAndView, Authentication authentication,
#ModelAttribute("ddata") DInputBean dInputBean) {
String domain = dInputBean.getdomain();
return modelAndView;
}
I'm hoping I'd get value of domain from the HTML in the Controller but it's always null. DInputBean has getters and setters for "domain" field.
The th:field attribute can be used on <input>, <select>, or, <textarea>.
A solution you could possibly replacing you second <span> with a hidden input element.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
If you wanted to keep the second div, just place the <input type="hidden"> inside the second <span> and remove the th:field attribute from the second <span>.
Edit:
If you wanted to add the value of domain in a span.
<form action="#" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<span>Domain</span>
<span th:text="${domain}">domain<span>
<input type="hidden" th:field="*{domain}" th:value="${domain}" />
<input type="Submit" value="close" />
</form>
http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#inputs
An option is to use a read-only input field:
<input type="text" th:field="*{domain}" th:value="${domain}" readonly="readonly"/>
This both displays the value and sends it on submit.
The key is to add the value of the domain variable to the form:
#GetMapping("/shutDownPage")
public String shutDownPage(Model model) {
model.addAttribute("ddata" new Ddata()); //or however you create your bean
String username = ... //however you get your username
String domain = myRepositoryService.findDomainByUsername(username);
model.addAttribute("domain", domain);
return "shutDownPage";
}
Include an HTML page in the action so that when you open the HTML page in a browser without a server/container, the button will still appear to work:
<form action="confirmationPage.html" th:action="#{/shutDown}" th:object="${ddata}" method="post">
<!-- You can benefit from using a conditional expression -->
<span th:text="${domain != null ? domain : 'No domain supplied'}">[domain]</span>
<input type="hidden" th:field="*{domain}" th:value="${domain}"/>
<input type="Submit" value="close"/>
</form>
And your post method:
#PostMapping("/shutDown") //use shorthand
public String shutDownPagePost(#ModelAttribute("ddata") DInputBean dInputBean {
String domain = dInputBean.getDomain();
//do whatever with it
return "confirmationPage";
}

Resources