Spring #MVC how to obtain a List of JavaBeans from request params without wrapping the list inside an object - spring

I need to have this controller:
#RequestMapping("/path/to/my/app")
public String process(List<JavaBean> javaBeans){
// Do something with the list
}
At client I need to manually create the list on user actions, like so
<form action="/path/to/my/app">
<input name="javaBeans[0].field1"/>
<input name="javaBeans[0].field2"/>
<input name="javaBeans[0].field3"/>
<br/>
<input name="javaBeans[1].field1"/>
<input name="javaBeans[2].field2"/>
<input name="javaBeans[3].field3"/>
<!-- and so forth -->
</form>
My primary problem is that I receive empty list inside the contoller. I have to wrap it inside another javabean to make the code work. Like following
public class BeanWrapper{
private List<JavaBean> javaBeans;
// Getter and setter
}
and in controller
#RequestMapping("/path/to/my/app")
public String process(BeanWrapper wrapper){
List<JavaBean> list = wrapper.getJavaBeans();
// Do something with the list
}
This version works but I have to unnecessarily wrap my list around an object. Is there a way around?
Thanks in advance.

You can try this:
public String process(#RequestParam("javaBeans[]") List<JavaBean> javaBeans) {
Update:
The syntax using indices (javaBeans[1]) is currently not supported by the Spring default binding. As mentioned in the comments you can get around this limitation by using the #RequestBody annotation.
public String process(#RequestBody List<JavaBean> javaBeans)
This requires a POST request, and either the data to be sent in a different format (e.g. JSON) or implementing your own message converter.

Related

HTML Form send an array of objects using thymeleaf and spring boot

I am trying to built an application where I need to send a list of objects, having a variable length.
[Form view]
(https://i.stack.imgur.com/ynE2T.png)
Is there any way I can capture such fields in the controller? I was trying something in the lines of:
public String setNewTratament(
#RequestParam("medicamente") MedicamentatieStartStop[] input,
Model model) {
// ...
}
and in the post request something like:
<input x-bind:name="`medicamente[${index}][medicament]\`" type="text" class="input input-bordered w-full"\>
(using alpineJS), where the last field will be the properties of the object "MedicamentatieStartStop". The names are copied from an th:inline script, pasting exactly one such array.
The problem was it throws the following error: 'required request parameter 'medicamente' for method parameter type MedicamentatieStartStop[] is not present.
Any ideas how I should approach this situation?
Solved it by adding a th:object on the form with a DTO and instead of [attribute] i changed it to .attribute, everything else stayed the same.

Easy way to submit an array field with Spring MVC forms?

I have a form with a list of nested fieldsets corresponding to a collection of objects, backed by a form object server-side. Fieldsets can be added or removed client-side. I want to submit a form without caring about object indices or sparse lists in command object.
Here's my Controller method code:
#PostMapping("/foobar")
public String doPost(FoobarForm form) {
//...
}
In PHP, Rails etc it's very easy:
<input name="prop[]">
, and it will automatically populate $_POST["prop"] with all the values.
Working with Spring MVC, I tried these things:
<input name="prop[]"> - doesn't work saying Invalid property 'prop[]' of bean class [...]: Invalid index in property path 'prop[]'; nested exception is java.lang.NumberFormatException: For input string: ""
<input name="prop"> - will not bind to a list-typed bean property, even when multiple fields present.
<input name="prop[${i}]"> - implies all that hassle with sparse list and index handling, both client-side and server-side. Certainly not the right way to do things when working with a powerful web framework.
I'm wondering why can't I just use [] in property name and let Spring create a list automatically? It was asked three times on Spring JIRA without any reasonable responses.
Spring form binding makes this more easy. You need to add List object in your bean and bind that in jsp using spring form.
class FoobarForm {
List<String> prop;
}
In jsp, if you need to show/edit value all at once then <form:input path="prop" />
.if you want to show one by one then use indexing<form:input path="prop[0]" />. Use proper CommandName in form. It will work.
I found the answer here.
You can use the MultiValueMap<String, String>
#RequestMapping("foo")
public String bar(#RequestParam MultiValueMap<String, String> parameters){ ... }
With these two inputs:
<input name="prop" value="One">
<input name="prop" value="Two">
the result will be {prop=["One","Two"]}
The code below will work too.
public String bar(#RequestParam("prop") List<String> props){ ... }

passing a model to the next controller from view

I have a form with few inputs, name, email, message and some checkboxes. I've created a model for these inputs and set all the validations i require.
But now I also want to pass my model (i.e. from #model MyModel) or rather some object property of my model together with those inputs.
Is populating a VewBag/viewData with my model a way to go?
#{
ViewBag.MyModel = Model;
// or ViewBag.ThatProperty = Model.ThatProperty
}
or do i still have a better way up my sleeve?
ViewBag and ViewData persist in one trip from server to client, and not the other way around.
There is no way to pass an object from the view to the controller. If it's a database object, you can pass the object Id using one of the two methods described below, then query the DB on post.
If you have no other way, you can encode the object as a JSON string (using the Newtonsoft package, for example) and pass it also using one of the two methods described below, but this isn't the best option.
To pass a property from the View to the Controller, you have two options:
Url Parameter
Hidden field
Url Parameter
<form ... asp-route-ThatProperty="#Model.ThatProperty">
...
</form>
Form Field
<form>
<input type="hidden" name="ThatProperty" value="#Model.ThatProperty" />
</form>
Controller Action
If 'ThatProperty' doesn't exist on your model, receive it as an extra parameter.
public IActionResult MyAction (MyModel model, string ThatProperty)
{
...
}

How to control two operations for same url in Spring mvc?

Consider the following problem.
The user has chosen to create a document by clicking on the Create document and then he writes data into the document. The url for creating the document is /document/save.
For the subsequent write up, the existing document must be saved instead of creating a new one.
Here is my code for that.
#Controller
public MyController implements Controller, TemplateTypeAware
{
#RequestMapping("/document/save")
public String saveOrCreateDocument(#ModelAttribute DocumentWrapper wrapper, ModelAndView m)
{
if(m.getModel().get("document_id")==null)
{
Document doc=createDocument(wrapper);
m.addObject("document_id",doc.getId());
}
else
{
saveDocument(m.getModel().get("document_id"), wrapper);
}
return documentView;
}
}
Template:
<form>
<input type="hidden" name="document_id" th:value="*{document_id}"/>
<!-- other fields -->
</form>
The problem here is, I am getting document_id always null. Is there any work around for this problem?
Thanks in advance. Hope you will reply as soon as possible.
Form fields will be automatically bound to your DocumentWrapper fields if they have matching names, that means DocumentWrapper needs a field named document_id, otherwise the document_id request parameter won't be bound to your object.
Model attributes will be exposed to the view, at this point the model will be empty, you can add attributes in your handler method and they will become in your view, but request parameters won't be in the model. That explains why you always get null.
If you just need the document_id parameter, use #RequestParam:
#RequestMapping("/document/save")
public String saveOrCreateDocument(#RequestParam("document_id") Long documentId, Model m) {
...
}
Please refer to the binding section of Spring MVC: http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestparam

Does a variable with #ModelAttribute get populated from request parameters?

I am interested in the specifics of Spring #ModelAttribute's work on method parameters.
As we know, when a requested attribute is absent from the model, then its instance gets created and populated from the view form.
My question concerns the scenario when the form does not have expected properties but when such properties are available in URL template parameters.
I would like to know if in this case our variable will be populated with the values of those request parameters?
Like here, for instance, will the variable attributeToPopulate get populated with the parameters 1,2,3 from the URL http://localhost:8080/MyApp/parameter1=whatever?parameter2=whatever?parameter3=whatever?:
RequestMapping(method = RequestMethod.GET)
public String fooMethod(#ModelAttribute("attributeName") FooClass attributeToPopulate){
// method implementation
return "view";
}
Neither Spring documentation, reference documentation, nor Q&A sites refer to such situations explicitly. However, one post here on Stack Overflow does mention that variables annotated with ModelAtrribute get populated in this way (answer of the user Xelian):
name="Dmitrij"&countries=Lesoto&sponsor.organization="SilkRoad"&authorizedFunds=&authorizedHours=&
Considering a small number of upvotes for that answer, I am bit skeptical but at the same time curious about whether #ModelAttribute indeed functions in such way.
Any informative input will be greatly appreciated.
You can set default values to fields in the FooClass instead of setting them in RequestMapping annotation. In this case you will not have to copypaste RequestMapping with default values in all the methods where you are working with FooClass as a ModelAttribute.
#tomatefraiche I have tried to do this by
<spring:url value="/hello?name=default" var="userActionUrl" />
<form:form method="get" modelAttribute="user" action="${userActionUrl}">
<form:input path="name" type="text" disabled="true" />
</form:form>
and
#GetMapping("/hello")
public String hello(#ModelAttribute("user") User user, Model model) {
model.addAttribute("name", user.name);
model.addAttribute("user", user);
return "hello";
}
and looks like Spring override values in the url. In the controller there is an empty value. Also URL is hello?name= after form submission. So looks like you can not set default values through url since it will be replaced.

Resources