How to Ajax POST after a Form Submit - javascript

I think this is pretty simple but I cannot find anywhere how to do this, as the title says, how do you do an ajax post after a successful submit form post. I tried to search for it but all I see is the reverse of what I need which is submit after an ajax post. I'll try to make a draft of a program very similar to what Im working on.
This is my form.
<h2>My Form </h2>
#using (Html.BeginForm(new { #class = "submitForm" }))
{
<label>Loan Amount</label>
#Html.DropDownListFor(model => model.Loan.LoanAmount, Model.DropDownOfLoanAmount, new { #class = "LoanAmount", #data_bind = "value: selectedLoanAmount" })
#Html.ValidationMessageFor(model => model.Loan.LoanAmount)
<label>Loan Receivable</label>
#Html.TextBoxFor(model => model.Loan.LoanReceivable, "{0:0,0.00}", new { #class = "LoanReceivable", #readonly = true, dir = "rtl", #data_bind = "value: loanReceivable" })
#Html.ValidationMessageFor(model => model.Loan.LoanReceivable)
<label>Interest</label>
#Html.TextBoxFor(model => model.Loan.Interest, "{0:0,0.00}", new { #readonly = true, #class = "Interest", dir = "rtl", #data_bind = "value: interest" })
<table class="input-group">
<tbody data-bind="foreach: loanDeductions">
<tr>
<td><strong data-bind='text: deductionName'></strong></td>
<td>
<input class="deductionCode form-control" data-bind='value: amount, valueUpdate: "afterkeydown"' /></td>
<td><a href='#' data-bind='click: $parent.removeLine'>Delete</a></td>
</tr>
</tbody>
</table>
<button type="button" class="btn btn-danger">Save Deduction</button>
<button type="submit" class="btn btn-primary">Save changes</button>
}
This is an example ajax post (dont mind the logic of the post):
$.ajax({
'url' :'#Url.Action("UpdateDeductionValues","LoanApp")',
'data': {amount : $('.LoanAmount').val()},
'success': function(result) {
$.each(result, function (idx, items) {
$('.' + items.Code + '').val(items.Amount.toFixed(2));
});
}});
Now what I want to happen is when I submit that form, if the submit is successful, the ajax post is triggered. So its like posting 2 times in one button, the difference is that the other one is a form submit while the other is an ajax post. PLS help.

What you should do is "take over the submit button" with your jQuery, and then use an ajax call inside.
For example having this submit button:
<input type="submit" value="Submit" onsubmit="$('#your-form-id-here').submit()">
And having this jQuery submit function with an ajax call, should give you a pretty good idea on what to do.
$('#your-form-id-here').submit(function (e) {
e.preventDefault();
var senddata = $(this).serializeArray();
var sendto = $(this).attr("action");
$.ajax({
url: sendto,
type: 'POST',
data: senddata,
success: function (data) {
$('.messages').html(data);
},
error: function (error) {
$('.messages').html(error);
}
});
});
This basically takes your normal form, and send it through your ajax call, to your normal form action, so basically it works just like normal, but you now have the opportunity to do stuff in your form action php file, and also in your ajax success data. For example you could use this to deliver validation messages directly to your user, without refreshing your site. And so on...

i'm not sure what you are trying to do, what the point of doing ajax request after submitting the form the page will reload anyway.
however if need need do the ajax request after the submitting the form you just need an ajax call. but that that will work on every time when page load.
what i think you need to do make two ajax request the first one is for the submitting the form and if the call is successful then call the second request.

Related

Regularly send POST request when user changes value in <textarea>

I created a form in which user puts some food names to the menu in textarea and then he/she can press button to submit this form and send a POST request. After submitting, all data from textareas (there are 20 of them) is being sent to mongoDB. It works absolutely fine.
The problem is: it takes time for user to fill all 20 textareas, so i wanted to make some kind of "autosave", just in case browser stops working or other errors occur and to prevent data loss.
Data must be sent to database every time changes occur in the textarea. How is it possible to send POST request from form without clicking submit button and just by changing value of textarea.
<form method="POST" action="/">
<table>
<caption style="margin-bottom: 20px; font-size: 2em"><b>MENÜÜ</b></caption>
<tr>
<td style="column-span: 2; border: none; font-size: 0.8em">ESMASPÄEV</td>
</tr>
<tr>
<td class="hommikusook">Hoomikusöök</td>
<td class="userinput"><textarea name="MondayBreakfast" class="textarea" placeholder="Kirjuta siia" autofocus></textarea></td>
</tr>
<tr>
<td class="vitamiiniamps">Vitamiiniamps</td>
<td class="userinput"><textarea name="MondayVitamin" class="textarea" placeholder="Kirjuta siia"></textarea>
</td>
</tr>
<tr>
<td class="louna">Lõunasöök</td>
<td class="userinput"><textarea name="MondayLunch" class="textarea textarealong" placeholder="Kirjuta siia"></textarea></td>
</tr>
<tr>
<td class="oode">Oode</td>
<td class="userinput"><textarea name="MondayOode" class="textarea" placeholder="Kirjuta siia"></textarea>
</td>
</tr>
</table>
<button type="submit">Submit</button>
</form>
I tried adding eventListener in frontend JS file and i managed to console.log these value of the textarea everytime it is being changed, but don't know how to send them to the backend so i could add this to Database.
var numberOfTextAreas = document.querySelectorAll('.textarea').length;
for (var i = 0; i < numberOfTextAreas; i++) {
document.querySelectorAll(".textarea")[i].addEventListener("change", function () {
console.log('New value: ', this.value);
console.log('Name: ', this.name);
});
}
For backend is used express.
Thanks!
You need to use fetch API or AJAX. So every time the user changes you have to call this function:
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
// Then you call the function like this:
postData('https://example.com/autosave', { text: 'This is the text I want to save' })
.then(data => {
// Autosaved successfully!
});
I would recommend debounce this function or call it every time the user blurs the textarea (performance reasons).
Auto-Save forms components are using the onchange (and/or onkeyup and/or onkeydown) attribute to register to the event and call the save method.
In addition, there is the issue of triggering to many requests. This can be solved by setTimeout and clearTimeout;
The code will look something like this (pseudo-code):
//HTML
<form method="POST" action="/" name="myForm">
<textarea name="textareaName_1" onchange="handleAutoSave"></textarea>
<textarea name="textareaName_2" onchange="handleAutoSave"></textarea>
...
</form>
// JS
var autoSaveProgress = null;
function handleAutoSave() {
var formData = new FormData(document.querySelector('[name]="myForm"'));
// cancel last request
if(autoSaveProgress){
autoSaveProgress();
autoSaveProgress = null;
}
autoSaveProgress = setTimeout(function() {
saveToLocalstorage('KEY', formData);
} ,1000); // 1s delay till commit
}

Waiting for an async call to finish before submitting a form

This one is a bit tricky.
So here's my jQuery code:
/**
* Updating websites
*/
$('.website').on('blur', function () {
var websiteId = this.id;
console.log(websiteId);
var website = this.value;
console.log(website);
fetch(`/api/edit/${websiteId}&${website}`).then(() => {
});
})
Here's my HTML
<form method="post">
<% for(let i = 0; i < websites.length; i++){ let website = websites[i]; %>
<fieldset id="site<%= i %>">
<p style="color: <% if(errorMsgs[i] === 'Wrong website address'){ %> red;<% } else { %> green;<% } %>
padding-bottom: 0;
padding-top: 10px;
margin-bottom: 0;"
class="">
<%= errorMsgs[i] %>
</p>
<div class="">
<input class="website"
name="website<%= i %>"
id="<%= i %>"
value="<%= website %>"
type="text"/>
<a href="/websites/edit/<%= i %>"
class="btn btn-info hide">
Edit
</a>
<a href="/websites/delete/<%= i %>"
data-id="<%= i %>"
class="btn btn-danger confirmation removeBtn">
Remove
</a>
</div>
</fieldset>
<% } %>
<button type="submit"
class="btn btn-primary col-sm-offset-2"
value="Generate a report"
name="generateReport"
data-toggle="modal"
data-target="#exampleModal">
Generate a report
</button>
<!-- Save button created using Javascript. In case JS is disabled -->
</form>
Here's my API
// GET: api/edit/_id - save updates
router.get('/edit/:_id&:url', (req, res, next) => {
Account.findById(req.user._id, (err, acc) => {
if (err) {
console.log(err);
} else {
acc.websites.set(req.params._id, req.params.url);
acc.save((err, webs) => {
if (err) {
console.log(err);
} else {
console.log('all good');
// res.redirect('/reports');
res.json(webs);
}
});
}
});
});
How it works: The user inputs the value into the input fields, on blur, I make an AJAX call to the API, the value is saved in the db. And all this works.
There is one problem though. If the user hits the submit button, the fetch has no time to run. And it makes sense.
But I need the submission to wait until the value is updated. I was thinking about doing smth like, adding a preventDefault to the submit button, then after it's saved, removing the preventDefault.
But I have no idea how to do it, if it's a good idea or even if it makes any sense.
So to recap: I need the form submission to wait until the AJAX call is finalized.
This <% smth %> is just the EJS templating system. Shouldn't affect how it all works.
It looks like fetch() returns a promise. Save those promises in a shared variable. In your submit button code, use Promise.all() to wait for all the promises to resolve. Something like:
const fetchPromises = [];
$('.website').on('blur', function () {
var websiteId = this.id;
console.log(websiteId);
var website = this.value;
console.log(website);
var fetchPromise = fetch(`/api/edit/${websiteId}&${website}`).then(() => {
});
fetchPromises.push(fetchPromise);
});
$('form').on('submit', function (e) {
e.preventDefault();
Promise.all(fetchPromises).then(() => this.submit());
});
You may want to add some error handling. If any of your fetch() calls fail, that will prevent the form from being submitted. Ideally a failed AJAX call would result in some messaging to the user that, once dismissed, removes the promise from the fetchPromises array, and until it is dismissed, the submit button should be disabled (actually, and visibly).
How about making a fake submit button with a preventDefault and hiding your real submit button with display:none? Then you can call a click() function on the hidden button once you've got your AJAX success.
You could set your button to a disabled state, add a listener to it, then throwing an event during your .then() function.
$('.website').on('blur', function () {
var elem = document.getElementById('submitButtonId');
// create the event
var isFinishedEvent = new Event('fetched');
elem.addEventListener('fetched', function() {
// logic to enable your button
}
var websiteId = this.id;
console.log(websiteId);
var website = this.value;
console.log(website);
fetch(`/api/edit/${websiteId}&${website}`).then(() => {
// Fire the event
elem.dispatchEvent(isFinishedEvent);
});
})

How to stop redirect from partial page after form submit

Having an issue, I have a partialview named Manage, I load the partial in:
Controller AdminPanel, View AdminProfile like so:
<div id="tab-2" class="tab-pane">
#{Html.RenderPartial("~/Views/Account/Manage.cshtml");
}
</div>
When I click on save changes I get redirected to /Account/Manage, it should be /AdminPanel/AdminProfile?
Not sure if controller is returning the correct redirect or information for the json if I try to use a ajax script:
public ActionResult Manage(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: "";
ViewBag.HasLocalPassword = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.ReturnUrl = Url.Action("Manage");
return View();
}
public ActionResult Manage(LocalPasswordModel model)
{
bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name));
ViewBag.HasLocalPassword = hasLocalAccount;
ViewBag.ReturnUrl = Url.Action("AdminProfile", "AdminPanel");
//ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalAccount)
{
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing
// OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
try
{
WebSecurity.CreateAccount(User.Identity.Name, model.NewPassword);
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
catch (Exception)
{
ModelState.AddModelError("", String.Format("Unable to create local account. An account with the name \"{0}\" may already exist.", User.Identity.Name));
}
}
}
// If we got this far, something failed, redisplay form
//return PartialView("Manage", model);
return Json(new { redirectTo = Url.Action("AdminProfile", "AdminPanel") });
}
This is the partial page that is loaded:
#model LocalPasswordModel
#{
ViewBag.Title = "Change Password";
}
<section class="hgroup">
<div class="panel-body">
<h1>#ViewBag.Title</h1>
<ul class="breadcrumb pull-right top-right">
<li>You're logged in as <strong>#User.Identity.Name</strong></li>
</ul>
<ul class="message-success">#ViewBag.StatusMessage</ul>
#using (Html.BeginForm("Manage", "Account", Form.Post, new { #class = "form-horizontal" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-group">
<label class="col-sm-2 control-label">Old Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.OldPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">New Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.NewPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Confirm Password</label>
<div class="col-sm-10">
#Html.PasswordFor(m => m.ConfirmPassword, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-sm-8 col-sm-offset-2">
<input type="submit" class="btn btn-primary" value="Change password" />
</div>
</div>
}
</div>
</section>
The script below is placed in the _LayoutPage however as mentioned in the comments it is not doing anything.
<script type="text/javascript">
$.ajax({
type: "POST",
url: '#Url.Action("Manage", "Account")',
data: $('form').serialize(),
success: function (result) {
if (result.redirectTo) {
// The operation was a success on the server as it returned
// a JSON objet with an url property pointing to the location
// you would like to redirect to => now use the window.location.href
// property to redirect the client to this location
window.location.href = result.redirectTo;
} else {
// The server returned a partial view => let's refresh
// the corresponding section of our DOM with it
$(".tab-2").html(result);
}
},
error: function () {
}
});
</script>
All I am trying to do is stop the redirect after I submit, I have opened this to a bounty. It would be nice if you could also include how I could recieve the status messages back aswell after a user hits submit.
For instance user hits save changes > saves to the server > messages are then sent back to the partial page (all without redirecting)
Firstly, you need to decorate your POST action result as a post.
[HttpPost]
public ActionResult Manage(LocalPasswordModel model){
Secondly, you can get rid of the javascript altogether by
testing the validity of the form submit result within your controller
and this will manage whether the user is redirected or not.
[HttpPost]
public ActionResult Manage(LocalPasswordModel model){
if(condition to redirect){
return RedirectToAction("AdminProfile", "AdminPanel");
}
return View(model);
}
Lastly, I cannot see where you've actually put you jquery.
It needs to be put at the end of your form page, in your script section.
You mention it's in your layout page?
I think this link may also help, jQuery.post(), also event.preventDefault() is also useful to prevent form submission client side.
To control specifically where you want to redirect to, add redirects at every place there is a result, example: please note I am only returning to the adminprofile as this is what the op wants
if (changePasswordSucceeded)
{
return RedirectToAction("AdminProfile", "AdminPanel", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
return RedirectToAction("AdminProfile", "AdminPanel");
}
As a sidenote, you need to think through your program flow to decide how you want to manage invalid attempts, etc.
Is it because when you press save its doing a regular http post to the server to the Account/Manage page so you are being redirected. So your javascript is never actually running?
try using Ajax.BeginForm or changing the save button to use your javascript.
If you are posting your form data using AJAX then simply in the submit function
<form onsubmit="mysubmitfunc(event)" ....>
And in the submit function
function mysubmitfunc(e)
{
e.preventDefault();
/*
YOUR AJAX CODE GOES HERE
*/
return false; //last line in the function
}
Use ajax submit in the right way, how? attach to form submission event, catch submit event, stop the post, post it from javscript, and process the response at success.
Replace your _LayoutPage js with next, make sure you are using ID to identify the form, if not this javascript may affect other forms.
$(function() { //shorthand document.ready function
//recommended to use Id on form identification not class... search on google how to
//attach handler on form submit
$('.form-horizontal').submit(catchSubmit);
//custom handler for form submit
function catchSubmit(event) {
event.preventDefault(); //stop html form post
// ajax post with serialized form
$.post($(this).attr("action"),
$(this).serialize(),
postSuccessHandler
);
}
//redirect decizion
function postSuccessHandler(data) {
if (data.redirectTo) {
window.location.href = data.redirectTo;
} else {
$(".tab-2").html(data);
}
}
});
You can also move the html button to outside of the form.

Prevent from ajax call in MVC

Hi i am making my project in the asp.net,
basic expected behaviour -- fill form name , select master module(dropdown), select sub modules(dropdown), ajax passes id of submodule dropdown, create(submit).. it will submit all values,
now code is behaves---- fill form name, select master and submodule, while selecting submodule from second dropdown is calling the ajax call, and create action is called, so the form name and masterID(that is extracted from first dropdown) gone blank... so i need to prevent the ajax call to call the controller
Myform in razor view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Form</legend>
<div class="editor-label">
#Html.LabelFor(model => model.FormName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FormName)
#Html.ValidationMessageFor(model => model.FormName)
</div>
<select id="State" name="state"></select><br />
<p>
<input id="sbmt" type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My ajax call
$('#State').change(function () {
var a = $('#State').val();
var token = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: "/form/create",
type: "POST",
data: { __RequestVerificationToken: token, 'SubID': a }
});
});
My controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Form form, int SubID)
{
if (ModelState.IsValid)
{
form.CreatedBy = 1;
form.SubId = SubID;
form.CreatedDate = DateTime.Now;
form.ModifyBy = 1;
form.ModifyDate = DateTime.Now;
form.IsActive = true;
form.IsDeleted = false;
db.Forms.Add(form);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.MasterID = new SelectList(db.Departments, "MasterId", "ModuleName", form.MasterID);
return View(form);
}
From the comments, you want to be able to post back the value of the selected state when you submit the form. The best approach would be to use a view model that includes the property SubId and bind your dropdownlist to that property (the SubId parameter in you POST method is then not necessary.
#Html.DropDownListFor(m => m.SubId, ....)
The alternative is to rename the control to SubId so it matches the parameter in your POST method
<select name="SubId" ...>
and delete the unnecessary javascript function $('#State').change(function () {...
I guess the easiest way for you would be to use MVC's ajax helpers. Change your form like so:
#using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { OnSuccess = "OnSuccess" }))
{
...
And afterwards add a javascript handler for onsuccess
function OnSuccess(){
alert("success");
}
This is barely functional code just to get you started.
PS. Make sure you add a reference to ~/Scripts/jquery.unobtrusive-ajax.min.js

Ajax form: OnSuccess, load partial div

I have this Ajax Form
#using(Ajax.BeginForm("AddAttendeeManual", "Attendee", new AjaxOptions { HttpMethod = "POST", OnSuccess = "doneManualEmail" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-group">
#Html.TextBoxFor(m => m.SelectedManualEmail.Email, new {#class = "form-control",PlaceHolder="Email"})
</div>
<input type="submit" id="btnManual"class="btn btn-default" value="Add>>" />
}
and a div, both are on same view
<div id="invitedPeoples" data-url="#Url.Action("InvitedAttendees", "Attendee", new { appointmentId = Model.AppointmentId })">
</div>
Everytime the form is submitted, I wanted to load the div that should have a partial view.
This is what I have done
function doneManualEmail(isRequestSucceded) {
$(#Html.IdFor(m=>m.SelectedManualEmail.Email)).val('');
var url = $("#invitedPeoples").data('url');
$.get(url, function (data) {
$('#invitedPeoples').html(data);
});
};
everytime the form is submitted, i get forwarded to InvitedAttendees method, but my partial view doesn't show up.
The first line of the jquery works fine but the part to load partial view doesn't work.
Any thing I'm missing?
Error in Console:

Categories

Resources