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.
Related
I want to implement a send mail form using Thymeleaf.
I have a page called start_page.html that contains this form :
<div class="w3-container w3-padding-64" id="contact">
<h1>Contact</h1><br>
<p>We offer full-service catering for any event, large or small. We understand your needs and we will cater the food to satisfy the biggerst criteria of them all, both look and taste. Do not hesitate to contact us.</p>
<p class="w3-text-blue-grey w3-large"><b>Catering Service, 42nd Living St, 43043 New York, NY</b></p>
<p>You can also contact us by phone 00553123-2323 or email catering#catering.com, or you can send us a message here:</p>
<form th:action="#{~/homePage/contact}" th:object="${contactMail}" method="post" target="_blank">
<p><input class="w3-input w3-padding-16" type="text" th:field="*{nom}" th:placeholder="#{homePage.nom}" required name="nom"></p>
<p><input class="w3-input w3-padding-16" type="text" th:field="*{prenom}" th:placeholder="#{homePage.prenom}" required name="prenom"></p>
<p><textarea class="w3-input w3-padding-16" type="text" th:field="*{message}" style="height: 250px;" th:placeholder="#{homePage.message}" required name="message"></textarea>
<p><button class="w3-button w3-light-grey w3-section" type="submit">[[#{homePage.envoyer}]]</button></p>
</form>
</div>
I have already implemented a controller for this form action
#Controller
#PropertySource(ignoreResourceNotFound = true , value = "classpath:messages.properties")
public class HomePageController {
#Autowired
private MailContactService mailService;
#RequestMapping(value = "/homePage/contact", method = RequestMethod.POST)
public String sendMessage(ContactMail contactMail){
mailService.sendContactMail(contactMail);
System.out.println("done");
return "/home/start_page";
}
}
I'm not getting the desired behavior: I though that my page will stay the same but my page is reloading.
I want to order the controller to do something without getting out of my page.
I googled and I found that I can send a service object to my page but I want to avoid this option if there is other solutions .
Thank you.
You'll need to use an AJAX call if you don't want to refresh your page.
What this means is that you want to intercept the default HTTP form post behavior (that will do a full page refresh) using javascript.
For this you need to :
Remove the action tag on your form (let javascript handle it when clicking the button to submit the form)
Add this to your page (will be executed when the form is submitted :
$(document).ready(function () {
$("#contact-form").submit(function (event) {
// do not post the form and trigger full page refresh
event.preventDefault();
var formData = .. // construct some formData
$.ajax({
type: "POST",
contentType: "application/json",
url: "/homePage/contact",
data: JSON.stringify(formData),
dataType: "json",
success: function (data) {
console.log("SUCCESS : ", data);
},
error: function (e) {
console.log("ERROR : ", e);
}
});
});
});
For a full example, as always, mkyong.com has got you covered :)
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 written an Ajax POST to submit a form, but it fails to succeed.
The Ajax POST comes through to the action in the controller which in turn will return a PartialView, which is also made correctly as I can debug this.
The goal is to let the user add a new log, whatever the outcome (failed captcha, failed validation) a Partialview will be returned with the right ViewBag errors messages. Razor takes care of the rest. This way the user will not be redirected to other pages.
When the Ajax succeeds it should put the data in the right <div>. The code is a copy of a working Ajax GET only changing it into a POST and providing the formdate serialized.
tldr; Ajax POST to Action in controller works, the partialview is rendered and returned yet the Ajax failes to succeed making it unable to update the designated <div>
Ajax call:
<script>
$('#add-log').click(function (event) {
event.preventDefault();
$.ajax({
url: $('#add-log').attr('data-url'),
type: 'post',
data: $("#log-form").serialize(),
succes: function (data) {
$('#add-log').attr('data-target').html(data);
console.log("Succes");
}
});
}
</script>
Controller:
[HttpPost, ValidateInput(false)]
[Authorize(Roles = "Student")]
public ActionResult Add(object sender, Log log, string returnURL, bool SendEmail)
{
ViewBag.Vulns = TempData["Vulns"];
//region Captcha: Here we have our Captcha settings
var response = Request["g-recaptcha-response"];
//secret that was generated in key value pair
const string secret = "Just a secret for our captcha, move along";
var client = new WebClient();
var reply = client.DownloadString(string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}", secret, response));
var captchaResponse = JsonConvert.DeserializeObject<CaptchaResponse>(reply);
if (ModelState.IsValid && captchaResponse.Success)
{
db.Log.Add(log);
db.SaveChanges();
if (SendEmail)
{
//Emails are sent from here on out, but that's out of the scope
}
}
else
{
if (!captchaResponse.Success)
{
ViewBag.fillcaptcha = "Please fill in the captcha";
}
else
{
ViewBag.Wrong = "Something went wrong please try again";
}
ViewBag.returnUrl = returnURL;
ViewBag.domain = log.DomainId;
return PartialView(log);
}
}
There's more code but I left that out. Most important thing in the controller is just the last IF. I test the ajax by posting an empty log with no captcha resulting in a partialview with the same log and the viewbag.captcha error. This partialview is rendered but somehow ajax doesn't succeed.
I have a form in which I need to call two action methods, one after the other. This is how the flow goes.
First I check if the prerequisite data is entered by the user. If not then I show a message that user needs to enter the data first.
If all the prerequisite data is entered, I call an action method which return data. If there is no data returned then I show a message "No data found" on the same page.
If data is returned then I call another action method present in a different controller, which returns a view with all the data, in a new tab.
The View:
#using (Ajax.BeginForm("Index", "OrderListItems", null, new AjaxOptions { OnBegin = "verifyRequiredData"}, new { #id = "formCreateOrderListReport", #target = "_blank" }))
{
//Contains controls and a button
}
The Script in this View:
function verifyRequiredData() {
if ($("#dtScheduledDate").val() == "") {
$('#dvValidationSummary').html("");
var errorMessage = "";
errorMessage = "<span>Please correct the following errors:</span><ul>";
errorMessage += "<li>Please enter Scheduled date</li>";
$('#dvValidationSummary').append(errorMessage);
$('#dvValidationSummary').removeClass('validation-summary-valid').addClass('validation-summary-errors');
return false;
}
else {
$('#dvValidationSummary').addClass('validation-summary-valid').removeClass('validation-summary-errors');
$('#dvValidationSummary').html("");
$.ajax({
type: "GET",
url: '#Url.Action("GetOrderListReport", "OrderList")',
data: {
ScheduledDate: $("#dtScheduledDate").val(),
Crews: $('#selAddCrewMembers').val(),
Priorities: $('#selPriority').val(),
ServiceTypes: $('#selServiceTypes').val(),
IsMeterInfoRequired: $('#chkPrintMeterInfo').val()
},
cache: false,
success: function (data) {
debugger;
if (data !== "No data found") {
//var newUrl = '#Url.Action("Index", "OrderListItems")';
//window.open(newUrl, '_blank');
return true;
} else {
//Show message "No data found"
return false;
}
}
});
return false;
}
}
The "GetOrderListReport" Action method in "OrderList" Controller:
public ActionResult GetOrderListReport(OrderListModel model)
{
var contract = new OrderReportDrilldownParamDataContract
{
ScheduledDate = model.ScheduledDate
//Setting other properties as well
};
var result = OrderDataModel.GetOrderList(contract);
if (string.IsNullOrWhiteSpace(result) || string.IsNullOrEmpty(result))
{
return Json("No data found", JsonRequestBehavior.AllowGet);
}
var deserializedData = SO.Core.ExtensionMethods.DeserializeObjectFromJson<OrderReportDrilldownDataContract>(result);
// send it to index method for list
TempData["DataContract"] = deserializedData;
return Json(deserializedData, JsonRequestBehavior.AllowGet);
}
The last action method present in OrderListItems Controller, the result of which needs to be shown in a new tab:
public ActionResult Index()
{
var deserializedData = TempData["DataContract"] as OrderReportDrilldownDataContract;
var model = new OrderListItemViewModel(deserializedData);
return View(model);
}
The problem is that I am not seeing this data in a new tab, although I have used #target = "_blank" in the Ajax.BeginForm. I have also tried to use window.open(newUrl, '_blank') as can be seen above. But still the result is not shown in a new tab.
Please assist as to where I am going wrong?
If you are using the Ajax.BeginForm you shouldn't also be doing an ajax post, as the unobtrusive ajax library will automatically perform an ajax post when submitting the form.
Also, if you use a view model with data annotation validations and client unobtrusive validations, then there would be no need for you to manually validate the data in the begin ajax callback as the form won't be submitted if any validation errors are found.
The only javascript code you need to add in this scenario is a piece of code for the ajax success callback. That will look as the one you currently have, but you need to take into account that opening in new tabs depends on the browser and user settings. It may even be considered as a pop-up by the browser and blocked, requiring the user intervention to allow them as in IE8. You can give it a try on this fiddle.
So this would be your model:
public class OrderListModel
{
[Required]
public DateTime ScheduledDate { get; set; }
//the other properties of the OrderListModel
}
The form will be posted using unobtrusive Ajax to the GetOrderListReport of the OrderList controller. On the sucess callback you will check for the response and when it is different from "No data found", you will then manually open the OrderListItems page on a new tab.
This would be your view:
#model someNamespace.OrderListModel
<script type="text/javascript">
function ViewOrderListItems(data){
debugger;
if (data !== "No data found") {
var newUrl = '#Url.Action("Index", "OrderListItems")';
//this will work or not depending on browser and user settings.
//passing _newtab may work in Firefox too.
window.open(newUrl, '_blank');
} else {
//Show message "No data found" somewhere in the current page
}
}
</script>
#using (Ajax.BeginForm("GetOrderListReport", "OrderList", null,
new AjaxOptions { OnSucces= "ViewOrderListItems"},
new { #id = "formCreateOrderListReport" }))
{
#Html.ValidationSummary(false)
//input and submit buttons
//for inputs, make sure to use the helpers like #Html.TextBoxFor(), #Html.CheckBoxFor(), etc
//so the unobtrusive validation attributes are added to your input elements.
//You may consider using #Html.ValidationMessageFor() so error messages are displayed next to the inputs instead in the validation summary
//Example:
<div>
#Html.LabelFor(m => m.ScheduledDate)
</div>
<div>
#Html.TextBoxFor(m => m.ScheduledDate, new {id = "dtScheduledDate"})
#Html.ValidationMessageFor(m => m.ScheduledDate)
</div>
<input type="submit" value="Get Report" />
}
With this in place, you should be able to post the data in the initial page using ajax. Then based on the response received you will open another window\tab (as mentioned, depending on browser and user settings this may be opened in a new window or even be blocked) with the second page content (OrderListItems).
Here's a skeleton of what I think you are trying to do. Note that window.open is a popup though and most user will have popups blocked.
<form id="formCreateOrderListReport">
<input type="text" vaule="testing" name="id" id="id"/>
<input type="submit" value="submit" />
</form>
<script type="text/javascript">
$('#formCreateOrderListReport').on('submit', function (event) {
$.ajax({
type: "POST",
url: '/home/test',
data: { id: $('#id').val()},
cache: false
}).done(function () {
debugger;
alert("success");
var newUrl = '/home/contact';
window.open(newUrl, '_blank');
}).fail(function () {
debugger;
alert("error");
});
return false;
});
</script>
Scale down the app to get the UI flow that you want then work with data.
I've got a page that contains multiple links. These links should do an ajax post and callback. However, the link is doing a Get instead of a Post. This causes a 404 error since I do not have an action method to handle a get at the requested URL.
If I remove the HTTPPost attribute from my Action method, the link works, but the call back fails and the Json I return is rendered in a new page.
Here is the code I'm using in my view.
<td id="action-#item.ItemID">#Ajax.ActionLink("Add", "AddToOrder", new { itemID = item.ItemID }, new AjaxOptions { HttpMethod = "POST", OnSuccess = "actionCompleted" }, new { id = "add-" + item.ItemID })</td>
This ends up adding this HTML:
<td id="action-012679"><a data-ajax="true" data-ajax-method="POST" data-ajax-success="actionCompleted" href="/mysite/neworder/AddToOrder?itemID=012679" id="add-012679">Add to Order</a></td>
My Controller has the following Action Method.
[HttpPost]
public JsonResult AddToOrder(string itemID) {
return Json(new { id = itemID, Action = "Added", "Just getting this working"});
}
My callback method that is called on Success looks like this:
<script language="javascript" type="text/javascript">
function actionCompleted(response, status, data) {
alert("We have returned");
}
</script>
If I change the [HTTPPost] attribute on my action method to [HTTPGet] I get an Json error. I can fix this by adding the JsonRequestBehavior.AllowGet to my return value, but this doesn't use the call back function defined on the page and fails.
Any help would be appreciated.
Probably you don't have jquery.unobtrusive-ajax.js script attached to page and link is gracefully degraded to regular anchor.
In order to have this working properly. You need to add references to these scripts on your page:
MicrosoftAjax.js
MicrosoftMvcAjax.js