Select2 - Multi Select Autocompletion - Programmatically population of field - javascript

I am using the bundled AUI Select2 Lib in a JIRA 8.13 instance.
What I would like to try is to code an autocompletion field with multiple values. My main components are running so far, but I have some problems while populating the field programmatically via JavaScript.
This my basic setup:
field.auiSelect2(
{
multiple: true,
closeOnSelect: true,
tokenSeparators: [","],
placeholder: PLACEHOLDER,
minimumInputLength: 2,
formatResult: format,
initSelection: function (element, callback) {
callback({id: $(element).val(), text: $(element).val()});
},
ajax: {
url: <...>,
dataType: 'json',
data: function (term) {
return {term: term,};
},
results: function (results) {
return results;
},
quietMillis: 250,
cache: false
},
});
The field population code is the following:
field.val(["A","B","C"]).trigger('change');
But this results in a single entry in the input field value "A,B,C" which is wrapped within a single gray box instead of multiple boxes - one for each passed value.
Any ideas on how to solve this issue? I guess it might be related to the initSelection parameter.
https://codepen.io/aschuma/pen/rNLRLWL

$(field).auiSelect2("data", [{id: "A", text: "A"},{id:"B", text: "B"},{id:"C", text: "C"}]);
https://codepen.io/aschuma/pen/ExyMNJP

This error is because you are using an <input> element instead of a <select> element. When you call this:
field.val(["A","B","C"]).trigger('change');
what you are really doing is setting the value of the <input> field which operates completely separately from select2. An <input> field can only have a single value, so it is likely that jQuery simply calls a standard .join() on the array and then sets the value to that. You can verify this is correct by simply opening a console and running:
console.log(["A","B","C"].join())
which will result in the string "A,B,C". What you might need is a <select> element which can support multiple values. Also note that the latest version of AUI Select2 (9.1.4) uses select2 version 3.4.5 instead of the latest version (4.0.13 as of the time of this post).
You could also set the data on the select2 instance itself like so:
$(field).auiSelect2('data', [
{id: 'A', text: 'A'},
{id: 'B', text: 'B'},
{id: 'C', text: 'C'},
]);

Found this thread because we had the same problem. I can confirm that it's also posible using elements if you use the following code.
AJS.$('#customfield_14882').auiSelect2(
{
multiple: true,
closeOnSelect: true,
tokenSeparators: [","],
placeholder: PlaceHolder,
minimumInputLength: 1,
allowClear:true,
initSelection: function (element, callback) {
var arr = $(element).val().split(',');
var res=[];
for(var i=0;i<arr.length;i++){
res[i]={id:arr[i],text:arr[i]};
}
callback(res);
},
ajax: {
url: "<url>",
dataType: 'json',
data: function (term) {
return {term: term};
},
results: function (results) {
return {results:results};
},
quietMillis: 250,
cache: false
},
});

Related

Selectize: Setting Default Value in onInitialize with setValue

I have a web application with multiple Selectize objects initialized on the page. I'm trying to have each instance load a default value based on the query string when the page loads, where ?<obj.name>=<KeywordID>. All URL parameters have already been serialized are are a dictionary call that.urlParams.
I know there are other ways to initializing Selectize with a default value I could try; but, I'm curious why calling setValue inside onInitialize isn't working for me because I'm getting any error messages when I run this code.
I'm bundling all this JavaScript with Browserify, but I don't think that's contributing to this problem.
In terms of debugging, I've tried logging this to the console inside onInititalize and found that setValue is up one level in the Function.prototype property, the options property is full of data from load, the key for those objects inside options corresponds to the KeywordID. But when I log getValue(val) to the console, I get an empty string. Is there a way to make this work or am I ignoring something about Selectize or JavaScript?
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
onInitialize: function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
this.setValue(val);
},
load: function(query, callback) {
$.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
})
}
});
};
...
After sprinkling in some console.logs into Selectize.js, I found that the ajax data hadn't been imported, when the initialize event was triggered. I ended up finding a solution using jQuery.when() to make setValue fire after the data had been loaded, but I still wish I could find a one-function-does-one-thing solution.
module.exports = function() {
var that = this;
...
this.selectize = $(this).container.selectize({
valueField: 'KeywordID', // an integer value
create: false,
labelField: 'Name',
searchField: 'Name',
preload: true,
allowEmptyOptions: true,
closeAfterSelect: true,
maxItems: 1,
render: {
option: function(item) {
return that.template(item);
},
},
load: function(query, callback) {
var self = this;
$.when( $.ajax({
url: that.url,
type: 'GET',
error: callback,
success: callback
}) ).then(function() {
var val = parseInt(that.urlParams[that.name], 10); // e.g. 5
self.setValue(val);
});
}
});
};
...
You just need to add the option before setting it as the value, as this line in addItem will be checking for it:
if (!self.options.hasOwnProperty(value)) return;
inside onInitialize you would do:
var val = that.urlParams[that.name]; //It might work with parseInt, I haven't used integers in selectize options though, only strings.
var opt = {id:val, text:val};
self.addOption(opt);
self.setValue(opt.id);
Instead of using onInitialize you could add a load trigger to the selectize. This will fire after the load has finished and will execute setValue() as expected.
var $select = $(this).container.selectize({
// ...
load: function(query, callback) {
// ...
}
});
var selectize = $select[0].selectize;
selectize.on('load', function(options) {
// ...
selectize.setValue(val);
});
Note that for this you first have to get the selectize instanze ($select[0].selectize).
in my case it need refresh i just added another command beside it
$select[0].selectize.setValue(opt);
i added this
$select[0].selectize.options[opt].selected = true;
and changes applied
but i dont know why?
You can initialize each selectize' selected value by setting the items property. Fetch the value from your querystring then add it as an item of the items property value:
const selectedValue = getQueryStringValue('name') //set your query string value here
$('#sel').selectize({
valueField: 'id',
labelField: 'title',
preload: true,
options: [
{ id: 0, title: 'Item 1' },
{ id: 1, title: 'Item 2' },
],
items: [ selectedValue ],
});
Since it accepts array, you can set multiple selected items

Select2 dropdown allow new values by user when user types

The select2 component can be configured to accept new values, as ask in Select2 dropdown but allow new values by user?
And you can see it at: http://jsfiddle.net/pHSdP/646/ the code is as below:
$("#tags").select2({
createSearchChoice: function (term, data) {
if ($(data).filter(function () {
return this.text.localeCompare(term) === 0;
}).length === 0) {
return {
id: term,
text: term
};
}
},
multiple: false,
data: [{
id: 0,
text: 'story'
}, {
id: 1,
text: 'bug'
}, {
id: 2,
text: 'task'
}]
});
The problem is that the new value is only added to the list if you enter new value and press enter, or press tab.
Is it possible to set the select2 component to accept this new value when use types and leave the select2. (Just as normal html input tag which keeps the value which you are typing when you leave it by clicking some where on the screen)
I found that the select2 has select2-blur event but I don't find a way to get this new value and add it to list?!
Adding attribute selectOnBlur: true, seems to work for me.
Edit: glad it worked for you as well!
I am using Select2 4.0.3 and had to add two options:
tags: true,
selectOnBlur: true,
This worked for me
And to be able to submit multiple new choices together with the existing ones:
select2({tags: true, selectOnBlur: true, multiple: true})

Loading values into Selectize.js

Problem
I have a text input that I have selectized as tags which works fine for querying remote data, I can search and even create new items using it and that all works OK.
Using selectize:
var $select = $('.authorsearch').selectize({
valueField: 'AuthorId',
labelField: 'AuthorName',
searchField: ['AuthorName'],
maxOptions: 10,
create: function (input, callback) {
$.ajax({
url: '/Author/AjaxCreate',
data: { 'AuthorName': input },
type: 'POST',
dataType: 'json',
success: function (response) {
return callback(response);
}
});
},
render: {
option: function (item, escape) {
return '<div>' + escape(item.AuthorName) + '</div>';
}
},
load: function (query, callback) {
if (!query.length) return callback();
$.ajax({
url: '/Author/SearchAuthorsByName/' + query,
type: 'POST',
dataType: 'json',
data: {
maxresults: 10
},
error: function () {
callback();
},
success: function (res) {
callback(res);
}
});
}
});
The text box:
<input class="authorsearch" id="Authors" name="Authors" type="text" value="" />
Examples:
Then when I select one (in this case 'apple') it comes up in a badge as you'd expect, and the underlying value of the textbox is a comma separated list of the values of these items.
Current Output
The problem is when I load a page and want values retrieved from the database to be displayed in the selectized text input as tags, it only loads the values and I can see no way of displaying the displayname instead.
<input class="authorsearch" id="Authors" name="Authors" type="text" value="1,3,4" />
Desired Ouput
I have tried all sorts of values for the inputs value field to have it load the items as showing their displayname and not their values. Below is an example of a single object being returned as JSON, being able to load a JSON array of these as selectized tags would be ideal.
[{"AuthorId":1,"AuthorName":"Test Author"},
{"AuthorId":3,"AuthorName":"Apple"},
{"AuthorId":4,"AuthorName":"Test Author 2"}]
How can I go about this? Do I need to form the value of the text box a particular way, or do I need to load my existing values using some javascript?
Thanks to your answer and based on your onInitialize() approach I ended up with a similar solution. In my case I just needed to translate one value, thus I was able to store the id and label as data attributes in the input field.
<input type="text" data-actual-value="1213" data-init-label="Label for 1213 item">
Then on initialization:
onInitialize: function() {
var actualValue = this.$input.data('actual-value');
if (actualValue){
this.addOption({id: actualValue, value: this.$input.data('init-label')});
this.setValue(actualValue);
this.blur();
}
}
According to these options:
$('input').selectize({
valueField: 'id',
labelField: 'value',
searchField: 'value',
create: false,
maxItems: 1,
preload: true,
// I had to initialize options in order to addOption to work properly
// although I'm loading the data remotely
options: [],
load: ... ,
render: ...,
onInitialize: ....
});
I know this does not answer your question but wanted to share just in case this could help someone.
I ended up using the onInitialize callback to load the JSON values stored in a data-* field. You can see it in action here in this jsfiddle.
<input class="authorsearch" id="Authors" name="Authors" type="text" value=""
data-selectize-value='[{"AuthorId":1,"AuthorName":"Test"},{"AuthorId":2,"AuthorName":"Test2"}]'/>
Basically it parses the data-selectize-value value and then adds the option(s) to the selectize then adds the items themselves.
onInitialize: function() {
var existingOptions = JSON.parse(this.$input.attr('data-selectize-value'));
var self = this;
if(Object.prototype.toString.call( existingOptions ) === "[object Array]") {
existingOptions.forEach( function (existingOption) {
self.addOption(existingOption);
self.addItem(existingOption[self.settings.valueField]);
});
}
else if (typeof existingOptions === 'object') {
self.addOption(existingOptions);
self.addItem(existingOptions[self.settings.valueField]);
}
}
My solution does presume my JSON object is formed correctly, and that it's either a single object or an object Array, so it may or may not be appropriate for someone elses needs.
So it parses:
[{"AuthorId":1,"AuthorName":"Test"},
{"AuthorId":2,"AuthorName":"Test2"}]
To:
Based of course on my selectize settings in my original post above.
Even simpler on new version of selectize using items attribute. Basically to set a selected item you need to have it first in the options. But if you use remote data like me, the options are empty so you need to add it to both places.
$('select').selectize({
valueField: 'id',
labelField: 'name',
options:[{id:'123',name:'hello'}],
items: ['123'],
...
This is working for me and took me a while to figure it out... so just sharing

Creating new tags in a Select2 tag textarea

I have an input (textarea) that has Select2's tags applied to it. So when a user types in the name of an item that exists in my data base, it shows a list of matching items and the user can select one and a tag is created.
Here is my code so far for basic tag functionality:
$('#usualSuppliers').select2({
placeholder: "Usual suppliers...",
minimumInputLength: 1,
multiple: true,
id: function(e) {
return e.id + ":" + e.name;
},
ajax: {
url: ROOT + 'Ajax',
dataType: 'json',
type: 'POST',
data: function(term, page) {
return {
call: 'Record->supplierHelper',
q: term,
page_limit: 10
};
},
results: function(data, page) {
return {
results: data.suppliers
};
}
},
formatResult: formatResult,
formatSelection: formatSelection,
initSelection: function(element, callback) {
var data = [];
$(element.val().split(",")).each(function(i) {
var item = this.split(':');
data.push({
id: item[0],
title: item[1]
});
});
//$(element).val('');
callback(data);
}
});
Is there a way for a new tag to be created if the text typed does not exist? Initially I thought this could some how be done by delimiting with spaces, but some items (supplier names) will have spaces in them, so that won't work.
I think when no matches are found the user needs to somehow "create" the tag by pressing a button that could appear in the drop down box, but I have no idea how to do this.
How can I allow users to create new tags that may have spaces in them and still be able to carry on adding more tags, existing or otherwise?
Yes you can do it. There is a example in the documentation. Look at http://ivaynberg.github.io/select2/#events
$("#e11_2").select2({
createSearchChoice: function(term, data) {
if ($(data).filter( function() { return this.text.localeCompare(term)===0;
}).length===0) {
return {id:term, text:term};
}
},
multiple: true,
data: [{id: 0, text: 'story'},{id: 1, text: 'bug'},{id: 2, text: 'task'}]
});
You have to create a function like createSearchChoice, that returns a object with 'id' and 'text'. In other case, if you return undefined the option not will be created.

Select2 - Deny to can clean the input field and disable the form if results not found

I have this json file on /api/searches:
[{"id":"513dbb61a61654a845000005","text":"ingeniero agrónomo"},{"id":"513a11d4a6165411b2000008","text":"ingeniero industrial"}]
I'm using select2 to show results.
This is my current select2 setting:
$('#query_txt').select2
createSearchChoice: (term, data) ->
if $(data).filter(->
#text.localeCompare(term) is 0
).length is 0
id: term
text: term
width: 'resolve'
minimumInputLength: 3
tags: true
showSearchBox: true
maximumSelectionSize: 1
closeOnSelect: true
multiple: false
selectOnBlur: true
ajax:
url: "/api/v1/cvs/searches"
quietMillis: 100
dataType: 'json'
data: (term, page) ->
q: term,
page_limit: 9
results: (data, page) ->
results: data
initSelection: (element, callback) ->
$.get "/api/searches/" + element.val(), (data) ->
callback {id: data.id, text: data.occupation}
In this example, http://jsfiddle.net/nqWNJ/6/ or http://jsfiddle.net/uDrvp/1/ if you type on text field the word "asdfasdf" or random word, and if you click output of the input field, the keyword remains inside the input and this keyword is not deleted by select2.
I would like, if no results are found or the user clicks outside the text field, the keyword is not removed and my form is not disabled
How can I achieve this purpose?
you have to add selectOnBlur:true option to your config and upgrade to latest master. here is a working fiddle: http://jsfiddle.net/nqWNJ/7/

Categories