I am trying to make a web application based on Django that takes user input and performs Heavy background task that completes in almost five to ten minutes. When the background task is completed, few parameters are supplied to the template to render. Everything works fine and the page loads after that.
But when I am trying to use AJAX for this as it does'nt seems good that the page is loading for so long due to background heavy processing, I am not able to figure out how to reload the page (Though I am able to show an alert on completion but instead of this I want to re-render the page)
Here is my views.py code:
def index(request):
#All Background process code goes here
return render(request, 'form.html', {'scanResults' : scanResults, 'context_list' : context_list, 'scanSummary' : scanSummary})
Here is my AJAX call
<script type="text/javascript">
$(document).on('submit','#scanForm', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '/scanner/',
data: {
email: $('#email').val(),
context: $('#context').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
},
success:function(response){
alert('Scan Completed');
location.reload();
}
});
});
I am not able to figure out, what should I write in success function to reload the page that index function has returned to template.
My main motive is to show a progress bar that tells the progress of process in background (I have'nt implemented the code yet )and once the process is completed , refresh the page with response.
Thank You
If you want to check the progress of a process you may need a polling mechanism
as a solution.
This requires you to have a Model that has a state to determine if your scan
is still in progress or has succeeded.
Since you will reload the page to display the results, you should have
a logic in your index view to return a different template or context
for when a user has yet to start scanning and when the scanning is successful.
from django.http import JsonResponse
def index(request):
if status == 'success':
# `status` may come from a Model which has a state .
# If `status` is 'success' this means that your scanning has
# finished so you can have a different page or update context_list
# based on success data.
# Display input form
form = scannerForm()
return render(request, 'form.html', {
'form': form,
'context_list' : context_list,
'scanSummary' : scanSummary
})
You need a view to continuously check the scan status and returns a JSON response.
def scanner(request):
#All Background process code goes here
form = scannerForm(request.POST)
status = form.perform_task()
# During the task, your Model state should also be
# updated and return the status whether it is success, pending or failed etc..
return JsonResponse({
'status': status,
})
Run the ajax poll to check the scanner view.
<script type="text/javascript">
$(document).on('submit','#scanForm', function(e){
e.preventDefault();
checkScanStatus();
});
function checkScanStatus () {
$.ajax({
type: 'POST',
url: '/scanner/',
data: {
email: $('#email').val(),
context: $('#context').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
},
success: handleCheckScanStatus,
error: handleScanError
});
}
function handleCheckScanStatus (result) {
if (result.status == 'success') {
// Reload the page and display the condition you set for 'success' state
// on the `index` view.
location.reload();
} else {
// Show progress bar indicating that the process running in the background
const interval = 5000; // Five seconds
window.setTimeout(checkScanStatus, interval);
}
}
function handleScanError (response) {
console.error(response)
}
</script>
I would suggest to look into django celery for async tasks and django-fsm for transitioning model states.
If you just want a simple loader and do not need the check the specific status of your background task, you can use jQuery AJAX's beforeSend method to display a progress bar until the AJAX request finishes.
Related
I am presently developing a web application with jQuery mobile. However, I found that when a "changePage" fails, I can no longer send ajax requests. After the failure, all ajax requests return an error. Here's the code executed when the submit button on the form is clicked (it's a basic user login screen):
// Event when user click the Submit login button
$('#submitLogin').on("click", function () {
// submit the user credentials to the server
$.ajax({
type: "POST",
url: "./LogUser",
data: {
EmployeeID: $('#EmployeeID').val(),
EmployeePIN: $('#EmployeePIN').val()
},
dataType: "text",
async: true,
cache: false,
error: function (rqst, text, thrownError) {
$('#dlg-login-error-message').text(thrownError);
$('#dlg-login-error-popup').popup("open");
},
success: function (data) {
if (data == "Success") {
$.mobile.changePage("./LoadScreen/Menu");
}
else {
$('#dlg-login-error-message').text(data);
$('#dlg-login-error-popup').popup("open");
}
}
});
return false;
});
If the post itself fails, I can resubmit without problem. If the .mobile.changePage fails, a "page not found" is displayed, but I am not able to resubmit, ajax no longer making request to the server and jumping directly to the error callback with a "not found" error.
I am guessing the problem comes from the fact that jQuery mobile uses AJAX request to load pages, and that somehow, ajax calls are getting mixed up somewhere.
I did more tests, even intercepted the pageloadfailed event, but nothing works. After the page change failure, AJAX calls no longer sends anything to the server and jump automatically to the error callback function.
I tried with async=false, same problem. I tried debugging jQuery-mobile, but I am still not able to find the "changePage" function itself ( the .code is quite confusing ).
I just spent the last two days trying to figure out a way to resolve this and I am seriously thinking of using something else than jQuery-mobile for our development.
I have found a workaround for my problem, but I do not know the full impact of this solution yet.
To prevent the problem, I had to set the "pushStateEnabled" configuration option to "false".
So if you find yourself with the same problem, try putting the following in a script right before the loading of the "jQuery-mobile" script.
$(document).bind("mobileinit", function () {
$.mobile.pushStateEnabled = false;
});
Example:
<!-- Load the script for jQuery -->
<script src="~/Scripts/jquery-2.1.4.js"></script>
<!-- Set default for jQuery-Mobile, before it is actually loaded -->
<script>
$(document).bind("mobileinit", function () {
$.mobile.pushStateEnabled = false;
});
</script>
<!-- Load the script for jQuery-Mobile -->
<script src="~/Scripts/jquery.mobile-1.4.5.js"></script>
I have a long running job that is started from the admins web page.
The Job can only be started once, and therefore I have a singleton, that also holds the different state messages of the job.
After the admin has started the job (Ajax call works) a timer in javascript gets started, that should trigger checks of the state of the job every 10 seconds (the timer works, the calls get triggered).
My Problem is, that these calls never reach the server, until the Job is finished ... After the job has finished, all the status calls (that should have been async) are processed..
WEB API CODE:
public class CleanUpServiceToolController : ApiController
{
[HttpPost]
public async Task StartJob(StartCondition startCondition)
{
if (CleanUpServiceTool.Instance.Status == "Neu")
{
CleanUpServiceTool.Instance.Status = "I'm Busy";
await Task.Delay(60*1000);//CleanUpServiceTool.Instance.Start(startCondition);
CleanUpServiceTool.Instance.Status = "Neu";
}
}
public string GetStatus()
{
return CleanUpServiceTool.Instance.Status;;
}
}
JAVASCRIPT the javascripts are logically splitted.. but all calls go through this code
return $.ajax({
url: url
type: type,
data: (type === "PUT" || type === "POST") ? JSON.stringify(input) : input,
contentType: contentType,
dataType: dataType,
jsonpCallback: jsonpCallbackFunctionName,
timeout: timeout,
async: true,
cache: false
}).done(function (response) {
var output = dataType === "jsonp" ? response : JSON.parse(response, true);
successCallback(output);
}).fail(function (response) {
var output;
try {
output = JSON.parse(response.responseText, false);
} catch (exception) {
output = response.responseText;
}
errorCallback(response.status, response.statusText, output);
}).always(function () {
if (alwaysCallback) {
alwaysCallback();
}
});
My code makes something like this:
get status of the job/ instance (Works)
if status is "Neu" then enable the "Start Job" Button (works)
on click send ajax POST data to the server (works, server starts the Job)
Start a timer and Ask for job status (Does NOT work: The calls get stacked and only get called when the StartJob Task is finished...
To test the javascript logic I used Task.Delay, I don't want to start up the job each time during testing.
I really don't know why my "async" calls are actualy "sync" calls!
I even tried making the GetStatus async , which really doesn't make much sence, (opening a thread just to read the Property of my instance)
Any Ideas, suggestions, fixes?
Thanks
Below is my react code I want that firstly the ajax code should execute then the rest of the code should execute.
expected output in console:
inside ajax
outside ajax
current output in console :
outside ajax
inside ajax
import React from 'react';
import request from 'superagent'
const UserItems = () => {
request.get('http://localhost:4000/user/1/items.json')
.then((res, err) => {
if (err) {
console.log("errror found")
}
var data = JSON.parse(res.text)
console.log("inside ajax")
console.log(data)
})
console.log("outside ajax")
console.log(data)
};
export default UserItems;
Any suggestion !!!
As hainguyen points out, ajax is typically asynchronous so the code afterwards will run until the request is complete, at which time the inner function is executed. So the outer console logs will almost certainly run first in your code. While there are ways around this as hainguyen points out, most recommend against it. Ajax is something which takes time, and therefore your code structure should reflect that. If you ever find yourself wanting to run code while the ajax request is in process, you might dislike a synchronous structure. My "I wait for no one" log shows the power of an asynchronous approach since that logic will run quickly while you would normally be waiting on the request without being able to do anything.
Rather than make it synchronous why not use functions to handle the asynchronous behavior better like wrapping whatever you want to run after the inside console log in a function: (I called it outside()) This will output "inside ajax", "outside ajax". This way you can create dependencies on your ajax return and still have the option for running stuff in the meantime.
import React from 'react';
import request from 'superagent';
const UserItems = () => {
request.get('http://localhost:4000/user/1/items.json')
.then((res, err) => {
if (err) {
console.log("errror found");
}
var data = JSON.parse(res.text);
console.log("inside ajax");
console.log(data);
outside();
});
function outside(){
console.log("outside ajax");
console.log(data);
}
console.log("I wait for no one, run me as quick as possible!");
};
export default UserItems;
I don't know about request library but ajax is async by default. If you want ajax perform sync request, you should do something like this:
function getRemote() {
return $.ajax({
type: "GET",
url: remote_url,
async: false
}).responseText;
}
Important line: async: false
I am trying to send data to server using ajax, but the problem is that I have a consuming process before sending the data.
The process takes about 5 seconds and the spinner has to run in the process.
So in my code the spinner doesnt show until the ajax call starts (probably because the process is blocking everything)
If I move the call "consumingprocess" into "beforesend", then it doesnt work and I am not sure why.
So the question is how to show the spinner, while everything is beeing called (the consumingprocess and the ajax call)
Thanks
This is my code:
$("#btnAccept").bind("click", function(event, ui) {
//start spinner, works fine but only shows after consumingprocess has finished
$.mobile.loading( 'show' );
console.log("btnAccept");
var data = consmuingprocess();
console.log(data);
// data is fine
$.ajax({
type : "POST",
url : url,
dataType : "xml",
contentType : "text/xml;charset=UTF-8",
data : data,
requestHeaders : {
Origin : '*'
},
crossDomain : true,
beforeSend : function(xhr) {
xhr.setRequestHeader("Authorization", "Basic xxxxxxxxxxxxxxx");
console.log("beforeSend");
},
error : errorAJAX,
success : parseXml
});
});
});
What you can do is
call your loading window
delay so the loading window has a chance to display
run the rest of your code.
You would do this using an interval:
$("#btnAccept").bind("click", function(event, ui) {
var intervalId;
function delayedStuff = function() {
// make sure we only run this once
window.clearInterval(intervalId);
var data = consmuingprocess();
$.ajax({
// set up your ajax request and handlers
});
};
$.mobile.loading( 'show' );
// wait 1/2 second, then run delayedStuff
intervalId = window.setInterval(delayedStuff, 500);
});
But this technique comes with an important caveat: while your very expensive consumingProcess function is running, all animations and javascript still comes to a halt. On Chrome, even animated gifs stop running. All we've done here is just given your page changes a chance to display.
There are a couple of possible solutions available:
Take a closer look at your consumingprocess() function and see if it can be optimized. There is probably a faster way to do whatever it is you're doing that's taking so long.
Use WebWorkers. The downside is compatibility: IE and most older browsers don't support it. I haven't done multi-threaded programming with JavaScript at all, so I don't know how effective this is.
I've built a jQuery mobile app that gets its content from an external SQL server database via JSON and a server side script (ColdFusion CFC) that interfaces with the database. This app has been packaged as a native app using PhoneGap. I need to enable the jQuery mobile app to be able to write back to the external SQL server db.
Im new to mobile development but have several years of server side development using ColdFusion. I am guessing that the best way to do this is for the mobile app to send the results of the submitted form elements to a server side script for processing. I dont want the native app to send this "as a web page" but rather stay in the app to do it (via AJax I assume).
My server side script will be written in ColdFusion and handles input sanitation and database interaction...I just need to figure out what is the best way to submit from my jQuery app to the server side script, but do it while staying inside of my native application.
I'm pretty much doing the same thing. Server side Coldfuison8/MySQL, front end Jquery Mobile (, requireJS) with all forms submits routed through AJAX to avoid reloading a page.
I'm using a generic form submitter in my controller.js, which looks like this:
var ajaxFormSubmit =
function ( form, service, formdata, targetUrl, successHandler, dataHandler, errorHandler ){
$.ajax({
async: false,
type: "post",
url: service,
data: formdata,
dataType: "json",
success: function( objResponse ){
if (objResponse.SUCCESS == true ){
// alert("success!");
// this passes the response object to the success handler
// in case data needs to be ... handled.
dataHandler == "yes" ? successHandler( objResponse ) : successHandler();
} else {
// alert("AJAX failed!");
if ( errorHandler != "" ){
errorHandler();
}
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
//alert("something else failed");
showErrors( [["server-request-error", "server_error"]], XMLHttpRequest, textStatus, errorThrown );
}
});
}
I'm returning results as a response object, which will contain Success = True/false, data (if there is any) and Error = error message.
A function call will look like this:
// the form
var form = $(this).closest('form'),
// trigger for cfcase inside my cfc
switcher = form.find('input[name="form_submitted"]').val(),
// which cfc to call
service = "../cfcs/form_handler_abc.cfc",
// the method in your cfc you are calling (validation/commit to database)
method = "process",
returnformat = "JSON",
// not using
targetUrl = "",
// serialized form plus any value you need to pass along
formdata = form.serialize()+"&method="+method+"&returnformat="+returnformat,
// specific error routine to run
errorHandler = function(){
// in my case, reset the whole form
cleanUp( $('form:jqmData(search="regular")'), "results" )
},
// inside my success handler I'm switching depending on submitted form
// `response` will be the AJAX object.response = whatever you send back from the server
successHandler = function( response ) {
switch (switcher) {
// form A - this is for a search form handling the results
case "getProducts":
// clean up
$('.ajaxContainer, .pagination').addClass('fade out').remove();
// AJAX data received
var makeUp = response.DATA;
// don't forget to trigger create to enhance all JQM elements you are sending
$('.results').append( makeUp ).trigger('create');
// redraw - will fire JQM updatelayout
$(window).trigger('dimensionchange');
// will set bindings on new elements
bindResults( $('.results').closest('div:jqmData(role="page")') );
break;
case "A":
// do sth else
break;
case "B":
// do sth else
break;
}
};
// now pass all of the above to the ajaxFormsubmit
ajaxFormSubmit( form, service, formdata, targetUrl, successHandler, handleData, errorHandler);
I have a number of CFCs, each with a main cfswitch and cfcase for each submitted form. I have built my backend using this sample. Took a while to get going, but now it's running more or less smooth.
Let me know if you have some questions regarding the above.