Search table with method query spring boot thymeleaf - java

I have a problem when I try to add search form to my table. I wont to search in some table column with method query. I use parameter with #RequestParam and process it to method but when I input something to search, it always show me whole table. There are no errors in Spring boot console.
Here are my classes:
predmeti.html
<form class="form-horizontal" th:object="${predmet}" th:action="#{/predmeti}" method="get">
<p class="col-sm-2"><input type="text" th:name="imeZaPretragu" th:value="${search}" class="form-control"/></p>
<p><input type="submit" class="btn btn-primary" value="Pretraži"/></p>
<div th:if="${not #lists.isEmpty(predmeti)}">
<table class="table table-striped">
<tr>
<th>Prezime</th>
PredmetController
private PredmetiRepository predmetiRepository;
#Autowired
public void setPredmetiRepository(PredmetiRepository predmetiRepository) {
this.predmetiRepository = predmetiRepository;
}
#GetMapping("/predmeti/")
public String pretraziPredmet(#RequestParam(value="search", required = true) String imeZaPretragu, Model model) {
model.addAttribute("predmeti", predmetiRepository.findByPrezime(imeZaPretragu));
return "predmeti";
}
PredmetRepository
public interface PredmetiRepository extends JpaRepository<Predmet, Long> {
Iterable<Predmet> findByPrezime(String imeZaPretragu);
}
Thank you in advance.

Related

How to submit table data in Spring Thymeleaf

I am following given link to submit table data to be saved in database
http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/
But the difference between given link and my implementation is that the front-end of link uses JSTL (JSP) while i am using Thymeleaf (HTML)
Below are the files being used
HTML Form :
<form method="POST" th:action="#{/updateAllRules}" th:field="${ruleForm}">
<table>
<thead>
<tr>
<th>S No</th>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr th:each="ruleModel,iteration : ${allRules}">
<td th:text="${ruleModel.id}"></td>
<td><input type="text" th:name="${'rule'+iteration.index+'.title'}" th:value="${ruleModel.title}"></td>
<td><input type="text" th:name="${'rule'+iteration.index+'.body'}" th:value="${ruleModel.body}"></td>
</tr>
</tbody>
</table>
<br>
<input type="submit" value="Update All">
</form>
Model Class :
public class Rule {
private Integer id;
private Date timestamp;
private String title;
private String body;
// constructor and Getter/Setters
}
Form Class :
public class RuleForm {
private List<Rule> rule;
public List<Rule> getRule() {
return rule;
}
public void setRule(List<Rule> rule) {
this.rule = rule;
}
}
Controller Method :
#RequestMapping(value = "/updateAllRules", method = RequestMethod.POST)
public String updateAllRules(#ModelAttribute("ruleForm") RuleForm ruleForm) throws IOException
{
System.out.println(ruleForm); // this prints com.web.model.RuleForm#235f9fcb
System.out.println(ruleForm.getRule()); //this prints null
return "redirect:/admin";
}
Please let me know what i am missing.
UPDATE 1:
Made changes as suggested. My new HTML form is as below
<form method="POST" th:action="#{/updateAllRules}" th:object="${ruleForm}">
<table>
<thead>
<tr>
<th>S No</th>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr th:each="rule,iteration : ${ruleForm}">
<td th:field="*{rule[__${iteration.index}__].id}"></td>
<td><input type="text" th:field="*{rule[__${iteration.index}__].title}"></td>
<td><input type="text" th:field="*{rule[__${iteration.index}__].body}"></td>
</tr>
</tbody>
</table>
<br>
<input type="submit" value="Update All">
</form>
On making these changes following exception is being received when the page loads.
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'rule' cannot be found on object of type 'java.util.ArrayList' - maybe not public?
Please see that i am sending original list in model attribute "ruleForm" on page load. Once page loads the data and user make changes , i want to POST complete table back to controller.
Forms should have a th:object, rather than a th:field:
<form method="POST" th:action="#{/updateAllRules}" th:object="${ruleForm}">
Instead of using th:name and th:value, you should instead be using th:field which does both of those for you. Fields also should be specified using the *{...} syntax, which assumes the th:object automatically.
<input type="text" th:field="*{rule[__${iteration.index}__].title}" />
Everything else looks correct to me.

Neither BindingResult nor plain target object for bean name 'matrix[0][0]' available as request attribute

I have a matrix (array of arrays) of boolean values which I want to show in a form and then submit after changing. I have problems rendering this and I have no more ideas why it is not working. Can anybody give me an advice?
My Controller:
#Controller
#RequestMapping(value = "/konfiguration")
public class VerteilungController {
#ModelAttribute("matrix")
public List<List<Boolean>> getVerteilungenMatrix() {
List<List<Boolean>> result2 = new ArrayList<>();
for (int i = 0; i < kategorien.size(); i++) {
result2.add(new ArrayList<>());
}
//...
return result2;
}
#RequestMapping(value = "/verteilung", method = RequestMethod.GET)
public String showPage(Model model) {
model.addAttribute("matrix", getVerteilungenMatrix());
return "konfiguration/verteilung";
}
}
The form:
<form id="verteilung_form" class="form-horizontal" method="post" action="/verteilung"
th:action="#{/konfiguration/verteilung}"
th:object="${matrix}">
<table class="table-hover">
<tr th:each="row: ${matrix}">
<td th:each="value: ${row}">
<input type="checkbox" th:field="${matrix[__${rowStat.index}__][__${valueStat.index}__]}"/>
</td>
</tr>
</table>
<div >
<button th:text="#{button.save}" class="btn btn-default" type="submit" name="save">Speichern</button>
<button th:text="#{button.reset}" name="reset" class="btn btn-default">Zurücksetzen</button>
</div>
</form>
Openening the page I get
Exception: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputCheckboxFieldAttrProcessor'
And in the log
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'matrix[0][0]' available as request attribute
You've misused th:object and th:field. th:object stands for command objects which represents the entire form.
Command object is the name Spring MVC gives to form-backing beans, this is, to objects that model a form’s fields and provide getter and setter methods that will be used by the framework for establishing and obtaining the values input by the user at the browser side.
On the other hand th:field does all the heavy work of binding your input with a property in the form-backing bean. Values inside th:field should point to a field of the object from th:object.
Values for th:field attributes must be selection expressions (*{...}), which makes sense given the fact that they will be evaluated on the form-backing bean and not on the context variables (or model attributes in Spring MVC jargon).
Please check it out here.
So back to your code. To fix it you should create a form-backing bean class and provide the matrix as a field inside that class, for example:
public class FormBean {
private List<List<Boolean>> matrix;
FormBean() { }
public FormBean(List<List<Boolean>> matrix) {
this.matrix = matrix;
}
public List<List<Boolean>> getMatrix() {
return matrix;
}
public void setMatrix(List<List<Boolean>> matrix) {
this.matrix = matrix;
}
}
Next please provide object of FormBean as a model attribute. When you provide method marked as #ModelAttribute then assignment to a model will be done for you. Update your controller body to the following:
#ModelAttribute("formBean")
public FormBean getFormBean() {
return new FormBean(getVerteilungenMatrix());
}
#RequestMapping(value = "/verteilung", method = RequestMethod.GET)
public String showPage() {
return "konfiguration/verteilung";
}
private List<List<Boolean>> getVerteilungenMatrix() {
List<List<Boolean>> result2 = new ArrayList<>();
for (int i = 0; i < kategorien.size(); i++) {
result2.add(new ArrayList<>());
}
//...
return result2;
}
Finally please update your form to the following:
<form id="verteilung_form" class="form-horizontal" method="post" action="/verteilung"
th:action="#{/konfiguration/verteilung}"
th:object="${formBean}">
<table class="table-hover">
<tr th:each="row: *{matrix}">
<td th:each="value: ${row}">
<input type="checkbox" th:field="*{matrix[__${rowStat.index}__][__${valueStat.index}__]}"/>
</td>
</tr>
</table>
<div >
<button th:text="#{button.save}" class="btn btn-default" type="submit" name="save">Speichern</button>
<button th:text="#{button.reset}" name="reset" class="btn btn-default">Zurücksetzen</button>
</div>
</form>
Now everything should work as expected.

How to get checked boxes in a Controller

I have got a html page (with Thymeleaf):
<form action="#" th:action="#{/changeme}">
<fieldset>
<table style="width: 500px">
<tr th:each="esfield : ${esfields}">
<td>
<div>
<div class="checkbox">
<input type="checkbox" name="optionsMulti"
th:text="${esfield}" />
</div>
</div>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<button type="submit"
class="btn btn-xs btn-primary margin10-right paddingNew"
name="save">Calculate!</button>
</td>
<td></td>
</tr>
</table>
</fieldset>
</form>
When I click Calculate! it goes to my controller
#RequestMapping(value = "/changeme", params = { "save" })
public String save(final ModelMap m) {
m.addAttribute("centers", /*params*/);
return "clustering";
}
I would like to get information about checked boxes in my controller?
How can I do that?
Thank you in advance
You have basically two options :
either you use a different name for each checkbox
or you use spring tag <form:checkbox> instead of native <checkbox>
If you don't posted data will not allow you to know exactly what boxes were actually checked (excepted in cases all and none)
With the approach, you should use in your controller a #ModelAttribute annotated object containing a List<Boolean> and spring will automagically populate it with the values of your checkboxes.
#RequestMapping(value = "/changeme", params = { "save" })
public String save(#ModelAttribute BoxesForm form, final ModelMap m) {
// do what you need with form.getCheckboxes() ...
m.addAttribute("centers", /*params*/);
return "clustering";
}
public class BoxesForm {
List<Boolean> checkboxes;
// getter and setter omitted ...
}

FreeMarker Form for nested Object

I am trying to write freemarker template but could not able to parse with my object class.
My POJO is
public class Metrix {
#Id
String _id;
String loginId;
Date date;
List<MatrixDetail> headers;
//All getters and setters
}
public class MatrixDetail {
String header;
int time;
String detail;
//All getters and setters
}
//Controller after saving form
#RequestMapping(value = "/matrix/save", method = RequestMethod.POST)
public View saveMatrix(#ModelAttribute Metrix matrix, ModelMap model) {
System.out.println("Reachecd in matrix save" );
return new RedirectView("/TrackerApplication/header.html");
}
FTL template form part
<form name="matrix" action="matrix/save.html" method="post">
<table class="datatable" align:"center">
<tr>
<th>Login Id:</th> <th> <input type="text" name="loginId" value= ${matrixList.loginId} required /> </th>
</tr>
<tr> <td></td><td></td><td></td></tr>
<tr>
<th>Header</th> <th>Time</th> <th>Details</th>
</tr>
**// I am not getting how this nested object which is of type List<MatrixDetail>
// will get parse in my form.**
<#list matrixList.headers as header>
<spring:bind path = "MatrixDetail">
<tr>
<td> <input name = "header" value = ${header.header} /> </td>
<td> <input name = "time" value = ${header.time} /> </td>
<td> <input name = "detail" value = ${header.detail} /></td></tr>
</#list>
</table>
<input type="submit" value="Save" />
</form>
How can we write freemarker template for form processing of such kind of nested object?
I am getting issues in form submission.
I would strongly advise against this.
Forms might be displayable in email in some cases, but they may not always work in the email client, not to mention those that only ever read emails in text-only form won't be able to use them whatsoever.
If you need users to enter a form, link to a page on your site and have the form there instead.

How to prepopulate an object before updating using spring mvc and hibernate?

What's the best practice to pre-populate an object before saving this object with hibernate?
What i've done:
My controller:
//The Form
#RequestMapping(value = "user/{id}/edit", method = RequestMethod.GET)
public String edit(#PathVariable("id") Long userId, ModelMap modelMap) {
modelMap.addAttribute("user", userService.find(userId));
return "user/userEdit";
}
//Updating database
#RequestMapping(value = "user/edit", method = RequestMethod.POST)
public String update(#ModelAttribute("user") #Valid User user, BindingResult result,
RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return "user/userEdit";
}else{
userService.update(user);
redirectAttrs.addFlashAttribute("message", "Success");
return "redirect:user/userEdit";
}
}
It works if i make a form containing all fields (username, password and id) , but what should i do if i want the user to update only the password?
Since i have a #NotEmpty at username, i get an error that username is null, since its not in the form, but i dont want to put the username field, just the password.
My html form:
<c:url var="url" value="/user/edit" />
<form:form method="post" action="${url}" modelAttribute="user" class="form-horizontal">
<form:hidden path="id"/>
<form:hidden path="version"/>
<fieldset>
<div class="control-group">
<form:label cssClass="control-label" path="password"><spring:message code="user.label.password"/>: </form:label>
<div class="controls">
<form:input cssClass="input-xlarge" path="password" />
</div>
<form:errors path="password"/>
</div>
<div class="control-group">
<form:label cssClass="control-label" path="userRole"><spring:message code="user.label.role"/>: </form:label>
<div class="controls">
<form:select path="userRole">
<form:options items="${userRoleList}" itemValue="id" itemLabel="name"/>
</form:select>
</div>
<form:errors path="userRole"/>
</div>
<div class="control-group">
<form:label cssClass="control-label" path="costumer.id"><spring:message code="user.label.costumer"/>: </form:label>
<div class="controls">
<form:select path="costumer.id">
<form:options items="${costumerList}" itemValue="id" itemLabel="name"/>
</form:select>
</div>
<form:errors path="costumer.id"/>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<a class="btn cancel link" href="<c:url value="/user" />">Cancel</a>
</div>
</fieldset>
</form:form>
I tried using #Sessionattributes, but it doesnt work well if i try to
edit two or more users using browser tabs.
I tried using property editors, but didnt work with #ModelAtrribute
User user.
I tried using convertors but didnt work.
Is the only way to make a User user = userService.find(id) first, and then set the updated values? Something like:
#RequestMapping(value = "user/edit", method = RequestMethod.POST)
public String update(#RequestParam("password") String password, BindingResult result, RedirectAttributes redirectAttrs) {
User user = userService.find(id);
if (password == null{
return "user/userEdit";
}else{
user.setPassword("password");
userService.update(user);
redirectAttrs.addFlashAttribute("message", "Success");
return "redirect:user/userEdit";
}
}
Which seens wrong, because there is no validation.
An alternate way, that I think is less messy and accident-prone, is to create a class that models the UI form, for example
public class EditUserForm {
// getters and setters for password and other fields...
}
and in the controller's update(EditUserForm,...) method, you simply map any fields populated by the user in the EditUserForm to the instance of User you wish to update.
In the code you posted it's obviously clear that you need some external helper classes to associate with your GUI side updating before you implement the move-control and persistence operations.
I've run into this problem too, if there's only a couple fields I use your second example and validate the fields one by one. Otherwise, you're going to have to do what the other posters have said and make a new class to match your form.
Try to use:
#PrePersist
#PreUpdate
public void prepare(){
//DO SOMETHING WITH YOUR ENTITY
//For example: if(name==null){ name="MYNAMEVALUE";}
}

Categories