#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greeting(HttpServletRequest request, Model model) {
String name = request.getParameter("name");
model.addAttribute("name", name);
return "greeting";
}
}
What does the Model do? Is this essentially the context variables that are passed to the "greeting.html" file? Is the only use of it addAttributes that will then be passed to the HTML template? Where could I read more about what Model does and how it should be used?
Did you see the documentation of Model?
Java-5-specific interface that defines a holder for model attributes. Primarily designed for adding attributes to the model. Allows for accessing the overall model as a java.util.Map.
Model is an essential part of MVC pattern which is widely used in Spring. As you have said, a Model is a holder of the context data passed by a Controller to be displayed on a View.
You can use only one Model which contains more data distinct with a unique key because the Model is based on the java.util.Map - as the documentation says..
Well, yes, that's what the Model do. But you can merge two Model objects parameters too, so this makes things easier.
More than it, you use ModelMap and ModelandView objects. If you want to read about it, take a look at: Spring MVC Model Map Model
In this case the model is used to pass data to the view. For example you are using JSP, or some template like thymeleaf, you can put thigns that you want to be displayed in the model, and you will be able to access it from those views.
You could read more about that in the Spring MVC Reference
Related
I have done a few MVC controllers now and used the spring form tags to pass data back and forth but I realise now my actual understanding is a little thin. In my current case I could actually just send the response as url parameters but there are about 15 and I would prefer to send it as a pojo if possible.
My actual question... is ... is it possible to set up a spring style model attribute in a jsp without the attribute having been passed in and without using the form tags ?
So for example something along the lines of
//Pojo
Class personclass
{
private String name + getters and setters
private String address + getters and setters
private String phone + getters and setters
...
}
////first mvc call
#RequestMapping ("/")
Public ModelAndView LandingPage()
{
// no mention of Person pbject
Return mandvobject;
}
//jsp page
//This is the question!
SET ModelAttribute that wasn't passed in to the page
personclass = X
//New MVC call without a submit
window.open ("/NewMVCCall")
//New mvc call
#RequestMapping ("/NewMVCCall")
Public void newMVCPage(#ModelAttribute ("pc") personclass pc, Model model)
{
//process pc object
}
Or am I missing the point and I would have to send it as a json string parameter? Sorry my grasp of this is pretty rudimentary and I'm not sure whether I could quite easily set my own http form content or whether it is because I have used Spring form objects so far that I haven't grasped the complexity of what is going on behind the scenes (i.e form tags converting pojos to json and so on) ?
Many thanks if anyone has the time to set me on the right path...
I am not sure if I am understood your question correctly but you can link a Model to your controller without having to manually pass it to a the view every time you need it, spring will take care of that:
in your Controller :
public class MyController{
#ModelAttribute("pc")
public PersonneClass getPersonnelClass(){
return new PersonneClass();
}
#RequestMapping ("/NewMVCCall")
Public void newMVCPage(#ModelAttribute ("pc") personclass pc, Model model)
{
//process pc object
}
//other methods
}
It is a good practice to stick to java conventions when naming classes so
(personneClass ) must start with an uppercase (PersonneClass) .
I am building an application with simple Servlets and the MVC pattern. I am not using any framework like Spring.
I have some Model classes like this:
public class BlogPost {
private List<Comment> _comments;
// Things
}
and
public class Comment {
// Code
}
Posts can have zero or more comments associated with them in that collection.
However, I want to attach some additional information to the BlogPost Model before it is passed to the View, namely a value I set in a Cookie once a user makes a comment on a BlogPost. Strictly speaking, this is not a part of the BlogPost Model itself -- it is unrelated, incidental information, however I am not sure if I should make it easy on myself and just add it to the BlogPost class or do something to abstract this out a bit more.
So, should I add a field to the BlogPost class to handle this additional value, OR should I make a "View Model" along the lines of this which gets passed to the JSP view:
public class BlogPostView {
public BlogPostView(BlogPost bp, String message) {
// Constructor stuff, save these to instance variables
}
public BlogPost getBlogPost() { /* ... */ }
public String getMessage() { /* ... */ }
}
If BlogPost and your cookie data are unrelated, it is a bad idea to put the cookie data in your BlogPost class. The BlogPost class should represent what it's called - a blog post. It would be confusing to have other data associated.
Your second option of creating a class specifically to pass to the view is a better idea, though I'm curious to know why you need to pass the blog post and the cookie data as one object to your view? If you're using raw servlets:
request.setAttribute("blogPost",blogPost);
request.setAttribute("cookieData",cookieData);
Using a model class (e.g. Spring MVC ModelMap):
model.addAttribute("blogPost",blogPost);
model.addAttribute("cookieData",cookieData);
Your view will have access to both pieces of data, which you can manipulate using JSTL or other tag libraries.
If there's something I'm missing, can you elaborate more?
Create a HashMap model - and pass it along with the response to view.
model.put("blog", blog)
model.put("message", "some message")
I'm working on converting a legacy project to Spring (trying to adjust little as possible for now) and I'm running into a small issue with mapping/translating legacy parameters to a model attribute object. I may be completely wrong in thinking about this problem but it appears to me that to translate a parameter to a specific model attribute setter is to pass in the request parameter through a method for creating a model attribute and manually call the correct setter:
#ModelAttribute("form")
public MyForm createMyForm(#RequestParameter("legacy-param") legacy) {
MyForm myForm = new MyForm();
myForm.setNewParam(legacy);
return myForm;
}
I don't necessarily want to change the request parameter name yet since some javascript and JSPs are depending on it being named that way but is there any way to do something like this? Or is there a different way to map/translate request parameters to model attributes?
public class MyForm {
#ParameterName("legacy-param")
private String newParam;
public void setNewParam(String value) { ... }
public String getNewParam() { ... }
}
#Controller
public class MyController {
#RequestMapping("/a/url")
public String myMethod(#ModelAttribute("form") MyForm myForm, BindingResult result) { ... }
}
The way you've written that model attribute method is indeed odd. I'm not entirely clear what you're actually trying to do.Assuming there are many parameters, you're going to end up with an awful lot of instances of MyForm in your ModelMap. A more 'normal' way to create model attribute would be like this:
#ModelAttribute("legacyParamNotCamel")
public MyForm createMyForm(#RequestParameter("legacy-param-not-camel") String legacy) {
return legacy;
}
Then in the JSP you can refer to it directly in expression language. e.g.,
<c:out value="${legacyParamNotCamel}"/>
If you want to put them onto a form backing object, you need to do it all in a single method that creates the object, not make new copies of it in each method. (assuming your form has more than a single parameter associated with it.)
--
It seems like what you're really trying to do though is translate the parameter names in the request before the web data binder gets ahold of it, so that you can bind oddly named parameters onto a java bean? For that you'll need to use an interceptor that translates the names before the binding process begins, or make your own subclass of the databinder than can take a property name translation map.
You placed the #ModelAttribute at the Method Level but the intention seems to be more of a formBackingObject hence we should be dealing at the Method Parameter Level
There's a difference.
I put up an explanation here on my blog along examples at Spring 3 MVC: Using #ModelAttribute in Your JSPs at http://krams915.blogspot.com/2010/12/spring-3-mvc-using-modelattribute-in.html
My task is - to create a model attribute by given request parameters, to validate it (in same method) and to give it whole to the View.
I was given this example code:
#Controller
class PromotionController {
#RequestMapping("promo")
public String showPromotion(#RequestParam String someRequestParam, Model model) {
//Create the model attribute by request parameters
Promotion promotion = Promotions.get(someRequestParam);
//Add the attribute to the model
model.addAttribute("promotion", promotion);
if (!promotion.validate()) {
BindingResult errors = new BeanPropertyBindingResult(promotion, "promotion");
errors.reject("promotion.invalid");
//TODO: This is the part I don't like
model.put(BindingResult.MODEL_KEY_PREFIX + "promotion", errors);
}
return
}
}
This thing sure works, but that part with creating key with MODEL_KEY_PREFIX and attribute name looks very hackish and not a Spring style to me. Is there a way to make the same thing prettier?
Skaffman answered the question but disappeared, so I will answer it for him.
The binding validation thing is there to bind and validate parameters, not arbitrary business objects.
That means, that if I need to do some custom validation of some general data that was not submitted by the user - I need to add some custom variable to hold that status and not use BindingResult.
This answers all the questions I had with BindingResult, as I thought it had to be used as a container for any kind of errors.
Again, thanks #Skaffman.
Using spring MVC, how would I create a form that doesn't map to a entity (i.e. it has properties from multiple entities).
I want to also validate and use their 'results' object that has the errors collection etc.
Any online examples of this? I need to see it to understand it (newbie)
You'd simply create a new class containing all the properties you need for the form and use this as model attribute for your form. On the receiving call you can then use this type, too. Spring will automatically bind properties for you. You should also consider using JSR-303 validation annotations.
The general approach is to load all the necessary entities to create the form backing object from on the GET request. Then you put that form backing object in the model. On the POST/PUT request you have to reconstruct the actual entities touched. Typically you load them again then and apply the new submitted partial data to them.
In general it might be a good idea to construct a dedicated component to handle that assembling behaviour for you to not pollute the controller with that code.
/**
* Prepares the form by setting up a custom form backing object.
*/
#RequestMapping(value = "account/{id}", method = GET)
public String processGet(#PathVariable("id") Long id, Model model) {
Account account = accountService.getAccount(id);
return prepareForm(dtoAssembler.createFormFor(account), model);
}
/**
* Process form submission. Uses JSR-303 validation and explicit error
* handling.
*/
#RequestMapping(value = "account/{id}", method = PUT)
public String processGet(#ModelAttribute("accountForm") #Valid AccountForm form, Errors errors, Model model) {
if (errors.hasErrors()) {
return prepareForm(form, model);
}
Account account = accountService.getAccount(form.getId());
accountService.save(dtoAssembler.applyData(account, form));
return "redirect:/accounts";
}
/**
* Populates the given {#code Model} with the given {#code AccountForm}
* and returns the view to show the form.
*/
private String prepareForm(AccountForm form, Model model) {
model.addAttribute("accountForm", form);
return "account";
}
I just wrote it this way here to emphasize what's going on. In a real world scenario I'd probably let the DtoAssembler do all the work with the service (so I'd inject the service into the assembler).
To ease transferring data from the DTO into the domain object using Dozer, BeanUtils or something similar is probably reasonable.