I have a fragment of HTML page with one form and 2 button:
<form action="#" data-th-action="#{/action/edit}" data-th-object="${model}" method="post">
<button type="submit" name="action" value="save">save</button>
<button type="submit" name="action" value="cancel">cancel</button>
</form>
And the controller:
#RequestMapping(value="/edit", method=RequestMethod.POST)
public ModelAndView edit(#ModelAttribute SomeModel model,
#RequestParam(value="action", required=true) String action) {
if (action.equals("save")) {
// do something here
}
if (action.equals("cancel")) {
// do another thing
}
return modelAndView;
}
This work good, but if I have more button, I must add more if statement to check the action string. Is there another way that I can create one action for each button in the form?
You can create separate methods with different #RequestMappings using the params variable.
#RequestMapping(value="/edit", method=RequestMethod.POST, params="action=save")
public ModelAndView save() {}
#RequestMapping(value="/edit", method=RequestMethod.POST, params="action=cancel")
public ModelAndView cancel() {}
this works in my problem.
use th:formaction on submit button this is work on how many you have submit button
and this is also usefull for give more links to one form with different submit button
<form action="#" class="form" th:action="#{'/publish-post/'+${post.id}}" method="post">
<input class="savebtn" type="submit" value="Save" th:formaction="'/save-post/'+${post.id}">
<input class="publish" type="submit" value="Publish Article">
</form>
Instead of an if-case you could have a switch case, should you not want to take in every option as a new request mapping.
#RequestMapping(value="/edit", method=RequestMethod.POST)
public ModelAndView edit(#ModelAttribute SomeModel model,
#RequestParam(value="action", required=true) String action) {
switch(action) {
case "save":
// do stuff
break;
case "cancel":
// do stuff
break;
case "newthing":
// do stuff
break;
default:
// do stuff
break;
}
}
For multiple submit buttons, the below worked for me.
Notice the: th:formaction in the cancel button.
<form action="#" th:action="#{/saveProducts}" th:object="${model}" method="post">
<button type="submit" name="action" value="cancel" th:formaction="#{/cancelProducts}">CANCEL</button>
<button type="submit" name="action" value="save">SAVE</button>
</form>
For the controller:
#RequestMapping(value="/saveProducts", method= RequestMethod.POST)
public String save(#ModelAttribute SomeModel model) {
*//Do something*
}
#RequestMapping(value="/cancelProducts", method=RequestMethod.POST)
public String cancel(#ModelAttribute SomeModel model) {
*//Do something*
}
You can know which submit button has been clicked and then act upon the button
Here is the code
String btnName = request.getParameter("action");
if(btnName.equals("save"))
// you code....
else if(btnName.equals("cancel"))
// you code....
Related
I have made a text field and a submit button in my view in the admin page, and i want to do so the text i submit is shown below my textbox on the same page.
This is my controller for getting to the adminPage:
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(Model model) {
return "adminPage";
}
this is what i have for my adminPage:
<form th:action="#{/adminPage}" method="post">
<textarea rows="4" cols="50">
</textarea>
<input type="submit" class="btn btn-lg btn-primary btn-block"
value="Submit Text"/>
</form>
I'm still very new at controllers and MVC in general, and i find it hard to use my knowledge in Java because Controllers doesn't look like any Java i've used before, so any help would be appreciated!
ok then, no Javascript. First, textarea needs a name attribute name="inputText".
That name will be used in the model object when your server method receives the request:
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(#RequestParam("inputText") String input, Model model) {
//Do stuff
model.addAttribute("theText", input); //add the text which can be accessed on "adminPage"
return "adminPage";
}
then you can add a <div> in "adminPage.jsp" and append your text there, like:
<div>${theText}</div>
If, I have understood your problem correctly than here is the workaround that will do the job.
When user visits /adinPage from browser than input_data variable will be null and the if condition won't be executed.
The JSP page will be returned with second textarea as blank.
You have to use JSP because HTML pages can't be altered.
Controller.java
#RequestMapping(value = "/adminPage", method = RequestMethod.POST)
public String adminPage(#RequestParam(value = "input_data", required = false) String input_data,Model model)
{
if(input_data!=null)
model.addAttribute("output_data",input_data);
return "adminPage";
}
adminPage.jsp
<form th:action="#{/adminPage}" method="post">
<textarea id="input_data" rows="4" cols="50">
</textarea>
<textarea rows="4" cols="50">
${output_data}
</textarea>
<input type="submit" class="btn btn-lg btn-primary btn-block"
value="Submit Text"/>
</form>
When the user submits the form, the if condition will be executed and the returned view will contain the previously input data in second textarea
I haven't tested the code so, it might contain some syntax errors.
I want to show validation error in my jsp page.
My object is:
public class MyObjectDTO{ #valid private TextDTO text1; #valid private TextDTO text2 }
public class TextDTO{ #NotBlank private String code;#NotBlank private String label;}
My controller:
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String creationProjet(#Valid #ModelAttribute MyObjectDTO obj, BindingResult result,
Model model) {
if (result.hasErrors()) {
model.addAttribute("hasErrors", true);
return "create";
} else {
....
return "redirect:/list";
}
}
my jsp
<div class="col-md-6 form-group ${requestScope['org.springframework.validation.BindingResult.obj'].hasFieldErrors('text1') ? 'has-error' : ''}">
<label class="col-lg-3 control-label">my label</label>
<div class="col-lg-5">
<form:select class="form-control" name="type" path="text1.code" id="selectType">
<option value="">---------</option>
<c:forEach items="${types }" var="type">
<form:option value="${type.id }">
<c:out value=" ${type.code}"></c:out>
</form:option>
</c:forEach>
</form:select>
<form:errors path="text1.code" class="has-error error"></form:errors>
</div>
</div>
My controller redirects to the page create but the errors are not showing. In debug mode there is one error that indicates text1.code cannot be a blank.
In your Jsp page add following line
<div class="col-md-6 form-group ${requestScope['org.springframework.validation.BindingResult.obj'].hasFieldErrors('text1') ? 'hasErrors' : ''}">
or use hasFieldErrors() instead
<div class="col-md-6 form-group ${requestScope['org.springframework.validation.BindingResult.obj'].hasFieldErrors()}">
And About addAttributes("hasErrors",true), use addFlashAttribute() which is store in flashmap and Object (In your case Error Message will be alive when you navigate to create page or redirect between two controller.) Look at this for more
In your controller Add RedirectAttributes Object like this
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String creationProjet(#Valid #ModelAttribute MyObjectDTO obj,
BindingResult result,
RedirectAttributes redirectAtt,
Model model) {
if (result.hasErrors()) {
redirectAtt.addFlashAttribute("hasErrors","ur message");//instead of true you can write your own message
return "create";
} else {
....
}
return "redirect:/list";
}
}
If you write your own message instead of true
<c:if test="${not empty hasErrors">
<p>${hasErrors}</p>
</c:if>
only you missing is put part to model map.You are putting only flag. But you need to put result.
if(result.hasErrors()){
mm.addAttribute("errors", result);
return "create";
}
I'm a trainee java-dev and this is my first question here so please, don't judge me!
I have a Controller class which works with jsp files. The first jsp (userinput.jsp) has 3 text fields (lat, lon, radius) and 2 buttons (submit, apply default values). The second jsp is just an HTML table filled with data (depends on user input), and a reset button which should return you to the starter page (userinput.jsp) and delete all existing data. How should I do this?
Bonus question: If I try to refresh the page at the second state (html table), the browser generates a warning that says I'll lose all data and I shouldn't refresh. How can I get rid of this?
#Controller
#EnableAutoConfiguration
class SpringBootController implements InitLogger {
#GetMapping(value="/geohash")
public String getUserInput(ModelMap model) {
model.put("command", new Tuple());
return "UserInput";
}
#PostMapping(value="/geohash", params="SubmitWithDefault")
public String defaultUserInput(ModelMap model) {
model.put("command", tupleFill (48.104564, 20.800041, 6) );
return "UserInput";
}
#PostMapping(value = "/geohash", params="Submit")
public String printHash(#ModelAttribute("user")Tuple tuple,ModelMap model) {
GetData.setLat1(tuple.getFirstCoordinate());
GetData.setLon1(tuple.getSecondCoordinate());
GetData.setRad1(tuple.getRadius());
LocationExecute.calculate();
model.addAttribute("geoItemList", LocationExecute.getTupleList());
model.addAttribute("listSize", LocationExecute.getTupleList().size());
return "Geohash";
}
#PostMapping(value = "/geohash", params="reset", method = RequestMethod.GET)
public ModelAndView method() {
return new ModelAndView("redirect:geohash");
}
}
userinput.jsp - buttons
<input type="submit" name="Submit" value="Submit" style="height:25px; width:100px"/>
<input type="submit" name="SubmitWithDefault" value="Default Values" style="height:25px; width:100px">
geohash.jsp - (html table) reset button
<input type="reset" name="reset" value="Reset" style="height:30px; width:100px">
You need to implement PRG(Post -Redirect-Get) design pattern in MVC to solve the issue.
Please go through the below for more information
http://www.c-sharpcorner.com/UploadFile/dacca2/implement-prg-pattern-in-mvc-architecture/
So instead of returning view name redirect it so your problem will be solved, if you want some data to be sent to redirect method then use flashAttributes of spring
Solved it this way:
geohash.jsp
(added form:form tags)
<form:form action="/geohash">
<th> <input type="submit" name="reset" value="Reset" style="height:30px; width:100px"> </th>
</form:form>
Controller
(changed method to post)
#RequestMapping(value = "/geohash", params="reset", method = RequestMethod.POST)
public ModelAndView method() {
return new ModelAndView("redirect:geohash");
}
I am trying to have 2 submit buttons post to a form, with each button action mapped to different controllers. Here are my mappings
#RequestMapping(value="/save", method=RequestMethod.POST, params="save")
#RequestMapping(value="/save", method=RequestMethod.POST, params="renew")
And my submit buttons look like these -
<input type="submit" name="save" class="button" value="Save" />
<input type="submit" name="renew" class="button" value="Renew" />
As you can see from my mapping, I am relying on the use of params to differentiate what button was clicked on. The problem is that it works 90% of the time but sometimes I get the exception below -
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8090/myapp/save': {public java.lang.String com.myapp.SaveController.save(MyEntity,javax.servlet.http.HttpSession), public java.lang.String com.myapp.SaveController.saveAndRenew(MyEntity,javax.servlet.http.HttpSession)}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:248)
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:194)
Strangely, when this happens and I re-submit the page, everything works fine afterwards. Is there a better way to achieve what I'm trying to do ?
Thanks!
if the form has these buttons specified:
input type="submit" class="button" name="save" value="Save"
input type="submit" class="button" name="delete" value="Delete"
input type="submit" class="button" name="cancel" value="Cancel"
you may direct to different url request according to button pressed with one controller.
for cancel button,
#RequestMapping(params = "cancel", method = RequestMethod.POST)
public String cancelUpdateUser(HttpServletRequest request) {
return "redirect:/users.html";
}
what request mapping does is to scan post request if it contains params name = cancel.
for save button,
#RequestMapping(params = "save", method = RequestMethod.POST)
public String saveUser(HttpServletRequest request, #ModelAttribute User user, BindingResult result, SessionStatus status) {
// validate your result
// if no errors, save it and redirect to successView.
}
Why not:
<input type="submit" name="action" value="save" />
and then:
#RequestMapping(value="/save", method=RequestMethod.POST)
public String handlePost(#RequestParam String action){
if( action.equals("save") ){
//handle save
}
else if( action.equals("renew") ){
//handle renew
}
}
If You have more controller methods with the same #RequestMapping that differs only in params attribute, You have to explicitly write:
which parameter is supposed to be present in the request, e.g. params="save"
which parameter is NOT supposed to be present in the request, e.g. params="!save"
In Your case:
#RequestMapping(value="/save", method=RequestMethod.POST, params={"save", "!renew"})
#RequestMapping(value="/save", method=RequestMethod.POST, params={"renew", "!save"})
This should fix error Ambiguous handler methods mapped for HTTP path ...
See Spring Web API 4.0.x - RequestMapping#params
Just create one controller with a method similar to this
#RequestMapping(value="/save", method=RequestMethod.POST)
public String handlePost(#RequestParam(required=false , value = "save") String saveFlag , #RequestParam(required=false , value = "renew") String renewFlag){
if(saveFlag != null{
//handle save
}
else if(renewFlag !=null{
//handle renew
}
}
One more solution:
#RequestMapping(value="/save", method={RequestMethod.POST}, params={"save=Save"})
#RequestMapping(value="/save", method={RequestMethod.POST}, params={"renew=Renew"})
On Spring with annotations, is there anyway that i can change the form action without changing the action using javascript?
For example on submit1 method invoked on the controller = method1
on submit2 method invoked on the controller = method2
#RequestMapping("/submit1")
public String submit1()
#RequestMapping("/submit2")
public String submit2()
...
<form:form id="dynamicfrm" method="post" action="archive/submit.do" commandName="submit">
<input type="submit1" value="">
<input type="submit2" value="">
Thank you!
If I understand you correctly - as others already said it isn't absolutely clear - than you want to map one single form to different action methods dependending on the button that was clicked.
In your JSP you can change the code to something like this:
<form action="/submit.do" method="post">
<input type="submit" name="action" value="show">
<input type="submit" name="action" value="edit">
</form>
And in your controller you can narrow the mappings like this:
#RequestMapping(value = "/submit", params="action=show")
public String showEntity() { /* ... */ }
#RequestMapping(value = "/submit", params="action=edit")
public String editEntity() { /* ... */ }