Search code examples
jquery-select2jquery-select2-4

Exclude existing tags from Select2 search results


I'm using Select2 ("select2": "^4.0.6-rc.1") as shown below, and it's mostly working well.

But now here is a scenario we want to prevent:

An admin wants to add TagXyz but does not see that this tag already exists (because there is already a long list of selected tags) and so starts typing the name of the tag and then presses Enter, thinking he added the tag, but in reality, it would remove the existing tag because it had already existed!

How can I exclude existing tags from Select2 search results (or otherwise achieve my goal)?

I thought maybe I should add matcher: matchCustom, (as suggested in the docs) and make my matchCustom exclude current tags.

So as a starting place for my matchCustom function, I tried copying the matcher function at https://github.com/select2/select2/blob/4.0.6-rc.1/dist/js/select2.js#L4931 but see that I'd need access to the stripDiacritics function.

What should I do?

var tagsSelect = $('select[name=tags]');
tagsSelect.select2({
    placeholder: 'Choose tags here',
    tokenSeparators: [',', ' '], //https://select2.org/tagging
    theme: "classic",
    width: "style" //element, style, resolve, or '<value>' https://select2.org/appearance#container-width
}).on('select2:select', function (e) {
    var data = e.params.data;
    addTag($(this).attr('data-contactId'), data.id, data.text);// https://select2.org/programmatic-control/events
}).on('select2:unselect', function (e) {
    var data = e.params.data;
    var contactTagId = data.element.getAttribute('data-contacttagid');//https://stackoverflow.com/a/30850437/470749
    removeTag(contactTagId, data.text); // https://select2.org/programmatic-control/events
}).on('select2:unselecting', function (event) {       
    var label = event.params.args.data.text;       
    var opt = tagsSelect.find('option[value="' + event.params.args.data.id + '"]').first();
    if (opt.is(':disabled')) {
        event.preventDefault();
    }
}).on('change', function (event) {
    changeClassOfDisabledTags();
});

Solution

  • I think I solved it.

    const DIACRITICS = require('select2/src/js/select2/diacritics.js');
    
    function stripDiacritics(text) {
        // Used 'uni range + named function' from http://jsperf.com/diacritics/18
        function match(a) {
            return DIACRITICS[a] || a;
        }
        return text.replace(/[^\u0000-\u007E]/g, match);
    }
    

    Then this is the relevant part of my matchCustom:

    if (original.indexOf(term) > -1) {
        var modifiedData = $.extend({}, data, true);
        //modifiedData.text += ' (matched)';
    
        // You can return modified objects from here
        // This includes matching the `children` how you want in nested data sets
        if (modifiedData.selected) {
            return null;//exclude existing (selected) tags from search results
        } else {
            return modifiedData;
        }
    }