I'm working on a Reddit-clone
with spring-mvc
which has a link and comment entity.
The problem is adding a new comment function.
When I try to submit the form, In my controller, the bindingResult
has an error.
The log info showed that there is a Field error in object 'comment
' on-field 'link
'. The error is due to type mismatch
from String to Link.
I couldn't figure out where the String comes from and why the link field is not bound correctly.
A Link and an empty Comment with an association to the link was added to the model with get mapping
for the current view page
I'm new to spring and being struggled for this, I appreciate any help.
Below is the code for controller
and thymeleaf
as well as my link and comment entity
@GetMapping("/link/{id}")
public String read(@PathVariable Long id,Model model) {
Optional<Link> optionalLink = linkRepository.findById(id);
if( optionalLink.isPresent() ) {
Link link = optionalLink.get();
Comment comment = new Comment();
comment.setLink(link);
model.addAttribute("comment",comment);
model.addAttribute("link",link);
}
@PostMapping("/link/comments")
public String addComment(Comment comment, BindingResult bindingResult) {
log.info(comment.toString());
if( bindingResult.hasErrors() ) {
log.info(bindingResult.toString());
log.info("Something went wrong.");
} else {
log.info("New Comment Saved!");
commentRepository.save(comment);
}
return "redirect:/link/" + comment.getLink().getId();
}
<form id="frmAddComment" method="POST" th:action="@{/link/comments}" th:object="${comment}">
<input type="hidden" th:field="*{link}"/>
<div class="form-group">
<textarea class="form-control" id="comment" rows="3" th:field="*{body}"></textarea>
</div>
<button type="submit" class="btn btn-primary">Add Comment</button>
</form>
@Entity
@Getter@Setter
@RequiredArgsConstructor
@NoArgsConstructor
public class Comment extends Auditable{
@Id
@GeneratedValue
private long id;
@NonNull
private String body;
@ManyToOne
@NonNull
private Link link;
}
public class Link extends Auditable{
@Id
@GeneratedValue
private long id;
@NonNull @NotEmpty(message = "Please enter a title")
private String title;
@NonNull @URL(message = "Please enter a valid url")
private String url;
// comments
@OneToMany(mappedBy = "link")
private List<Comment> comments = new ArrayList<>();
}
HTML only works with Strings. The problem is that Spring does not know how to convert from a String to a Link
.
It is easier to create a dedicated form data object and use that in our @GetMapping
and @PostMapping
.
public class AddCommentToLinkFormData {
private long linkId;
@NotNull
@Size(min=1,max=1000) // Set this to the values you want
private String comment;
// add getters and setters
}
In your controller:
public class MyController {
@GetMapping("...") // URL that you want to use
public String linkInfo(Model model) {
model.addAttribute("formData", new AddCommentToLinkFormData());
return "..." // name of your Thymeleaf view
}
@PostMapping("/link/comments")
public String addComments(Model model,
@Valid @ModelAttribute("formData") AddCommentToLinkFormData formData,
BindingResult bindingResult) {
if( bindingResult.hasErrors() ) {
return "..." // name of your Thymeleaf view. Use `#fields.hasErrors('comment')` in your HTML if you want to show a message if the comment String is not valid.
} else {
service.addCommentToLink(formData.getLinkId(), formData.getComment());
return "redirect:/link/" + formData.getLinkId();
}
}
}
The HTML should be:
<form id="frmAddComment" method="POST" th:action="@{/link/comments}" th:object="${formData}">
<input type="hidden" th:field="*{linkId}"/>
<div class="form-group">
<textarea class="form-control" id="comment" rows="3" th:field="*{comment}"></textarea>
</div>
<button type="submit" class="btn btn-primary">Add Comment</button>
</form>