Warre Buysse
Warre Buysse

Reputation: 1345

Select2 add image icon to option dynamically

This is what select2.github.io gives you:

function addIcons(opt) {
    if (!opt.id) {
        return opt.text;
    }
    var $opt = $(
            '<span><img src="/images/flags/' + opt.element.value.toLowerCase() + '.png" class="img-flag" /> ' + opt.text + '</span>'
            );
    return $opt;
}

I'd like to add a data-image attribute to my options:

<option value="flag" data-image="/images/flags/flag.png">Country 1</option>

and log it in the function:

function addIcons(opt) {
    if (!opt.id) {
        return opt.text;
    }

    var optimage = opt.attr('data-image');
    var $opt = $(
            '<span><img src="/images/flags/' + optimage + '" class="img-flag" /> ' + opt.text + '</span>'
            );
    return $opt;
}

Sadly, a simple console.log(opt); doesn't return anything in the function, so I can't see if I can access my data-image attribute. The above block of code returns an error, so this obviously doesn't work. Any suggestions on this matter?

Upvotes: 20

Views: 46134

Answers (6)

Gavin Miller
Gavin Miller

Reputation: 43815

Every single answer here is incorrect as it exposes your application to an XSS vulnerability when returning a jQuery object to select2. Let's read the docs and find out why:

Built-in escaping

By default, strings returned by templateResult are assumed to contain only text and will be passed through the escapeMarkup function, which strips any HTML markup.

If you need to render HTML with your result template, you must wrap your rendered result in a jQuery object. In this case, the result will be passed directly to jQuery.fn.append and will be handled directly by jQuery. Any markup, such as HTML, will not be escaped and it is up to you to escape any malicious input provided by users.

Looking at the other answers, they all have the fatal flaw of not escaping the text before passing it back. Instead the correct code should look like this (assuming a proper escape function, I used lodash in my circumstance):

$(".class").select2({
    templateResult: formatState,
    templateSelection: formatState
});

function formatState (opt) {
    if (!opt.id) {
        return opt.text;
    } 

    var optimage = $(opt.element).attr('data-image'); 
    if(!optimage) {
       // This is safe, since you're returning a string
       return opt.text;
    } else {
       // Create an individual image element to properly escape the src url
       var img = $("<img width='60px'>");
       img.attr("src", optimage);

       // And remember to escape the text we're outputing
       var $opt = $(
         '<span>' +
         img.prop('outerHTML'); +
         '" width="60px" /> ' + 
         escape(opt.text) + 
         '</span>'
       );
       return $opt;
    }
};

Upvotes: 0

If optimage returns "undefined" try my sample: its working fine:

$("#selectUserForChat").select2({
  templateResult: addUserPic,
  templateSelection: addUserPic
});

function addUserPic(opt) {
  if (!opt.id) {
    return opt.text;
  }
  var optimage = $(opt.element).data('image');
  if (!optimage) {
    return opt.text;
  } else {
    var $opt = $(
      '<span class="userName"><img src="' + optimage + '" class="userPic" /> ' + $(opt.element).text() + '</span>'
    );
    return $opt;
  }
};

Upvotes: 16

JHOAN B. HENRICHE
JHOAN B. HENRICHE

Reputation: 409

Solved using attr and tested on Select2 4.0.6-rc.0.

$(".class").select2({
    templateResult: formatState,
    templateSelection: formatState
});

function formatState (opt) {
    if (!opt.id) {
        return opt.text.toUpperCase();
    } 

    var optimage = $(opt.element).attr('data-image'); 
    console.log(optimage)
    if(!optimage){
       return opt.text.toUpperCase();
    } else {                    
        var $opt = $(
           '<span><img src="' + optimage + '" width="60px" /> ' + opt.text.toUpperCase() + '</span>'
        );
        return $opt;
    }
};

Upvotes: 23

Lucas Bustamante
Lucas Bustamante

Reputation: 17178

Not necessarily related to the question, but in my case it wasn't working because if you are using Select 2 < 4.0, templateResult and templateSelection does not exist. Use formatResult and formatSelection instead.

Upvotes: 0

user4540007
user4540007

Reputation:

I solved problem with this code: var optimage = $(opt.element).data('image');

$(".category").select2({
            templateResult: formatState,
            templateSelection: formatState
        });
        function formatState (opt) {
            if (!opt.id) {
                return opt.text;
            }               
            var optimage = $(opt.element).data('image'); 
            if(!optimage){
                return opt.text;
            } else {                    
                var $opt = $(
                    '<span><img src="' + optimage + '" width="23px" /> ' + opt.text + '</span>'
                );
                return $opt;
            }

        };

Upvotes: 9

PeterKA
PeterKA

Reputation: 24638

Try this:

var optimage = $(opt).data('image'); //or $(opt).attr('data-image')
var $opt = $(
    '<span><img src="' + optimage + '" class="img-flag" /> ' + $(opt).text() + '</span>'
);

Upvotes: 3

Related Questions