Striving to understand Spring MVC model and specific controller method - java

I have encountered the following code in a Spring-based Java application.
This is the controller:
#RequestMapping(value="/plants/form")
public String form(Model model){
model.addAttribute("plant", new Plant());
return "plants/create";
}
I have several questions regarding this snippet.
I know that form method is called for requests to /plants/form,
but I would like to know which method invokes form and passes arguments to it?
To me, it seems that a model is like a database. So, it looks like
model.addAttribute("plant", new Plant()); creates a Plant
instance and makes it accessible in a model under the name plant.
Thus, an attribute of a model seems to be
something like a field in DB. What is the exact meaning of
an attribute of a model?
What is value in #RequestMapping(value="/plants/form") and what
is the difference between aforementioned notation and
#RequestMapping("/plants/form")?
Consider this incomplete snippet of HTML:
<form class="form-horizontal" role="form" th:object="${plant}" th:method="post" th:action="#{/plants}">
<div class="form-group">
<label for="name" class="col-sm-2 control-label">Name</label>
<input type="text" th:field="*{name}"></input>
</div>
</form>
What is the meaning of $, *, # in "${plant}","*{name}" and
"#{/plants}". It is not clear to me.

1) If I understand your question correctly, then Spring MVC DispatcherServlet is responsible for routing request to your handler method based on configured HandlerMapping/HandlerAdapter.
2) Model is M in MVC, it has nothing in common with relational database. Rather consider it as a Map of String keys to Object values. Spring MVC model attributes are stored in request scope under the hood.
3) There is no difference, value is the attribute of #RequestMapping annotation. When you want to pass a single value argument and no other arguments, then it is possible to omit value by convention.
4) These are tags and attributes of Thymeleaf Standard and SpringStandard dialects. More information here.

Related

Thymeleaf template and Spring Boot : Creating a radio input from Java enum

I would like to populate a radio input control in a Thymeleaf automatically from a Java enum type called "source". I am using Spring Boot in the backend.
My controller initialises a list of the enum values like this:
this.sourcesAsList = Arrays.asList(Source.values());
model.addAttribute("sources", sourcesAsList);
This works fine, at least as far as the List is concerned (as I can see in the log output).
The Thymeleaf template then tries to instantiate a radio control based on this attribute in the model, like so:
<div th:each="source : ${sources}">
<input type="radio" th:field="*{source}" th:value="${source.value}"/><label th:text="| ${source.value} |">Should not see this !</label>
</div>
However, when I try to load this page, I get the following error:
[Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/feedbackapp2.html]")] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'source' available as request attribute
The enum is pretty simple:
public enum Source {
VALONE, VALTWO, VALTHREE, VALFOUR;
public String getName() {
return this.name();
}
}
It's the first time I work with Thymeleaf, so I guess it is just a simple syntax problem somewhere, but even after googling I couldn't find a working example. Any ideas? Is it possible at all to do this with an enum? If not, what kind of datatype would be more appropriate?
Thanks a lot.
Cheers,
Martin
I played around a bit more and got it working. The following HTML snippet displays the radio buttons correctly based the list of Enums and also is wired to the model correctly, in that I received the selected value in the controller's POST method:
<div th:each="source : ${sources}">
<input name="source" type="radio" th:value="${source}"/><label for="source" th:text="| ${source} |">Something is wrong !</label>
</div>
There were two problems:
it is not necessary to access the name() attribute of the enum (so,
using ${source} instead of ${source.name} is fine)
instead of th:field, use the namne attribute of the input control
Thanks a lot to Periklis for the comment.

Bind list or array to form in Thymleaf

On my website I have a few checkboxes each of them contains id in value attribute. After submitting form I'd like to have a list containing ids of checked checkboxes to be passed to the controller. That's how I want to make new page comparing n products.
Controller can accept List<Long> or long[]. That's what I have for now:
HTML:
<form th:action="#{/comparison}" th:object="${productsComparison}" target="_blank" method="post">
<table>
<tr data-th-each="item, iter : ${items.item}">
<td>
<input type="checkbox" th:name="|productsComparison.ids[${iter.index}]|" th:value="${item.id}"/>
</td>
</tr>
</table>
I've added to my controller List<Long> wrapped in ProductComparison with appropriate getters and setters. After submitting form list is always null.
Controller:
#RequestMapping("/productsPage")
public String showProducts(Model model) {
ProductsComparison productsComparison = new ProductsComparison();
model.addAttribute("productsComparison", productsComparison);
}
#RequestMapping("/comparison")
public String compareProducts(#ModelAttribute ProductsComparison productsComparison) {
System.out.println("List: " + productComparison.getIds());
// Always shows null
return "comparison";
}
public class ProductsComparison {
private List<Long> ids;
// Getters & setters
}
The underscores __ are ThymeLeaf's syntax for a preprocessing expression. Or in layman's terms, the stuff inside of the underscores are processed before the rest of the expression.
This is important because your expression is using an array, and the ${iter.index} portion of it gives the array index.
If you are curious or if anyone else stumbles upon this. th:name is no different than the html attribute name. The ThymeLeaf engine will just shove a name attribute into the html entity or overwrite the old one. ThymeLeaf has a ton of properties like this. th:field is a totally different beast though. It is what tells ThymeLeaf to bind the input of your form to the back forming bean or your model attribute, th:object="${productsComparison}". So with the expression th:field="*{ids[__${iter.index}__]}", Thymeleaf is going to call productsComparison.getIds[0] and the corresponding setter.
Note about th:name if you are submitting with Javascript of any kind, you probably want to use this. Most likely you are serializing your form, and the JSON created by this method call will use the name property.
If you want to know more about ThymeLeaf preprocessing expressions, there is a bit in the documentation about it.
I've finally found solution. Instead of th:name I had to use th:field.
th:field="*{ids[__${iter.index}__]}"
Because it's field I didn't have to specify object productsComparison before ids. ids field is List<String>.
To be honest I have no idea what underscores do, I will be glad if someone could explain.

How do I get data from the form inputs in a Freemarker template into a spring MVC controller?

I am trying to receive simple text input from a form generated by a freemarker template. The correct method is being invoked, but the model is empty, where I am expecting it to be populated.
I am working in the context of a pre-existing application, or I would have tossed this combination aside long ago and gone to a straight Spring MVC/JSP implementation.
I have been through every tutorial on the web, and many postings on this very topic in stackoverflow, and I am still failing to grasp something. Whatever I am missing may be so elementary that no one bothers to post it.
The examples from within the application are not very helpful because they are constructed with sufficient indirection that I can't see how they work at all.
Freemarker template
<#import "/spring.ftl" as spring />
<#spring.bind "model"/>
<form name="model" method="post">
<input type="text" name="user" id="user" value="${model.user}"/>
<input type="hidden" id="submission" name="submission"/>
<input type="submit" name="test" id="test" value="Test" onclick="document.getElementById('submission').value = 'test'"/>
</form>
Controller code:
#RequestMapping(value = "/config", method = RequestMethod.POST)
public ModelMap postConfig(#ModelAttribute("model") ModelMap model) {
logger.debug("User name was {}", model.get("user")); // Why is model empty here?
return model;
}
In the end, I was unable to use the ModelMap as an input. What I believe is the reason is that, while Spring has a million ways to accomplish any given task, the freemarker integration is not as robust as the jsp integration.
There is a little known but widely used feature of spring that resolves form elements to method parameters by name. Based on other people's comments I don't believe it is well documented. But, since it is core to the Groovy on Grails framework, I believe it is safe to consider it to be core to Spring.
The solution I finally used is this:
#RequestMapping(value = "/config", method = RequestMethod.POST)
public ModelMap postConfig(String user) {
logger.debug("User name was {}", user); // Why is model empty here?
// ... snip ...
model.put("user", user);
return model;
}
Spring resolves form entities to the controller method parameters by type and name. It's a bit inconvenient for my intention, but it gets the data where I can work with it.

How do I use BeanUtils.populate to populate a bean from a form which has a checkbox

I am guessing I may be missing something simple here but I have done a lot of searching and have not found the answer...
Summary
How can I use the ready made libraries which support JavaBeans to help me take a checkbox from an HTML form and use it to populate a boolean field in a JavaBean which will then be used to update a database table? If this isn't going to work what approach would be best? Ultimately I am trying to avoid writing field specific code in what is, in all other respects, generic code.
Description
I have a jsp file (addScreen.jsp) which displays a form.
I want to use the data which is entered into the form to populate a JavaBean (type Screens).
I will then use the JavaBean (via Hibernate) to update a record in a database table.
The database table (Screens) contains a column, enabledFlag which has a boolean type.
The way I have represented this in the html form is as a checkbox.
In my controller code, when I process the form I want to use the elegant and generic functionality provided to support JavaBeans.
So I am trying to use the BeanUtils.populate() method to take data from the HTML form and use it to populate the screen field of type Screens.
This approach works very well for most of the fields in screen and converts the data from the form into the right type and stores it within the screen JavaBean.
But (and here is the problem) it doesn't process the checkbox and create a true or false value to go into the Boolean field within the Screens JavaBean. In fact it always leaves that field populated with false. I'm guessing it doesn't do anything and it defaults to false.
I think I can see why this doesn't work exactly as I have done it (but feel free to correct me). The way the checkbox state is recorded in the HttpServletRequest parameters probably isn't going to reflect what BeanUtils.populate() is expecting so it can't do anything useful. But I am not sure what it is expecting so I don't know how to manipulate the input so that populate() gives the right answer (not sure if that is very clear).
So given the above, my questions are:
Is there anything fundamentally wrong with what I am trying to do - i.e. use BeanUtils or other general purpose JavaBean library to populate a JavaBean from an HTML form which includes a checkbox? If so please let me know a better way of achieving my goal.
Assuming that there isn't a fundamental problem with what I am trying to do, am I using the wrong method or approach or should I be manipulating the data in some way before calling populate() so that it interprets the checkbox correctly?
Any other tips about how to go about this?
I have tried to include some relevant code below without swamping readers with irrelevant detail, but feel free to ask to see more if it would help. All feedback welcome.
Code Snippets
Form from addScreen.jsp which includes the enabledFlag checkbox
<form id="editScreen" method="post" action='Controller.do'>
<table>
<col class='label' />
<tr>
<td>
<label>Screen Name:</label>
</td>
<td>
<input type='text' name='name'>
</td>
</tr>
<tr>
<td>
<label>Enabled?:</label>
</td>
<td>
<input type="checkbox" name="enabledFlag" value="Enabled" checked>
</td>
</tr>
<tr><td>
<input type='submit' name='addButton' value='Add'>
</td></tr>
</table>
</form>
Declaration of enabled flag within Screens JavaBean
private boolean enabledFlag;
#Column(name = "EnabledFlag", nullable = true, insertable = true, updatable = true, length = 0, precision = 0)
#Basic
public boolean isEnabledFlag() {
return enabledFlag;
}
public void setEnabledFlag(boolean enabledFlag) {
this.enabledFlag = enabledFlag;
}
Call to BeanUtils.populate()
BeanUtils.populate(data, request.getParameterMap());
data is of type Screens and request is the HttpServletRequest containing the form data.
Just a guess - the value of your flag is "Enabled", try something like "true", "on" or "1".
Or do you have mapping code that maps "Enabled" to true?
Also, there seems to be a problem when the checkbox is unchecked, as in that case, nothing is sent to the server - there seems to be a common solution in using Javascript to set a hidden field to a value "on" or "off" and considering the hidden field only, and ignoring the checkbox itself.
See here for further reference: http://books.google.de/books?id=KNjkjBDEKssC&lpg=PT107&ots=9wZRkd_Y48&pg=PT107#v=onepage&q&f=false

Separately managing obiects from lists in spring 3 mvc + jsp web application

After thinking a lot, this is the best question title I could find. Not that representative, I'm sorry :-(.
By the way let me explain the problem.
I have to implement an administration panel. It displays a table where every line contains a user with his roles and account status. You can see the idea sketched in the following image
now, my problem is about managing the backing model object:
I have to pass a list of users to populate the administration panel but
I have to bind each user to a model object and
I have to submit each user separately.
I found many tips for managing the table as a whole so that a single button submits all the users at the same time, but what I want to do is populating the table with a list and managing every list record (the user) separately.
I thought to manage each line with javascript to keep trace of the modified values and to use them to build a uri like http://authority/app/user_management/{user_id}/{is_locked}/{is_admin}/.../. The uri will be triggered by the corresponding submit button, but I prefer to avoid this approach.
Moreover, to populate the table with the correct checkbox value it is necessary to have a binding enstablished.
Any advice?
Thanks!
I think you can use separate form for each row with a User object as a Model. You will have simple 'updateUser' handler with User object in input parameters list.
As I remember it could be a User you are iterating through in your foreach, but I need to check it with the real example..) At least you can use simple form and pass 'id', 'isLocked', etc. as request parameters.
<!-- Inside foreach loop -->
<form action="/updateUser" method="POST">
<input type="hidden" name="id" value="${user.id}"/>
<tr>
<td><input type="checkbox" name="isAdmin" value="Is Admin" checked="${user.isAdmin}"/></td>
<td><input type="submit" value="Submit"/></td>
</tr>
</form>
And something similar on server side:
#RequestMapping(value = "/updateUser", method = RequestMethod.POST)
#ResponseBody
public ResponseDTO updateUser(#RequestParam String id, #RequstParam boolean isAdmin) {
// your update logic
}

Categories

Resources