Migrate select2 from 3 to 4 - javascript

I'm migrating the select2 component from 3.4.5 to 4.0.13 (to solve this issue: https://snyk.io/test/npm/select2/3.4.5) but I've a lot of wrapped code around it and I'm having a lot of problems upgrading.
I've a sandbox and I found the first problems there:
If I use a "div" to create the component (as in my application), the (obsolete) callback "initSelection" works as I need, but then I've no way to get the selected values from the component. And I think that is the problem because the elements of the "initSelection" are shown but not "set".
var sourceData = [
{id:"1",text:"Hello!"}, {id:"2",text:"Welcome!"}, {id:"3",text:"Good bye!"}, {id:"3",text:"Good night!"}
];
$(document).ready(function() {
$('.mpm-select').select2({
data: sourceData,
multiple: true,
initSelection: function (element, callback) {
var data = sourceData;
$(element.val()).each(function () {
data.push({id: this, text: this});
});
callback(data);
},
});
});
.mpm-select {
width: 100%;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.9/css/select2.min.css" integrity="sha512-nMNlpuaDPrqlEls3IX/Q56H36qvBASwb3ipuo3MxeWbsQB1881ox0cRv7UPTgBlriqoynt35KjEwgGUeUXIPnw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.9/js/select2.full.min.js"></script>
<div class="mpm-select"></div>
If I use a "input" to create the component (it will requires some changes on my application), "initSelection" callback is not triggered on creation, but I can use the jQuery element to call val() function.
var sourceData = [
{id:"1",text:"Hello!"}, {id:"2",text:"Welcome!"}, {id:"3",text:"Good bye!"}, {id:"3",text:"Good night!"}
];
$(document).ready(function() {
$('.mpm-select').select2({
data: sourceData,
multiple: true,
initSelection: function (element, callback) {
var data = sourceData;
$(element.val()).each(function () {
data.push({id: this, text: this});
});
callback(data);
},
});
});
.mpm-select {
width: 100%;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.9/css/select2.min.css" integrity="sha512-nMNlpuaDPrqlEls3IX/Q56H36qvBASwb3ipuo3MxeWbsQB1881ox0cRv7UPTgBlriqoynt35KjEwgGUeUXIPnw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.9/js/select2.full.min.js"></script>
<input class="mpm-select"></div>
How I can get the values of the select2 element when it's a "div" or how can I force to trigger "initSelection" callback when it's an "input"?

Related

Trigger an event within a jquery plugin from outside

I use the jquery easy autocomplete plugin which has an internal event "onSelectItemEvent".
now i want to call this event from outside (since it is bound to an extensive function i wrote already).
I prepared a fiddle, but triggering the plugins event with this doesn't work :)
$("#example-ac").easyAutocomplete(this.list.onSelectItemEvent());
$(document).ready(function() {
var options_ac = {
url: "https://fcgi.msk.link/model-json.pl?site=test",
getValue: function(element) {
//console.log(element);
return element;
},
list: {
match: {
enabled: true
},
sort: {
enabled: true
},
maxNumberOfElements: 8,
onSelectItemEvent: function() {
$("output").html($("#example-ac").val())
}
},
theme: "square"
};
$("#example-ac").easyAutocomplete(options_ac);
$("#trigga").click(function() {
$("#example-ac").easyAutocomplete(this.list.onSelectItemEvent());
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/easy-autocomplete/1.3.5/easy-autocomplete.themes.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/easy-autocomplete/1.3.5/easy-autocomplete.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/easy-autocomplete/1.3.5/jquery.easy-autocomplete.min.js"></script>
<input id="example-ac" />
<br>
<input id="example-ac" /><br/>
<output></output>
<button id="trigga">
trigger
</button>
http://jsfiddle.net/Lgetus29/
Thanks for a hint if this works or if the solution has to be done another way. the codepen example is just small code while the real coding for this internal function is massive, so i want to reuse. maybe i better create a function outside the plugin then reuse this function in the "onSelectItemEvent()", is that even possible?
THANKS !!
Why not use it like
$("#trigga").click(function() {
options_ac.list.onSelectItemEvent()
});
So your final code will look like
$(document).ready(function() {
var options_ac = {
url: "https://fcgi.msk.link/model-json.pl?site=test",
getValue: function(element) {
//console.log(element);
return element;
},
list: {
match: {
enabled: true
},
sort: {
enabled: true
},
maxNumberOfElements: 8,
onSelectItemEvent: function() {
$("output").html($("#example-ac").val())
}
},
theme: "square"
};
$("#example-ac").easyAutocomplete(options_ac);
$("#trigga").click(function() {
options_ac.list.onSelectItemEvent();
});
});

Jquery UI appendTo NOT WORKING

Im trying to use jquery ui for search bar autocomplete. When I use div id="inputs" autocomplete works fine, but if I use input id="inputs" it's not working and i need to use input in order to search works properly.
(function ($) {
$.fn.googleSuggest = function(opts){
opts = $.extend({service: 'web', secure: false}, opts);
var services = {
web: { client: 'hp', ds: '' },
}, service = services[opts.service], span = $('<span>');
opts.source = function(request, response){
$.ajax({
url: 'http'+(opts.secure?'s':'')+'://clients1.google.com/complete/search',
dataType: 'jsonp',
data: {
q: request.term,
nolabels: 't',
client: service.client,
ds: service.ds
},
success: function(data) {
response($.map(data[1], function(item){
return { value: span.html(item[0]).text() };
}));
}
});
};
return this.each(function(){
$(this).autocomplete(opts);
});
}
}(jQuery));
$.each("web".split(" "), function(i, v){
var div = $("<div>").appendTo("#inputs")
, input = $("<input>").appendTo(div)
input.googleSuggest({ service: v });
});
<html>
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/ui-lightness/jquery-ui.css" type="text/css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
</head>
<body>
<div id="inputs"></div>
</body>
</html>
<input> tags can't have child elements, so you can't append nodes to them.
It looks like what you're trying to append is a div, which contains another input, which you run googleSuggest() against:
var div = $("<div>").appendTo("#inputs")
, input = $("<input>").appendTo(div)
input.googleSuggest({ service: v });
So it seems that you don't need to append anything. Instead, just put googleSuggest on the <input> that's already in the DOM:
$('#inputs').googleSuggest({ /*...*/ })

Vue.js: Passing anonymous functions in props object

I'm trying to extend the select2 example to be more practically useful. So far I've added multiselect functionality, and trying to allow custom select2 configuration.
https://jsfiddle.net/5ytm3LL6/
It appears that function properties of props objects are being stripped when handed to component.
What is a solution for a parent to give widget component configuration with js functions?
I'm somewhat confused, because the embedded version actually shows params being transferred properly in the built-in console output, while jsfiddle browser console output does not. However, both versions do not pass the functions to select2 widget.
Vue.component('select2', {
props: ['options', 'value', 'params'],
template: '<select><slot></slot></select>',
mounted: function () {
var vm = this, params = $.extend({}, this.params || {});
console.log(this.params, params);
params.data = this.options;
$(this.$el).val(this.value).select2(params).on('change', function () {
vm.$emit('input', $(vm.$el).val());
});
},
watch: {
value: function (value) {
var $el = $(this.$el);
if (!_.isEqual($el.val(), value)) {
$el.select2('val', value);
}
},
options: function (options) {
$(this.$el).select2({ data: options });
}
},
destroyed: function () { $(this.$el).off().select2('destroy'); }
});
new Vue({
el:'#app',
data: function () {
return {
value: 'a',
options: [{id:'a', label:'A'}, {id:'b', label:'B'}],
params: {
test: 'TEST',
formatSelection: function (item) {return item.label;},
formatResult: function (item) {return item.label;}
}
};
}
});
<link rel="stylesheet" type="text/css" href="https://unpkg.com/select2/dist/css/select2.css"></style>
<script src="https://unpkg.com/lodash"></script>
<script src="https://unpkg.com/jquery"></script>
<script src="https://unpkg.com/select2"></script>
<script src="https://unpkg.com/vue"></script>
<div id="app">
<select2 v-model="value" :options="options" :params="params"></select2>
</div>
I see js functions you have passed being printed in console log. I have updated your fiddle and put following logs in select2 compoent:
console.log(params.formatResult);
console.log(params.formatSelection);
Which prints following:
function (item) {return item.label;}
function (item) {return item.label;}

Javascript Variable Scope with KendoUI Components

I am trying to capture the text from a Kendo AutoComplete component and save it to a variable to later use in other components. The code is below and I have a link to the code at jsbin.com. Note: the code jsbin.com does not work for me in IE9, Firefox and Chrome work fine.
I attach my onSelect function fires when a selecton is made in the autoComplete control. I then assign the dataItem to my variable "selectedGeoname". I successfully write the object's text property to an element on screen and I use an alert to confirm that selectedGeoname is populated by the object.
However, when I later try to use the variable to pass the value to another component, the variable is null. I instantiate both components and declare the variable inside $(document)ready. I would have to believe that this is all ablout scope so perhaps one of you javascript wizards can help poor old .Net guy to fix this code.
Thanks,
Greg
HTML as follows:
<!DOCTYPE html>
<html>
<head>
<link href="http://cdn.kendostatic.com/2012.2.710/styles/kendo.common.min.css"
rel="stylesheet" type="text/css" />
<link href="http://cdn.kendostatic.com/2012.2.710/styles/kendo.default.min.css"
rel="stylesheet" type="text/css" />
<link href="http://cdn.kendostatic.com/2012.2.710/styles/kendo.dataviz.min.css"
rel="stylesheet" type="text/css" />
<link href="http://cdn.kendostatic.com/2012.2.710/styles/kendo.mobile.all.min.css"
rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="http://cdn.kendostatic.com/2012.2.710/js/kendo.all.min.js"></script>
<title></title>
</head>
<body>
<p>
<label for="customer">Choose a customer:</label>
<input id="customer" />
<div id="result"></div>
</p>
<p>
<label for="stores">Choose a store:</label>
<input id="stores" />
</p>
<script type="text/javascript">
$(document).ready(function () {
$("#customer").kendoAutoComplete({
dataTextField: "name",
dataSource: {
serverFiltering: true,
transport: {
read: {
url: "http://ws.geonames.org/searchJSON",
dataType: "json",
data: {
featureClass: "P",
style: "full",
maxRows: 12,
name_startsWith: function () {
return $("#customer").val();
}
}
}
},
schema: {
data: "geonames"
}
},
filter: "startswith",
placeholder: "Select Customer...",
height: 500,
suggest: true,
select: onSelect
});
var selectedGeoname;
function onSelect(e) {
var dataItem = this.dataItem(e.item.index());
selectedGeoname = dataItem;
//output
$("#result").html(selectedGeoname.name);
alert(selectedGeoname);
}
$("#stores").kendoDropDownList({
autoBind: false,
enable: true,
dataTextField: "StoreName",
dataValueField: "StoreId",
dataSource: {
serverFiltering: true,
transport: {
read: {
url: '#Url.Action("JsonGetStores", "Home")',
data: {
customer: selectedGeoname
}
}
}
}
});
});
</script>
</body>
</html>
You can use this code as your onSelect to achieve this:
function onSelect(e) {
selectedGeoname = this.value();
//output
$("#result").html(selectedGeoname.name);
alert(selectedGeoname);
}
When called from the context of the AutoComplete object this.value() can be used to get the value of the AC input element.
If you need to access the current value of the variable outside of the scope you can use this method: $("#customer").data("kendoAutoComplete").value().
Hope this helps!

Click event not firing

I've looked around for a while now and have not been able to find anything that suggests what the cause of this is.
My code:
var faqView = Backbone.View.extend({
tagName: 'div',
id: 'faq-list',
initialize: function() {
var view = this;
this.collection = new faqCollection();
this.collection.fetch({
success: function(collection, response) {
collection.each(function(faq){
view.$el.append(_.template($('script#faq_item').html(),{faq: faq.attributes}));
});
},
error: function(collection, response) {
view.$el.html("<p>Unable to get the FAQ items.<br>Please try again later.</p>");
}
});
},
render: function() {
this.$el.appendTo('div#container');
return this;
},
events: {
'click h3': 'toggleAnswer'
},
toggleAnswer: function(event) {
console.log(this);
console.log(event);
}
});
var router = Backbone.Router.extend({
routes: {
"faq": "faq",
"*other": "defaultRoute"
},
faqView: {},
initialize: function() {
this.faqView = new faqView();
},
defaultRoute: function() {
this.resetPage();
},
faq: function() {
this.resetPage();
$('body').addClass('page-faq');
this.faqView.render();
},
resetPage: function() {
$('body').removeClass('page-faq');
this.faqView.remove();
}
});
The above code is included as the last items in the <body>. The HTML is as follows.
<body>
<div id="container">
</div>
<script type="text/template" id="faq_item">
<h3 class="contracted"><span>{{faq.question}}</span></h3>
<p style="display: none;">{{faq.answer}}</p>
</script>
<script type="text/javascript" src="./js/models.js"></script>
<script type="text/javascript" src="./js/views.js"></script>
<script type="text/javascript" src="./js/collection.js"></script>
<script type="text/javascript" src="./js/router.js"></script>
<script type="text/javascript">
//<![CDATA[
$(function() {
var app = new router;
Backbone.history.start();
});
//]]>
</script>
</body>
All the required elements exist (as far as I can tell) and I'm not manually setting the el attribute of the View. I'm lost as to why the events are not binding/firing when the <h3> is clicked.
Edit No errors thrown and the functionality works if I don't use the router and create the view by it self. e.g.
var app = new faqView();
app.render();
Your problem is in your router. Right here in fact:
resetPage: function() {
$('body').removeClass('page-faq');
this.faqView.remove();
}
View#remove is just jQuery's remove on the view's el by default and that:
[...] method takes elements out of the DOM. [...] all bound events and jQuery data associated with the elements are removed
So once you this.faqView.remove(), the delegate handler that drives the view's events is gone.
The usual approach is to create and destroy views as needed instead of creating a view and caching it for later. Your router should look more like this:
var router = Backbone.Router.extend({
routes: {
"faq": "faq",
"*other": "defaultRoute"
},
defaultRoute: function() {
this.resetPage();
},
faq: function() {
this.resetPage();
$('body').addClass('page-faq');
this.view = new faqView();
this.view.render();
},
resetPage: function() {
$('body').removeClass('page-faq');
if(this.view)
this.view.remove();
}
});
Demo: http://jsfiddle.net/ambiguous/aDtDT/
You could try messing around with detach inside an overridden remove method in faqView as well but there's really no need to have an instance of faqView around all the time: create it when you need it and remove it when you don't.

Categories