0

I have simplified the code just to focus on the essential part and to describe my problem.

I have a function with an AJAX request returning an ID.

function addProposal(){
    var name_en = $("#name_en").val();
    var use_en = $("#use_en").val();
    var desc_en = $("#desc_en").val();  

    $.ajax({
        type:'POST',
        url:'http://localhost/index.php?option=com_test&task=addProposal&format=raw',
        data:{  user_id: user_id,
            name_en: name_en,
            use_en: use_en,
            desc_en: desc_en
        },
        beforeSend:function(){
            $("#loading_modal").modal("show");
        },
        complete:function(){
            $("#loading_modal").modal("hide");
        },
        done:function(id){
            //------I get the ID here, when it's done----
            alert(id); 
            idNewProposal=id;
        }
   });
}

I call this function when the user click on a button

$(document).ready(function()
{
    //...

    $(document).on('click', '#btn_add_valid', function() {
         addProposal();
    });

     //...
 }

When the user click on this button I have another function attached to the event in order to upload files (see code below). My problem is that I need the Id generated in the previous AJAX call before executing the upload (line "data.submit")

$(document).ready(function()

{
    //....

    //Function to upload file

    $('#file-upload').fileupload({
         dataType: 'json',
         autoUpload: false,
         paramName: 'files[]',
         maxNumberOfFiles:1,
         add: function (e, data) {
            //---The callback "add" is triggered each time that one file is selected
            // I did not write all the code here but you have to know that I make some test 
            //for each selection under this callback (size and type of file).
            //So if the user select a file which not match with the constraints, 
            //the input stay empty and an error message is displayed. 

            //--However I want to start the upload after the click on the button
             $(document).on('click', '#btn_add_valid', function() {
                 data.formData = { id: idNewProposal}; //------I need the ID here----
                 data.submit(); //--The upload start Here (after the click on the button, all the files are sent)
             });

         },
         done: function (e, data) {
                 $("#loading_file").modal("hide");
                 $("#validation_modal").modal("show");
         },
         progressall: function (e, data) {
                 var progress = parseInt(data.loaded / data.total * 100, 10);
                 $('#upload-file .bar').css(
                     'width',
                      progress + '%'
                 );
         },
         fail: function (e, data) {
                 alert('Error');
                 $("#loading_file").modal("hide");
         }
  });

}

To summarize: After a click on the button I need to execute first the addProposal() function and when it's done, I need to launch the upload (data.submit in $('#file-upload').fileupload()) Apparently there is a way to manage this kind of problem with "promises" in Jquery but I can't understand how to use it in this situation.

A huge thanks to anybody able to help me with this problem.

ZioTonio
  • 79
  • 2
  • 10
  • But your `addProposal` function does **not** `return` anything? – Bergi Nov 10 '14 at 16:44
  • You are right the id is not returned by the function but by the Ajax request idNewProposal=id; (when the request is done) – ZioTonio Nov 10 '14 at 16:48
  • What is that `fileupload` plugin you're using? When/how often does it call the `add` callback, and what is `data`? – Bergi Nov 10 '14 at 17:33
  • Why are you binding the event `$(document).on('click', '#btn_add_valid', …)` multiple times? Do you actually want to do multiple things on one click? – Bergi Nov 10 '14 at 17:34
  • fileupload is the instance of "blueimp/jQuery-File-Upload" for uploading file. The add callback of fileupload is called each time that you select a file from the input[type=file] on your page. The upload starts only when you call data.submit. – ZioTonio Nov 10 '14 at 17:36
  • Yes, I want to do 2 things when the button is clicked: 1- Modify the database thanks to the AJAX call in the function addProposal() 2- upload file selected in the input[type=file].... The problem is that I need to modify first the database because the folder used to store the files is named with the id of the last row inserted in the database – ZioTonio Nov 10 '14 at 17:42
  • But you want to do that for every selected file? So if there are multiple selected files, and you hit the button once, what should happen? – Bergi Nov 10 '14 at 17:46
  • No, The user fill some input[type=text], select files in 2 input[type=files] (one for image and the other one for document). Each time that a file is selected I control the type and the size (I did not write this part in order to simplify). When the user click on the validation the data are sent and the files are uploaded, a message is displayed and the user come back on the home page. Important: the user can't select more than one file per input – ZioTonio Nov 10 '14 at 17:56
  • Do you intend calling the `upload` after the `addProposal()` function has executed? – BlackPearl Nov 10 '14 at 20:13
  • yes I would like to call the upload after addProposal in order to get the id which will be used as name for the folder (with all the uploaded files). The problem is that both functions are linked to the button click and I can't manage the order of execution – ZioTonio Nov 10 '14 at 20:25

3 Answers3

1

Let's use promises.

function addProposal(){
    var name_en = ...
    return $.ajax({ // note the return here, the function now returns a promise
        ...
        beforeSend:function(){
            $("#loading_modal").modal("show");
        },
        complete:function(){
            $("#loading_modal").modal("hide");
        }
   });
}

Now, we want to launch the upload when our function is done:

addProposal().then(function(result){
    $(..).fileupload(...) // access addProposal's data in `result`
})

Now, you might want to do it on two different clicks like in your sample, or simply have the add:

     add: function (e, data) {
       // ...
       $(document).on('click', '#btn_add_valid', function() {
         addProposal().then(function(res){
           data.formData = { id: res};
           data.submit(); 
         });
       });
     },...

That calls addProposal each time add: is called, you can cache it in another function if you want to always call addProposal once:

var p = null;
function cachedProposal(){
    return p || (p = addProposal());
}

These are the two simplest ways to manage calling addProposal, if your logic is different - adapt.

Note that addProposal should probably take arguments instead of using globals.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • @Lauradev it's impossible for me to debug your code remotely. I can only help with an isolated test case. – Benjamin Gruenbaum Nov 11 '14 at 14:53
  • You are totally right, I did not provide enough details. If I try to use your solution, it's almost ok. There is just a problem with 2 entries in the database each time that I submit (it means that appProposal is called 2 times). I have tried to make my own deguger with the console just to be sure that the execution order is OK (see the post below). I will continue to search on my side. – ZioTonio Nov 11 '14 at 19:42
  • OK, it's fixed. I did a mistake with my test for checking if the input files were empty. Now everything is working (database modification, folder creation, files upload, message displayed). The only thing is this error message in the console "too much recursion". It does not seem to impact the running. Taking into account the code and the trace below, do you have any idea about the origin of this error? – ZioTonio Nov 11 '14 at 22:54
0

Put functions together:

done:function(id){


            //------I get the ID here, when it's done----
            alert(id); 
            idNewProposal=id;

Put your onclick check before the function, still wrapped up together, if necessary wrap another onclick element before that

I am not sure what this onclick should return, but ether way, just do an if check, and if it's correct run another function...

     $(document).on('click', '#btn_add_valid', function() {
             addProposal().;
      if(idNewProposal != //or equals to something){
 proposalCheck(); // call the function
    function proposalCheck() {
                     data.formData = { id: idNewProposal}; //------I need the ID here----
                     data.submit(); //--The upload start Here (after the click on the button, all the files are sent)


         $('#file-upload').fileupload({
                 dataType: 'json',
                 autoUpload: false,
                 paramName: 'files[]',
                 maxNumberOfFiles:1,
                 add: function (e, data) {
     ...



         }
    });
    });
};
SergeDirect
  • 2,019
  • 15
  • 20
  • I can't do that, because $('#file-upload').fileupload() needs to be under $(document).ready(function() {...}) to be correctly attached to the input[type=file] on the loading of the page (otherwise the Upload won't work). – ZioTonio Nov 10 '14 at 22:20
  • Yes at the beginning I thought that a solution like that could work but in fact both functions are Asynchronous. It means that when the button #btn_add_valid will be clicked both function will be launched at the same time and the id won't be generated before data.submit... I need to find a automated way to launch the upload when addProposal() has been done. (Sequential execution when the button is pressed : first addProposal() and then data.submit with the parameter "idNewProposal") – ZioTonio Nov 10 '14 at 22:46
  • just remove second onclick check - make if statement to check for the first one, if first one is ok...then continue – SergeDirect Nov 11 '14 at 20:07
0

Based on the solution proposed by Benjamin. I wrote the following functions:

function addProposal(){
    //console.log("Execute AddProposal");
    var name_en = $("#name_en").val();
    var use_en = $("#use_en").val();
    var desc_en = $("#desc_en").val();  

    return $.ajax({
        type:'POST',
        url:'http://localhost/index.php?option=com_test&task=addProposal&format=raw',
        data:{  user_id: user_id,
                name_en: name_en,
                use_en: use_en,
                desc_en: desc_en,
        },
        beforeSend:function(response){
                $("#loading_modal").modal("show");
                //console.log("AddProposal - BEFORESEND");
        },
        complete:function(response){
                $("#loading_modal").modal("hide");
               //console.log("AddProposal - COMPLETE");

        },
    });
}

function cachedProposal(){
    //console.log("Execute cachedProposal");
    return p || (p = addProposal());
}

And in the main function "$(document).ready(function()", I wrote this code

$(document).ready(function()
{
   p = null;
   //....

   $('#file-upload').fileupload({
        dataType: 'json',
        autoUpload: false,
        paramName: 'files[]',
        maxNumberOfFiles:1,
        add: function (e, data) {
            var uploadErrors = [];
            var acceptImgTypes = /^image\/(gif|jpe?g|png)$/i;
            var acceptDocTypes = /^application\/(pdf|msword|vnd.openxmlformats-officedocument.wordprocessingml.document)$|^text\/plain$/i;

            //console.log("ADD FILE: "+ data.files[0].name);

            if (data.fileInput[0].id=="file-img"){
                if (data.originalFiles[0]['type'] && !acceptImgTypes.test(data.originalFiles[0]['type'])) {
                    uploadErrors.push('Not an accepted file type');
                }
                if (data.originalFiles[0]['size'] && data.originalFiles[0]['size'] > 4000000) {
                    uploadErrors.push('File size is too big');
                }
            }else{
                if (data.originalFiles[0]['type'] && !acceptDocTypes.test(data.originalFiles[0]['type'])) {
                    uploadErrors.push('Not an accepted file type');
                }
                if (data.originalFiles[0]['size'] && data.originalFiles[0]['size'] > 10000000) {
                    uploadErrors.push('File size is too big');
                }
            }

            if (uploadErrors.length > 0) {
                    alert(uploadErrors.join("\n"));
            } else {
                if (data.fileInput[0].id=="file-img"){
                    $("#txt-file-img").val(data.files[0].name);
                }else{
                    $("#txt-file-doc").val(data.files[0].name);
                }

                 $(document).on('click', '#btn_add_valid', function() {
                    cachedProposal().then(function(res){
                        //console.log("cachedProposal-Promise : id="+res.id);
                        data.formData = {id: res.id};
                        data.submit(); 
                        //-----$("#loading_file").modal("show");
                    });
                 });

             }
        },
        done: function (e, data) {
            //-----$("#loading_file").modal("hide");
            $("#validation_modal").modal("show");
        },
        progressall: function (e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            $('#upload-file .bar').css(
                'width',
                progress + '%'
            );
            $('#file-percentage').text(
                    progress + '%'
            );
        },
        fail: function (e, data) {
                alert('Error');
                //-----$("#loading_file").modal("hide");
        }
   });


    $(document).on('click', '#btn_add_valid', function() {
        //console.log("---CLICK ON SUBMIT BUTTON---");
        var $img =$("#txt-file-img").val();
        var $doc =$("#txt-file-doc").val();
        if (($img == '') && ($doc == '')){
           // console.log("Submit - NO FILES");
            addProposal();
            $("#validation_modal").modal("show");
        }
    });
});

Trace And Result

Scenario : NO FILES SELECTED

Trace:

---CLICK ON SUBMIT BUTTON---
Submit - NO FILES
Execute AddProposal
Add Proposal - BEFORESEND
Add Proposal - COMPLETE

Result:

1 Entry in the database - NO FOLDER & NO UPLOADED FILES -> OK


ONE FILE SELECTED (Input for image file)

ADD FILE: capture.jpg
---CLICK ON SUBMIT BUTTON---
Execute cachedProposal
Execute AddProposal
AddProposal - BEFORESEND
cachedProposal-Promise : id=76
AddProposal - COMPLETE
ERROR too much recursion //???

1 Entry in the database (ID=76) AND

1 FOLDER created with the name 76 and with the img file uploaded inside -> OK


ONE FILE SELECTED (Input for doc file)

ADD FILE: test.pdf
---CLICK ON SUBMIT BUTTON---
Execute cachedProposal
Execute AddProposal
AddProposal - BEFORESEND
AddProposal - COMPLETE
cachedProposal-Promise : id=79
AddProposal - COMPLETE"
ERROR too much recursion //???

1 Entry in the database (ID=79) AND

1 FOLDER created with the name 79 and with the doc file uploaded inside -> OK


TWO FILES SELECTED (one for each input)

ADD FILE: capture.jpg
ADD FILE: test.pdf
---CLICK ON SUBMIT BUTTON---
Execute cachedProposal
Execute AddProposal
AddProposal - BEFORESEND
Execute cachedProposal
cachedProposal-Promise : id=80
AddProposal - COMPLETE
ERROR too much recursion //???

1 Entry in the database (ID=80) AND

1 FOLDER created with the name 80 and with both files uploaded inside -> OK


Everything is working except that I have this strange error message in the console "ERROR too much recursion". It's displayed every time that I upload files. Someone know where does it come from?

EDIT : If I comment the lines with $("#loading_file").modal(...); The message disappears! see the code with the comments(//-----). I don't understand why?

ZioTonio
  • 79
  • 2
  • 10