Ok so I'm submitting a simple form to my Spring Controller through jQuery. Here is the code:
<form id="addNote" role="form" action="addNote" method="POST">
<div class="form-group error">
<label for="note">Enter your Note </label>
<textarea rows="3" cols="50" name="note" class="form-control"
id="note"></textarea>
</div>
<div class="alert alert-danger" style="display: none">
<strong>Error!</strong> Text length must not exceed 100 characters
</div>
<button type="submit" class="btn btn-default">Save Note</button>
$(document).ready(function(){
$("#addNote").submit(function(e){
e.preventDefault();
var formObj = $(this);
var formURL = "addNote";
var formData = $("#note").val();
$.ajax({
url: formURL,
type: 'POST',
data: formData,
success: function(response){
alert(response);
},
error: function(response){
}
});
});
});
And the Controller method:
#RequestMapping(value="/addNote", method=RequestMethod.POST)
public Vector<String> addNote(Locale locale, Model model, HttpServletRequest httpServletRequest, HttpSession httpSession){
String note = httpServletRequest.getParameter("note");
notes.add(note);
ModelAndView mv = new ModelAndView("notes");
mv.addObject("thenotes", notes);
return notes;
}
I need to return the notes object to the jQuery so that I can display it's data as output. But this is the error I'm getting in the Chrome console:
So apparently there is a problem in the path. I have tried changing var formURL = "addNote"; to var formURL = "assessment/addNote"; in the jQuery but it doesn't work.
But for some reason if I change return value of addNote() function in the Controller to ModelAndViewand return mv then it works but it's not the response in the jQuery I need.
First of all You are using AJAX, so You can't return data using modelAndView.You have to return data as xml/json.
In Your javascript code add the following function:
function getContextPath() {
return window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
}
reference: How do you get the contextPath from JavaScript, the right way?
Then change You formURL to:
var formURL = getContextPath()+"/addNote";
With the above code requests reaches the controller. Within the controller You have to change Your requestMapping a bit:If You want to return some simple message then You can return it as a string(I am not sure whether it is the best way, but this is how I do it)
#RequestMapping(value = "/addNote", produces=MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
#ResponseBody
public String showNote(){
System.out.println("saving note..");
return "<response>Example</response>";
}
If You want to return a Vector or java object to JQuery, then the best idea is to use jackson-mapper or some similar library, You can find info easily with google.A simpler(not necessarily better solution) would be to define a method that would create the xml String by looping through the vector, sth like:
<notes>
<note>note1</note>
<note>note2</note>
</notes>
and return it as a String from controller, and then process xml within JQuery.
I also learn spring, so if someone knows a better solution, I will be happy to read it ;-)
Regards,
Mikajlo8
EDIT:I Have changed the requestMethod to GET, otherwise You have to deal with CSRF TOKENS.If You want to use POST, is is easy to find on the internet how to deal with csrf tokens.
Related
I have set up a Controller with two methods. The URL mappings are exactly the same, and the only difference is the mapping annotation method. One is #PostMapping and the other #DeleteMapping written in that order. However, when I try to call the #DeleteMapping method, the #PostMapping method is called.
Note that even though variables in the two URL forms are different, they have the same values. They are just from different HTML pages.
The question is how to call the desired method each time.
#PostMapping method
#Secured({"ROLE_ADMIN", "ROLE_STUDENT"})
#PostMapping("students/{username}/internships/{id}")
public String addInternship(Model model, #PathVariable("username") String username, #PathVariable("id") int id) {
/* DOES SOME STUFF
Student student = userService.getUser(username).getStudent();
Internship internship = userService.getInternship(id);
StudentInternship studentInternship = new StudentInternship(internship, student, "Sent");
internship.setNumberOfPositions(internship.getNumberOfPositions() - 1);
userService.updateInternship(internship);
student.setApplicationNumber(student.getApplicationNumber() + 1);
userService.updateStudent(student);
userService.addStudentInternship(studentInternship);
*/
return "redirect:/internships";
}
#DeleteMapping method
#Secured({"ROLE_ADMIN", "ROLE_STUDENT"})
#DeleteMapping("students/{username}/internships/{id}")
public String removeInternship(Model model, #PathVariable("username") String username, #PathVariable("id") int id) {
/* DOES SOME STUFF
Student student = userService.getUser(username).getStudent();
Internship internship = userService.getInternship(id);
int studentInternshipID = userService.getStudentInternshipByParams(student, internship).getId();
internship.setNumberOfPositions(internship.getNumberOfPositions() + 1);
userService.updateInternship(internship);
student.setApplicationNumber(student.getApplicationNumber() - 1);
userService.updateStudent(student);
userService.removeStudentInternship(studentInternshipID);
*/
return "redirect:/students/" + username + "/internships";
}
HTML
<form:form action="${pageContext.request.contextPath}/students/${username}/internships/${tempInternship.id}" method="POST">
<input type="submit" value="Request" ${disabled}/>
</form:form>
<form:form action="${pageContext.request.contextPath}/students/${tempStudentInternship.student.username}/internships/${tempStudentInternship.internship.id}" method="DELETE">
<input type="submit" value="Dismiss" />
</form:form>
Browsers only support GET and POST as http request methods. The solution is to send your form with the POST method and inject a hidden field inside the same html form called _method with your desired method as a value, in your case here, it is just DELETE. For the case of POST, just write your form as usual.
Example :
<form:form action="${pageContext.request.contextPath}/students/${tempStudentInternship.student.username}/internships/${tempStudentInternship.internship.id}" method="POST">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="Dismiss" />
</form:form>
Please, have a look at this answer for creating the spring bean and then applying the mentioned form attribute inside spring:form html forms.
Only GET and POST are allowed from a FORM. You need to use AJAX to specify additional types of the request.
Hi you need to use javascript and XMLHttpRequest in order to specify the type of the request.
Here is one example I took randomly from internet:
// Delete a user
var url = "http://localhost:8080/api/v1/users";
var xhr = new XMLHttpRequest();
xhr.open("DELETE", url+'/12', true);
xhr.onload = function () {
var users = JSON.parse(xhr.responseText);
if (xhr.readyState == 4 && xhr.status == "200") {
console.table(users);
} else {
console.error(users);
}
}
xhr.send(null);
I would like to know how to create forms that uses th:object for each object looped in a th:each. For example, I have the following code.
HTML
<th:block th:each="store: ${stores}">
<form th:object="${store}" th:action="#{/modify-store}">
<input th:field="*{idStorePk}"/>
<input th:field="*{name}"/>
<input th:field="*{phoneNumber}"/>
<button type="submit">Modify</button>
</form>
</th:block>
Controller
#RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
So, I would like to add a form for each object, but it seems that it is not possible and I get the following error.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'store' available as request attribute
So, I decided to add a #ModelAttribute in my controller, but can't get to return the actual store.
#ModelAttribute("store")
public Store getStore(Store store) {
return store;
}
With this approach all my forms have null values. I also tried to add a #PathVariable, but can't see to bind it using th:object. Is there a solution for this?
So for anyone stuck at a similar problem. I find out a work around that might help you out. First, you can't use th:object, it simply won't cut it. Instead, do the following.
<th:block th:each="store: ${stores}">
<form class="store-form" th:action="#{/modify-store}">
<input th:name="idStorePk" th:value="${store.idStorePk}"/>
<input th:name="name" th:value="${store.name}"/>
<input th:name="phoneNumber" th:value="${store.phoneNumber}"/>
<button class="submit-button" type="submit">Modify</button>
</form>
</th:block>
Then just add something similar to the controller.
#PostMapping(value = "/modify-store")
#ResponseBody
public boolean deleteEntry(#ModelAttribute Store store) throws Exception {
// Your code here...
return true;
}
If you want to send it asynchronously then you will need to add some JS code in order for it to work. It should look something like the code below.
const forms = document.querySelectorAll('.store-form');
forms.forEach(form => {
form.addEventListener('submit', event => {
// Stop the normal form submit triggered by the submit button
event.preventDefault();
const formInputs = form.getElementsByTagName("input");
let formData = new FormData();
for (let input of formInputs) {
formData.append(input.name, input.value);
}
fetch(form.action,
{
method: form.method,
body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log(error.message))
.finally(() => console.log("Done"));
});
You're sending stores in your controller in model-attribute and on your second controller where you're submitting your form you're using store that's the reason you're getting this error. So correct the spelling error on any one of your controller. Like this :-
#RequestMapping(value = "/stores")
public String getIndex(Model model) {
model.addAttribute("stores", storeService.getAllStores());
return "store";
}
And Your second controller where you're submitting your form will be like this -
#ModelAttribute("stores")
public Store getStore(Store store) {
return store;
}
I have a table for adding one object to another and deleting one object from another.
This is my controller:
#Controller
#RequestMapping("/proj/{pid}/coupling/{r1}")
public class CouplingController {
#RequestMapping(method = RequestMethod.GET)
public String getAllCouplings( ){
return "riskCoupling";
}
#RequestMapping(value = "/{r1}", method = RequestMethod.POST)
public String saveCoupling( ){
return "/projects";
}
#RequestMapping(value = "/{r2}", method = RequestMethod.DELETE)
public String removeCoupling(){
return "/projects";
}
}
This is my Thymeleaf view
<td>
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "post">
<input type="submit" name="Couple" value="Couple" class="btn btn-info" />
</form>
</td>
<td">
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "delete">
<input type="submit" name="RemoveCoupling" value="RemoveCoupling" class="btn btn-info" />
</form>
</td>
When I go to the url /proj/{pid}/coupling/{r1} I get the overview so the GET works.
When I press the Couple button it works. So the POST works.
When I press the RemoveCoupling button I get the error:
Request method "Get" not supported.
I really don't know why I get this error.
Since browsers unfortunately do not support any other verbs than POST and GET in HTML forms, they will just send POST/GET requests instead. This might be the reason why only that one does not work.
See more:
http://martinfowler.com/articles/richardsonMaturityModel.html
http://amundsen.com/examples/put-delete-forms/
http://www.quora.com/HTTP/Why-are-PUT-and-DELETE-no-longer-supported-in-HTML5-forms
I fixed this with Thymeleaf - Button click to call http delete method.
Basically putting a hidden field in my form.
I am using Spring Boot with Tymeleaf and am fairly new to it. I am using the Twitter API to make an app. I have a list of tweets displayed using th:each. When the user clicks the retweet button for a tweet, that particular tweet's id should be sent to the controller as a parameter for the retweet method, so that it can be processed there. The user should not be aware of the id from the front-end.
As of now my only solution is to include the id as a url in an 'a' tag and have it be taken as a path variable using #PathVariable in the controller and return the index page which has the list of tweets. This works fine but I do not like this method as the user should not have to leave the page and I may later prefer use a modal. Basically I want to know of a way to send the id to the controller without having it displayed in HTML.
index.html
<div class="result" th:each="result : ${results}">
<h3><a th:href="${result.fromUser}" th:text="${result.user.name}"></a></h3>
<p th:text="'#' + ${result.fromUser}"></p>
<span th:text="${result.text}"></span>
<div id="retweet">
<a th:href="'user/' + ${result.id}">Retwt</a>
</div>
</div>
Maincontroller.java
#RequestMapping(value = "")
public String getUserProfile(Model model) {
TwitterProfile userInfo = twitterService.getUserProfile();
List<Tweet> homeTimeline = twitterService.getUserTimeline();
model.addAttribute("profileImg", userInfo.getProfileImageUrl());
model.addAttribute("results", homeTimeline);
return "pages/index";
}
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String retweet(#PathVariable long id) {
twitterService.retweet(id);
return "/";
}
This can be achieved changing your controller method as bellow and access it using jQuery ajax http GET.
Changing controller to accept ajax request,
#ResponseBody
#RequestMapping(value = "/user", method = RequestMethod.GET)
public String retweet(#RequestBody Long id) {
twitterService.retweet(id);
return "/";
}
Change your HTML,
<div class="result" th:each="result : ${results}">
<h3><a th:href="${result.fromUser}" th:text="${result.user.name}"></a></h3>
<p th:text="'#' + ${result.fromUser}"></p>
<span th:text="${result.text}"></span>
<div id="retweet">
<a href="JavaScript:Void(0);" th:onclick="'doRetweet(\'' + ${result.id} + '\');'" >Retwt</a>
</div>
</div>
JQuery part,
function doRetweet(userId){
var data = JSON.stringify(userId)
if (data) {
$.ajax({
url : '/user',
headers : {
'Content-Type' : 'application/json'
},
method : 'GET',
dataType : 'json',
data : data,
success : function(data) {
if (data.status == 200) {
// Handle success response
}
},
error : function(xhr, status, error) {
// Handle errors here
}
});
}
}
I have a JSP page containing a data Table. I want to pass the content of the first column of a row ,when clicked, to another jsp page. I am working with spring framework. I have tried to achieve it,but with some problem. The problem is : When I am retrieving the parameter in another jsp page, i get some content,but the contents after the first blank space are skipped.
Here is the code I have tried:
source jsp page:
$(document).ready(function(){
$("#mytable tbody tr").click(function(){
var aPos = $('#mytable').dataTable().fnGetPosition(this);
var aData = $('#mytable').dataTable().fnGetData(aPos);
pass_on=aData[0];
window.location.href = "/test/integrate"+"?"+"group="+pass_on;
});
});
the controller:
#RequestMapping(value="/test/integrate", method = RequestMethod.GET)
public String pass_parameter_by_type(ModelMap model,HttpServletRequest request,HttpServletResponse response) {
return "destination";
}
the destination jsp page:
<body>
<input type="button" id="sub_but">
<input type="hidden" value=<%=request.getParameter("group") %> id="group_parameter">
$("#sub_but").click(function(){
alert($("#group_parameter").val());
});
</body>
if the value passed is "cool guy", then in url it shows test/integrate?group=cool guy.
but in alert it shows only "cool".
Please help.
Here is what I have done to get the results:
I replaced blank with an underscore("_").
pass_on=aData[0];
var res=pass_on.replace(/ /g,"_");
window.location.href = "/test/integrate"+"?"+"group="+res;
No more changes. Rest of it works fine.
I still don't know why %20 didn't work.
You need to pass cool%20guy.
URLs don't understand special chars
http://www.w3schools.com/tags/ref_urlencode.asp
Encode your text. For javascript
escape(...)
For java, use:
URLEncoder.encode( ,encoding)
In your controller do like this:
#RequestMapping(value="/test/integrate", method = RequestMethod.GET)
public String pass_parameter_by_type(ModelMap model,#RequestParam String group,HttpServletRequest request,HttpServletResponse response) {
model.addAttribute("group",group);
return "destination";
}
In your other JSP do like this:
<body>
<input type="button" id="sub_but">
<input type="hidden" value="${group}" id="group_parameter">
$("#sub_but").click(function(){
alert($("#group_parameter").val());
});
</body>
I hope it should solve your problem. :)