0

I've got some IDS that I want to use to fill / generate some jQuery Objects. I use these IDS in ajax requests. After the jQuery Objects have been filled, I want to sort them.

Has to work in IE11.

What my problem is: At the moment I've no idea how the best practice would look like, to wait for all ajax requests and as well all jQuery objects to be filled.

  • I have to wait until all ajax requests have been completed (so always, indepedent from response code (so on .done and .fail))
  • I have to wait until all jQuery Objects are filled with the results from the ajax Requests

Problem conclusion: sort function is called before the items are filled.

I've tried to break down my whole code to a simple example, maybe someone can help here:

function sortEntries(a, b) {
 var aa = ($(a).data('name') || '').toLowerCase(),
  bb = ($(b).data('name') || '').toLowerCase();
 return aa < bb ? -1 : aa > bb ? 1 : 0;
}

function getEntryInfo(infoObj, callback) {

 function getLabelByResult(result) {
  var name = '';
  switch (result) { // For this demo, we don't use the response data.
   case 'page-header':
    name = 'Hello World 1';
    break;
   case 'thumbnails':
    name = 'It works!';
    break;
   case 'nav':
    name = 'Great!';
    break;
   case 'btn-groups':
    name = 'BTN GROUP';
    break;
   default:
    name = 'NOT SET!'
  }
  return name;
 }

 return $.ajax({
  method: 'GET',
  url: infoObj.URI,
  cache: false
 }).done(function(data) {
  if ($.isFunction(callback)) {
   callback(true, {
    name: getLabelByResult(infoObj.element)
   });
  }
 }).fail(function() {
  if ($.isFunction(callback)) {
   callback(false, {
    name: getLabelByResult(infoObj.element)
   });
  }
 });
}

function setInfoForEntry(item$, config) {
 return getEntryInfo(config, function(isOk, responseObjInfo) {
  item$.attr('data-name', responseObjInfo.name || '').text(responseObjInfo.name || '');
 });
}

function generateItems() {
 var parentItem$ = $('body').append($('<ul/>').addClass('allItems').hide()),
  ids = ['page-header', 'thumbnails', 'nav', 'btn-groups'], // for testing purposes of course
  requests = [],
  extractedItems$;

 $.each(ids, function(ignorel, el) {
  var newItem$ = $('<li/>').addClass('idItem').on('click', function(e) {
   alert($(e.currentTarget).data('name'));
  });
  parentItem$.append(newItem$);
  requests.push(setInfoForEntry(newItem$, {
   URI: 'https://getbootstrap.com/docs/3.3/components/#/' + el,
   element: el
  }));
 });
 // HERE I HAVE TO ENSURE THAT:
 // -ALL AJAX REQUESTS ARE DONE
 // -ALL jQuery Elements are filled
 extractedItems$ = parentItem$.find('.idItem');
 extractedItems$.sort(sortEntries);
 extractedItems$.detach().appendTo(parentItem$);
 parentItem$.show();
}
$(document).ready(function() {
 generateItems();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body></body>
SiriSch
  • 180
  • 1
  • 3
  • 16
  • Why are you doing the Ajax requests in the first place? You don't seem to use the response data anywhere...? – Tomalak Feb 25 '18 at 13:32

1 Answers1

1

To wait for multiple promises, jQuery has the $.when() utility function. Also see this thread and this thread (there are quite a few posts that discuss this, look around).

I've also streamlined your code a bit, I think it's much easier to read this way:

function by(func) {
    return function (a, b) {
        var aa = func(a), bb = func(b);
        return aa < bb ? -1 : aa > bb ? 1 : 0;
    };
}    

$(function () {
    var things = {
        'page-header': 'Hello World 1',
        'thumbnails': 'It works!',
        'nav': 'Great!',
        'btn-groups': 'BTN GROUP'
    }, requests;

    requests = $.map(Object.keys(things), function (key) {
        return $.get('https://getbootstrap.com/docs/3.3/components/#/' + key)
            .then(function (data) {
                return $('<li class="idItem">', {text: things[key] || 'NOT SET!'})[0];
            });
    });

    $.when.apply($, requests).done(function () {
        // each argument here is a single <li> element
        var items = $(arguments).sort(by(function (el) {
            return ($(el).text() || '').toLowerCase();
        }));
        $('<ul class="allItems">').appendTo('body').append(items);
    }).fail(function (jqXhr, status, error) {
        // show error
    });
});
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Thank you. I've adapted it to my code now, but have troubles with: `var items = $(arguments).sort(by(function (el) { return ($(el).data('name') || '').toLowerCase(); }));` arguments contains three values, all undefined. Can you help me out there? – SiriSch Feb 25 '18 at 18:34
  • Hm, I suspect you adapted something not quite the right way. `arguments` should contain 4 values, all of them jQuery objects that contain one `
  • ` element.
  • – Tomalak Feb 25 '18 at 18:44
  • ok, the example seems to work if I change the following line: `[...].append(items);` to `$('
      ').appendTo('body').append($.map(items, function(el, ignore) {return el[0]}));` But now I have another trouble. My .then looks like this: `.then(function(data) { parseEntryInfo(data, config, function(isOk, responseData) { return buildItem$(config, responseData); }); });` The method parseEntryInfo needs to have a callback as I do necessary ajax requests there either. Do you know how to return buildItem$ in this context?
    – SiriSch Feb 25 '18 at 19:36
  • Ahh, you're right. As I said, the `arguments` are jQuery objects containing an `
  • `, element. So `arguments` looks like this: `[[
  • ], [
  • ], [
  • ], [
  • ]]`, but for `.append()`, we actually need this: `[
  • ,
  • ,
  • ,
  • ]`. My mistake, I've made the necessary change to my answer. When you click the "edited..." link below the answer, you can see the changes in diff #4.
  • – Tomalak Feb 25 '18 at 20:05
  • Apart from that, I've replaced your functions for a reason. They were unnecessarily complex. For example, you don't need any callbacks, that's what promises are for. I'd encourage you to throw out those functions and take over a bit more of the approach demonstrated in my code. – Tomalak Feb 25 '18 at 20:13