I've been having a problem regarding using AJAX with Spring MVC. I have a form which has a lot of fields, and each field retrieves data depending on the associated button that was clicked.
So, each one of my buttons needs to call an AJAX request. Each response will be displayed on the associated field.
I wonder if it is possible to call a different method in my Spring controller once I clicked on a different button?
In other words, I want to make multiple ajax requests to the same controller where each request will call a different method in that same controller.
See this example :
// when get account detail is clicked it will call this method
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody String getAccountDetails(#RequestParam(value="accountid") String accountid){
return somefunct.getAccountDetails(accountid);
}
// when get account summary is clicked it will call this method
#RequestMapping(method=RequestMethod.POST)
public #ResponseBody String getAccountSummary(#RequestParam(value="accountid") String accountid){
return somefunct.getAccountSummary(accountid);
}
/* when submit button is clicked... Form is submitted for saving*/
#RequestMapping(method=RequestMethod.POST)
public String submitForm(){
// save here
return "myform";
};*/
Currently, I can have only one AJAX request. How can I modify this code so that I can have different functionality for different AJAX requests?
First, consider that when you retrieve data from a server without modifying the state of that server, the commonly accepted standard is to use the HTTP GET method, not POST. Thus, for your first two methods, you are misusing the HTTP Methods.
Second, you can map individual URL patterns to a specific method using the value property of the RequestMapping annotation.
Third, the most RESTful way to represent your account details resource is to use the PathVariable annotation and include your identifying accountid in the actual path:
#RequestMapping(value="/account/{accountid}/details", method = RequestMethod.GET)
public #ResponseBody String getAccountDetails(#PathVariable(value="accountid") String accountid){
return somefunct.getAccountDetails(accountid);
}
Next, you can represent your account summary using a different URL pattern where the URL is built like a tree, where the first two parts of the path are once again "Account" and the accountid:
// when get account summary is clicked it will call this method
#RequestMapping(value="/account/{accountid}/summary", method=RequestMethod.GET)
public #ResponseBody String getAccountSummary(#PathVariable(value="accountid") String accountid){
return somefunct.getAccountSummary(accountid);
}
Now, your submit method, on the other hand, has side effects. This is just a fancy way of saying that the state of your server will be different at the end of this request, and any GET requests made to that resource will be different than they were prior to the change. The appropriate HTTP method to use when modifying a resource or adding a resource to a collection is the HTTP POST Method. When replacing a collection, the HTTP Method PUT is the generally accepted method of choice.
Another differentiating factor between PUT and POST is that PUT is idempotent, meaning that the same request repeated over and over again doesn't change the state on the server. If hitting the same request multiple times creates more records, then use POST.
Lastly, this request can be mapped to a URL as well. In the example below, I've assumed you are creating a new Account record and inserting a new record in the collection of accounts in the database. Thus, I've used POST. I also modified your parameter list to use PathVariable to take the accountid from the URL path, and I added a RequestBody annotation so that you can send an object in the body of the request, which could be deserialized into a Java object:
/* when submit button is clicked... Form is submitted for saving*/
#RequestMapping(value="/account/{accountid}", method=RequestMethod.POST)
public String submitForm(#PathVariable String accountid, #RequestBody Account account){
// save here
return "myform";
}
For more information on Spring MVC, please check out the Spring documentation on Spring MVC.
Related
I have a page, rendered by an angularjs controller, that shows the details of a user on page load.
This list is given by the controller of Spring:
#RequestMapping(value = "/contact/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
public User getContact(#PathVariable String id) {
User user = userService.findById(id);
return user;
}
Which is requested by angularjs inside a details controller:
$http.get(urlBase+'/contact/'+$routeParams.userid).success(function(data) {
$scope.user = data;
});
Now I also want to show, on the same page, other details such as whether the visitor on that page is friends with that user. This information should be loaded while the page loads (same as retrieving the details of the user)
Should I use a separate get method to do this kind of check and put it under the first get method for example? E.g.
#RequestMapping(value = "/isFriends/{userId}", method = RequestMethod.GET, headers = "Accept=application/json")
#ResponseBody
public RETURNWHAT loadProfile(#PathVariable String userId, RequestParameter request) {
/*
* Do some checks whether a user is friends with the user on the page.
*/
request.setParameter("isFriends", true); //Perhaps not a good idea to use set parameter to retrieve this inside the angularjs' controller?
}
Other get:
$http.get(urlBase+'/isFriends/' + $routeParams.userid).success(function(data) {
});
Or is there a better way to achieve this?
I thought of only calling 1 get method that retrieves this information from 1 method in the controller. But then I assume that the controller has to send back a map with all the details.
Edit:
I believe using 1 method in the controller of Spring which returns a Map<String, Object> and 1 get function in angularjs should be the best option, am I correct?
Actually is a trade off between no of http requests and amount data being sent back from server, so as specific to your user case, its just one field you want, piggybacking to existing response is better way than making two requests.
I agree with #vinayakj that it is more efficient to combine the data in one HTTP request (less server load, less bandwidth). Definitely makes sense if you expect alot of these requests. But instead of adding the "isFriends" information to User, I'd wrap both in an "aggregate" object:
class UserDetails {
User user;
boolean isFriends;
}
The resulting JSON might look like this:
{
user: {
id: 1,
name: "abc"
},
isFriend: true
}
Why not just adding "isFriends" to User? Because what we are doing here is solely a webservice performance optimization. It should not make your model less maintainable.
Why not Map<String, Object>? Those maps are pretty generic - it would be harder to see which response contains which kind of data. A dedicated class adds type-safety and documentation. Also, you may want to annotate the components separately.
The obvious drawback is that you need to create extra classes with somewhat funny names.
I have a page with pagination links at the top.
When I click the pages it takes me from record 1-50, 51-100 and so on.
I am having issue when i click the second action like when I click page # 2 #ModelAttribute values gets null.
this is tha page url: http://localhost:8080/tax/taxedYear.html?p=2
It takes me to spring controller class with /taxedYear.html and the method is as below:
#RequestMapping(value = "/taxedYear.html", method = RequestMethod.GET)
public ModelAndView showTaxResults(#ModelAttribute("criteria")
Criteria criteria, Model model, HttpSession session, HttpServletRequest request) {
String src = criteria.getSource();
System.out.println("src === "+src);
//....
//
}
When it is called anything from criteria is null. The same method is called from the previou page and it works fine.
This happens only when I click the page urls which also calls the same method in the controller and sends page # in addition.
From Spring reference:
An #ModelAttribute on a method argument indicates the argument should be retrieved from the model. If not present in the model, the argument should be instantiated first and then added to the model.
Model is populated by controller. You assume that the model remains the same when second call is made, but apparently your assumption is wrong. Because Spring initializes the model, I believe you thought it's persistent. And it is the reasonable way, model shouldn't be persistent among HTTP calls.
This is the question which is pressing my head since long time a go.
Suppose I have an index page and there's login form in there.
What I noticed is that for handling #ModelAttribute I should have instantiate the object of my model first in coming Http-GET request:
#RequestMapping(value="/index", method=RequestMethod.GET)
public String renderHomePage(#ModelAttribute("userCredential") UserCredential userCredential, ....){
return "index-page";
}
and then I can fetch my Object when I post the form using HTTP-POST:
#RequestMapping(value="/index", method=RequestMethod.POST)
public String checkCredential(#ModelAttribute("userCredential") UserCredential userCredential, ....){
//do some user credential checking
return "faileOrSuccessPage";
}
and both method must have the same #RequestMapping value for the submission form to work.
now I have several question:
suppose my index page(value="/index", method=RequestMethod.GET) has 10000 hit a day, Does it mean that for each coming get request, An object of UserCredential will be created? (I want to know about object life cycle)
Is there any methodology for us to bind a #ModelAttribute object with the second method (checkCredential) only (just when we want to submit a form)?
If in my case I just have to use #RequestParam to fetch my input values, how I can validate the #RequestParam without using BindingResult and return them back to the view for show?
Regarding your questions:
.1. suppose my index page(value="/index", method=RequestMethod.GET) has 10000 hit a day, Does it mean that for each coming get request, An object of UserCredential will be created? (I want to know about object life cycle)
Yes, but it is tied to the http request scope and the object will be eligible for garbage collection as soon as your page is rendered. 10000 is a fairly small number for the JVM to handle.
.2. Is there any methodology for us to bind a #ModelAttribute object with the second method (checkCredential) only (just when we want to submit a form)?
Depends on your flow - I am assuming you are using a spring form tag to show validation errors back to the user if the user or password don't conform to certain standards you have, if that is the case you will need to set the #ModelAttribute to populate your domain object at the point of redirection back to your index page.
If on the other hand you don't intend to show feedback to your user with the original values that have been set by the user then you don't to set the ModelAttribute, you can totally ignore it and use normal form html elements instead of spring tag.
.3. If in my case I just have to use #RequestParam to fetch my input values, how I can validate the #RequestParam without using BindingResult and return them back to the view for show?
I wouldn't recommend this, a wrapper type is way better and will be validated with the registered validator and the BindingResult will cleanly have the validation errors that you can directly carry over to the UI. If you absolutely want to validate the #RequestParam on your own, you can call the validator yourself. There are ways to set more model attributes to carry back the validation exceptions and present on the UI.
I have defined #ModelAttribute in my controller, which needs to be excuted based on the requested methods output. So when I trying to accessing the my ModelAttribute from JSP, but it is producing the previous result. For example below:
class MyController{
#modelAttribute("Address")
protected getAddress(HttpRequest req){
HttpSession sess = req.getSession();
return sess.getAttribute("Address");// For example now Address is "Test Address"
}
#RequestMapping("sample.do", method=RequestMethod.GET)
public Model requestMethod(......)
{
// after execution of this method
sess.setAttribute("Address","Changed Address");
return model;// request directed to my JSP.
}
}
When I use ${Address} in my JSP, it is displaying "Test Address", I need "Changed Address" in my JSP. But my ModelAttribute is executed after the jsp is loaded. Is it possible to make this possible using #ModelAttribute, if so then how.? . Is there anyother way to acheive this apart from #ModelAttribute.?
#ModelAttribute, on a method, is used to populate the model before the request mappingmethod is called. So if multiple views need to display the address, you can add the same #ModelAttribute-annotated method in all their controllers, and the views will thus find the address in the model and will thus be able to display it.
The problem here is that your request mapping method, called after the #ModelAttribute-annotated method, changes the valud of the address, but doesn't set the new value of the address in the model. So the view still displays the old address, added to the model by the #ModelAttribute-annotated. You shouldn't have many methods changing the address, so resetting the address in the model should be done there, but not everywhere else.
That said, the address comes from the session, so it's already available for all the views anyway, without needing any #ModelAttribute-annotated method (which just stores the same address in the request as well). Just removing the #ModelAttribute-annotated method would still let you access the right address in the views, since views have access to everything stored in the session. #ModelAttribute is useful when your model must contain data that comes from, typically, the database: the method gets the data from the database, and this data is stored in the model (the request) by Spring.
Your flow and use of ModelAttribute maybe incorrect/redundant.
From the official Spring documentation
#ModelAttribute is also used at the method level to provide reference
data for the model (see the populatePetTypes() method below). ..
Note: #ModelAttribute annotated methods will be executed before the
chosen #RequestMapping annotated handler method. ...
getAddress is getting called twice:
once before requestMethod is executed (since it's annotated with RequestMapping)
and again in your jsp.
Each time it's being called it's returning an Address with "Test Address". You should remove the call in your JSP (by removing the modelAttribute on the form). In your case it's redundant since you're already putting an updated version of Address in requestMethod.
I am using Starbox in my Spring page. I want to submit the user rating so I can store it in the database and not have to refresh the page for the user. How can I have a Spring controller that accepts this value and doesn't have to return a new view. I don't necessarily need to return any updated html - if the user clicks the Starbox, that is all that needs to happen.
Similarly, if I have a form with a submit button and want to save the form values on submit but not necessarily send the user to a new page, how can I have a controller do that? I haven't found a great Spring AJAX tutorial - any suggestions would be great.
If you use annotations, perhaps the more elegant way to return no view is to declare a void-returning controller method with #ResponseStatus(HttpStatus.OK) or #ResponseStatus(HttpStatus.NO_CONTENT) annotations.
If you use Controller class, you can simply return null from handleRequest.
To post a from to the controller via AJAX call you can use the appropriate features of your client-side Javascript library (if you have one), for example, post() and serialize() in jQuery.
The AJAX logic on the browser can simply ignore any data the server sends back, it shouldn't matter what it responds with.
But if you really want to make sure no response body gets sent back, then there are things you can do. If using annotated controllers, you can give Spring a hint that you don't want it to generate a response by adding the HttpServletResponse parameter to your #RequestMapping method. You don't have to use the response, but declaring it as a parameter tells Spring "I'm handling the response myself", and nothing will be sent back.
edit: OK, so you're using old Spring 2.0-style controllers. If you read the javadoc on the Controller interface, you'll see it says
#return a ModelAndView to render, or
null if handled directly
So if you don't want to render a view, then just return null from your controller, and no response body will be generated.