Animate a progress bar during an action with symfony 2 Framework - ajax

After several days of research and test, I decided to post this request of information as a last resort :-)
What I'm trying to do is quite simple but despite all the tests I could do ... I can not do it :-(
With the symfony Framework v2.8, I would like to animate a progress bar bootstrap during the execution of an action of a controller and this when the user clicks on a button.
The principle I use is
updating a SESSION variable during the execution of a processing action
the reading of this SESSION variable by another action
When the user clicks a button:
with AJAX I execute my processing action
with AJAX I execute my action of reading the SESSION variable as long as my treatment action is not completed.
In short, nothing very complicated :-)
The problem I am facing is that my SESSION variable reading action does not execute during the execution of my processing action. Before my action
Treatment yes, after yes, but during ... no!
-> It is impossible for me to read the variable of SESSION during the processing and therefore impossible to animate the progress bar.
Here is the JS code:
$('#generator').click(function() {
var isInProgress = false;
traitement();
function traitement()
{
isInProgress = true;
checkProgress();
$.ajax({
type: "GET",
url: '{{ path('sws_back_createplanning', { 'idChampionnat': Championnat.id })}}',
async : true,
cache: false,
dataType:'html',
success: function(data){},
error: function(){ },
complete: function(){
isInProgress = false;
}
});
}
function checkProgress()
{
if( isInProgress != false )
{
$.ajax({
type: "GET",
url: 'championnat/getprogress/',
async : true,
cache: false,
dataType:'html',
success: function(data){
/* animation de la progress bar */
},
error: function(){ },
complete: function(){
checkProgress();
}
});
} else { }
}
})
Here is the processing action code:
public function traitementAction($id){
$session = new Session();
$session->set('progress', 0);
// traitement #1
$session->set('progress', 25);
// traitement #2
$session->set('progress', 50);
// traitement #3
$session->set('progress', 75);
// traitement #4
$session->set('progress', 100);
return new Response ("Traitement terminé");
}
Here is the code of the reading action of the SESSION variable:
public function getProgressAction(Request $request)
{
$percent = $this->get('session')->get('progress');
if ($percent == 100){
$session = new Session();
$session->remove('progress');
}
return new Response($percent);
}
In my tests, I also converted my action to "command" and executed this "command" with the Process component.
with the RUN () method, the same behavior as with my controller action is detected
with the START (asynchronous execution) method, execution of the "command" is stopped at the end of the action of the controller which executes the "command" -> due to the concept of process
Parent / child. This means, if I understood correctly, that as soon as the parent process (execution of the controller action) ends, this ends the child process that started the process
"COMMAND" -> a child process does not survive its parent process.
Here is the code of the controller action that starts the "COMMAND":
public function commandAction($id)
{
$commandline = "php console app:generate-planning ".$idChampionnat;
$process = new Process($commandline);
$dossier = $this->get('kernel')->getRootDir() . DIRECTORY_SEPARATOR;
$process->setWorkingDirectory($dossier);
$process->start(); // https://github.com/symfony/symfony/issues/20513
$process->wait(); // Pour ne pas que l'exécution de la commande ne soit 'Killée' ... mais empêche la lecture de la variable de SESSION tant que la commande n'est pas terminée
return new Response ("Commande démarrée");
}
So to summarize my problem, I can not execute my two queries at the same time. I certainly have to miss something ... can you help me?
Best Regards

Problem solved ... the problem was with the session management (close session).
-> https://openclassrooms.com/forum/sujet/plusieurs-requetes-ajax-en-meme-temps

Related

Laravel 7: how to redirect to success page with Paypal after transaction?

I'm trying some new things in Laravel, how can I redirect the user to a new view ''success page'' after the transaction, instead of having the standard alert of the script?
paypal.Buttons({
createOrder: function(data, actions) {
// This function sets up the details of the transaction, including the amount and line item details.
return actions.order.create({
purchase_units: [{
amount: {
value: '0.10' // <---- totale
}
}]
});
},
onApprove: function(data, actions) {
// This function captures the funds from the transaction.
return actions.order.capture().then(function(details) {
// This function shows a transaction success message to your buyer.
alert('Transaction completed by ' + details.payer.name.given_name);
});
}
}).render('#paypal-button-container');
//This function displays Smart Payment Buttons on your web page.
</script> ```
In that particular callback function you can use actions.redirect()
The current PayPal demo now has a sample with that commented as an option, vs. a no-redirect option of showing a message within the <div> also: https://developer.paypal.com/demo/checkout/#/pattern/client
The general JS answer is to set window.location.href to the new destination

Django render template on AJAX success

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.

Spring MVC, Rest Ajax Call and Session Scope Objects

I want to solve following issue. I have a Spring-MVC Application with Thymeleaf, with a post request (sent by a form) I trigger a simulation task, what could take several minutes. The task process big number of data and we would like to have a progress bar via JavaScript. If there are two sessions, the simulation should be triggered independently and each browser shows its progress status.
Currently we have a solution, what is not really working well all the time.
The MVC Controller gets the Post request:
#Autowired SimulatorView view; // SESSION SCOPE
#PostMapping("/view")
public String run(#ModelAttribute(CHECKS) ChecksDto checksWrapper, Model model) throws InterruptedException, ExecutionException {
view.setStatisticDto(simulate(checksWrapper)); // Can take several minutes
return "simulation/result :: simulated";
}
When I trigger the simulation on my WebGUI, a progress bar has been displayed and via JavaScript I am calling Rest Methods frequently to ask for the status of the progress.
RestController
#RequestMapping("simulation/api")
public class SimulatorApi {
#Autowired SimulatorView view; // SESSION SCOPE
#RequestMapping("/progressStream")
public double progressStream() {
return view.getProgress().progressStream();
}
#RequestMapping("/progressInvoice")
public double progressInvoice() {
return view.getProgress().progressInvoice();
}
}
My JavaScript code snippet looks like:
function registerSimulationRunEvent() {
// this is the id of the form
$("#simulatorForm").submit(function(e) {
handleSimulationStarted();
var url = location.protocol + "//" + location.host + "/fdsclient/simulation/view";
$.ajax({
type: "POST",
url: url,
data: $("#simulatorForm").serialize(), // serializes the form's elements.
success: function(data) { handleSimulationFinished(); },
error: function(xhr, error) { handleSimulationError(); }
});
e.preventDefault(); // avoid to execute the actual submit of the form.
});
}
function handleSimulationStarted() {
replaceResultPanelRunning(); // THYMELEAF FRAGMENT EXCHANGE
}
function handleSimulationFinished() {
stopResultPanelAnimation(); // STOP PROGRESS BAR ANIMATION
replaceResultPanelSimulated(); // EXCHANGE THYMELEAF FRAGMENT
}
function handleSimulationError() {
stopResultPanelAnimation();
replaceResultPanelError();
}
function replaceResultPanelRunning() {
var url = // URL;
$("#resultDiv").load(url);
startResultPanelAnimation();
}
// ANIMATION
var animationInterval = null;
function startResultPanelAnimation() {
animationInterval = setInterval(animateResultPanel,4000);
}
function stopResultPanelAnimation() {
clearInterval(animationInterval); // stop the interval
}
function animateResultPanel() {
$("#simulatorProgressLabel").animate({opacity: '0.4'}, "slow");
$("#simulatorProgressLabel").animate({opacity: '1.0'}, "slow");
}
I know using session scope for rest services is a bad thing, but I didn`t know yet what is a good and easy solution. On the other hand currently different browser can simulate independently, but not always the progress bar works (especially when trigger first time mostly doesnt work). The IE11 only works when the Developer Tools are activated. When deactivating the tool while progress, the progress bar stops to grow.
What I would like to know is, how a good solution looks like when using template engine with Spring-MVC and Thymeleaf for triggering the process and displaying the status of progress via Javascript (as JQUery). Thank you in advance.
I have done a similar thing using Jquery AJAX POST submission. You can do something like this. This will submit POST request as a JSON format to the controller and wait for a response. A progress UI component can be shown during this waiting period.
//Start Progress display
function setStatistic(){
var data = JSON.stringify(//build your ChecksDto)
if (data) {
$.ajax({
url : '/view',
headers : {
'Content-Type' : 'application/json'
},
method : 'POST',
dataType : 'json',
data : data,
success : function(data) {
if (data.status == 200) {
// Stop Progress display
// Handle success status
}
},
error : function(xhr, status, error) {
// Stop Progress display
// Handle errors here
}
});
}
}
You also need to change Controller method to retrieve ajax requests as follows,
#ResponseBody
#PostMapping("/view")
public String run(#RequestBody ChecksDto checksWrapper, Model model) throws InterruptedException, ExecutionException
At least I found the solution in another Stackoverflow Page. The magic word is setting ajax cache to false.
$.ajaxSetup ({
// Disable caching of AJAX responses */
cache: false
});

async ajax call - show loading gif

I'm using ajax to call a POST method in my controller. On average, this method runs between 15 and 20 seconds.
I'm using aync false in that call because I need to wait the answer to know which way to go. But, when i using async false my loading (gif) isn't showed.
$(document).ajaxStart(function() {
$('#overlay').show();
});
$(document).ajaxStop(function() {
$('#overlay').hide();
});
What is the best way to resolve it?
EDIT 1
I have the save function that performs multiple validations and calls the method in the controller:
function salvarInformacoes(pedidos, ums, callback) {
$.ajax({
url: 'PlanoCortes/SalvarInformacoes',
type: 'POST',
data: {
sglDeposito: $("#ddl-Deposito option:selected").text(),
codUnimetPCP: $('#ddl-Um-sip').val(),
numEtapa: $("#ddl-Operacao").val(),
rotinaUM: $('#chk-Rotina-UM').is(":checked"),
dscEtapa: $("#ddl-Operacao option:selected").text(),
dadosPedidosJson: pedidos,
dadosUMsJson: ums,
corteVirtual: corteVirtual
},
success: callback
});
}
function salvar() {
var resultado = false;
...
salvarInformacoes(JSON.stringify(pedidos), JSON.stringify(ums), myCallback);
function myCallback(retorno) {
if (retorno.success != false) {
...
}
else {
resultado = false;
return;
}
resultado = true;
}
return resultado;
}
...
Before the method "myCallback" is called, the function return false. In this way, the code inside the statement below is never executed:
if (salvar()) {
...
}
What is the best way to resolve it?
Don't use async: false.
The browser doesn't show the changes because async: false makes the operation not asynchronous and locks the browser. Keep asynchronous code asynchronous and you can do other things while that code is executing.
I need to wait the answer to know which way to go.
This is the result of a design flaw somewhere in the code. You might try looking through the question and answers here for some help. Essentially you don't want to block the client-side code while waiting for the response, but instead want to handle the response when it arrives.

Async Web API and Ajax

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

Resources