How to use input radio button with thymeleaf and Spring MVC - spring-mvc

I would like to get a destination address from a input radio button list. The DestinationAddress class is the following:
public class DestinationAddress {
private Integer destinationAddressId;
private String name;
private Location location;
private User owner;
public DestinationAddress(String name, Location location, User owner) {
this.name = name;
this.location = location;
this.owner = owner;
}
public DestinationAddress() {
}
// getter and setter
}
The controller who handles the get and post is the following:
#PreAuthorize("hasRole('USER')")
#GetMapping(value = "/select-address")
public String selectAddress(Principal principal, Model model) {
List<DestinationAddress> addresses = destinationAddressService.getAllByUsername(principal.getName());
model.addAttribute("destinationAddresses", addresses);
model.addAttribute("destinationAddress", new DestinationAddress());
return "purchase/select-address";
}
#PreAuthorize("hasRole('USER')")
#PostMapping(value = "/select-address")
public String selectAddress(#ModelAttribute DestinationAddress destinationAddress, Principal principal) {
Purchase purchase = purchaseService.addPurchase(principal.getName(), destinationAddress);
return "redirect:/purchases/pay/" + purchase.getPurchaseId();
}
And the html page is the following:
<form th:object="${destinationAddress}" method="post">
<fieldset>
<legend>Your addresses</legend>
<ul>
<li th:each="destinationAddress : ${destinationAddresses}">
<input type="radio" th:field="*{destinationAddressId}" th:value="${destinationAddress.destinationAddressId}" />
<label th:for="${#ids.prev('destinationAddress.destinationAddressId')}" th:text="${destinationAddress}"></label>
</li>
</ul>
</fieldset>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
The error message is the following:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'destinationAddressId' available as request attribute
I don't know what's the problem here. I don't know which type will the form return to the controller. So I don't know which variable pass to the model and which one to get from the post controller method. Integer or DestinationAddress? I cannot find anything googling it, just small pieces of code without any explanations. Any suggestions?

I found a solution to my problem. I changed the html page, now it looks like this:
<form th:object="${address}" method="post">
<fieldset>
<legend>Your addresses</legend>
<ul>
<li th:each="destinationAddress : ${destinationAddresses}">
<input type="radio" th:field="${address.destinationAddressId}" th:value="${destinationAddress.destinationAddressId}" />
<label th:for="${destinationAddress.destinationAddressId}" th:text="${destinationAddress}"></label>
</li>
</ul>
</fieldset>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
I changed the name of the object inside the model because it was the same as the name of the temp destinationAddress of the loop. I also replaced '{#ids.prev(' because it was giving me an error:
Cannot obtain previous ID count for ID ...
Now it works fine.

Related

Saving record in a proper way

I have a problem with saving records to DB with Spring-Mvc and Thymeleaf.
When I click "Update" button on record, to enter the update form (included beneath), all values are in place correctly, but, when I want to subbmit, an error occur. There is no any stacktrace in console, only error in web page, that I am not able to solve.
This is my code:
Controller:
#GetMapping("/{maltId}")
public ModelAndView showMalt(#PathVariable("maltId") Long maltId) {
ModelAndView mav = new ModelAndView("malt/malt-show");
mav.addObject(maltService.findById(maltId));
return mav;
}
#GetMapping("/{maltId}/edit")
public String initUpdateMaltForm(#PathVariable("maltId") Long maltId, Model model) {
model.addAttribute("malt", maltService.findById(maltId));
return VIEWS_MALT_CREATE_OR_UPDATE_FORM;
}
#PostMapping("/{maltId}/edit")
public String processUpdateMaltForm(#Valid Malt malt, BindingResult result, #PathVariable("maltId") Long maltId) {
if (result.hasErrors()) {
return VIEWS_MALT_CREATE_OR_UPDATE_FORM;
} else {
malt.setId(maltId);
Malt savedMalt = maltService.save(malt);
return "redirect:/malt/" + savedMalt.getId();
}
}
Model:
#Column(name="malt_name")
private String maltName;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="producer_id")
private Producer producer;
#Column(name="malt_filling")
private int maltFilling;
#Column(name="malt_ebc")
private int maltEbc;
#Column(name="malt_usage")
private String maltUsage;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="country_id")
private Country country;
#ManyToMany(mappedBy="malts")
private Set<Batch> batches;
This is the view:
<body>
<form th:object="${malt}" th:action="#{/malt/}" method="post">
<input type="hidden" th:field="*{id}" />
<label>Malt name:</label>
<input type="text" class="form-control" th:field="*{maltName}" />
<label>Producer:</label>
<input type="text" class="form-control"
th:field="*{producer.producerName}" />
<label>Country:</label>
<select class="form-control" th:field="*{country.id}">
<option value="0">Select country</option>
<option th:each="country : ${countries}"
th:value="${country?.id}"
th:text="${country?.countryName}">
</option>
</select>
<label>Malt filling:</label>
<input type="text" class="form-control"
th:field="*{maltFilling}" />
<label>Malt usage:</label>
<input type="text" class="form-control"
th:field="*{maltUsage}" />
<label>Malt EBC:</label>
<input type="number" class="form-control"
th:field="*{maltEbc}" />
<button class="submit-button" type="submit">Submit</button>
</form>
</body>
When I hit Submit button, I get this error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed May 15 22:46:22 CEST 2019
There was an unexpected error (type=Not Found, status=404).
No message available
I have tried couple of different approaches, but nothing helps, and since there is no stacktrace in console, I have no idea what is wrong here.
Link to repo: https://github.com/fangirsan/maruszka-new
No stack trace 404 normally indicates that there is no mapping. Since you have, potentially, provided only a part of your Controller I assume that the causation for this is the code in your view right here:
<form th:object="${malt}" th:action="#{/malt/}" method="post">
The action takes to ("/malt/"), however, your controller has not got mapping for this?!
I expect that this should fix it:
<form th:object="${malt}" th:action="#{${'/' + malt.id + '/edit'}}" method="post">
Update
Had a look at your controller and you have the following annotations on your class
#Controller
#RequestMapping("/malt")
public class MaltController{..
#RequestMapping("/malt") will now make your path to save ../malt/{id}/edit'. The code below now should work:
<form th:object="${malt}" th:action="#{${'/malt/' + malt.id + '/edit'}}" method="post">
On using "#{${...}}"
#{} is a link variable, contents within this tag will be appended to the applications root context, e.g., at Stack Overflow #{'/posts'} would result with https://stackoverflow.com/posts
The ${} is a variable expression which will return a String or the object's .toString() value.
If we want to pass a variable within #{} link variable we must include the ${} variable within it, thus resulting in :
#{${'/hope/this/helps' + yourVariable}}

Creating a new User Account Spring data rest framework

I have been trying to create a new user for my webApp. I am using spring-data-rest. At the front end, a prospective user submits his information and then the app is supposed to create back-end information. The codes are as below.
User.java
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String email;
private String password;
private String address;
//getters and setters
}
UserRepository.java
#RepositoryRestResource(collectionResourceRel = "user", path = "user")
public interface UserRepository extends PagingAndSortingRepository<User, Long>{
}
Is it okay to have view as below? Do I need to worry about id? It is supposed to be generated by backend, so it does not make any sense to put id field in view.
registration.html
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
</head>
<body>
<h3>Welcome, Enter The User Details</h3>
<form:form action="#" th:action="#{/user}" th:object="${user}" method="post">
<div><label> User Name : <input type="text" th:field="*{username}"> </label></div>
<div><label> Password: <input type="password" th:field="*{password}"/> </label></div>
<div><label> Email: <input type="email" th:field="*{email}"/> </label></div>
<div><label> Address: <input type="text" th:field="*{address}"/> </label></div>
<div><input type="submit" value="Submit"/></div>
</form:form>
</body>
</html>
I don't know about thymeleaf, but answer for your question is it is ok.
You don't need to worry about id in view, it is automatically created by your dao framework. By #GeneratedValue(strategy= GenerationType.AUTO)
You can use this kind of thing since it's a Spring MVC
<form:hidden path="id"/>

Handling map-like request parameters in Spring MVC

Say I have a form for a list of questions where I need a 0/1 answer. I could easily model a static list with radios, something like
<input type="radio" name="question1" value="0">
<input type="radio" name="question1" value="1">
<br>
<input type="radio" name="question2" value="0">
<input type="radio" name="question2" value="1">
#RequestMapping("/answer")
public String answer(Integer question1, Integer question2) {
But I have a dynamic list of questions instead, where each question has a numeric ID. I therefore tried to model it like the following (the HTML is dynamically created with an iteration on the question list):
<input type="radio" name="question[42]" value="0">
<input type="radio" name="question[42]" value="1">
<br>
<input type="radio" name="question[51]" value="0">
<input type="radio" name="question[51]" value="1">
where 42 and 51 are the question id.
I was expecting to capture all values in a Map parameter of my Spring controller, like so:
#RequestMapping("/answer")
public String answer(#RequestAttribute("question") HashMap<Integer, Integer> question) {
It didn't work (the method isn't called).
I also tried with string ids:
<input type="radio" name="question['42']" value="0">
#RequestMapping("/answer")
public String answer(#RequestAttribute("question") HashMap<String, Integer> question) {
Same as before.
It only works if I use a map of string/string, but in this case I get all request parameters in the map, which I will then need to parse:
#RequestMapping("/answer")
public String answer(#RequestAttribute("question") HashMap<String, String> question) {
--> question.keys: "question[42]", "question[51]"
So what is the proper way of handling dynamic radios, or more generally map-like request parameters?
I don't know why, but it works if I put the map inside a bean:
public class QuestionForm {
private HashMap<Long, String> question;
public HashMap<Long, String> getQuestion() {
return question;
}
public void setQuestion(HashMap<Long, String> question) {
this.question = question;
}
}
#RequestMapping("/answer")
public String answer(QuestionForm questionForm) {

Form submit error, Failed to convert value of type 'java.lang.String' to required type error in browser, In spring MVC

So, I'm trying to create comments on a post using spring mvc, spring boot, spring data, jpa, and thymeleaf, and so far I can get to the specific page I want, using the controller and pathvariables, and I can load up the page just how I want, but when I go to submit the comment I get the error
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'com.example.domain.Comment'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.lang.Long for value 'comment 1'; nested exception is java.lang.NumberFormatException: For input string: "comment1"
This error is only in my browser, nothing comes up in the console in my IDE. Also I can access the page just fine, so there I don't think there's an issue in my get method in my controller, but I'm not really sure where the problem is, so I'll show you guys some of my code.
Here's my controller.
private PostRepository postRepo;
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.GET)
public String postViewGet (#PathVariable Long postId, ModelMap model)
{
Post post = postRepo.findOne(postId);
model.put("post", post);
Comment comment = new Comment();
model.put("comment", comment);
return "post";
}
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.POST)
public String postViewPost (#ModelAttribute Comment comment, #PathVariable Long postId, ModelMap model)
{
Post post = postRepo.findOne(postId);
comment.setPost(post);
post.getComments().add(comment);
postRepo.save(post);
return "redirect:/viewCourse/{postId}";
}
#Autowired
public void setPostRepo(PostRepository postRepo) {
this.postRepo = postRepo;
}
Here's my thymeleaf html page
<div class="PostContent">
<h2 th:text = "${post.title}"></h2>
<p th:text = "${post.content}"></p>
</div>
<br/>
<div class="CommentPost">
<form th:action="${post.id}" method="post" th:object="${comment}" id="comment">
<div class="form-group">
<textarea rows="2" th:field="${comment.comment}" class="form-control" placeholder="comment" id="comment"></textarea>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="submit" value="Comment" class="btn btn-success"/>
</form>
</div>
<br/>
<div class="Comments">
<div th:each = "comment : ${comments}" th:object="${comment}">
<span th:text="${comment.comment}"></span>
</div>
<div th:if = "${#lists.isEmpty(comments)}">
There are no comments to display
</div>
</div>
</div>
Also on this page the message comes up "There are no comments to display", just like I tell it to in the code, but it still says "There are no comments to display" even if I manually insert a comment into the database.
Here's my comment object, although I'm pretty sure that's fine.
#Entity
public class Comment {
public Long id;
public String comment;
public Post post;
public User user;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
#ManyToOne
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
#ManyToOne
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And my postRepo, although this should be fine, just thought I'd include it
public interface PostRepository extends JpaRepository <Post, Long>{
}
If anyone can see my issue, and let me know, that would be awesome, thanks.
When you use th:object don't have to reference to object, you access directly atributes of object. Try with this code:
<div class="PostContent">
<h2 th:text = "${post.title}"></h2>
<p th:text = "${post.content}"></p>
</div>
<br/>
<div class="CommentPost">
<form th:action="${post.id}" method="post" th:object="${comment}" id="comment">
<div class="form-group">
<textarea rows="2" th:field="*{comment}" class="form-control" placeholder="comment" id="comment"></textarea>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="submit" value="Comment" class="btn btn-success"/>
</form>
</div>
<br/>
I don't see in the controller where you put the comments in the model. I suppose that comments there are inside the post so modify the refereces of comments to post.comments
<div th:each = "comment : ${post.comments}" th:object="${comment}">
<span th:text="*{comment}"></span>
</div>
<div th:if = "${#lists.isEmpty(post.comments)}">
There are no comments to display
</div>
</div>
</div>
The problem is that the name of Class - Comment - and the field - comment - are the same, regarding to insensitive way, causing problem due to Java Reflection use to read the field and its class.
The solution was to rename the field, like "comment" to "commentary", and to avoid to change again in database, if there is some, just put the annotation #Column(name="comment") above the field.
Perhaps, reference from main template on thymeleaf template in question look like:
th:href="#{/post/{${post.getId()}}",
but it should look like:
th:href="#{/post/{postId}(postId=${post.getId()})}"
In my occasion, it helped me

ASP.NET WEB API Not able to retrieve Post Parameters

I trying to retrieve post parameters in web API but I do get null values everytime.
My html
<form method="POST" action="http://localhost:16192/update" name="myform">
<input name="title" type="text"/>
<input name="isbn" type="text"/>
<input name="author" type="text"/>
<input type="submit" value="Submit"/>
</form>
And My WebAPI
[HttpPost]
[Route("UPDATE/")]
public String updateRecord([FromBody]String title,String isbn="", String author="")
{
return "Updated";
}
The updateRecord method is being called but I always get null values. Any help would be greatly appreciated.
[HttpPost]
[Route("UPDATE/")]
public String updateRecord([FromBody]dynamic values)
{
var title = values.title.Value;
....
return "Updated";
}
or you can create a DTO object (paragraph 2):
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/

Resources