How to pass value from drop-down list - spring-mvc

I'm working with the Spring MVC + Thymeleaf project, and I have a problem with passing the field value to object.
There are the malt and country entities. In malt form, there is a drop-down list, that are populated from DB - only country names - nothig fancy. I'm able to populate the list, but when I'm clicking "submit" button, there are some errors. Code below (only relevant parts):
Malt entity:
#Setter
#Getter
#NoArgsConstructor
#Entity
#ToString
#Table(name="malt")
public class Malt extends BaseEntity {
#Column(name="malt_name")
private String maltName;
#ManyToOne(fetch=FetchType.EAGER,
cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#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.EAGER,
cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="country_id")
private Country country;
#ManyToMany(mappedBy="malts")
private Set<Batch> batches;
Malt controller:
#Controller
#RequestMapping("/malt")
public class MaltController {
#ModelAttribute("countries")
public Collection<Country> populateCountries() {
return countryService.findAll();
}
#RequestMapping("{id}/update")
public String updateMalt(#PathVariable String id, Model model) {
model.addAttribute("malt", maltService.findById(Long.valueOf(id)));
return "malt-form";
}
#PostMapping
public String saveOrUpdate(#ModelAttribute Malt malt) {
Malt savedMalt = maltService.save(malt);
return "redirect:/malt/" + savedMalt.getId() + "/malt-show";
}
Malt form:
<div class="form-field-input">
<select class="form-control" th:field="*{id}">
<option value="0">Select country</option>
<option
th:each="country : ${countries}"
th:value="${country.id}"
th:text="${country?.countryName}">
</option>
</select>
</div>
<div class="form-field-submit">
<button class="submit-button" type="submit">Submit</button>
</div>
Malt show template:
<div class="wrapper">
<div class="main">
<div class="page-title">
<p th:text="${malt.maltName}">Malt name</p>
</div>
<div class="show">
<div class="form-row">
<div class="form-field-name">
<label>Producer:</label>
</div>
<div class="form-field-input">
<p th:text="${malt.producer.producerName}">Producer name</p>
</div>
</div>
<div class="form-row">
<div class="form-field-name">
<label>Country:</label>
</div>
<div class="form-field-input">
<p th:text="${malt.country.countryName}">Country</p>
</div>
</div>
<div class="form-row">
<div class="form-field-name">
<label>Malt filling:</label>
</div>
<div class="form-field-input">
<p th:text="${malt.maltFilling}">Malt filling</p>
</div>
</div>
<div class="form-row">
<div class="form-field-name">
<label>Malt usage:</label>
</div>
<div class="form-field-input">
<p th:text="${malt.maltUsage}">Malt usage</p>
</div>
</div>
<div class="form-row">
<div class="form-field-name">
<label>Malt EBC:</label>
</div>
<div class="form-field-input">
<p th:text="${malt.maltEbc}">Malt EBC</p>
</div>
</div>
</div>
</div>
</div>
End error that I'm getting:
An error happened during template parsing (template: "class path resource [templates/malt-show.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/malt-show.html]")
.
.
.
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "malt.country.countryName" (template: "malt-show" - line 44, col 11)
at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
... 52 more
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "malt.country.countryName" (template: "malt-show" - line 44, col 11)
at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:290)
.
.
.
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'countryName' cannot be found on null
Link to repo: https://github.com/fangirsan/maruszka-new/tree/malt-form-problem
I've tried a lot of different approaches, but with no result.

As the exception said, problem is ${malt.country.countryName} inside your malt-show form. In the last line of exception stacktrace, I see Property or field 'countryName' cannot be found on null. This means that you are trying to get a property of related model that is null. Probably, the column country_id in your malt table is null. In the other words, country_id not saved with the other fields. With these assumptions, you will find the problem in the malt-form form where you are saving malts. I inspected this form, probably the problem is <select class="form-control" th:field="*{id}">. I think this must be changed to <select class="form-control" th:field="*{country.id}">.
Important Note:
Some of related models can be null, for example assume country in your malt model can be null(it depends on application business logic). In case of nullable relations, accessing the model relation fields in ${model.relation.field} pattern may produce above error. So you should use null checking inside your thymeleaf template in those cases.

Related

Send list object from thymeleaf to controller

I have a big problem about saving an Object list from Thymeleaf to the controller. Object List in thymeleaf is generated by Jquery. but I don't know how to get the data to Controller, that Object list doesn't know the size. Because users can add it anytime.
Please help me to send a list object in thymeleaf to controller.
I’ve Created a new class with 1 properties: ArrayList loaiDoans;
"LoaiDoan" is a Object that i want to save.
And using that class is an object to Save List "LoaiDoan" from thymeleaf to controller.
But List don't know the size first.because that genarated in thymeleaf. The first time i load the Model, the Model contain List is empty,so that list is not display in screen.
This is my class
public class ListLoaiDoan {
private ArrayList<LoaiDoan> loaiDoans;
//Getter Setter
}
My controller bind list object from controller to thymeleaf
#RequestMapping("/luunhieuobject")
public String LoadNhieuObjectCungLuc(Model model) {
ListLoaiDoan listLoaiDoanAAA = new ListLoaiDoan();
model.addAttribute("listLoaiDoan111",listLoaiDoanAAA);
return "/MHtrangchu/LuuNhieuObjCungLuc";
}
//This is the method save list Object from thymeleaf to controller
#PostMapping("/luunhieuobject")
public String processQuery(#ModelAttribute("listLoaiDoan111") ListLoaiDoan listLoaiDoan) {
System.out.println(listLoaiDoan.getLoaiDoans() != null ? listLoaiDoan.getLoaiDoans().size() : "List Empty");
System.out.println("--");
return "/MHtrangchu/LuuNhieuObjCungLuc";
}
LuuNhieuObjCungLuc.html
<form th:object="${listLoaiDoan111}" method="post" th:action="#{/luunhieuobject}">
<!--INPUT FIELDS-->
<div class="row">
<div class="col">
<div id="movieList">
<div class="row">
<div style="margin-left:100px;" class="col-4 form-group">tenloaidoan</div>
<div style="margin-left:100px;" class="col-4 form-group">madoan</div>
</div>
<div class="row item" th:each="row, stat : ${listLoaiDoan111.loaiDoans}">
<div class="col-lg-6 form-group">
<input th:field="*{loaiDoans[__${stat.index}__].tenloaidoan}" type="text" class="form-control"/>
</div>
<div class="col-lg-6 form-group">
<input th:field="*{loaiDoans[__${stat.index}__].madoan}" type="text" class="form-control"/>
</div>
</div>
</div>
</div>
</div>
<!--ADD NEW ROW BUTTON-->
<div class="row">
<div class="col">
<button type="button" class="btn btn-success" onclick="addRow()">Add row</button>
</div>
</div>
<!--SUBMIT FORM BUTTON-->
<div class="row text-right">
<div class="col">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
That not display annything in the screen, i know that because "listLoaiDoanAAA" is the empty and "th:each" in thymeleaf have nothing to show, how to generate "input" tag and save to controller help me !
I solved this problem !
Set a size for ArrayList before bind it to thymeleaf !
I save my day. thanks Stackoverflow.
i fix my controller like this
#GetMapping("/luunhieuobject")
public String LoadNhieuObjectCungLuc(Model model) {
ListLoaiDoan listLoaiDoanAAA = new ListLoaiDoan();
ArrayList<LoaiDoan> LDD = new ArrayList<LoaiDoan>(10);
listLoaiDoanAAA.setLoaiDoans(LDD);
model.addAttribute("listLoaiDoan111", listLoaiDoanAAA);
return "/MHtrangchu/LuuNhieuObjCungLuc";
}

How to ignore specific fields in form in thymeleaf?

I'm creating a form in thymeleaf that contains a file upload field that's not part of my model.
When I load the page, thymeleaf complains and throws NotReadablePropertyException for that field.
How can I get thymeleaf to ignore the fact that the field does not exist on the model?
Code:
<div class="form-group"
th:classappend="${#fields.hasErrors('uploadFile')}? 'has-error'">
<label class="col-sm-12 control-label" style="text-align: left; margin-bottom: 7px;">Upload Photo</label>
<div class="col-sm-12" style="margin-bottom:5px;">
<div class="fileinput fileinput-new input-group" data-provides="fileinput">
<div class="form-control" data-trigger="fileinput">
<i class="glyphicon glyphicon-file fileinput-exists"></i>
<span class="fileinput-filename"></span>
</div>
<span class="input-group-addon btn btn-default btn-file">
<span class="fileinput-new">Select file</span>
<span class="fileinput-exists">Change</span>
<input type="file" name="uploadFile" accept="image/*" required>
</span>
Remove
</div>
<p id="error" style="position:absolute;color:#FF0000;margin-top:-7px"></p>
</div>
</div>
Error:
org.springframework.beans.NotReadablePropertyException: Invalid property 'uploadFile' of bean class [bean.Library]: Bean property 'uploadFile' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
the error that you get is because of the line
th:classappend="${#fields.hasErrors('uploadFile')}
that expects a field expression as a parameter.
You can replace
th:classappend="${#fields.hasErrors('uploadFile')}
with
th:classappend="${#fields.hasErrors('*')}
where the has-error class will appear if there is an error with any of the fields.
Or you can even replace it with
th:classappend="${#fields.hasErrors('global')}
that is not associated with any specific field in the form.
Or alternatively, you can add the field (uploadFile) in the model as a transient attribute.

Thymeleaf Set Binding for Neo4j insert

I have been working on an Spring MVC application with a Thymeleaf UI. For Neo4j handling entities, I use Set that contains NeoImages that Belong to the class posts:
#Data
#NodeEntity
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class NeoPost {
#Id
#GeneratedValue
Long postId;
#NotNull
#NotBlank
#Size(min = 1, max = 600)
String question;
/**
* Images that are involved in that post
*/
#NotEmpty
#Size(min = 2)
#Relationship(type = "STARES", direction = Relationship.OUTGOING)
Set<Neoimage> neoimageSet = new HashSet<>();
/**
* User that made this post
*/
#Relationship(type = "OWNS", direction = Relationship.INCOMING)
NeoUser user;
/**
* Users that somehow in a way possible described in Userinteractiontype enum
* with this current post.
*/
#Relationship(type = "INTERACTED_WITH", direction = Relationship.INCOMING)
Set<NeoInteraction> incomingInteractions = new HashSet<>();
}
Here the class of the NeoImage type:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
#NodeEntity
public class Neoimage {
#Id
#GeneratedValue
Long imageId;
//For testing purposes use this type of information
#ImageUrlValidator
String imageFull;
}
Do not get me wrong I know that I want to use a Set to store the Neoimages in the Neopost class. The Problem is not the persistence or anything, but I want to delivery input results from a thymeleaf form.
<form autocomplete="off" action="#" th:action="#{/postQuestion}"
th:object="${neoPost}" method="post" role="form">
<div class="form-group">
<div class="col-sm-12">
<label th:if="${#fields.hasErrors('question')}" th:errors="*{question}"
class="validation-message"></label>
<input type="text" th:field="*{question}" placeholder="Question"
class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<input type="text" th:field="*{neoimageSet[0].imageFull}"
placeholder="Image 1" class="form-control" /> <label
th:if="${#fields.hasErrors('neoimageSet[0].imageFull')}" th:errors="*{neoimageSet[0].imageFull}"
class="validation-message"></label>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<input type="text" th:field="*{neoimageSet[1].imageFull}"
placeholder="Image 1" class="form-control" /> <label
th:if="${#fields.hasErrors('neoimageSet[1].imageFull')}" th:errors="*{neoimageSet[1].imageFull}"
class="validation-message"></label>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="submit" class="btn btn-outline-secondary my-2 my-lg-0 loginButton">Upload Post</button>
</div>
</div>
<span th:utext="${successMessage}"></span>
</form>
When I then access the post request, the question is filled in the model as expected, but the neoimageset does not hold the two strings from the two input fields. I have heard that databinding is somehow not possible to a set with thymeleaf. I can fully understand if you need any further elaborations, thank you for helping.
I have solved this issue using a wrapper method within:
/**
* Wrapper method since set elements cannot be accessed by index which is needed in thymeleaf
*/
#Transient
AutoPopulatingList<Neoimage> neoImageWrapperList = new AutoPopulatingList(Neoimage.class);
public AutoPopulatingList<Neoimage> getNeoImageWrapperList() {
//Copy content to neoimageset
this.neoImageWrapperList.forEach(neoimage -> neoimageSet.add(neoimage));
return this.neoImageWrapperList;
}
Now I just call the neoImageWrapperList instead of the actual set.

400 error due a SPRING conversion fail

i'm trying to convert an id to an object using the SPRING Converter interface but i'm having i 400 bad request error when i post the form i have a code that looks like this
this is the converter
#Component
public class LocationConverter implements Converter<Object, Location> {
#Autowired
LocationService locationService;
#Override
public Location convert(Object element) {
Integer id = Integer.parseInt((String)element);
Location l = locationService.getById(id);
return l;
}
}
i'm using annotation based configuration so i'm registering it like this
#Override
public void addFormatters(FormatterRegistry registry){
registry.addConverter(new CategoryConverter());
registry.addConverter(new LocationConverter());
registry.addConverter(new SupplierConverter());
}
the controller
#RequestMapping(value="/create", method=RequestMethod.POST)
public String createOrUpdateArticle(#ModelAttribute Article article,String action,MultipartFile file)
throws IOException{
if(action.equals("create")){
if( !file.isEmpty() ) {
article.setImage(file.getBytes());
}
articleService.create(article);
}
if(action.equals("update")){
articleService.update(article);
}
return "redirect:/article/";
}
and in the view
<f:form modelAttribute="article" enctype="multipart/form-data" method="post"
action="${pageContext.request.contextPath}/article/create">
<input type="hidden" name="action" value="${action}"/>
<f:input type="hidden" path="id"/>
<div class="col-md-6 form-group">
<label>CodeBarre</label>
<f:input path="code" class="form-control"/>
</div>
<div class="col-md-6 form-group">
<label>Description</label>
<f:input path="description" class="form-control"/>
</div>
<div class="col-md-6 form-group">
<label>Prix Grossier</label>
<f:input path="price1" class="form-control"/>
</div>
<div class="col-md-6 form-group">
<label>Prix Semi Grossier</label>
<f:input path="price2" class="form-control"/>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Categorie</label>
<f:select cssClass="form-control" path="category" items="${categories}"
itemValue="id" itemLabel="name"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Location</label>
<f:select cssClass="form-control" path="location" items="${locations}"
itemValue="id" itemLabel="name"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Fournisseur</label>
<f:select cssClass="form-control" path="suppliers" items="${suppliers}"
multiple="true" itemValue="id" itemLabel="firstName"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Photo</label>
<input type="file" name="file" class="form-control" />
</div>
</div>
<button type="submit" class="btn btn-sm-default pull-right">Ajouter</button>
</f:form>
the error message
Field error in object 'article' on field 'category': rejected value [3]; codes [typeMismatch.article.category,typeMismatch.category,typeMismatch.ma.stock.app.entities.Category,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [article.category,category]; arguments []; default message [category]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'ma.stock.app.entities.Category' for property 'category'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type #javax.persistence.ManyToOne ma.stock.app.entities.Category for value '3'; nested exception is java.lang.NullPointerException]
Field error in object 'article' on field 'location': rejected value [2]; codes [typeMismatch.article.location,typeMismatch.location,typeMismatch.ma.stock.app.entities.Location,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [article.location,location]; arguments []; default message [location]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'ma.stock.app.entities.Location' for property 'location'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type #javax.persistence.OneToOne ma.stock.app.entities.Location for value '2'; nested exception is java.lang.NullPointerException]
Field error in object 'article' on field 'suppliers': rejected value [4]; codes [typeMismatch.article.suppliers,typeMismatch.suppliers,typeMismatch.java.util.Set,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [article.suppliers,suppliers]; arguments []; default message [suppliers]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Set' for property 'suppliers'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type #javax.persistence.ManyToMany #javax.persistence.JoinTable ma.stock.app.entities.Supplier for value '4'; nested exception is java.lang.NullPointerException]
i don't know what i'm missing here,
thanks in advance.

Spring MCV 3 showErrors doesn't display anything

I try to validate a simple form. The validation is well executed but the result page doesn't display the errors.
I use velocity to render the page.
I've used as example the PetClinic project from spring website.
Here is my controller when I hit the "post form" button:
#Controller
#RequestMapping("/subscription")
public class SubscriptionController {
#RequestMapping(value = "/newCustomer", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute Customer customer, BindingResult result, SessionStatus status) {
new CustomerValidator().validate(customer, result);
if (result.hasErrors()) {
return "subscription";
}
else {
status.setComplete();
return "redirect:/admin";
}
}
}
When I go in debug, I see the errors. I'm successfully redirected on the subscription page but the errors are not displayed.
My webpage (simplified):
...
#springBind("customer")
#springShowErrors("<br/>" "")
<form class="form-horizontal" method="post" action="#springUrl("/subscription/newCustomer/")">
....
<!-- Button -->
<div class="controls">
<button class="btn btn-primary">#springMessage("website.subscription.signup")</button>
</div>
</form>
...
if you need anything else, don't hesitate to tell me. Thanks for your help! I'm stuck on this since several days.
EDIT :
I finally found the error. It was with the springBind tag. I didn't well understand that you need to bind the field to show the associated error. Here is the fixed code for one field for twitter bootstrap framework.
#springBind("customer.name")
<div class="control-group #if(${status.error})error#end">
<!-- Prepended text-->
<label class="control-label">#springMessage("website.subscription.name")</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on"><i class="icon-user"></i></span>
<input class="input-xlarge"
placeholder="John Doe" id="name" name="name" type="text">
</div>
<p class="help-block">
#springShowErrors("<br/>" "")
</p>
</div>
</div>
springShowErrors(...) will show all the errors associated with the field name of the POJO customer.

Resources