Spring MVC: How to retrieve data apart from the Model submitted to a view - spring

I have the following requirement. I submit a Model object to a view as follows...
#RequestMapping(value ="/addItem", method = RequestMethod.GET)
public ModelAndView showContacts() {
ModelAndView modelAndView = new ModelAndView("addItem", "command", new Item());
return modelAndView;
}
But on post, I need to retrieve a value apart from the "Item" object (model) that is returned to me. I can't have this variable be a part of the Item model object because it does not belong there. But I need it returned in order to act on that value. How can I get about doing this ?
I.e. In my JSP file, I have the following fields...
<form:input type="text" path="val1"/>
<form:input type="text" path="val2"/>
<form:input type="text" path="val3"/>
Out of the above, only fields val1 and val2 have mappings to the Item object, where as val3 does not. Nevertheless, I need the value of val3 passed back to my controller as well. The code I have right now to handle the POST is as follows, but I can't figure out how to get the value for val3. The code does not compile right now because it says that there is no field or appropriate getter method in the Item class for val3.
#RequestMapping(value = "/postItem", method = RequestMethod.POST)
public String postItem(#ModelAttribute("item") Item item , BindingResult result) {
logger.info("Post Item:");
return "home";
}
How can I modify the above code to suite my requirement ?
Some guidance on this matter will be highly appreciated.

You can pass a map as the model, and include all sorts of different things in there. You are not limited to a single domain object, that constructor is there as a convenience, it is not the only way to do it. There is a variation of the constructor:
public ModelAndView(Object view,
Map model)
Create a new ModelAndView given a View object and a model.
Parameters:
view - View object to render (usually a Servlet MVC View object)
model - Map of model names (Strings) to model objects (Objects).
Model entries may not be null, but the model Map may be null if
there is no model data.

Related

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)
{
...
}

Spring Boot Post Request Method not having all Object values

Using Spring Boot, Hibernate JPA and Thymeleaf.
I have an Order database table which currently only holds 1 record. This record has a few columns and some of the columns are not seen on any forms, they are set upon saving the Order, for instance the creation date.
On the GET request below I select the specific Order and all values are returned into the Order object as expected.
This is my GET Request method:
#RequestMapping(value = "/editorder/{orderId}", method = RequestMethod.GET)
public String editOrderGet(Model model, #PathVariable long orderId)
{
Order order = orderService.findById(orderId);
model.addAttribute("order", order);
return "/editorder";
}
This is a small snippit of my edit order html form using Thymeleaf, binding the Order object to the form using th:object as below:
<form role="form" th:action="#{/editorder}" th:object="${order}" method="post">
<input type="hidden" th:field="*{orderId}"/>
<button type="submit" class="btn btn-primary">Update Order</button>
.
.
</form>
And this is my POST Request method:
#RequestMapping(value = "/editorder", method = RequestMethod.POST)
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult)
{
//rest of code here
}
As you can see, on the GET request I am adding the Order object to the model.
On the html form, I am binding the Order object to the entire form. Then on the POST request I am getting the Order object.
But on the POST it is seen as a new Order and only contains the fields as specified in the form, it does for instance not contain the creation date as seen in the GET request.
My question is this:
Am I missing something or do I explicitly need to go set each of those fields as hidden fields on my form?
In your GET response you may be returning the whole Order object into the Model, but Thymeleaf when trying to build the actual html from template will pick only the items it needs to build the template. So only the fields that are used in the form are used to build the form in your html page.
So when u resubmit the form to the POST service only those fields that are available in the form is reposted.
If u want these fields to be displayed on the page then add these fields in the Form. Thymeleaf picks them and displays in the form. If you dont want them to be shown in the Page then just ignore them. The Order object which u receive in the POST would not have that fields as they were not available in original form.
U can get them by querying the database, any how you do have the order id saved as the Hidden field.
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult){
Order orderFromDB = orderService.findById(order.getId());
// Code to update the orderFromDB from order object
orderService.save(order);
}
This will save the updated fields to the database.
Generally its not a good practice to expose the Entity objects to the API. Try using a DTO/value object. This can have only fields that define your business fields. Also u can use BeanMapper frameworks like dozer/mapstruct/modelmapper to copy from DTO to Entity and vice versa.

Default selected value for <form:radiobutton /> using spring mvc

In spring mvc I am taking the dynamic hobbies values from the database.
I have to select any one the hobby.
I can take the value and rendering in HTML
Map<String, List<?>> map = new HashMap<String, List<?>>();
map.put("hobbiesList", hobbiesList);
return new ModelAndView("register", "map", map);
in html
<form:radiobuttons path="hobby"
items="${map.hobbiesList}" />
If I do that , it is showing the list, but my requirement is when loading these hobbies, the default value has to be in selected mode.
So there's two things, one is the list of available options (hobbiesList) and one is the hobby that is to be selected.
You are setting the path to hobby, but I don't see where there comes from.
You would normally pass in an Entity or Form Backing Object as the structure representing the information filled in with the form. Can you update your code to show that?
In your controller's get handler, create a form object, and set it's hobby to something:
#RequestMethod
ModelAndView getForm() {
ModelAndView mav = new ModelAndView();
MyForm myForm = new MyForm();
myForm.hobby = "some hobby";
mav.addObject("map", myForm);
//add your hobbiesList too here, or in a #ModelAttribute method
mav.setViewName("register");
return mav;
}

How we assingn value to spring form fields from reference data method on loading

I have one spring form name UpdateStock.jsp
<form:form........>
<form:input path="compAmount"/>
............
</form:form>
My intent is initialize the above spring form text box by some default values from database. So I have form text tag to
<form:input path="compAmount" value=${compamount}/>
Here ${compamount} is one of the value returned from referenceData() method.
But the problem is value=${compamount} is invalid.
So I leave it and do the next thing as below:
that is initialize Object command object in reference_Data() method as below. But it is not working.
protected Map reference_Data(HttpServletRequest request, Object command,Errors errors, int page) throws Exception {
UpdateStockBean bean=new UpdateStockBean();//which is correspond to UpdateStock.jsp page
bean.setCompAmount(300);//this do not change the value of corresponding field
command=new Object();
command=(Object)bean;
}
Can you suggest solution!
It sounds like you want to some default values for some text fields of your form. If you are using SimpleFormController you can simply override the method protected Object formBackingObject(HttpServletRequest request) to populate your form with some default data in the fields of your form.
You should not use protected Map referenceData(HttpServletRequest request) method for this purpose. Because referenceData is used for providing list data for checkboxes or radio buttons. This is nicely explained in this nice article of mkyoung.
If you are using AbstractWizardFormController, still you have the methods protected Object formBackingObject(HttpServletRequest request) and protected Map referenceData(HttpServletRequest request). Because AbstractWizardFormController is a subclass of AbstractFormController, which eventually holds the above mentioned methods. Here is a sample demonstration of using AbstractWizardFormController with form baking object.
Cheers!
you can try somethig like this, you are using Map Collection:
Map yourname = new HashMap();
yourname.put("compamount", bean.getCompAmount());
So a here ${compamount} is one of the value returned from reference_Data() method.
I hope help you :)

What, exactly, does a modelbinder do? How to use it effectively?

I was researching something and came across this blog post at buildstarted.com about model binders. It actually works pretty darn well for my purposes but I am not sure exactly whats going on behind the scenes. What I did was create a custom ModelBinder called USerModelBinder:
public class UserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
return user;
}
}
Then in my Global.asax.cs I have:
ModelBinders.Binders.Add(typeof(User), new UserModelBinder());
My understanding is that using the model binder allows me to NOT have to use the following lines in every controller action that involves a "User". So instead of passing in an "id" to the action, the modelbinder intercepts the id, fetches the correct "item"(User in my case) and forwards it to the action for processing.
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
I also tried using an annotation on my User class instead of using the line in Global.asax.cs:
[ModelBinder(typeof(UserModelBinder))]
public partial class User
{
}
I'm not looking for a 30 page white paper but I have no idea how the model binder does what it does. I just want to understand what happens from when a request is made to the time it is served. All this stuff "just working" is not acceptable to me, lol. Also, is there any difference between using the annotation versus adding it in Global.asax.cs? They seem to work the same in my testing but are there any gotchas?
Usually the Model Binder (in MVC) looks at you Action method and sees what it requires (as in, the objects types). It then tries to find the values from the HTTP Request (values in the HTTP Form, QueryString, Json and maybe other places such as cookies etc. using ValueProviders). It then creates a new object with the parameters that it retrieves.
IMO What you've done is not really "model binding". In the sense that you've just read the id and fetched the object from the DB.
example of usual model binding:
// class
public class SomeClass
{
public int PropA {get;set;}
public string PropB {get;set;}
}
// action
public ActionResult AddSomeClass(SomeClass classToBind)
{
// implementation
}
// pseudo html
<form action="">
<input name="PropA" type="text" />
<input name="PropB" type="text" />
</form>
if you post a form that contains the correct values (lets say you post a form with PropA and PropB ) the model binder can identify that you've sent those values in the form and build a SomeClass object.
If you really want to create a real working example you should use a strongly typed View and use HtmlHelper's EditorFor (or EditorForModel) to create all the correct names that MVC needs.
--
for reference MVC's default binder is the DefaultModelBinder, and some (there are more, you can look around in the System.Web.Mvc namespace) ValueProviders that it uses by default are the FormValueProvider and the QueryStringValueProvider
So, as I already said, how this basically works is that the default model binder reads the model that the action is recieving (say SomeClass in the example) reads what are the values that it can read (say PropA and PropB) and asks the ValueProviders for the correct values for the properties.
Also, if I recall correctly, you can also see the value providers in runtime using the ValueProviderFactories static class.
A ModelBinder looks at the arguments of the selected Controller action's method signature, then converts the values from the ValueProviders into those arguments.
This happens when the ControllerActionInvoker invokes the action associated with the ControllerContext, because the Controller's Execute() method told it to.
For more about the ASP.NET MVC execution process, see Understanding the MVC Application Execution Process

Resources