I have a timing problem with an ASP.NET core 5 system I'm working on. My page shows a DataTable with id='outsideDataTable', and when an item is selected a modal bootstrap dialog is shown. The submit button invokes method submitModal() which does this:
$.ajax({
type: 'POST',
url: '/api/Submit',
dataType: 'json',
statusCode: {
200: SubmitDone()
},
error: 'SubmitError',
data: $('#ModalForm').serialize()
});
The /api/Submit function calls the server's Submit function which does an update to the database. None of the C# code uses async coding. The database interface is NPoco. At the end of the update the function calls Ok() which I believe returns the status 200 to the ajax call.
[HttpPost("api/[action]")]
public IActionResult Submit(
int recordId,
... other formdata ...)
{
if (recordId == 0)
{
var sr = new Record() { ... fill with form data ... };
db.Insert(sr);
}
else
{
var sr = db.Single("select * from Records where recordId=#0", recordId);
if (sr == null)
return BadRequest($"Couldn't find record with ID={recordId}");
... update sr with form data ...
db.Update(sr);
}
return Ok();
}
The OK() function returns status of 200 back to the client, which should now execute the js SubmitDone() function.
function SubmitDone() {
$('#ModalDlg').modal('hide');
$('#outsideDataTable').DataTable().draw();
}
The problem is that when the outsideDataTable is redrawn from within the SubmitDone function, it will retrieve the data, which does not yet include the changes put into the database by the submit action routine. Why is that? In my opinion the database should have done its thing by the time the status 200 is returned to the ajax call, ergo when the redraw happens the database should have the new data.
As a matter of fact, in fiddler I see that the list load from the redraw happens before the ajax to the submit function.
I have not isolated this into working code I can share, but can do so if needed - unless someone knows what I'm doing wrong.
When you assign the SubmitDone function to the statusCode.200 callback you shouldn't use parentheses because this is making the function execute immediately. Instead, it should be like this:
$.ajax({
type: 'POST',
url: '/api/Submit',
dataType: 'json',
statusCode: {
200: SubmitDone
},
error: 'SubmitError',
data: $('#ModalForm').serialize()
});
Related
I had a question;
How would one call multiple PHP functions and output them onto the page?
Now i have found a way, could anyway let me know how i could improve my answer.
It works perfectly, I just want to see what could be a better way.
AJAX CALL;
$.ajax({
url: 'functioncalls.php', //URL TO PHP FUNCTION CONTROL
data: {call: 'Login', parameters: {Username, Password}}, //CALL the function name with an array of parameters
type: 'post'
}).done( function( Output ){
try{
Output = JSON.parse(Output); // see if out put is json as that is what we will pass back to the ajax call
} catch (e){
alert('Error calling function.');
}
});
PHP "functioncalls.php" page
if(isset($_POST['call']) && !empty($_POST['call'])){ //check if function is pasted through ajax
print call_user_func_array($_POST['call'], $_POST['parameters']);//dynamically get function and parameters that we passed in an array
}
PHP FUNCTION - make sure your function is either on the page or included
function Login($Username, $Password){
// function control
return json_encode($return);// return your json encoded response in value or array as needed
}
And that's that, Nothing else needed you can call any function and use it in your done ajax promise.
Note: Your parameters have to be passed as an array.
Thank you
change your ajax request like this
$.ajax({
url: 'functioncalls.php', //URL TO PHP FUNCTION CONTROL
data: {call: 'Login', parameters: JSON.stringify([Username, Password])}, //CALL the function name with an array of parameters
type: 'post'
}).done( function( Output ){
try{
Output = JSON.parse(Output); // see if out put is json as that is what we will pass back to the ajax call
} catch (e){
alert('Error calling function.');
}
});
in php you have to do something like this:
$params = json_decode($_POST['parameters']);
login($params[0], $params[1]);
I'm using jQuery and AJAX in the View to send some data to the Controller that writes it to the database. On success I show a div tag with a green background with "OK" text. But what if I do a check first in the Controller if the data already exist in the database, then I would like to alert the user that the data could not be added. Is there a way to pass some kind of message back to the AJAX script?
I guess the success option is just a confirm of contact with the Controller and not a real confirm that everything is OK and the data has been added to the database!?
What action in the Controller would cause the error function in the AJAX code to run?
Finally I just wonder what kind of return I should use since I'm actually not returning anything?
My script in the View:
$.ajax({
url: "/Orders/AddOrder",
type: "GET",
cache: false,
data: { a: order, b: seller },
success: function () {
console.log('AJAX successful');
// Show confirm message
$(".messageOk").show();
$(".messageOk").text("OK").delay(2000).queue(function () {
location.reload();
});
},
error: function () {
????
},
complete: function () {
????
}
});
Controller:
// Add new Resource
public ActionResult AddOrder(int a, int b)
{
var order = new Order
{
OrderNumber = a,
Seller = b
};
db.Orders.Add(order);
db.SaveChanges();
//return new EmptyResult();
return RedirectToAction("Index", "Home"); // ??????
}
You could return the appropriate HTTP status code from your controller action: 409 Conflict.
if (the resource already exists in db) {
return new HttpStatusCodeResult(HttpStatusCode.Conflict);
}
which will trigger the error AJAX function in which you could check whether you are in this situation and act accordingly:
error: function(jqXHR) {
if (jqXHR.status == 409) {
alert('Sorry but this resource already exists');
}
}
As you can see this way it's up to the view to decide what error messages to display based on proper HTTP status codes returned from the server. This way you are not cluttering the server with view logic.
Along with the correct response status code, you can also pass in your response body error messages from the server may be as JSON string or plain string
i have a recursive Ajax call that work properly (PhP script called do his job, recursion is working, everything is fine) EXCEPT that in between the ajax calls i try to update a input text value to show the progress and it only updates once the whole loop is done.
Why is the line
$('#start_'+code_regional).val(msg);
not executing ?
function addLeads(code_regional, phone_numbers_start)
{
var databases = [];
var file = document.getElementById('file_'+code_regional).files[0];
var formData = new FormData();
formData.append('selectedDatabases', JSON.stringify(databases));
formData.append('code_regional', code_regional);
formData.append('phone_numbers_start', phone_numbers_start);
formData.append('phone_numbers_end', $('#end_'+code_regional).val());
formData.append('filePath', file);
$.ajax({
type: 'POST',
url: 'execute.php',
data: formData,
processData: false,
contentType: false,
success: function(msg){
$('#start_'+code_regional).val(msg);
if(msg < $('#end_'+code_regional).val())
{
addLeads(code_regional, msg);
}
else
{
$('#start_'+code_regional).val($('#end_'+code_regional).val());
}
}
});
}
If msg is an integer, you should compare integers instead of strings:
if( parseInt(msg,10) < parseInt($('#end_'+code_regional).val(),10) )
If it's still failing, try to add a console.log(...) before and after the test to know what's going on.
What do you mean in between the ajax calls? The code tells me, "if msg is less than y, call addLeads again, otherwise update the start field". So that's not tracking progress, the start field is only updated at the end. If you want to always update, move the update line outside the conditional.
Inside my MVC view I have javascript that is executed by a button click. I'm trying to set a string to a random set of characters which I can get to work fine but when I try and set that string to 'randomchars' string inside the javascript I get a NullReferenceException when I try and run the view.
Below is the code snippet, the CreateRString is where the model parameter (RString) is set to the random string.
<script type="text/javascript">
function showAndroidToast(toast) {
var url = '#Url.Action("CreateRString", "Functions")';
$.ajax({ url: url, success: function (response) { window.location.href = response.Url; }, type: 'POST', dataType: 'json' });
var randomchars = '#(Model.RString)';
}
</script>
Is the syntax correct? I'm not too sure why it's getting the NULL.
The javascript is executed after the page been delivered to the client (i.e. web browser). Your razor code here is executed on the server before the page is sent to the client. Therefore, the ajax method will execute after you try to access Model.RString
To fix this you can either call CreateRString on the server, or you can set randomchars by using the response in the success callback.
To explain option 2 a bit further. You could do something like this:
//Action Method that returns data which includes your random chars
public JsonResult CreateRString()
{
var myRandomChars = "ABCDEF";
return new JsonResult() { Data = new { RandomChars = myRandomChars } };
}
//The ajax request will receive json created in the CreateRString method which
//contains the RandomChars
$.ajax({ url: url, success: function (response) {
var randomchars = response.Data.RandomChars;
window.location.href = response.Url;
}, type: 'POST', dataType: 'json' });
More specifically, the razor calls #Url.Action("CreateRString", "Functions") and #(Model.RString) execute first on the server.
Then showAndroidToast executes in the client's browser when you call it.
Following up on my question from the other day, I've run into another thing that now I've spent too many hours banging my head against.
Mostly, I'm having trouble getting the SUCCESS form to submit. I tried this as well:
jQuery form submit
Here's the code in a semi-functional fiddle:
http://jsfiddle.net/ZcgqV/
Essentially what happens is this:
I bind a method to the form's submission via onSubmit (rather than click)
On submit, it calls a remote server via jQuery .ajax() call
If the response is "PENDING", retry every 1s, nine times
On failure, don't submit the form
On success, submit the form
No matter what I try, I can't get the form to either submit when I want it to without going into a loop, or not submit immediately while it tries the remote server.
~Frustrated-trying-100-things-that-fail-ly yours...
Here's the code directly in case you dislike fiddles:
var retries = 0;
var success = false;
var token = "toki wartooth is not a bumblebee";
$(document).ready(function() {
// Attach the action to the form
$('#tehForm').attr('onSubmit', 'onsubmit_action(event)');
});
function async(fn) {
setTimeout(fn, 1000);
}
function pollServer() {
$.ajax({
type: "POST",
cache: "false",
url: "/remoteCall",
dataType: "json",
data: {
ref_token: token
}
}).done(function(data, code, jqXHR) {
switch (data.status) {
case "SUCCESS":
alert("Success");
success = true;
// --> HERE IS WHERE I WANT THE FORM TO SUBMIT <--
break;
case "PENDING":
if (retries < 9) {
retries += 1;
async(function() {
pollServer();
});
} else {
alert("Failed after 9 tries");
}
break;
case "ERROR":
alert("Error");
break;
default:
alert("Some kind of horrible error occurred");
break;
}
}).fail(function(jqXHR, textStatus) {
var statusCode = jqXHR.status;
alert("Request failed: " + statusCode + " " + textStatus);
});
}
function onsubmit_action(event) {
pollServer();
if (success === false) {
// RETURN FALSE DIDN'T WORK, SO I FOUND THIS
event.preventDefault();
}
}
EDIT:
Again, the real problem here is that I stop submission of the form. On SUCCESS, I want the form to submit. Currently if I use .submit() in SUCCESS, the AJAX is called again, starting the process over. What I want is the ACTION of the FORM to fire on SUCCESS only.
Trying to use as much of the original code as possible; here is a solution:
Post form with post back
http://jsfiddle.net/tpm7v/4/
Post form via Ajax
http://jsfiddle.net/tpm7v/5/
var retries = 0,
token = "toki wartooth is not a bumblebee",
sendRequest,
handelResponse,
postFormToServer,
$theForm = $('#tehForm');
$(document).ready(function() {
// Attach the action to the form
$theForm.bind('submit', onsubmit_action);
});
sendRequest = function() {
$.ajax({
type: "POST",
cache: "false",
url: "/remoteCall",
dataType: "json",
data: {
ref_token: token
},
success: handelResponse
});
};
postFormToServer = function() {
$.ajax({
type: "POST",
cache: "false",
url: "/remoteCallToTakFormData",
dataType: "json",
data: $form.serialize(),
success: function() {
alert('success!');
}
});
};
handelResponse = function(data, code, jqXHR) {
switch (data.status) {
case "SUCCESS":
postFormToServer();
break;
case "PENDING":
if (retries < 9) {
retries += 1;
setTimeout(sendRequest , 1000);
} else {
alert("Failed after 9 tries");
}
break;
case "ERROR":
alert("Error");
break;
default:
alert("Some kind of horrible error occurred");
break;
}
};
function onsubmit_action(evt) {
evt.preventDefault();
sendRequest();
}
Keep in mind I am going off the code your provided. You should be able to port this to work with your actual implementation. You may also want to try something like https://github.com/webadvanced/takeCommand to help clean up all the Ajax calls.
Please see my comment above for more information, but I think the problem you're seeing here is this:
Every time pollServer() fires, it's not only doing another ajax call, but it's prepping to do 9 possible ajax calls every second based on the retries loop. Since you're then setting another pollServer() call with the async() method, you're basically compounding your ajax calls out of control. You want to get the ajax call out of your retry loop, then you should at least be only getting 1 request a second, not 1, then 2, then 3, etc. I may have read the code wrong, but this is my best guess on what you're seeing.
UPDATE: I'm not sure my explanation was clear, so I thought I'd add some additional info. Basically, every time pollServer() is called and gets a PENDING response, it calls async, which registers a setTimeout(). setTimeout() keeps running every second, doing pollServer(), which then calls asynch, which registers another setTimeout() which also runs every second. Now you have two functions, which each then call setTimeout(), assuming they're still getting PENDING as a response from the server. So after 2 rounds of failed calls, you have 4 setTimeout() calls each firing an ajax call (and a new setTimeout) every second.
First off it should be: $('#tehForm').submit(onsubmit_action); or $('#tehForm').on("submit",onsubmit_action); or something like that. Never use the string form to pass a function. It uses the evil eval statement.
Next, after POST the data is already submitted. That is the whole reason for post. Why do you need all sorts of error handling in the done section. Fail should handle error handling.
If you are asking about how to try again after a timeout, try this:
Is it possible to check timeout on jQuery.post()?
I believe timeout will fall into fail.
So try this:
var retries = 0,
max_tries = 9,
success = false,
token = "toki wartooth is not a bumblebee";
$(document).ready(function() {
// Attach the action to the form
$('#tehForm').on("submit",submit_the_form);
});
function submit_the_form(e){
var dfd = $.ajax({
url : "sendTokenPolling",
data : {"token":token},
timeout : 5000 //you may want 1000, but I really think that is too short
});
dfd.done(function(){
//success, form posted
});
dfd.fail(function(){
//did not work/timedout
if (retries < max_tries){
retries += 1;
submit_the_form(e);
}
});
}