#ModelAttribute at method level - java

I am referring the example from here
where the #ModelAttribute is placed at the method level
/**
* Retrieves all addresses and allows them to be used as a model
* #return a model attribute containing addresses
*/
#ModelAttribute("addresses")
public List<Address> getAllAddresses() {
// Delegate to service
return addressService.getAll();
}
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
dateFormat.setLenient(false);
webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
/**
* Handles and retrieves a JSP page containing all addresses.
* We use the #ModelAttribute to pass the data to the view
*
* #return the name of the JSP page
*/
#RequestMapping(value="list1", method = RequestMethod.GET)
public String getAllUsingModelAttribute() {
logger.debug("Received request to show all addresses page");
// No need to add the model here
// It has been automatically added when we used the #ModelAttribute annotation earlier
// The name of the ModelAttribute is "addresses". Your JSP should reference "addresses"
// This will resolve to /WEB-INF/jsp/addressespage.jsp
return "addressespage";
}
My question is, is the method annotated with #ModelAttribute autofired when the request comes to the controller? In this case method getAllAddresses(). As in the example I don't see this method called explicitly anywhere.
Or it is fired when the request comes to the method immediately placed after it containing #RequestMapping?
Same question for #initBinder is the method annotated with it autofired after form submission?

When a page is going to render if the page has a form which has been made by form taglib using Spring like:
<form:form commandName="addresses" method="post">
this lead your page to look for a proper #ModelAndAttribute with name 'addresses'. If your program was able to find that (like the one you have defined)
#ModelAttribute("addresses")
public List<Address> getAllAddresses() {
// Delegate to service
return addressService.getAll();
}
that make a model available for your form with a List ready to change !
then you can use another method for submitting the form like this:
#RequestMapping(value="path", method=RequestMethod.POST)
public String doSomethingForMe(#Valid #ModelAttribute("addresses") List <Address>, BindingResult result) {
if(result.hasErrors()){
return "TO_THE_PAGE_YOU_WANT_FOR_SHOWING_THE_ERROR_TO_USER";
}
// do the other stuffs you want !
}
I just write this code here, so sorry if you found out any typo problem but that's the story !
Good Luck !

Try to debug your controller and put some logging statements.
The first things you culd see is the call to #ModelAttribute annotated method.
Your initBinder will be called if you put a custom object into Model or if your handler method declare a custom Object as parameter.
#ModelAttribute("obj")
public List<Address> getAllAddresses() {
// Delegate to service
return new CustomObject();
}
#RequestMapping(value="list1", method = RequestMethod.GET)
public String getAllUsingModelAttribute(CustomObject customObject) {

Related

Spring : Configure xml to make a controller return a view depending on a parameter

I have a spring MVC based application and I want to add a functionality in which some of my controllers will return the same view depending on the value of a parameter.
#RequestMapping("/someView")
public String returnView(Model model, HttpServletRequest request, String param){
if(param.equals("condition")){
return "commonView";
}
// do stuff
return "methodSpecificView";
}
Is there a way in which the first if condition can be configured in an xml? Since similar functionality needs to implemented in many controllers and I don't want to write boilerplate code an xml configuration can make things simpler.
Furthermore, if the first one is possible, can it be extended to eliminate the parameter param from request mapping method signature and put that in xml too?
You can use #RequestMapping:
#RequestMapping(value = {"/someView", "/anotherView", ...}, params = "name=condition")
public String returnCommonView(){
return "commonView";
}
In Spring 3.2 which is annotation based the below code snippet will give you an idea for your problem:
#RequestMapping("formSubmit.htm")
public String onformSubmit(#ModelAttribute("TestBean") TestBean testBean,BindingResult result, ModelMap model, HttpServletRequest request) {
String _result = null;
if (!result.hasErrors()) {
_result = performAction(request, dataStoreBean);//Method to perform action based on parameters recieved
}
if(testBean.getCondition()){
_result = "commonView";
}else{
_result = "methodSpecificView";
}
return _result;
}
TestBean//Class to hold all the required setters and getters
Explanation:
As the request from your view comes to this method the ModelAttribute reference will hold all the values from view if the condition is obtained from the view than you can directly obtain it from model attribute and return the corresponding view.
If your condition is obtained after applying certain logic than you can set the condition in the testBean and again get it to return the corresponding view.
You should consider implementing this via AOP - Around advice something like below.
#Around("#annotation(RequestMapping)") // modify the condition to include / exclude specific methods
public Object aroundAdvice(ProceedingJoinPoint joinpoint) throws Throwable {
Object args[] = joinpoint.getArgs();
String param = args[2]; // change the index as per convenience
if(param.equals("condition")){
return "commonView";
} else {
return joinpoint.proceed(); // this will execute the annotated method
}
}

How can I populate two beans from my form with Spring MVC?

So I am using Spring MVC 3 with annotations.
I have a simple html form (ExtJS actually) that has three fields.
1) Username
2) Password
3) Color
OK, so username and password belong to a databean called User. color belongs to another bean called Color.
In my UserController, I have:
#RequestMapping(value = "/users/login", method = RequestMethod.POST)
#ResponseBody
public String handleLogin( #ModelAttribute("user") User paUser,
#ModelAttribute("color") Color paColor,
ModelMap map) {
// at this point "paUser" contains both username AND password submitted from form
// however, there is nothing in "paColor"
...
return "user.jsp"
}
What am I doing wrong?
I'm new to Spring, btw.
Thanks
Usually you would create a new class that represents the form (this is known as a form-backing object), such as UserColorForm, that contains properties for each of the inputs in the request body.
Your controller method would then look like:
#RequestMapping(value = "/users/login", method = RequestMethod.POST)
#ResponseBody
public String handleLogin(UserColorForm form, ModelMap map) {
// now you can work with form.getUsername(), form.getColor() etc.
If the FBO bean has property names that match the form input names, Spring will bind the input in the request directly to the properties. i.e. if the form input is username=matt&color=blue then Spring will create a new instance of my form and call setUsername("matt") and setColor("blue").
By the way, you probably don't want the method to be annotated with #ResponseBody if you are going to return the name of the view from the method (user.jsp). #ResponseBody means that the return value of the method should be written directly to the response stream.

Getting my first Spring webapp to work

I have a controller that gets an ID from a form in search.jsp. I want it to redirect to entitydemo.jsp, which should be able to access EntityDemo and output its attributes. How do I do that? Do I need to use redirect and put EntityDemo as a session attribute somehow?
#Controller
public class SearchEntityController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public EntityDemo getEntityDemoByID(#ModelAttribute("search") Search search, BindingResult result) {
EntityDemo entityDemo = null;
if (search.getId() != null) {
int id = Integer.parseInt(search.getId());
entityDemo = DBHelper.getEntityDemo(id);
}
return entityDemo;
}
}
Assuming that you have some class named EntityDemo which has Getters and Setters for all the fields, I think you should do something like so:
#Controller
public class SearchEntityController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public ModelAndView getEntityDemoByID(#ModelAttribute("search") Search search, BindingResult result) {
EntityDemo entityDemo = null;
Map<String, Object> model = new HashMap<String, Object>();
if (search.getId() != null) {
int id = Integer.parseInt(search.getId());
entityDemo = DBHelper.getEntityDemo(id);
model.put("entityDemo", entityDemo);
}
return new ModelAndView(new RedirectView(pageIWantToRedirectTo), model);
}
}
Then, in your JSP, you can use JSTL and do something like this: ${entityDemo.name}, where name is a field I am assuming that the EntityDemo class has together with an appropriate Getter, this being public String getName(){return this.name;}.
To my knowledge, Controller methods do not return entire objects, they either return String values which denote the name of the view, such as \foo\bar\myPage.jsp or else, entire ModelAndView objects (there are 2 types of objects, one of them has portlet in its full name and the other has servlet. In this case you must use the one that has servlet in its full name. Just for clarity, when I say full name, I mean the name which includes the package within which it resides. If memory serves me well the one you are looking for is in springframework...servlet.ModelAndView or something like that.
EDIT: If you want to redirect upon submit, then, you will need to make 2 controllers, one which will render the form and the other which will redirect once the form is submitted.
Regarding your JSP Page, you should have an xml file name dispatcher-servlet.xml. The name could be different, depending on your configurations in web.xml, but they all have the structure of <servletname>-servlet.xml. There should be a property named viewResolver (Although this should be the case, certain IDE's do not populate much for you. On the other hand, IDE's such as Netbeans set up most of the initial configuration for you). This is another controller which acts upon your views. What it does is that it automatically appends items before and after your view name which you specify in your controller. Usually it appends a prefix of pages/jsp/ and a suffix of .jsp. So, if you have a page with the following path pages/jsp/myPage.jsp, all you need to pass in your controller would be myPage. The full path to the page will be constructed by the view resolver. If you pass in the whole URL, it will still keep on adding stuff so the page still won't be found even though you specified a correct path.
I got it to work using 2 methods in my controller - one to display the form and another for the search results
Controller:
#Controller
public class SearchEntityController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public void searchForm(Model model) {
model.addAttribute(new Search());
}
#RequestMapping(value = "/entitydemo", method = RequestMethod.POST)
public void showSearchResult(#ModelAttribute Search search, Model model) {
model.addAttribute("entityDemo", getEntityDemo(search));
}
// code to load entity here
}
(Search is a class with an int id and accessors)
Form in search.jsp:
<form:form action="entitydemo" commandName="search">
ID: <form:input path="id" />
</form:form>
Showing results in entitydemo.jsp:
<core:out value="${entityDemo.foo}" /> <br/>
<core:out value="${entityDemo.bar}" />

How to retain the values of the Spring MVC form backing object?

My controller has a method to return a form backing object:
#ModelAttribute(“userData”)
public UserData formBackingObject() {
return new UserData();
}
When the form submission fails its validation checks, it is redisplayed but when it is re-rendered, the userData object does not contain the user-submitted values - only the values present upon initialiation above.
#RequestMapping(method = RequestMethod.POST)
public void userData(HttpServletRequest request, #ModelAttribute(“userData”) UserData userData, BindingResult bindResult, ModelMap model) {
// do validation checks
if (bindResult.hasErrors()) {
// perform redirect back to same page
}
    return "userData";
}
You need to do a model.addAttribute("key", value) . This will help bind the values to the model object check http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html for sample.
#ModelAttribute at method level is generally given to add reference data kind of data to the model. And these annotations are executed before #RequestMapping; hence your attribute "userData" is refreshed with new object before control reaches your public void userData(). method.
The solution is to add userData to the model within the method which returns your userdata form jsp to browser.

Creating your own custom helpers with Freemarker?

From my controller I set my Model and view like:
ModelAndView mav = new ModelAndView();
mav.setView("index");
mav.addObject("user", user);
mav.addObject("someCollection", someCollection);
return mav;
Now I want to create a helper type object that will take the someCollection and the user object as parameters.
My helper function will output some HTML etc., is this possible?
You can write macros and directives using FTL or Java, expose them to your templates and invoke them same way you normally do with built-in macros/directives.
Nothing prevents you from putting any Java object, e.g. a helper instance, to the model and then call a method of it using the syntax like this: ${helper.myMethod(arg)}.
/**
* Add logotype logotype1AsBase64.
* #return
* #throws IOException
#ModelAttribute("logotype1AsBase64")
public String getLogotype()
throws IOException {
return logotypeService.getLogotype();
}
*/
#ModelAttribute
public void addAttributes(Model model) throws IOException {
//...
model.addAttribute("logotype1AsBase64", logotypeCacheServ.getImage("logotypeInEnglish1From20190101.png"));
//...
}
Then to use it:
<img src="<#if locale == 'specificLocale'>${logotype1AsBase64}></#if>" alt="description for picture">
And the model attribute can contain more html other than base64 for logotype, if the setup is as such.

Categories

Resources