I'm developing a simple web application using spring MVC and thymeleaf. I have a form correctly handled by this method in my controller
#RequestMapping(value = "/list", method = RequestMethod.POST)
public ModelAndView searchJob(
#ModelAttribute(SEARCH_PARAMS) #Valid SearchParams params,
BindingResult bindingResult) {
ModelAndView output = null;
if (!bindingResult.hasErrors()) {
JobsAPIImplService service = new JobsAPIImplService();
JobsAPI api = service.getJobsAPIImplPort();
ArrayList<NoaJob> jobs = (ArrayList<NoaJob>) (api.search(JobUtils.toSearchParams(params))).getItem();
output = new ModelAndView("scheduler/list");
output.addObject("jobs", jobs);
} else {
// errors handling
}
return output;
}
So in my result page I can access to the ArrayList "jobs" in this way:
<tr th:each="job : ${jobs}">
...
</tr>
In the same page, I have a simple link which calls another GET method on the same controller. The goal here is to have the same ArrayList in another page in order to implement a "back" button without re-executing the search logic (a call to a web service).
Here is the method called
#RequestMapping(value="/list/{id}", method = RequestMethod.GET)
public ModelAndView details(#PathVariable("id") String jobUuid,
#ModelAttribute("jobs") ArrayList<NoaJob> jobs) {
ModelAndView output = new ModelAndView("scheduler/details");
LOGGER.info("Size jobs list: " + jobs.size());
NoaJob job = new NoaJob();
job.setJobUuid(jobUuid);
output.addObject("job", job);
output.addObject("jobs", jobs);
return output;
}
the problem is that the arraylist here is always null! I read that in GET requests Spring allocates a new ModelAttribute, so how can I pass this object throug pages?
Define a session attribute like this in the head of your controller:
#SessionAttributes({ "myFancyList"})
#Controller
public class HomeController {
// your code here
}
Now.. when you have to insert the "list" to be viewable via thymeleaf:
output.addObject("myFancyList", jobs);
and modify thymleaf pseudocode accordingly.
in the "post" of the search "override" the session attribute with the current search result..
i think this should do the trick for you
Related
My objective is to pass model attributes from controller to JSP page during a redirect and avoid the attribute being displayed in URL. The source code below is validating login from datastore using java data objects.
Controller:
#Controller
public class LoginController {
int count;
PersistenceManager pm = PMF.get().getPersistenceManager();
//Instance of data class
User user;
ModelAndView modelAndView=new ModelAndView();
#RequestMapping(value="/Login",method = RequestMethod.POST)
public ModelAndView loginValidate(HttpServletRequest req){
//Getting login values
String uname=req.getParameter("nameLogin");
String pswd1=req.getParameter("pswdLogin");
count=0;
user=new User();
//Generating Query
Query q = pm.newQuery(User.class);
q.setFilter("userName == userNameParam");
q.declareParameters("String userNameParam");
try{
List<User> results = (List<User>) q.execute(uname);
for (User u: results) {
String userName=u.getUserName();
if(userName.equals(uname)){
System.out.println(u.getPassword());
if(u.getPassword().equals(pswd1)){
count=count+1;
modelAndView.setViewName("redirect:welcome");
modelAndView.addObject("USERNAME",uname);
return modelAndView;
}
//rest of the logic
}
JSP:
<h1>Welcome ${USERNAME} </h1>
My current URL is /welcome?USERNAME=robin
My goal is to display it as /welcome
Also, my page is supposed to display "Welcome robin" whereas it displays only Welcome.
RedirectAttributes only work with RedirectView, please follow the same
#RequestMapping(value="/Login",method = RequestMethod.POST)
public RedirectView loginValidate(HttpServletRequest req, RedirectAttributes redir){
...
redirectView= new RedirectView("/foo",true);
redir.addFlashAttribute("USERNAME",uname);
return redirectView;
}
Those flash attributes are passed via the session (and are destroyed immediately after being used - see Spring Reference Manual for details). This has two interests :
they are not visible in URL
you are not restricted to String, but may pass arbitrary objects.
You need to be careful here because I think what are you trying to do is not supported for a good reason. The "redirect" directive will issue a GET request to your controller. The GET request should only retrieve existing state using request parameters, this is the method contract. That GET request should not rely on a previous interaction or on any object stored some where in the session as a result of it. GET request is designed to retrieve existing (persisted) state. Your original (POST) request should have persisted everything you need for you GET request to retrieve a state.
RedirectAttributes are not designed to support you in this case, and even if you managed to correctly use it it will only work once and then they will be destroyed. If you then refresh the browser you will get an application error because it cannot find your attributes anymore.
I have been using ModelAndView as the object type in my controller to handle method to do data binding with front-end html form.Such as this:
#RequestMapping(value = "/postSth", method = RequestMethod.POST)
public ModelAndView postSomething(#RequestParam("name") String name){
ModelAndView model = new ModelAndView("displayPage");
model.addObject("name", name);
return model;
}
This method will allow me to bind the name and I can display the name to the displayPage.jsp after the POST method. displayPage is the JSP page name and by using the InternalResourceViewResolver from Spring framework.
Lately I have been using jQuery ajax function to pass the data to my controller, and I am working on method looks like this:
#RequestMapping(headers = "Content-Type=application/json", value = "/postSth", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
public ModelAndView postSomething(#RequestBody String name){
ModelAndView model = new ModelAndView("displayPage");
model.addObject("name", name);
return model;
}
I noticed I can successfully grabbing the JSON string with this controller, but this method will not redirect my webpage to the displayPage and the data binding with .addObject no longer work.
Why it does not work? How do I change it to still allow me to direct to the displayPage.jsp? I understand I can do the redirect with javascript at front-end, but it is not what I want to do.
I understand your requirement as you trigger an ajax call but instead of loading the output of data to current jsp you need to populate it in new jsp.
I have had the similar requirement, I used jquery load() to implement it. It worked fine in my case. Basically its like a 3 step process;
Trigger Ajax call to the controller to get the required data to be loaded on new jsp.
use load() once required data is available to load the new jsp in current page.(you can use some div to load it, if you want to completely replace current page then empty the contents of current page and laod with new jsp)
Write javascript/jquery codes to manipulate the dom in new JSP which we rendered on previous step.
Below is the snippet of how it can be done.
#RequestMapping(headers = "Content-Type=application/json", value = "/postSth", method = RequestMethod.POST)
#ResponseStatus(value = HttpStatus.OK)
public String postSomething(#RequestBody String name){
return model;
}
<div id='currentContent'>
<!--Existing content -->
</div>
var ajaxResult = '';
$.ajax({
type : POST,
url: "postSth",
async:false}).then(function(data){
//DIV ID here would be the ID which you need to have in your page to append the HTML content.
//if you want to completely reload the page then use the id where your current contents are displayed
//Inside your somenewjsp.jsp you can write your jquery codes to access the variable ajaxResult on document ready
ajaxResult = data;
$('#currentContent').load('/somenewjsp.jsp');
});
});
Please share your results on this approach.
I want setting the attribute in first modelandview method with the help of bean and trying to get the attributes in other modelandview method in same controller but getting null value my code is below
#RequestMapping(value="/insert",method=RequestMethod.POST)
public ModelAndView inserData(#ModelAttribute SavingBeansavingBean,HttpServletRequestrs,Model m) {
System.out.println(savingBean.getFirstName());
if (savingBean != null)
System.out.println("abho");
SavingBean saving = persionalService.insertData(savingBean);
custid = saving.getCustomerId();
System.out.println(custid);
m.addAttribute("customId",saving);
System.out.println(saving.getDisgnProf());
List<SavingBean> list = new ArrayList<SavingBean>();
list.add(saving);
return new ModelAndView("AccountInfo","list", list);
}
#RequestMapping(value="/accinsert",method=RequestMethod.POST)
public ModelAndView inserData(#ModelAttribute AccountBean accbean,HttpServletRequest rs,Model m) {
SavingBean b = new SavingBean();
System.out.println("saas" + b.getCustomerId());
session = rs.getSession();
System.out.println("xxx" + rs.getAttribute("customId"));
accbean.setCustid((Long) rs.getAttribute("customId"));
AccountBean accbean1 = persionalService.insertacc(accbean);
return new ModelAndView("welcome");
}
From the first look to your code , I notice that your request method not specified. At this case (When using #ModelAttribute) you have to make it as (POST) request.
#RequestMapping(value = "/insert", method = RequestMethod.POST)
#RequestMapping(value = "/accinsert" , method = RequestMethod.POST)
Why ? because actually your object will be retrieved due to Form Submission which is treated as POST request. Try that thing and check the results. If the problem is still maybe you have some real problem in your Presentation Layer (e.g JSP Page) that is responsible about submitting the data.
Good Luck !
I have an object called Request which is the main object of my portal that stores all the information of a request, the user, their form selections, etc. How to I persist all the previous information in between the different forms? In each .GET I have to set the request object, and then in each .POST, the only information that is passed to it is what is in the forms on the .GET pages. So on each page I have to have hidden fields such as
<form:input path='requestId' style='display:none' />
<form:input path='currentUserId' style='display:none' />
<form:input path="step" style='display:none' />
I need these fields, and would also like to have the rest of the fields in the request object that are not on the form without having to repeat that for each and every field in my object.
#RequestMapping(value = "/review", method = RequestMethod.GET)
public ModelAndView showCorReview(#RequestParam(value = "requestId") String requestId,
#CookieValue(value = "EMP_ID", defaultValue = "168") int userId)
{
Request request = requestManager.getRequestById(Integer.parseInt(requestId));
request.setCurrentUserId(userId);
String pageTitle = "2.1: Initiate New Service Request -- (Review)";
ModelAndView mav = new ModelAndView();
mav.setViewName("newRequest/review");
mav.addObject("title", pageTitle);
mav.addObject("request", request);
mav.addObject("cpm", userManager.getUserById(request.getCpm()).getName());
return mav;
}
#RequestMapping(value = "/review", method = RequestMethod.POST)
public String saveReview(Request request, #RequestParam(value = "commentData", required = false) String[] additionalComments)
{
if (additionalComments != null)
commentLogManager.addCommentLog(additionalComments, request);
if (request.getRejectReason() == "")
{
request.setCpm(admin.getCPM(request.getContract()).getId());
request.setCor(admin.getCOR(request.getContract()).getId());
requestManager.updateRequest(request);
}
else
{
if (request.getSubmitType().equals("return"))
{
request.setNextStep(1);
requestManager.moveRequestToStep(request);
}
}
return worksheetUrl + request.getId();
}
Alternatately I could also in the .POST do the
Request request = requestManager.getRequestById(Integer.parseInt(requestId))
Then use setters on all the form fields, but again, I would prefer the data to actually persist on it's own without explicitly calling that.
#Tim, if I understood your requirement correctly, you have a sequence of forms and you would like to transfer information from one form to the next without having to hit a database or copy over request variables from one form to another. I could support #JB Nizel's suggestion of employing HTTP Session, but you may not want to make the session "heavy"; after all, it is the next most persistent scope after application-scope.
Spring Web Flow may be the answer. Flow-scope will allow you to build up form-state as the user progresses from one form to the next. Plus you don't have to worry about form-scoped variables when the flow finishes, unlike session variables that you don't want to have lingering around.
An item is displayed at this URL:
/item/10101
using this Controller method:
#RequestMapping(value = "/item/{itemId}", method = RequestMethod.GET)
public final String item(HttpServletRequest request, ModelMap model,
#PathVariable long itemId)
{
model = this.fillModel(itemId);
return "item";
}
The page contains a form that submits to the following method in the same controller:
#RequestMapping(value = "/process_form", method = RequestMethod.POST)
public final String processForm(HttpServletRequest request,
#ModelAttribute("foo") FooModel fooModel,
BindingResult bindResult,
ModelMap model)
{
FooModelValidator validator = new FooModelValidator();
validator.validate(FooModel, bindResult);
if (bindResult.hasErrors())
{
model = this.fillModel(fooModel.getItemId());
return "item";
}
return "account";
}
If the validator finds errors in the form, it redisplays the item but instead of displaying it at the original url:
/item/10101
it displays it at its own url:
/process_form
Is it possible to redisplay the form at the original URL?
/item/10101
(I've tried getting the referrer and redirecting to it in processForm but then all of the model contents end up displayed as URL name/value pairs:)
#RequestMapping(value = "/process_form", method = RequestMethod.POST)
public final String processForm(HttpServletRequest request,
#ModelAttribute("foo") FooModel fooModel,
BindingResult bindResult,
ModelMap model)
{
String referrer = request.getHeader("referer");
FooModelValidator validator = new FooModelValidator();
validator.validate(FooModel, bindResult);
if (bindResult.hasErrors())
{
model = this.fillModel(fooModel.getItemId());
return "redirect:" + referrer;
}
return "account";
}
Short answer: No.
What happens is a server-side redirect (forward), which is within the same request, and so the submitted values are preserved (and displayed in the form)
The url will change if you use a client-side redirect (return "redirect:item";), but in that case a new request will come and the submitted values will be lost.
But here are two options that you have:
use the same URL in the mappings for both methods and distinguish them based on request method - GET for the former, POST for the latter. This might be confusing, so document it.
find / implement flash scope for spring-mvc. There's nothing built-in. The flash scope means that values are preserved (in the session usually) for a submit and the subsequent redirect. This option includes the manual handling, by putting the submitted object in the session, and later retrieving & removing it