Javascript – jquery select2 (4.0) ajax with templateResult and templateSelection

ajaxjavascriptjqueryjquery-select2jquery-select2-4

My Select2 was working on 3.5 correctly..

Since upgrading to v4.0 (not "full" – and changing keywords/functions as needed), I have a weird problem where there are extra AJAX calls being made. However, the URL is not defined, so they generate 404 Not Found errors. The URL is https://localhost:8443/myapp/undefined

They seem to be related to the templateResult and templateSelection being present. If I comment them out, the select2 works correctly (but my data is not formatted).

With them not commented out, I get the mysterious/undefined AJAX call once at initialization, then one call is made when I click on the select box, then once for every character I type (even if I set minimumInputLength). However, even with these bogus AJAX calls, my "real" ajax call will fire and return results (which are properly formatted by templateResult/templateSelection. I have tried with and without "escapeMarkup" with no difference in behavior.

What is triggering these bad AJAX calls and how can I stop them? (Because otherwise, it is working just fine)

Thanks in advance!

Edit
Here is a full page that demonstrates the issue. The extra network calls are generated by the tag that I use in the formatResult function. But why is it returning the html when it should be in a "loading" state?

Well, it turns out that setting the "placeholder" causes the loading variable to not be set, thus the html is returned (with its ill-formed tag)

So if a placeholder is set, the templateResult and templateSelection should also check for an empty id..

if (result.id == "" || result.loading) return result.text;

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title></title>

<link rel="stylesheet" href="${pageContext.request.contextPath}/lib/select2/dist/css/select2.css" />
</head>
<body>

<form id="organization_lookup_form" class="form-horizontal" >
<div>
    Select2 using placeholder <select id="search1" style="width:300px"></select>
</div>
<div style="padding-top:250px">
    Select2 WITHOUT placeholder <select id="search2" style="width:300px"></select>  
</div>
</form>

<script src="${pageContext.request.contextPath}/lib/jquery/dist/jquery.min.js"></script>
<script src="${pageContext.request.contextPath}/lib/select2/dist/js/select2.min.js"></script>
<script>
$(document).ready(function () {
      function formatResult (result){
          console.log('%o', result);
          if (result.loading) return result.text;
          var html = '<div>'+
                '<img src="' + result.image + '">' +
                '<h4>'+result.label+'</h4></div>';
          return html;
      };

      $('#search1').select2({
          placeholder: "Search...",
          ajax: {
              url: '/search',
              dataType: 'json',
              data: function (params, page) {
                  return {
                      term: params.term, // search term
                      page: page
                  };
              },
              processResults: function (data, page) {
                  return {results: data.results};
              },
              cache: true
          },
          templateResult : formatResult,
          templateSelection : formatResult,
          escapeMarkup: function(m) {
              // Do not escape HTML in the select options text
              return m;
           },
          minimumInputLength: 3
      });
      $('#search2').select2({
//        placeholder: "Search...",
          ajax: {
              url: '/search',
              dataType: 'json',
              data: function (params, page) {
                  return {
                      term: params.term, // search term
                      page: page
                  };
              },
              processResults: function (data, page) {
                  return {results: data.results};
              },
              cache: true
          },
          templateResult : formatResult,
          templateSelection : formatResult,
          escapeMarkup: function(m) {
              // Do not escape HTML in the select options text
              return m;
           },
          minimumInputLength: 3
      });
});
</script>

</body>
</html>

Best Answer

Since version 4.0, the value of templateResult and templateSelection are just "appened". This behavior is not documented.

So if you pass html, all tags are displayed.

The workaround is to return a jQuery object :

 function formatResult (result){
      console.log('%o', result);
      if (result.loading) return result.text;
      var html = '<div>'+
            '<img src="' + result.image + '">' +
            '<h4>'+result.label+'</h4></div>';
      //return html;
      return $(html);
  };