I am using select2 version 4 with multiple select and I support users adding new tags but I want to prevent people choosing a new tag if that tag already exists on my backend.
Right now if a user enters a tag that already exists and I have tags:true then it shows two items in the dropdown (the existing one and the new one). Here is an example:
as you can see, "testTag2" is a valid tag from my backend so it shows up in the selection but because of the templateResult function and the fact that tags:true it also shows up as a second item (making the user think they can select it as a new tag).
Is there anyway to only show the "NEW" tag choice in the dropdown, if that text is NOT listed in the dropdown as another option?
here is my javascript code:
function SetupAppTags() {
$("#Tags").select2({
theme: "classic",
width: "98%",
tags: true,
ajax: {
url: "/Tag/Search",
dataType: 'json',
delay: 300,
data: function(params) {
return { q: params.term };
},
processResults: function(data, params) {
return { results: data };
},
cache: false
},
escapeMarkup: function(markup) { return markup; },
minimumInputLength: 3,
templateResult: tagFormatResult,
templateSelection: tagSelectionResult
});
}
function tagFormatResult(tag) {
if (tag.loading) {
return "Loading . . . <img src='/Content/Images/ajax-loader.gif' />";
} else {
if (tag.name) {
var existsAlready = $("#Tags option[value='" + tag.id + "']").length > 0;
if (existsAlready) {
return null;
}
return tag.name;
}
var length = $('#tagsContainer .select2-selection__choice').filter(function () {
return $(this).attr("title").toUpperCase() === person.text.toUpperCase();
}).length;
if (length == 1) {
return null;
}
return tag.text + " [NEW]";
}
}
This behaviour exists not because Select2 is forcing it, but because the browser is auto-selecting the first option for you. We can't control that, we can only work around it. Your best option is to use the placeholder support in Select2 and include a blank option tag as your default selection.
New options can be added to a Select2 control programmatically by creating a new Javascript Option object and appending it to the control: var data = { id: 1, text: 'Barn owl' }; var newOption = new Option(data. text, data.id, false, false); $('#mySelect2'). append(newOption).
select2({ ajax: { url: "/admin/api/transactions/creditorlist", dataType: 'json', delay: 250, data: function (params) { return { q: params. term, c_id: initial_creditor_id, page: params.
Method 1: Append the option tag to the select box The select box is selected with the jQuery selector and this option is added with the append() method. The append() method inserts the specified content as the last child of the jQuery collection. Hence the option is added to the select element.
According to Select2 Options
Change how options are matched when searching When users filter down the results by entering search terms into the search box, Select2 uses an internal "matcher" to match search terms to results. When a remote data set is used, Select2 expects that the returned results have already been filtered.
Key matcher Value A function taking search params and the data object. Select2 will pass the individual data objects that have been passed back from the data adapter into the matcher individually to determine if they should be displayed. Only the first-level objects will be passed in, so if you are working with nested data, you need to match those individually.
matcher: function (params, data) {
// If there are no search terms, return all of the data
if ($.trim(params.term) === '') {
return data;
}
// `params.term` should be the term that is used for searching
// `data.text` is the text that is displayed for the data object
if (data.text.indexOf(params.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
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
I think this is your typical case ,except that you should replace ("matched") with ("") , and add else to add that "[NEW]" in your case.
The returned data from ajax call should be the input to your matcher.
so your code should look like:
matcher: function (params, data) {
// If there are no search terms, return all of the data
if ($.trim(params.term) === '') {
return data;
}
// `params.term` should be the term that is used for searching
// `data.text` is the text that is displayed for the data object
if (data.text.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
//match was found then just show it.
// modifiedData.text += ' (matched)';
// You can return modified objects from here
// This includes matching the `children` how you want in nested data sets
return modifiedData;
}
else
{
//there is not match found , suggest adding NEW Tag.
modifiedData.text += '[NEW]';
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
Please correct me if I have misunderstood your question. But as per my understanding I have come up with following solution.
For demo purpose I have pre-loaded couple of <option>
elements within <select>
box and instead of ajax
data feed to select2
I am using plain JavaScript array
object mimicking ajax
response.
The Updated fiddle link: https://jsfiddle.net/vijayP/akyzt9Ld/11/
The HTML is as follows:
<div id="tagsContainer">
<select id="Tags" multiple="" name="Tags">
<option value="white">white</option>
<option value="green">green</option>
</select>
</div>
Here we can see that, the last <option>
present in dropdown has text green
.
JSON data
object which mimic ajax
response looks like below:
var data = [{ id: 0, name:'yellow', text: 'yellow' },
{ id: 1, name:'green', text: 'green' },
{ id: 2, name:'cyan', text: 'cyan' },
{ id: 3, name:'violet', text: 'violet' },
{ id: 4, name:'gray', text: 'gray' }
];
Here again the green
is at number 2.
select2
initialization and supporting JavaScript code goes like this:
var uniqueTexts = null; //array for holding unique text
$("#Tags").select2({
tags: true,
data: data,
templateResult: tagFormatResult,
escapeMarkup: function (markup) {
uniqueTexts = null;
return markup;
},
matcher: function (params, data) {
if(!uniqueTexts){
uniqueTexts = [];
}
var modifiedData = null;
if ($.trim(params.term) === '' || data.text.indexOf(params.term) > -1) {
if(uniqueTexts.indexOf(data.text) == -1)
{
modifiedData = data;
uniqueTexts.push(modifiedData.text);
}
}
if(modifiedData)
{
return modifiedData;
}
return null;
}
});
function tagFormatResult(tag) {
if (tag.loading) {
return "Loading . . . <img src='/Content/Images/ajax-loader.gif' />";
}
else
{
var length = $('#tagsContainer .select2-selection__choice').filter(function () {
return $(this).attr("title").toUpperCase() === tag.text.toUpperCase();
}).length;
if (length == 1) {
return tag.text;
}
if (tag.text) {
if(getOptionCount(tag.text) >1)
return tag.text;
else
return tag.text + " [NEW]";
}
return tag.text + " [NEW]";
}
}
function getOptionCount(tagText)
{
var count = 0;
var selectBoxOptions = $("#Tags option");
$(selectBoxOptions).each(function(){
if(tagText == $(this).text())
count++;//increment the counter if option text is matching with tag text
});
return count;
}
var uniqueTexts
is an array to hold unique text to be shown to the end user. While initiating we are setting it to null
. So the idea is whenever user focus-in to the select box OR type-in search keyword; matcher:
callback gets called for each option. We check each option and see if it is already present in uniqueTexts[]
or not. If its a first occurrence then we allow it to show; otherwise we return null
to avoid it from showing 2nd time.
I also added a escapeMarkup:
callback handler which gets called whenever options are shown to the end user. This is the point when we can again set uniqueTexts
to null
. This is the complete one round of cycle. Again user can focus-in or type-in to select box.
function tagFormatResult(tag)
works as follows:
1) First it checks whether current tag is already been selected or not. If it is already selected then don't add "[NEW]" text to tag.text
2) Secondly it checks whether current tag.text
is present as select option
text more than once or not. If it is present at more than one places, then also don't add "[NEW]" to tag.text
.
3) In all other cases go ahead and add "[NEW]" to tag.text
.
I hope this will help you a bit. Updated Link: https://jsfiddle.net/vijayP/akyzt9Ld/11/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With