Persist radio checked value in Thymeleaf Spring - 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>

Related

Getting a NumberFormatException with thymeleaf on th:field but not on others th:*

I'm rather new to Thymeleaf, so this maybe a newbie mistake. And I've been looking everywhere online for an answer and haven't found one. So sorry if this is really basic.
Basically I'm using Thymeleaf with SpringBoot 2.6.7 and I want to populate a object using a form. Something I've been able to do in the past, but this time for the first time the object I want to populate contains a list of other objects. And it's getting quite tricky.
My html looks like this :
<form action="#" th:action="#{/character}" th:object="${input}" method="post">
<div th:each="i : ${#numbers.sequence(0, input.attributes.size - 1)}">
<div th:object="${input.attributes[i]}">
<input type="range" min="1" th:max="*{maxValue}" th:field="*{value}"> <!-- this doesn't work -->
<p th:text="*{name} + ' = ' + *{value}"></p> <!-- this works -->
</div>
</div>
</form>
The error I get is java.lang.NumberFormatException: For input string: "i". So I'm guessing there's an issue with the parsing of attributes[i] when processing th:field.
I've tried to change the loop to <div th:each="attribute : ${input.attributes}"> (and the associated th:object) but that just made it worse, got the Neither BindingResult nor plain target object for bean name 'attribute' available as request attribute error message.
If it's any help, here is my controller :
#GetMapping("character")
public String startingForm(Model model) {
model.addAttribute("input", new FormInput());
return "character";
}
#PostMapping("character")
public String processingForm(#ModelAttribute FormInput input, BindingResult bindingResult, Model model)
throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException {
if(bindingResult.hasErrors()){
log.error("something went wrong");
}
// Other stuff
return "character";
}
My input class
#Getter
public class FormInput{
protected List<Attribute> attributes;
public FormInput(){
attributes= new ArrayList<>();
for (AttributeEnum ae : AttributeEnum.values()) {
attributes.add(new Attribute(ae.getName(), ae));
}
}
}
And the Attribute class
#Getter
public class Attribute {
protected String name;
protected AttributeEnum typeAttribute;
protected Integer value = 1;
protected Integer maxValue = 5;
public Attribute(String n, AttributeEnum ta){
name = n;
typeAttribute = ta;
}
public boolean setValue(Integer v){
if (v > maxValue ) return false;
value = v;
return true;
}
}
Does someone know a fix to this issue?
Just found the solution. So I'm posting the answer is anyone has the same issue.
<div th:each="i : ${#numbers.sequence(0, input.attributes.size - 1)}">
<input type="range" min="0" th:max="${input.attributes[i].maxValue}" th:field="${input.attributes[__${i}__].value}"> <!-- this works -->
</div>
Just had to replace attributes[i] with attributes[__${i}__].

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)

Spring - Thymeleaf Date type

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);
}

Redirect on Spring Tiles Causing Parameters to be Appended to the URL

I am using Spring 3 and Tiles 3. Below is just a simplified example I made. I have a test controller where I list all the SimpleEntity objects. And there is an input field on the JSP to add a new entity via a POST. Here is the controller.
#Controller
#RequestMapping(value="/admin/test")
public class TestAdminController {
private String TEST_PAGE = "admin/test";
#Autowired
private SimpleEntityRepository simpleEntityRepository;
#ModelAttribute("pageName")
public String pageName() {
return "Test Administration Page";
}
#ModelAttribute("simpleEntities")
public List<SimpleEntity> simpleEntities() {
return simpleEntityRepository.getAll();
}
#RequestMapping(method=RequestMethod.GET)
public String loadPage() {
return TEST_PAGE;
}
#RequestMapping(method=RequestMethod.POST)
public String addEntity(#RequestParam String name) {
SimpleEntity simpleEntity = new SimpleEntity();
simpleEntity.setName(name);
simpleEntityRepository.save(simpleEntity);
return "redirect:/" + TEST_PAGE;
}
}
Everything works fine. However, when I submit the form, the URL adds the pageName parameter, so it goes from /admin/test to /admin/test?pageName=Test+Administration+Page. Is there anyway to prevent this from happening when the page reloads?
UPDATE
Here is the JSP form.
<form:form action="/admin/test" method="POST">
<input type="text" name="name" />
<input type="submit" name="Save" />
</form:form>

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