Fire Event After results have rendered in dropdown


#1

Hi, I have been struggling with this issue for a while. Basically I need to initialize an avatar image once html is rendered in the dropdown after user has entered a query.

 $(".js-select2").select2({
    placeholder: "Search active connections",
    theme: "material",
    templateResult: formatState,
    ajax: {
      url: '/connections/active',
      dataType: 'json',
      data: function (params) {
        var query = {
          q: {full_name_or_email_cont: params.term},
        }
        return query;
      }
    }
 })

function formatState (state) {
  if (!state.id) {
    return state.text;
  } // image html string created but not added to dom yet
  var $state = $(`<span> ${state.avatar} ${state.text} </span>`);
  // need to run this to render avatar otherwise image doesnt appear but have to wait until results are populated
   $('.initialjs-avatar').initial({
     radius: 30
   }); 
    return $state;
};

Essentially I am looking for a event that wil fire after new results have been appended to ul element. I have tried open and select but those are after the results have already appeared. Thank you.


#2

Have you tried the select2:opening event? It fires before the dropdown opens, and since it can be cancelled, I would think the ul has already been populated when that event fires.

My guess about the sequence of events when using an AJAX data source is:

  1. Search results come back from AJAX call.
  2. “templateResult” callback is called once for each item in the results. This builds the ul.
  3. The select2:opening event fires.
  4. The dropdown is displayed.
  5. The select2:open event fires.

If the select2:opening event doesn’t work (e.g., because it actually fires before the “templateResult” callbacks), then I can think of a way you might be able to “hack” it using setTimeout in the “templateResult” callback. Basically you’d want to keep a “global” variable (outside the “templateResult” callback—formatState in your example code) holding the return value from setTimeout. Then, at the top of your “templateResult” callback you would clear the timeout, then set it again, then return your template string. When the timeout finally fires (which it will only do after the final result’s template has been rendered to the ul), it runs the code to initialize your avatar images. The trick would be discovering the smallest timeout value you can get away with, because you want the avatar initialization to happen as soon as possible after the last call to the “templateResult” callback. Of course, if the avatar initialization takes some time, then it’s possible the dropdown would still be displayed before some or all of the avatars are initialized.

Another approach might be to pre-initialize all of the possible avatars (assuming that set is not very large), and then the “templateResult” callback would insert the initialized avatars instead of initializing them after outputting the templates. But this assumes (a) you know what all the potential avatars could be, and (b) the set is not too large to pre-create & initialize.


#3

Hi, thank you for your response. I have tried the ''select2:opening" event but this fires only when you first open the dropdown ( there are no results because user hasnt started typing yet). But when user starts typing, which is when ul actually gets populated with results, the event does not fire. Thus, the new results arent initialized with avatar. I will try the other approach with timeout but I am usually hesitant to use timeout for things like these because there are always things that can go wrong. Should I add this in the github I think its a pretty common use case to want to do something after results have been populated?


#4

Yes, I think it would be worthwhile to suggest this to the Select2 development team.