Passing object's variable to controller in thymeleaf - java

I'm trying to create my very first CRUD ever.
Here's my journeySite.html table code.
<table>
<tr th:each="trip : ${trips}">
<td th:text="${trip.title}"></td>
<td th:text="${trip.destination}"></td>
<td th:text="${trip.id}"></td>
<form th:action="#{/journeys}" th:object="${trip}" method="post">
<input type="hidden" th:field="${trip.id}" />
<button type="submit">Delete</button>
</form>
</tr>
</table>
And make a my controller looks now like that.
#RequestMapping(value = {"/journeys"}, method = RequestMethod.GET)
public String journeysPage(Model model){
tripRepository.save(new Trip("Asian Trip", "Asia"));
tripRepository.save(new Trip("European Trip", "Europe"));
tripRepository.save(new Trip("African Trip", "Africa"));
model.addAttribute("trips", tripRepository.findAll());
return "journeysSite";
}
#RequestMapping(value = {"/journeys"}, method = RequestMethod.POST)
public String journeysPageTripDeleting(#RequestParam Long id) {
tripRepository.delete(id);
return "journeysSite";
}
All i want is to show my all my trips on the /journeys in table. In each row there'd be a delete button which would POST trip.id, delete it from db and redirect to the exact same page, but with trip deleted.
But obviously error has occured: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'id' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144) ~[spring-webmvc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
Would anyone give me a tip how to do it? Thanks.

At your form, you defined a th:object="${trip}" object which means whenever this form gets submitted this trip object will be sent as request body.
So, to receive this object you have to accept it in controller's method.
#RequestMapping(value = {"/journeys/"}, method = RequestMethod.POST)
public String journeysPageTripDeleting(#ModelAttribute Trip trip){
tripRepository.delete(trip.getId());
return "redirect:/journeys";
}
The th:field="${id}" will be included with the object that model attribute provides so, trip object will have the id you're looking for.
More on this.
UPDATE:
With your current controller's method implementation, I think all you need to change is this,
<input type="hidden" th:field="*{id}" /> // No trip.id

You need to change your controller method post code from #RequestMapping(value = {"/journeys/"}, method = RequestMethod.POST) to #RequestMapping(value = {"/journeys/{id}"}, method = RequestMethod.POST).
As you can see,You forgot to add {id} in your RequestMapping which is required.
Also its standard to use DELETE http method for deleting an entity not POST method.

This is not good practice to create form for each row. Instead do it this way:
<table>
<tr th:each="trip : ${trips}">
<td th:text="${trip.title}"></td>
<td th:text="${trip.destination}"></td>
<td th:text="${trip.id}"></td>
<td><button class='delete' data-id="${trip.id}">Delete</button></td>
</tr>
</table>
Add this js in your html:
<script>
$(document).ready(function () {
$(document).on('click', '.delete', function () {
$.ajax({
url: "<c:url value="/journeys/delete"/>",
data: {
id: $(this).data("id")
},
success: function(data) {
location.reload();
}
})
})
})
</script>
And change your controller method like this:
#RequestMapping(value = {"/journeys/delete"}, method = RequestMethod.GET)
#ResponseBody
public String journeysPageTripDeleting(#RequestParam Long id) {
tripRepository.delete(id);
return "success";
}

Related

Http status 400 -The request sent by the client was syntactically incorrect. Spring MVC

I spent a lot of time on this problem, but can`t resolve it. Please Help! When I submit form from JSP I get this error.
My code:
<form action="/albums/add" method="post">
<%--<div><input type="file"></div>--%>
<div>Name: <input type="text" name="name" placeholder="Name"></div>
<div>Year: <input type="text" name="year" placeholder="Release year"></div>
<div>
<select name="singer" id="singer">
<c:forEach items="${singers}" var="s">
<option value="${s.id}">${s.name}</option>
</c:forEach>
</select>
</div>
<input type="submit" >
And my controller code:
#RequestMapping(value = "", method = RequestMethod.GET)
public String albumsGET(ModelMap map) {
List<Album> albums = albumDAO.getAllAlbums();
map.addAttribute("albums", albums);
return "forward:/Albums.jsp";
}
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String albumsAddGET(ModelMap map) {
map.put("singers", singerDAO.getAllSingers());
return "forward:/WEB-INF/Pages/Album/AddAlbum.jsp";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String albumsPOST(#ModelAttribute Album album, HttpServletRequest req) {
Singer s = singerDAO.getSingerById(Long.valueOf(req.getParameter("singer")));
s.getAlbumList().add(album);
album.setSinger(s);
singerDAO.updateSinger(s);
return "redirect:/albums";
}
}
You can throw custom exception provided by spring, create class with #ControllerAdvice annotation and throws custom exception with different HTTP status code
That is parameter mapping error for #ModelAttribute which HTTP return code 400.
There are some reasons that rises this error.
There is no parameter name for your Album Object.
There are NULL value for mapping Album Obejct.
So, if you can check item ${singers}, you should check substituted properly value.
Next, in Album Object, you should check getter/setter method.
I could have resolved that issue to setting default values for Object (etc. VO)

Spring, IllegalStateException when opening page with form

I am new to Spring and I get this exception, when entering campaigns.jsp with browser.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'campaign' available as request attribute
I have this controller :
#Controller
#RequestMapping(value = "/admin")
public class AdminIndexController {
#RequestMapping(value = "/secure/campaigns.jsp", method = RequestMethod.GET)
public String campaigns() {
return "campaigns";
}
#RequestMapping(value = "/secure/create", method = RequestMethod.POST)
public String addContact(#ModelAttribute("campaign")
Campaign campaign) {
return "campaigns";
}
}
Campaings.jsp (if I remove this part, it shows the page correctly) :
<form:form method="post" action="create" commandName="campaign">
<table>
<tr>
<td><form:label path="question">Question</form:label></td>
<td><form:input path="question" />
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Add Campaign"/>
</td>
</tr>
</table>
</form:form>
I suppose there is a problem with mapping action="create", I am not sure where it is pointing to. I thought it is pointing to the same place as the .jsp is. This is address I use http://localhost:8080/server/rest/admin/secure/campaigns.jsp
This
<form:form method="post" action="create" commandName="campaign">
expects a model attribute (or command object), an HttpServletRequest attribute in reality, with the name campaign to use as a template for the form fields.
You haven't added a request attribute with that name. You need to do that before the view is rendered. For example
#RequestMapping(value = "/secure/campaigns.jsp", method = RequestMethod.GET)
public String campaigns(Model model) {
model.addAttribute("campaign", new Campaign());
return "campaigns";
}
The object doesn't need to have any fields set since it is only used as a template.

Model property is set to null by thymeleaf

I have been dealing with the following issue:
I have a simple model class called User.
public class User{
private Long id;
private String name;
...}
And this is my the controller code:
#RequestMapping ( value = "/users", params = { "id" } )
public String showEditForm( final Model model, final HttpServletRequest req )
{
User edit = this.userRepository.findOne( Long.valueOf( req.getParameter( "id" ) ) );
model.addAttribute( "user", edit );
return "edit-user";
}
#RequestMapping ( value = "/users", method = RequestMethod.POST, params = {"edit"
})
public String editUser( #Valid #ModelAttribute ( "user" ) final User user,
final BindingResult bindingResult, final Model model )
{
if ( bindingResult.hasErrors() )
{
return "edit-user";
}
return "redirect:/users";
}
The following is the code snippet to displaying all the users:
<div class="userlist" th:unless="${#lists.isEmpty(users)}">
<h2 th:text="#{list.user.title}"></h2>
<table>
<thead>
<tr>
<th th:text="#{name}" />
<th th:text="#{details}" />
</tr>
</thead>
<tbody>
<tr th:each="u : ${users}">
<td th:text="${u.name}" />
<td><a th:href="#{/users(id=${tc.id})}" th:text="#{edit}"></a></td>
</tr>
</tbody>
</table>
</div>
And eventually the submit form:
<form action="#" th:action="#{/users}" th:object="${user}"
method="post">
<fieldset>
<ul th:if="${#fields.hasErrors('*')}" class="errorlist">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
<div>
<input type="text" th:field="*{id}"/>
</div>
<div>
<label for="name"> <span th:text="#{name}"></span>
</label> <input type="text" th:field="*{name}"
th:class=" ${#fields.hasErrors('name')}? 'fieldError'" />
</div>
<div class="submit">
<button type="submit" th:text="#{update}" name="edit"></button>
</div>
</fieldset>
</form>
And now the problem description:
As long as the 'id'-field is present within the submit form, everything works fine. If I remove the 'id'-field from the submit form though, because the id property isn't meant to be modified, the workflow doesn't work anymore. In fact, the id is null in the editUser() method. I assume that Thymeleaf does set the value of the id property to null if it is not present within the submit form. But I'm not sure about this. And I think there must be some solution to this problem other than having to let the id-property reside in the submit form.
I hope anyone can help here out.
Thanks.
Edmond
That has nothing to do with Thymeleaf, but how binding works. Spring will only bind attributes to the model object which are present as parameter in the request. If you remove id that isn't present and as such cannot be bound (what should it be bound to?).
A solution to this is to either specify the id as a hidden form, so that it is present. Or in between requests store your object in the session (using #SessionAttributes on the controller) that way the earlier retrieved object is going to be used for binding.
#Controller
#SessionAttributes("user")
public class UserController {
#ModelAttribute("user")
public User initUser(#RequestParam("id") Long id) {
return this.userRepository.findOne(id);
}
RequestMapping ( value = "/users", params = { "id" } )
public String showEditForm() {
return "edit-user";
}
#RequestMapping ( value = "/users", method = RequestMethod.POST, params = {"edit"})
public String editUser( #Valid #ModelAttribute ( "user" ) final User user, final BindingResult bindingResult, SessionStatus status;) {
if ( bindingResult.hasErrors() ) {
return "edit-user";
}
status.setComplete(); // Indicate we are done,so #SessionAttributes can be cleaned up
return "redirect:/users";
}
Something like that should preserve the User between sessions, and the SessionStatus can then be used to trigger the cleanup of the session attributes.
Id field should be present in form otherwise how controller would know which user should be updated.
If this field is meaningless for client (that is it's some generated unique id) it should be done hidden. If it may be of some interest for client this field can be done read-only.

Spring Form Tag Error (Binding Result)

I am teaching myself Spring Form tags, and have run into what is probably a simple error that I haven't been able to solve. I get the following error when I launch this app in a browser:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'index' available as request attribute
I've tried most of the fixes that came up in a Google search to no avail. Can someone spot where I went wrong? Below are the relevant components. Thanks very much.
Controller:
#Controller
#RequestMapping("/registration")
public class LoginController {
#RequestMapping(method = RequestMethod.GET)
public String setupForm(ModelMap model) {
Registration registration = new Registration();
model.addAttribute("registration", registration);
return "index";
}
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("registration") Registration registration, Map model) {
return "success";
}
}
JSP (/index.jsp):
<form:form commandName="index">
<table border="0" cellspacing="12">
<tr>
<td>
<form:input path="email"/>
</td>
</tr>
<tr>
<td>
<form:password path="password"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="Submit"/>
</td>
</tr>
</table>
</form:form>
Command Object (Registration.java) :
public class Registration {
private String password;
private String email;
// getters,setters
Facing same issue few days back, What i understood from hit and trail is, Index page is a static page and no processing happens before the same is rendered. In case i want to use a form model binding in index page i should have a controller's handler method which will create a Registration object and place the same in ModelAndView before index.jsp is rendered
add a method in your controller like the below and try
#RequestMapping(method = RequestMethod.GET, value="/")
public ModelAndView initiate(){
ModelAndView objModView = new ModelAndView("/index.jsp");
objModView.addObject("registration",new Registration());
return objModView;
}
In your index page correct the following and try
<form:form commandName="index"> to <form:form commandName="registration">
You can also do it like this if the above does not work
<form:form modelAttribute="registration" commandName="registration">
Thanks
The error which is seen is because when you submit the form you have to have a Binding Result associated with the #ModelAttribute annotation.
Try changing you code to this :
#RequestMapping(method = RequestMethod.POST)
public String onSubmit(#ModelAttribute("registration") Registration registration, BindingResult result, Map model){
return "success";
}
Also note that the Binding Result object should be followed immediately after the Model Attribute.
And if you are using two #ModelAttributes then each one should have its own binding result object which follows it.
Please refer the spring source guide for all the related documentation
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html

why autocomplete does not work with JSON and SPRING MVC

I am puzzled why autocomplete does not work. Here is the form in the .jsp code below:
<form:form method="post" action="save.html" modelAttribute="word">
<table>
<tr>
<th>German word</th>
<td><form:input path="german" id="german" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save" /></td>
</tr>
</table>
<br />
</form:form>
here are the javascript functions (in the same .jsp file)
$(document).ready(function() {
$( "#german" ).autocomplete({
source: '${pageContext. request. contextPath}/get_word_list.html'
});
});
and here is the relevant part of the controller:
#Autowired
private WordService wordService;
#RequestMapping(value = "/goToDictionary", method = RequestMethod.GET)
public ModelAndView index() {
Word word = new Word();
return new ModelAndView("dictionary", "word", word);
}
#RequestMapping(value = "/get_word_list", method = RequestMethod.GET, headers = "Accept=*/*")
public #ResponseBody
List<String> getCountryList(#RequestParam("term") String query) {
System.out.println(query);
return getMatch(query);
}
public List<String> getMatch(String query) {
query = query.toLowerCase();
List<String> matched = new ArrayList<String>();
for (Word v : wordService.getAllWord()) {
if (v.getGerman().toLowerCase().startsWith(query)) {
matched.add(v.getGerman());
}
}
return matched;
}
I know for sure that the getMatch(String query) gets called and works properly.
So I guess the problem is on the jsp. file
any help is greatly appreciated.
[For JSON to fill the list]
Maybe you should look at the produces property of the #RequestMapping annotation. It takes a String[] as value. Available since Spring 3.1.x version. I currently use the 3.1.2 and I can get some application/json without any problem.
And of course you should add the produces in the data provider of your country list.
[For JavaScript to fill the list]
Maybe the part ${pageContext. request. contextPath} is not evaluated correctly. Did you check inside the produced code of your JSP e.g. with Firebug.
OK, I have found the solution. it is in the spring-servlet.xml file. It did not work because i failed to add this.
xsi:schemaLocation="ttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
I added, and now everything is OK

Categories

Resources