简体   繁体   中英

Ajax success variable not set despite other data being returned

EDIT: The base question is the same, but instead of a global buttons array getting passed back, I'm now passing back a getButtons() function.

I'm making an AJAX request to the server to get data to populate a popup box, and it's passing the data back as a JSP. Part of the JSP includes a JSON object with data used by the client to draw some buttons.

The issue is that sometimes the JSON object isn't ready on the success callback, so the buttons don't get drawn. The rest of the JSP is there, so the ancillary functionality works fine.

For most pages this does work flawlessly, but on certain pages, where the content is ready much more quickly, the buttons haven't yet been set. How can I ensure that buttons will be set?

Server code:

//Arbitrary JSP code prior to this function

function getButtons() {
    var buttons = [];

    buttons.push(addButton('Send', 'functionName', buttonDescription));        
    buttons.push(addButton('Cancel', 'functionName', buttonDescription));

    return buttons;
}

Client code:

popupBoxFunction(action, params, global) {
 $.ajax({
    url: action,
    type: 'POST',
    data: params,
    dataType: 'html',
    global: global,
    success: function (data, textStatus, xhr) {

    $("#container").html(data);

    if (typeof getButtons() != "undefined") {
       var myButtons = generatePopupButton(getButtons());
       for (var i = 0; i < myButtons.length; i++) {
           //do something
       }

    }

//arbitrary rest of function
}

Option - 1

If you want your generatePopupButton to be called only after all the buttons are available, why don't we just call it inside getButtons . That way you don't have to check if buttons are available and everything happens one after the other.

Your JSP :

<script>
// make it an IIFE so that it's executed right after parsing
(function() {
    var buttons = [];

    buttons.push(addButton('Send', 'functionName', buttonDescription));        
    buttons.push(addButton('Cancel', 'functionName', buttonDescription));

    var myButtons = generatePopupButton(buttons);
      for (var i = 0; i < myButtons.length; i++) {
           //do something
      }
})();
</script>

Your Client Code :-

popupBoxFunction(action, params, global) {
    $.ajax({
       url: action,
       type: 'POST',
       data: params,
       dataType: 'html',
       global: global,
       success: function (data, textStatus, xhr) {
          /* just add your html and script to DOM
           and wait for the browser to execute it for you */
          $("#container").html(data);
       }
    });

   //arbitrary rest of function
}

Option - 2

If you don't like the idea of moving your generatePopupButton and other script code in to your JSP, you can make use of Function Body As a String technique used in most of the template engines.

In your JSP :-

<!-- make sure type is not text/javascript, something gibberish that 
     is not known to browser -->
  <script id="template" type="my-type">

     var buttons = [];
     buttons.push(addButton('Send', 'functionName', buttonDescription));        
     buttons.push(addButton('Cancel', 'functionName', buttonDescription));
     return buttons;

  </script>

Make sure that script type is not something that is known to browser. You can even change the script tag to div tag if you please :)

In your client code :-

     $.ajax({
           url: action,
           type: 'POST',
           data: params,
           dataType: 'html',
           global: global,
           success: function (data, textStatus, xhr) {
              $("#container").html(data);
              var funBody = $('#template').html();
              /* Now that we've function body as text  
                 retrieve that in code*/
              var fn = new Function(funBody);
              // Now that we've the function invoke it to get buttons
              var myButtons = generatePopupButton(fn());
              for (var i = 0; i < myButtons.length; i++) {
                //do something
              }
           }
        });

Here's a bin to play with the second approach.

There are even other ways of doing this, but these two are just from the top of my head :)

According to me the best way would be to move the code where html is set $("#container").html(html) , inside the IF statement so that html is rendered only after the buttons are set.

EDIT

As rendering HTML and BUTTON are independent. A minimum amount of delay can be introduced before rendering the HTML so that difference between rendering HTML and rendering buttons gets reduced.

function popupBoxFunction(action, params, global) {
var ajaxPromise = $.ajax({
                  url: action,
                  type: 'POST',
                  data: params,
                  dataType: 'html',
                  global: global
                 });


}

promiseAjax.done(function(html){
setTimeout(function(html) {
           $("#container").html(html);
        }, 300);
if (typeof getButtons() != "undefined") {
   var myButtons = generatePopupButton(getButtons());
   for (var i = 0; i < myButtons.length; i++) {
       //do something
   }
 //  $("#container").html(html);
}
 });

Could you please allow some milliseconds to pass before checking if (typeof getButtons() != "undefined") { .

For example inside success handler can you please try something like following:

 success: function (data, textStatus, xhr) { $("#container").html(data); setTimeout(function(){ if (typeof getButtons() != "undefined") { var myButtons = generatePopupButton(getButtons()); for (var i = 0; i < myButtons.length; i++) { //do something }// end for loop } //end if check },500); //500 milli sec delay }// end success handler 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM