I'm attempting to save a record using an Ajax call in Grails 3.3.4. I've looked over other posts but none of them seem to have a resolve that has helped me.
The remote tags are deprecated in Grails 3, so that won't be any option. I also need to grab all my form fields, not individual text boxes. But it seems like my form data isn't getting passed through. Any ideas?
When I submit the form, I receive this error:
Error 500: Internal Server Error
URI /user/saveAjax
Class groovy.lang.MissingMethodException
Message null
Caused by No signature
of method:
com.vogella.grails.guestbook.$UserServiceImplementation.save() is
applicable for argument types:
(grails.web.servlet.mvc.GrailsParameterMap) values: [[controller:user,
format:null, action:saveAjax]] Possible solutions:
save(com.vogella.grails.guestbook.User), wait(), any(), wait(long),
any(groovy.lang.Closure), isCase(java.lang.Object)
Domain
package com.vogella.grails.guestbook
class User {
String name
String last
static constraints = {
name (blank:false, nullable:false, size:3..30, matches:"[a-zA-Z1-9_]+")
}
String toString(){
return name;
}
}
Controller
package com.vogella.grails.guestbook
import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
class UserController {
UserService userService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
static scaffold = User
def saveAjax(params) {
render params
userService.save(params)
render "Success!"
}
}
GSP
<!doctype html>
<html>
<head>
<meta name="layout" content="main"/>
<title>Welcome to Grails</title>
<g:javascript library='jquery' />
</head>
<body>
<g:form id = "my_awesome_form">
<g:textField id = "box1" name="mytextbox"/>
<g:textField id = "box2" name="mytextbox2"/>
<input type="button" id = "mybutton" onclick="changeName()" value="create user"/>
</g:form>
<div id="resultDiv"></div>
<script>
function changeName()
{ event.preventDefault();
$.ajax({
url:"<g:createLink url="[action:'saveAjax',controller:'User']" />",
dataType: "json",
type:"post",
data: $('#my_awesome_form').serialize(),
success: function() {
$( "#resultDiv" ).addClass( 'alert alert-info' ).append( 'Successfully saved event' )
},
error: function(xhr, status, error) {
$( "#resultDiv" ).addClass( 'alert alert-info' ).append(xhr.responseText);
}
});
}
</script>
</body>
</html>
When trying to call params.box1 when passing the data by
$('#my_awesome_form').serialize();
I receive:
Caused by Ambiguous method overloading for method
grails.artefact.controller.support.ResponseRenderer$Trait$Helper#render.
Cannot resolve which method to invoke for [class
com.vogella.grails.guestbook.UserController, null] due to overlapping
prototypes between: [interface
grails.artefact.controller.support.ResponseRenderer, interface
java.lang.CharSequence] [interface
grails.artefact.controller.support.ResponseRenderer, interface
java.util.Map]
The error indicates you're passing a map params to the userService save method which expects a User object, you could either change the userService save method implementation so it accepts a map or just pass it on like this:
userService.save( new User( params ) )
This assumes the field ids in your form match the field names in your User domain so Grails is able to bind them, if not change them to match (much easier) or you'll have to manually set them like:
def user = new User()
user.name = params.box1
...
Related
How can I get a simple message (like msg) from jsp/ajax in spring controller and return a response (like resp) from controller back to the jsp, while jsp could get this response using ajax and show that in a <div> ?
Maybe the following sample is incorrect, especially the ajax part, appreciate to modify:
here is my controller:
#RequestMapping("/testAjax")
protected ModelAndView testActiveX(HttpServletRequest request,HttpServletResponse response){
ModelAndView model = new ModelAndView("test_Ajax");
Date date=new Date();
model.addObject("date",date.toString());
return model;
}
here is my test_Ajax.jsp page:
<html lang="en">
<%#taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<head>
<spring:url value="/resources/jquery-3.1.1.min.js" var="jqueryJs"></spring:url>
<script src="${jqueryJs}"></script>
<script >
$(document).ready(function($)
{
$("#translate").click(translation());
});
function translation() {
var words = $("input").val();
$.ajax({
url: '/WEB-INF/jsp/test_Ajax.jsp',
data: {
word: words
},
success: function (data) {
$("#container").html(data);
},
type: 'GET'
});
}
</script>
</head>
<body>
<input type="text" id="input" name="inputword"><br/><br/> <-- used to provide msg --%>
<button id="translate">Translate</button><br/><br/>
<div id="container"></div>
</body>
</html>
As I can see, you have a mistake in the ajax call url, as you are trying to access a jsp inside WEB-INF folder. As you are using spring mvc with a #RequestMapping, you must point the ajax call to the url listened by this mapping.
In your case you must change the url to point the servlet endpoint, which is the actor who serves you the composed html of the view
$.ajax({
url: '/testAjax',
data: {
word: words
},
success: function (data) {
$("#container").html(data);
},
type: 'GET'
});
Maybe there can be another mistakes as your request mapping can be in a context (so you can put the requestContext concat to the requestmapping url.
Hope this helps
The code you posted should work with the correct url in the ajax call. Java apps have a context (start of the URI) so you have to be aware to this when telling the url (you can use jstl expressions like ${contextPath} to get this value if you are using a web 3.0 spec).
You can also receive query params (sent via post or get) adding params to the controller method and annotating them with #QueryParam who receives the name of the param to map (this also work with complex json objects if you have a JacksonMapper bean defined in the spring context). So for example. to receive the word string you have to add
#QueryParam("word") String word
to the method signature.
You can also work directly with the HttpServletRequest in that method and get the content of the param like this:
String word = request.getParam("word");
inside the controller.
I'm trying to add a search form in the navbar on every page in my spring mvc web app, just like the one here on stackoverflow, and I'm having issues. Right now I have a working search functions on a couple of my pages, using the typical mvc forms. I take the inputted string and store a variable called "searchString" in an object I created called "searchForm.java". Then I try to query that inputted string in the database using spring data's findbycontaing method, and then put that result on the model, and then represent that on the view, using thymeleaf. However I think that the navbar should be done using ajax, since it's on every page and pages with other forms.
So I think I'm sending the string that was submitted to the search form in the navbar to the controller where I queried it in the repository to bring back search results, then I tried to put the search results on the model, but I get nothing, all it does is redirect me to the search page. I may not be making very much sense, but I'll show my code, and if anyone could let me know if I'm going about my problem in the right way or not, and if you guys see any errors in my code. Thanks in advance.
So here's my ajax and jquery to submit the form.
<script th:inline="javascript">
/*<![CDATA[*/
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#searchButton").on("click", function(ev) {
$.ajax({
url : "navSearch",
type : "post",
data : {
"newSearch" : $("#newSearch").val()
},
success : function(data) {
console.log(data);
},
error : function() {
console.log("There was an error");
}
});
});
});
/*]]>*/
</script>
There may be an issue here, because in the console in the chrome developer tools, before it redirects, a message pops up very quickly that says uncaught TypeError: Cannot read property 'toLowerCase' of undefined, and it's coming from jquery.min.js:5 so that could be my issue, but I have no idea how to go about fixing this, and I've searched for answers so far with no luck.
Here's my html form, I think this shouldn't be a problem, but who knows, so I'll put it up anyways. And I'm using thymeleaf for this view.
<form action = "setSearch" class="navbar-form navbar-right">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" id="newSearch"></input>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</div>
<button type="submit" class="btn btn-default" id="searchButton">Search</button>
</form>
Here's my searchForm.java class, where I temporarily store the string to be queried in the database.
public class SearchForm {
private String searchString;
public String getSearchString()
{
return searchString;
}
public void setSearchString(String searchString)
{
this.searchString = searchString;
}
}
And Here's my controller, where I'm trying to handle the ajax submission and return it as search results on the setSearch.html page. What I'm thinking here is that the string "newSearch" from the form could be matched using the Spring Data query methods, and then be able to return it and add it to the model, but it's not working, it's just redirecting me to the /searchSet page with no data, because that's where the form action goes and that's what I tell it to return. So honestly I'm no sure if any data is even getting to this point.
#RequestMapping(value="setSearch/navSearch", method=RequestMethod.POST)
public #ResponseBody String navSearch (#RequestParam String newSearch, ModelMap model)
{
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(newSearch);
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
And here's an example of a working search method that I have in my controller that I use on a regular form, with no ajax, on the /searchSet page.
#RequestMapping(value="/setSearch", method=RequestMethod.GET)
public String searchGet(ModelMap model) {
SearchForm searchForm = new SearchForm();
model.put("searchForm", searchForm);
return "setSearch";
}
#RequestMapping(value="/setSearch", method=RequestMethod.POST)
public String searchPost(#ModelAttribute SearchForm searchForm, ModelMap model) {
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(searchForm.getSearchString());
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
UPDATE
I've changed my code in the form from <button type="submit" class="btn btn-default" id="searchButton">Search</button> to <button type="button" class="btn btn-default" id="searchButton">Search</button> and now I get the Uncaught TypeError: Cannot read property 'toLowerCase' of undefined from earlier and nothing happens with the page.
UPDATE
I can now submit the ajax form without a problem, I was missing meta tags in the header, so the csrf wasn't submitting correctly, so now I get this error in the chrome developer tools console XHR Loaded (navSearch - 405 Method Not Allowed - 7.265999971423298ms - 634B)
UPDATE
Now everything works on the Ajax side, I needed to adjust my url to match the url I had in the request mapping on the controller and it runs through all the code fine. However the overall search function still doesn't work, here's my updated controller.
I know my issue here is that I'm returning a string and not an object, but I'm not sure how to return the object and then redirect the url to the /setSearch page. It's running through the code and returning a string "setSearch" in the console, because I told it to at the end of the controller.
#RequestMapping(value="/setSearch/search", method=RequestMethod.POST)
public #ResponseBody String search (#RequestParam String newSearch, ModelMap model)
{
List<QuestionAnswerSet> questionAnswerSetByQuestion = (List<QuestionAnswerSet>) questionAnswerSetRepo.findByQuestionContaining(newSearch);
model.put("searchResult", questionAnswerSetByQuestion);
return "setSearch";
}
Here's my working ajax
<script th:inline="javascript">
/*<![CDATA[*/
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
$(document).ready(function(){
$("#searchButton").on("click", function(ev) {
$.ajax({
url : "/setSearch/search",
type : "post",
data : {
"newSearch" : $("#newSearch").val()
},
success : function(data) {
console.log(data);
},
error : function() {
console.log("There was an error");
}
});
});
});
/*]]>*/
</script>
but it's not working, it's just redirecting me to the /searchSet page
with no data, because that's where the form action goes and that's
what I tell it to return
You are right, it is because you are submitting the form and in the action you specify it to submit to setSearch, that is why the page is getting redirected to the same page. Just replace button type="submit" with button type="button" so that the form will not be submitted when searchButton is clicked.
I have a loaded jsp page with products and addtocart link against it. I am trying to show the cart in a div in the same page. I want to send html as response. This is what i have done. It just returns the string <div>output</div>. Can someone show me how i should do it.
Controller
#RequestMapping(value="/addtocart{id}", produces = "text/plain;charset=UTF-8")
#ResponseBody
public String addToCart(#PathVariable("id") int id, #ModelAttribute("cart") Cart cart,Model model)
{
Product product = productService.getProductById(id);
if (product != null) {
CartLine line = new CartLine();
line.setProduct(product);
line.setQuantity(1);
productService.updateProduct(product);
}
return "<div>output</div>";
}
JSP
<td><a id="demo4" href="addtocart${product.id}">Add To Cart</a> </td>
$('#demo4').click(function() {
$.ajax({
url : '/addtocart{id}',
dataType: 'json',
contentType: "text/html",
type : 'GET',
data :{id:id},
success : function(response) {
$('#output').html(response);
}
});
});
<div id="output" style="display:none">
<h2>Cart Content(s):</h2>
</div>
I also favor the approach with using the view, and separate page even on ajax call. Nevertheless what you're asking is possible, simply change your produces = "text/plain;charset=UTF-8" to produces
produces = "text/html;charset=UTF-8"
There are many other aspects that appear wrong, not related to Spring MVC, so even with the produces corrected you still have to do few corrections to get what you're expecting.
I think that you're not sending an ajax call at all. You're most likely doing a complete browser redirect. When first read, I was confused that "text/plain" vs "text/html" makes a difference in an ajax response, but now I believe that you're actually redirecting via browser. Change this <a id="demo4" href="addtocart${product.id}">Add To Cart</a> into something like this <a id="demo4" href="#">Add To Cart</a> and add return false to the end of your function. This will execute the function, and the return will make sure that the link is not followed
When you do this you'll notice a few issues with your ajax call as well; firstly, url : '/addtocart{id}' should be url : '/addtocart${product.id}
Capture your response in complete function not in success, and get the output as response.responseText, the response will return fine, but the browser will attempt to parse it as json and fail.
Your div will remain invisible, you should add some js to toggle that
One Spring MVC gotcha, your Cart bean seems to have a property called id as well. Because of this, your id path variable should be renamed, otherwise it'll be ignored
This would be something that, if not completly, than much closer to working
<a id="demo4" href="#">Add To Cart</a>
<div id="output"></div>
<script>
$('#demo4').click(function() {
$.ajax({
url : '/addtocart${product.id}',
dataType: 'json',
contentType: "text/html",
type : 'GET',
data :{id:4},
complete: function(response) {
$('#output').html(response.responseText);
}
});
return false;
});
</script>
Renaming PathVariable
#RequestMapping(value="/addtocart{productId}", produces = "text/plain;charset=UTF-8")
public String addToCart(#PathVariable("productId") int productId, #ModelAttribute("cart") Cart cart,Model model)
You can do it with AJAX and still return a separate page for your Card, as Slava suggested.
JSP page cart.jsp:
<%# page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!-- custom html for your cart representation, just an example -->
<div>
<h1>${cart.headline}</h1>
<p>${cart.otherProperty}</p>
</div>
Controller:
#RequestMapping(value="/addtocart{id}")
public String addToCart(#PathVariable("id") int id, #ModelAttribute("cart") Cart cart, Model model) {
doSomethingWithCart(cart);
model.addAttribute("cart", cart); // add cart to model after doing some custom operations
return "cart"; // resolved to cart.jsp by your view resolver
}
This way you are using AJAX but still you are returning dynamic html content (
adjusted to one specific cart).
I have already read this and this to get some help but there is something wrong in my code. Well I want to insert a form to database via Ajax and this is what i did:
The Ajax function :
<script type="text/javascript">
function doAjaxPost() {
var form = $('#ajf');
frm.submit(function () {
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/ajouter_user",
data: form.serialize(),
success: function(response){
// we have the response
$('#info').html(response);
},
error: function(e){
alert('Error: ' + e);
}
});
});
}
</script>
'#info' is the ID of the DIV where i want to show the success message returned by the controller.
this is my controller :
#RequestMapping(value="/ajouter_user",method=RequestMethod.POST)
public #ResponseBody String addUser(#ModelAttribute User us,BindingResult result,ModelMap model){
String returnText;
if(!result.hasErrors()){
model.addAttribute("us", new User());
userservice.AddUser(us);
model.addAttribute("usersystem", userservice.getAllUsers());
return returnText = "User has been added to the list." ;
}else{
return returnText = "Sorry, an error has occur. User has not been added to list.";
}
}
HTML :
<form:form id="ajf" method="POST" commandName="user">
Here are my fields ...
<input type="submit" value="Créer" onclick="doAjaxPost()"/>
</form:form>
What is wrong is : I don't get the String that the controller return , I get an alert error (object [] object ), the data is inserted to database and the page reload after submitting without giving any error
Can someone give me a toturial how to use Ajax with spring (inserting to database )
please help me
If you are using jQuery (it looks like you are), then you need to include the event in the function and do an event.preventDefault() to prevent the form from submitting. Otherwise the event will propagate up and the form will submit, and you will get the ajax post and the form post.
I want some help in liferay with ajax.
Right now I am calling the ajax method from my view.jsp page to submit some data.
Here is sample code I am using in view.jsp:
<%# include file="/init.jsp"%>
<portlet:actionURL name="AddTest" var="add1" />
<portlet:resourceURL id="AddTest" var="AddTest"></portlet:resourceURL>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript">
function addToDo(addToDo){
var todo =document.getElementById('toDo').value;
$.ajax({
url :addToDo,
data: {"todo":todo,"CMD":"addToDo"},
type: "GET",
dataType: "text",
success: function(data) {
$("#toDoList").html(data);
}
});
}
</script>
</head>
<body>
<portlet:resourceURL var="addToDo" id="addToDo"></portlet:resourceURL>
<form>
<input type="text" name="toDo" id="toDo">
<button name="Add" type="button" onclick="addToDo('<%=addToDo%>')">Add</button>
<div id="toDoList">
</div>
</form>
</body>
</html>
and in my portlet.java class there is one method which is called by this ajax call:
#Override
public void serveResource(ResourceRequest request, ResourceResponse response){
if(request.getParameter("CMD").equals("addToDo")) {
System.out.println("came here for add");
mediatype userToDo = new mediatypeImpl();
//userToDo.setMediaId(12345);
try {
userToDo.setPrimaryKey((CounterLocalServiceUtil.increment()));
userToDo.setMedianame(request.getParameter("todo"));
mediatypeLocalServiceUtil.addmediatype(userToDo);
}
catch (SystemException e) {
e.printStackTrace();
}
}
}
So my question is that right now it is just caling by default #override method from any ajax class.
But how can I call specific method of portlet.java class on ajax call?
I am new bee in ajax. So please guide me anyways u can.....
I got following error when calling ajax with url of following
<portlet:actionURL name="ajax_AddAdvertise" var="addToDo" windowState="<%= LiferayWindowState.EXCLUSIVE.toString()%>"> </portlet:actionURL>
06:47:03,705 ERROR [http-bio-8080-exec-23][render_portlet_jsp:154] java.lang.NoSuchMethodException: emenu.advertise.portlet.RestaurantPortlet.ajax_AddAdvertise(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
at java.lang.Class.getMethod(Class.java:1605)
my process action method as follows
#ProcessAction(name = "ajax_AddAdvertise")
public void ajax_AddAdvertise(ResourceRequest request,ResourceResponse response) {
}
how can I call specific method of portlet.java class on ajax call?
I think we can't have two different versions of serveResource methods like we do for action methods atleast not with the default implementation.
If you want different methods you would have to go the Spring MVC (#ResourceMapping) way to have that.
Still, you can define different logic in your serveResource method using the resourceId as follows (a full example):
In the JSP:
<portlet:resourceURL var="myResourceURL" id="myResourceID01" />
In the portlet class the serveResource method will contain the following code:
String resourceID = request.getResourceID();
if(resoureID.equals("myResourceID01")) {
// do myResourceID01 specific logic
} else {
// else do whatever you want
}
Please keep in mind [Important]
In a portlet you should not use <html>, <head>, <body> tags since portlets generate fragment of the page and not the whole page. Even if it is allowed your resultant page will not be well-formed and will behave differently on different browsers. And moreover the javascript which modifies DOM element will be totally useless.
Edit after this comment:
You can also use ajax with action methods:
People use <portlet:actionURL> with ajax generally for <form>-POST.
For this the actionURL is generated in a slightly different way in your jsp like this:
<portlet:actionURL name="ajax_AddAdvertise" var="addToDo" windowState="<%= LiferayWindowState.EXCLUSIVE.toString()%>">
</portlet:actionURL>
And in your portlet you can have (as in the question):
#ProcessAction(name = "ajax_AddAdvertise")
public void ajax_AddAdvertise(ActionRequest request, ActionResponse response) {
// ... your code
}