Closed Jrizzi1 closed 6 years ago
I think you can check the small piece of code included in the Announcement link
http://select2.github.io/announcements-4.0.html
Personally I really hate this new way of defining custom data loader. It makes the process of defining loader extremely complex especially when you use it with other libs such as angularjs.
What with the $.fn.select2.amd.require? Now suddenly we have to learn about AMD as well.
CustomData.prototype.current can't replace initSelection, because you set value only with id prorerty and don't have text. You must manualy create options. (if you use ajax, data is empty)
Easier manualy create options with attribute selected.
P.S. plz add back initSelection
@Jrizzi1
I'm not sure if you have a similar case, but do look into the source code to see how each Adapter works. For example I think select2 SelectionAdapter will scan your selected options and auto add them.
This change is really unlucky & developers unfriendly. Previous case when using initSelection was significantly less effort-demanding implementation for developers. There is no documentation with real-world examples (there's only 1 simple example in announcement) and it's really a nightmare to study details of implementation, quite a time loss.
Please provide at least 2-3 more examples on how to initiate dropdown lists for existing values when loading dropdown from remote sources via ajax.
Couldn't agree more with @yellow1912's first comment. What's wrong with this picture:
OLD way:
initSelection : function (element, callback) {
var data = [];
$(element.val()).each(function () {
data.push({id: this, text: this});
});
callback(data);
}
NEW way:
$.fn.select2.amd.require(
['select2/data/array', 'select2/utils'],
function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.current = function (callback) {
var data = [];
var currentVal = this.$element.val();
if (!this.$element.prop('multiple')) {
currentVal = [currentVal];
}
for (var v = 0; v < currentVal.length; v++) {
data.push({
id: currentVal[v],
text: currentVal[v]
});
}
callback(data);
};
$("#select").select2({
dataAdapter: CustomData
});
}
We go from 7 lines of code to 30 lines of code to achieve the same functionality? The old way was much cleaner and easier to understand. The new way introduces new concepts (amd
) that seems overly complex and confusing.
I think people are missing the point here (as I was initially). Although 'current' is the replacement for 'initSelection', it is rarely needed at all. From what I understand (and how I've implemented it), the typical way to initialize selections is to set the options elements to selected prior to calling select2. This is easily done in either HTML or AJAX.
It's actually quite easy and much more logical than it used to be. I think the documentation could make this point a bit more clear though because I was also confused at first. You only need this CustomData stuff if you want to override the default behavior of select2, which most people don't.
@pjclas, so based on the example in the release nodes, how would/did you go about easily implementing the solution in 4.0? Can you provide examples? Maybe it's something we all missed...
Sure, here is a simple ajax example:
If you aren't using ajax, you can just add the option elements directly to the select tag in the html.
so based on the example in the release nodes, how would/did you go about easily implementing the solution in 4.0? Can you provide examples? Maybe it's something we all missed...
It sounds like @pjclas is referring to the second example in the release notes, which is roughly equivalent to the example in the jsfiddle.
And I agree, the second example is much closer to what most people need to use when migrating. If someone wants to create a pull request that either clarifies that or switches the example, that'd be great.
I believe that all API select2 v4 was very complicated.
Mainly working with ajax.
The InitSelection for me is very important. I understand that even with the tag "select" it must exist.
Imagine the situation. I have only the "id" of the object in my javascript and i need assigns it to "select" to display the information in the version 3.x enough to do that
$ ('#selectIdPessoa').val('BFDC6247-8FCD-4689-A69E-1C7625409924').trigger('change')
Then the ajax (InitSelection) was shot and the component would go to the server and search the "Text".
But in 4.X version nothing happens, even rewriting the code using adapters
I believe that if the API remain so complicated, there will be an exodus of developers for other plugins. We need to simplify things, and quite
The Example here https://select2.github.io/announcements-4.0.html on section "Removed the requirement of initSelection" show how to do without ajax (both examples).
We need a sample of replacement to "InitSelection" with remote data (ajax) on documentation
See http://jsfiddle.net/s8s2ho28/17/
I finally managed to solve my problem in migrating to version 4.
In my scenario all my "select2" are linked to a customBinding the knockoutjs. So I took my customBinding to solve the problem that was previously done by select2 internally on the "InitSelection"
So I'm posting here to help someone who has the same problem.
In the current solution, not yet tested it with "multiple". But soon I will test
Yeah I'm thinking about migrating from selectize.js to select2 4.0 but the new API looks extremely overengineered...
I figure I might as well reiterate that most use cases do not need a custom data adapter, including pre-selecting options using AJAX.
For those few that do, we need some actual documentation. I've been looking into Docco for generating some more direct documentation automatically, but there are plans to overhaul the documentation.
We need a sample of replacement to "InitSelection" with remote data (ajax) on documentation
The second example under that same section is designed for AJAX use cases, which was mentioned above.
Well the data adapter issue is obvious to solve and people crying for initSelection
to be brought back really should just RTFM. However I'm referring to the general idea with adapters/decorators and wrapping that in this AMD thing. It really makes integration between select2 and Angular for example that much harder when you have to deal with another layer of dependency injection stuff.
Anyway, what's done is done, perhaps we'll get used to it.
Data Adapter is stressful solution, spend hours to understand it plus trials-errors. Well my monkey patch isnt good, but at least it works without bleeding to Data Adapter
$.each $(el).find('.select2-ajax'), (i, item)->
$(item).select2
ajax:
url: $(item).data('url')
dataType: 'json'
delay: 250
data: (params)->
return {q: params.term}
processResults: (data, page)->
return {results: data}
cache: true
minimumInputLength: 3
templateResult: (data)->
return data.label
templateSelection: (data)->
return data.label
ajax_selected = $(item).parent().find('.select2-selection__rendered')
if ajax_selected.length
ajax_selected.text(ajax_selected.attr('title'))
$(item).parent().find('.select2-container').css('width', 'auto')
@jackbit If you used text
for your template (you should be - it's mostly required) you likely wouldn't need to fiddle with how it is automatically rendered.
Alternatively, if you don't feel like doing the re-mapping, you should fall back to data.text
in your templateSelection
.
templateSelection: (data) ->
return data.label or data.text
templateSelection: function (data) {
return data.label || data.text
}
And you can set the width to auto
by default when initializing Select2.
width: null // or 'auto', both are equivalent
@kevin-brown I have been using templateSelection on my snipped code above. My issue was when I want edit selection that i have already set or save, it doesn't display default value that i have made before. But in original select tag, it has my selected value.
In my opinion. We already use the "selec2" one day we will get used to the new API and everything will work out.
But my concern is that the use of a plugin should be as simple as possible. And definitely this is not happening in the new API, and the consequence of this is that new users do not opt for our plugin, and over time the "select2" come into disuse.
I think we should simplify much the API plugin. Mainly in integration with AJAX.
The end of InitSelection brings disastrous consequences for those who used the plug connected to a MVVM framework (knockoutjs, angularjs and etc ...). For a task that "select2" made automatically, should now be made in advance by the application code itself.
See my example: http://jsfiddle.net/s8s2ho28/17/
In Version 3.x I used the InitSelection and voila, problem solved by integrating with knockoutjs I just attributing the value of the element and the "select2" resolved to me how to display it properly (searching via ajax text)
In 4.X version now after a long time to understand where it should be done to change (trying with or without DataAdapters) I managed to get a solution. But now my application via ajax search text, and then everything is already in HTML ready, oh then I apply the plugin.
It works, but makes using more painful, and it does not attract users, and it amazes beginners programmers.
I love the plugin, do not want to change it, so I think we should prioritize better documentation, especially a simpler API.
but, this is only my opinion, and not based on technical arguments, but rather on the user experience (developer)
@penihel, many share the same opinion and to your point about being turned off with the new api, I ended up downgrading back to 3.5.x.
It's not a matter of being able to figure out how to make it work, it goes back to the beginning of why we choose to use the library in the first place.... Ease of use and a simple, not overly complex, api that new developers can pick up without having to look at code and scratch their heads.
I'm also running into a problem with pre-setting selections via ajax but also using templateSelection & templateResult based on ab object returned via ajax.
How would I preset options in a multiselect with a known data object ahead of time that filters into the templateSelection?
I'm seriously confused about why everyone is having problems here. Kevin made the initSelection process way simpler and more logical with select2 4.0. He has two examples that show how to do it with or without ajax as he's mentioned several times. I think anyone who thinks they need a custom DataAdapter probably doesn't understand the simplicity of the new design...
Step 1: Statically or dynamically create/change options in a select element. Step 2: Trigger a change on the select2 interface. Step 3: Realize how easy this was and stop complaining! :-P
Friend @pjclas.
You do these three steps is not so simple, especially for those who are now beginning to use the select2.
And in the previous version, these three steps were all made using settings when applying the "select2" to the element.
Now this version you have to do some steps before applying the "select2". And some scenarios it sharply affects the quantity and complexity of code.
In my scenario I have a system with a 40 "select2" different, I have to do a treatment on each to ensure that there is the element "option" within the element "select" before applying the plugin "select2". (I do this for an ajax request and apply the "select2" in callback)
Even with a seemingly simple step, in various scenarios it is a painful step.
As you said, to use the select2 now we need (at least) the steps 1,2 and 3.
In the previous version it was solved in just one step. And lose this characteristic is not advantageous for the "select2" in my opinion
Another example, show me an example of how to change the CSS class Dropdown element without using Adapter? In the previous version there was a property, now only using the full version.
You don't have to have everything created prior to creating the select2, you can modify it any time you want, and then simply trigger a change event by calling "$select2_element.trigger('change');". As far as the CSS goes, you can change that at any time like you can with any element...
I'd think there would be a more difficult time converting the <input />
to a <select>
, and maybe that's inspiring some of the issues here, as that is what requires the most changes in the long run. I wouldn't mind finding possible solutions to making migration easier, but stopping the deprecation is not a realistic long-term solution.
You do these three steps is not so simple, especially for those who are now beginning to use the select2.
Admittedly, that's my current issue with the solutions that are out there: It's no longer a "try initSelection
and hope it works" kind of situation. So far we've solved the "hope it works" part of that, now it's just time to make it easier.
In my scenario I have a system with a 40 "select2" different, I have to do a treatment on each to ensure that there is the element "option" within the element "select" before applying the plugin "select2". (I do this for an ajax request and apply the "select2" in callback)
I'd hate to suggest "wrap it up in a helper function" as a possible solution here, as that adds an additional (albeit small) step to initializing Select2. But it might not be that much code, considering the small amount of code currently in there for compatibility that handles the job (somewhat) well.
The end of InitSelection brings disastrous consequences for those who used the plug connected to a MVVM framework (knockoutjs, angularjs and etc ...). For a task that "select2" made automatically, should now be made in advance by the application code itself.
I think it might just be sheer luck, but for some reason I haven't heard of a ton of issues converting Angular and Ember apps. Perhaps it's because Angular has other (better suited, imo) alternatives and Ember actually has a distinct "set everything up ahead of time" step that people are used to.
Part of my issue with Knockout, which is why I can't help a ton when converting it, is that I'm not at all familiar with what common conventions are. I can't speak for where application preparation happens with it, but I do know that it has the ability to synchronize data through bindings. And that should automatically create a new <option>
, which Select2 should be able to pick up on.
But again, it's unfamiliar territory for me.
show me an example of how to change the CSS class Dropdown element without using Adapter?
The dropdownCssClass
option still works in the full builds of 4.0.0, it's not yet documented though.
Hi @kevin-brown you're the best.
I think you understand my point.
I also think that the direction is not returning to the depreciated methods.
I believe that the direction is to facilitate as possible adding simpler options such as the placeholder (example: css changes, container)
and greatly improve the documentation. We need to understand that most people who use select2 not know concepts such as "decorator", "adapter" and "amd"
Thanks for listening, and sorry the heated argument. My intention has always been to contribute to the plugin, not just criticize negatively.
@kevin-brown I also agree with not moving backwards, and I can see wanting to use methods on the original select element when setting the values of said select element as it's more intuitive, but with all the examples I've seen so far, none of them deal with handling setting an option via an object and letting templateSelection display it like it should.
Example http://jsfiddle.net/q1q00hnm/3/ (ajax example from options page modified to show image in selection)
How would you pre-set, or set after intializing, a value in that select2 with a known repo object contacting the data for the selection? {id: 1, full_name: 'test repo', owner: { avatar_url: 'test_image.jpg' } }
We did it !. We came up with this solution for the AJAX problem ( Init with default value without < option > html tags (just < select > < /select > in DOM) and this data adapter: (select2 4.0)) This is done with Typescript.
!! Key point ONE: don't save the current values on the select element with $.val() as the options come from the ajax call. Val() does not work on non existing dom options - we save and read current selected value with:
LINE: (set value) (
!! Key point TWO: The result data from the ajax request musst be returned as a collection data.results property:
LINE: callback({ results: mapped });
///
class Select2View {
el:any;
constructor(options?) {
console.log('constructor of select2wrapper');
}
showselect2(options?) {
this.el = options.el;
console.log('init with value:', options.value);
(<any>$).fn.select2.amd.require(['select2/data/array', 'select2/utils'], function(ArrayData, Utils) {
function CustomData($element, options) {
(<any>CustomData).__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.current = function (callback) {
var data = [];
var currentVal = this.$element.data('selectedValues');
console.log('current',currentVal);
if (currentVal == null) {
callback([]);
return;
}
if (!this.$element.prop('multiple')) {
currentVal = [currentVal];
}
for (var v = 0; v < currentVal.length; v++) {
data.push({
id: currentVal[v],
text: currentVal[v]
});
}
callback(data);
};
CustomData.prototype.query = function (params, callback) {
console.log('query',params);
(<any>$).ajax({
url: "/myapi/layouts/5"
}).done(function (data) {
var mapped = $.map(data.Layout, function(obj) {
return { id: obj.Key, text: obj.Key };
});
callback({ results: mapped });
});
};
(<any>$(options.el)).select2({
dataAdapter: CustomData
});
});
(<any>$(this.el)).data('selectedValues', [options.value]).trigger("change");
}
}
export = Select2View;
---------------------- snip -------------------- ps: remove the <....> typings from typescript to get javascript ps2: the options Methode parameter is just used to configure this class from the outside
Another one of my near-future goals is to get a pull request going that switches the style of the documentation, so it focuses more on working examples and less on explanations about them.
none of them deal with handling setting an option via an object and letting templateSelection display it like it should.
One thing I've considered off and on for the past 21 days is the idea of converting the data attributes on the <option>
into properties on the data object. That could mean that an <option>
like the following
<option data-test="this" value="1">Something</option>
would automatically be converted into a data attribute with the test
property
{
"id": "1",
"text": "Something",
"test": "this"
}
Which is something we already do for data attributes on a <select>
, and most likely would not be difficult to bring over as well.
The other alternative is to just allow an "extra data" property on an <option>
and automatically combine it with the generated data object, which would allow for freeform JSON as long as it can be parsed by jQuery.
I like your last idea. the "extra data"
Today we have a problem. When you start ajax select2 with a default initial option tag and seek method
$ ('select'). select2 ('data')
// output: {id: xxx, text: yyyy}
After I seek any other item via ajax, and call the same method:
$ ('select'). select2 ('data')
// output: {id: xxx, text: yyyy, myprop1: zzz, myprop2: 2334}
I (and I think everyone else) need to always return the object always as shown in the second example
// output: {id: xxx, text: yyyy, myprop1: zzz, myprop2: 2334}
Because we need the object with all properties completed for use elsewhere in the html page.
This is one reason that the late "InitSelecion" was useful, he guarantee that every time I call the "data" would return me whenever my entire object received from the ajax request.
Now including a initial default select tag, to call the "data" will return me a different object.
You could understand the problem?
converting the data attributes on the
I would love to see that implemented, as it seems it could solve peoples' problems with initSelection's demise, as well as allowing more flexibility in handling events and to make use of richer or more complex data.
@kevin-brown, The data-*
options are a possibility but you'd have to have a syntactically safe way of describing nested objects within the data attributes
Example: how would {id: 1, full_name: 'test repo', owner: { avatar_url: 'test_image.jpg' } }
be described in multiple data-attribute fields.
One option would be to just fit the entire JSON (escaped) into a single data field on the option, and have it be parsed by the templateSelection
option when initially rendering
<select>
<option data-select2-option="{id:1,full_name:\"test repo\",owner:{avatar_url:\"test_image.jpg\"}}" selected></option>
</select>
However neither option I think solves the underlying issue, or an associated issue I just thought of.
My thinking is this, if there's already a method templateSelection
built into select2 to take a selection object (that being clicked on in the dropdown) and return a jQuery object or string to populate the select2 element itself, why couldn't that be called by itself instead of only being able to be called from clicking an item on the dropdown list. Something as simple as $(element).select2('manual-select', obj)
or similar that would basically be the same as if an ajax search had occured and a user selected a returned data object.
The associated issue is this, say I have two dropdowns, (forked from first demo: http://jsfiddle.net/zvgjLLkh/2/ ). And I wanted to programatically set the second select based on picking an option from the first one (or vice versa). There's no way, (and if I'm wrong please let me know), to pass an object to either select that would pass it through the templateSelection before rendering it.
Both would be solved by having a manual selection method.
I've put a really ugly hack together for manual selection. On the current v4.0.0 build of select2.full.js starting at around line ~5841
} else if (typeof options === 'string') {
var instance = this.data('select2');
console.log(instance);
if (instance == null && window.console && console.error) {
console.error(
'The select2(\'' + options + '\') method was called on an ' +
'element that is not using Select2.'
);
}
var args = Array.prototype.slice.call(arguments, 1);
/**
* Added by RG for temporary bandaid to manually select select2 options
*/
if (options == 'manualSelect') {
instance.trigger('select', {
data: args[0]
});
}
var ret = instance[options](args);
// Check if we should be returning `this`
if ($.inArray(options, thisMethods) > -1) {
return this;
}
return ret;
} else {
usage is just $(element).select2('manualSelection', obj)
where obj is the object data that you want to select / add to the multi-select.
I agree with others, the new dataAdapter way is over-design and really horrible. The docs are really unclear (it's deprecated, but still moved? It seems a breaking change, which is deprecated...) and this is really annoying.
We hope this will be improved in a new version. For now we reverted the upgrading process (moved back to 3.5)
I was able to preset original multiselect values this way:
<select id="subjects" name="subjects" multiple="multiple">
<option value="62501" selected="selected" data-display-name="Ask your colleagues" data-image-url="#{image_url}"Ask you colleagues</option>
</select>
and in the templateSelection
template function:
var attrData = {};
$(item.element.attributes).each(function() {
if ('data' === this.nodeName.substring(0, 4)) {
// remove "data-" prefix and convert dashes to underscores
return attrData[this.nodeName.substring(5).replace("-", "_")] = this.nodeValue;
}
});
_.extend(item, attrData);
This way all the option attributes are available for formatting selection item.
I faced the same problem: Pre-selecting some value when using a multiple tag, Ajax query select.
What I did was, write the option tag to the select,
<select id="mySelect">
<option value="1">Pre-selected tag</option>
</select>
and then after building the Select2, call the trigger('change') like this:
$j('#mySelect').select2({
ajax: {
....
}
});
$("#mySelect").val("1").trigger("change");
This added the tag to my select
Ridiculous! How come I need to spend hours understanding how to preselect value
Will this point be improved in the future?
Hi juanitoddd, thank you, but this only select 1 value (even if I call this many times for different options), because in the select2.js file, it clear the "ul" on update (by calling $this.clear(), which get the "ul" and .empty() it). BUT! I see that when we manually select, it's not disappearing because it actually recreate all the "li" using the attribute "data" (which must be empty when we call .trigger("update") like you prescribed.
By the way, I can bypass all that by changing "MultipleSelection.prototype.clear" in the select2.js directly (changing the clear to NOT remove the initial options), but as it is "unclean" and a bypass of the natural behavior of this function, I would like to be legit (and now those options are immortal and have to be handled manually... it's a mess).
Also, saying .select2('data', anArrayWithMyValues) does not work... Maybe I do THAT wrong..
So.
Do you know how to populate "data" with the function .trigger("update"), or do you know of a way to simply have more than one pre-selected option in a multiple select2?
Thanks!
Hello everybody, I initiated an activity to migrate Select2 to version 4.0, but it's hard just to contribute in conversation, I use to AngularJS and we have some complementary implementations because the needs of our systems, and use this new version is not giving right. I am unable to apply all current resources, not to mention some limitations on the types mentioned above.
We love this feature and use it to its maximum power, but the new version is killing us. We are going back to 3.5, let's wait a while and see how the new updates will walk and try again in the future.
Thanks guys for conversation.
Also rolling back to 3.5
@juanitoddd : Doesn't your ajax stop working on that step ?
+1
I'm also creating an AngularJS wrapper for this control and was extremely happy with it until I realized there was absolutely no easy way to programmatically control the data - which should be the most basic operation on the API. (due to the DOM searching of the API)
I'm finding using a data adapter isn't working with Ajax because it's searching through the DOM for results..... which aren't there because they're remotely queried.... seems kinda basic? C'mon guys
(to be more specific the initial value won't set because the DataAdapter.current method is searching through the DOM for a value that isn't there yet)
+1 in the hopes this gets better in the next version.
In my case, I am using the DataAdapter method because I'm dealing with a large data set that I need to do paging/filtering on. However I've spent hours trying to figure out how to get the initial selection to populate correctly (still with no luck).
That said, I realize this is free software and I am not contributing to it so keep up the good work :)
will this ever get fixed?
This is so confusing. This is my fifth time reading this page in the past month.
And it doesn't look like this will ever get improved...
My major problem with this API is there is no way to access the data context. You are expected to construct it entirely from within the data adapter. Which means it is not a true adapter. It is a one-direction plug.
The most immediate fix would be to allow the constructor for this object to take two additional, optional, parameters:
But the problem with even this is there is no way to handle asynchronous data loads using this paradigm, which is EXACTLY WHY all the docs basically tell you to work around anything ajax-related by first inserting your options elements using AJAX, and THEN calling select2. But that defeats the purpose of a modern data-bound UI where you don't know the ID of the element, and are only using select2 to add a little bit of jazz to the UI.
Just my 2 cents after spending all day at work on a Friday trying to figure out a solution to this.
Here is the exact problem: https://github.com/select2/select2/blob/4.0.3/src/js/select2/core.js#L31
I think we may be able to build a proxy for initSelection and make a backward compatible fix for 3.x users if we just pretend that initSelection was never removed from the options object. Consumers may have to slightly modify their initSelection code, and we'd have to provide a BackCompatInjectedAdapter extension that drives this logic, but the wonky idea would be you would call it like this:
$("#example).select2({
dataadapter: BackCompatInjectedAdapter,
current: [this.KnockoutObservableStoringCurrentOptionId()];
});
Going to try that next... there HAS to be a way as part of the options object to specify a current value.
OK, here is where I am at with my attempt at a workaround. I tried writing my own custom data adapter, and gave up after TypeScript kept complaining. The whole way Select2 does modules is too advanced for me to figure out how to integrate into TypeScript.
So, here is what I did instead to progress my idea. I edited select2/data/select.js, as that is where current prototype function is defined.
I changed the function to the following, but I am still not getting select2 to set the current element (obviously, this is bad code, but I am trying to understand what 'current' is really doing for me that needs so many layers of indirection:
SelectAdapter.prototype.current = function (callback) {
var data = [];
var self = this;
var ic = this.options.get('initCurrent');
console.log(ic);
if (typeof ic !== "undefined")
{
data.push(new Option(ic, ic, true, true));
}
else
{
this.$element.find(':selected').each(function () {
var $option = $(this);
var option = self.item($option);
data.push(option);
});
}
callback(data);
};
I am attempting to migrate from Select2 3.5.2 to Select2 4.0.0rc1
I have current code which uses InitSelection to prepopulate values
But i see that this is being deprecated which makes sense, initselection focused on input val, where 4.0.0 will be migrating to the select element
The point is, I can find no documentation on how this is accomplished