Remove option from results when selected

Im looking at upgrading our use of select2 to the latest version and I was trying to make it work like we were using earlier versions.

Basically my question is, I have a multi select that i attach select2 to, and when you select the item, it will put it in a “pill” and if you click the dropdown to expose the select options, I want the one selected to not be in the list.

I have a jsfiddle with a sample of what I am doing.

I was looking at the templateResult and in that callback I check to see if the option is selected. if it is, then I return null, otherwise i return the text property. This works, but previously, when you tried to select something and you have already selected all the options, it would display the “No results found” or whatever your noMatches message is set to. It shows up when you type something in the select field to search, but not when you try to expand the dropdown and all the options are selected.

What is the best way to handle this? Do i need to make a custom data adapter or ?

Thank you!

templateResult is the correct way to handle this, and it seems you’ve already made that work. If I understand you correctly, the issue you’re having is that, after the user has selected all of the options (so nothing is displayed in the dropdown any more), the “noMatches” message no longer appears, whereas it used to appear under this circumstance in the previous version of the Select2 widget.

I think the issue is that simply opening the dropdown does not cause any “matching” to happen, so it doesn’t trigger the “noMatches” message.

I think you could “hack” this to behave like you want to as follows:

  1. Include an option in your select (or your data, if you’re getting it remotely or in memory) whose text is the same as the “noMatches” message. (Ideally, you should make this the last option or data item, and it should be disabled.)
  2. In your templateResult function, keep track of how many “real” items you’ve allowed to display. If that number is greater than zero (i.e., there are items the user has not selected yet), then when the callback receives the “noMatches” item, return null so it does not display. If the number of displayed items you tracked is zero (i.e., the user has selected all of the items), then when the callback receives the “noMatches” item, return its text so it will be displayed in the dropdown. Note that you’ll need to keep this tracking variable outside of your templateResult callback function, since that function is called anew for each item in the data. You’ll also need to reset/clear its value, probably when the Select2 closes (assuming the dropdown closes after every selection) by attaching an event handler for the “select2:close” event.

I’ve forked your jsfiddle with additional logic that (mostly) works. It only fails when the user types into the search box: if there are no remaining (unselected) options, it does not display the “noMatches” text. To fix that you’d probably need to add a custom matcher callback to do the same counting. And you’d need to include logic (probably setting another “global” flag variable) to prevent the templateResult callback from doing the counting, since you’d get “double” counting on any matched options. And, of course, you’d have to figure out when to reset that flag (maybe in your “select:opening” event handler). As you can see, this gets complicated, but it probably can be done. It just depends on whether the effort is worth the outcome to you. Good luck!

Thank you for your reply!
I don’t really like that I have to “hack” my way to make it do what I want, but if that is the only way in this new version I guess it will have to do. I could leave it be and deal with this change.

Just FYI, here is another jsfiddle with 3.5.1 where what I want is working out of the box. Can this be put back in or added as an option?

I’m not on the Select2 development team, so I can’t answer your question about whether the previous behavior can be put back.

I’ve found that many things that seem to be obvious aren’t built in and have to be hacked.