JQuery .change() how to get value - javascript

I am implementing dynamic drop-down selectors using JQuery. I am new to frontend in general. I am struggling to access the value that a dropdown has changed to when I use JQuery.
The user journey is (1) pick a 'collection' and then (2) from within that collection, pick a 'unit'.
I implemented the 'collection' dropdown onchange attribute in HTML like this:
<select name="collection" id="collection" onchange="collectionChanged(this.value)">
With this method, the collectionChanged function gets the value that the dropdown selected has changed to. I then tried to implement the 'unit' onchange behaviour with JQuery. But I get an object that doesn't have a value attribute or anything that looks comparable. I looked at the documentation at the JQuery Website and also at W3CSchools. In both cases, the functions they use do not take any arguments, so I have nothing to compare to. What am I missing?
function getOrCreateUnitSelect() {
let unitSelect;
if ( ! unitSelectExists() ) {
unitSelect = $('<select />', {"id": "unit-select"});
// This is where I add the 'on change' function
unitSelect.change(unitChanged);
unitSelect.insertAfter($('#collection'));
}
else {
unitSelect = $('#unit-select');
}
return unitSelect;
}
function unitSelectExists() {
return $('#unit-select').length;
}
function collectionChanged(value) {
let processFilterResponse = function(serverResponseData, textStatusIgnored, jqXHRIgnored) {
let serverDataObject = JSON.parse(serverResponseData);
let unitSelect = getOrCreateUnitSelect();
serverDataObject['content']['units'].forEach(
function(unit){
$(
"<option />",
{value: unit, text: unit}
).appendTo($("#unit-select"));
}
);
}
let config = {
type: "GET",
url: filterUrl,
data: {'collection': value},
dataType: 'html',
success: processFilterResponse
}
$.ajax(config);
}
function unitChanged(e) {
console.log("unit has been changed");
// Here I get some object that doesn't have a 'value' attribute
console.log(e);
}

When .change calls a handler, the this keyword is a reference to the element where the event is being delivered, so in unitChanged(e) you can just do console.log(this.value).
Note: .change(handler) is just shorthand for .on("change", handler)

Related

Coding a response to a button press event rather than a change event with javascript

I hope this can be understood. I’ve been working on this for about two weeks now and I’m just digging a bigger hole.
The following (fairly standard code) refills a selection option object (id=brand) from a mysql query , the parameter for which is the value of ‘brand’, called in ‘fetch.php’ when one of the previous options is selected:
$('#brand').change(function(){
$.getJSON(
'fetch.php',
'brand='+$('#brand').val(),
function(result){
$('#brand').empty();
$.each(result.result, function(){
$('#brand').append('<option>'+this['brand']+'</option>');
});
}
);
});
However, I also want to have a button on index.html that will also do the same thing except, rather than use the value of ‘brand’ as the parameter of the query, I want to use the value of a text object (id=demo) on the webpage.
My problem is how to construct the code above to call ‘fetch.php’, after the click of a button, and using the value of demo, so that the selection object ‘brand’ will be rebuilt.
The most immediate solution would be to clone the existing code and make the needed modifications to it (listen on button click, use the value of the other field).
$('#brand').change(function(){
$.getJSON(
'fetch.php',
'brand='+$('#brand').val(),
function(result){
$('#brand').empty();
$.each(result.result, function(){
$('#brand').append('<option>'+this['brand']+'</option>');
});
}
);
});
$('#button').click(function(){
$.getJSON(
'fetch.php',
'brand='+$('#demo').val(),
function(result){
$('#brand').empty();
$.each(result.result, function(){
$('#brand').append('<option>'+this['brand']+'</option>');
});
}
);
});
Instead, you could extract the common part of the two code blocks to make it reusable:
// The common part.
function fetchBrands(brand) {
$.getJSON(
'fetch.php',
'brand='+brand,
function(result){
$('#brand').empty();
$.each(result.result, function(){
$('#brand').append('<option>'+this['brand']+'</option>');
});
}
);
}
// Listen for change event on the select element.
$('#brand').change(function(){
fetchBrands($('#brand').val());
});
// Listen for click event on the button.
$('#button').click(function(){
fetchBrands($('#demo').val());
});
You can use .triggerHandler( eventType [, extraParameters ] ) and use extraParameters (they are explained in the .trigger() documentation).
They will get passed as parameters to the registered event handlers for that event.
The event object is always passed as the first parameter to an event
handler. An array of arguments can also be passed to the .trigger()
call, and these parameters will be passed along to the handler as well
following the event object. As of jQuery 1.6.2, single string or
numeric argument can be passed without being wrapped in an array.
Note the difference between the extra parameters passed here and the
eventData parameter to the .on() method. Both are mechanisms for
passing information to an event handler, but the extraParameters
argument to .trigger() allows information to be determined at the time
the event is triggered, while the eventData argument to .on() requires
the information to be already computed at the time the handler is
bound.
Modify the signature of your change event handler to:
function(ev, brand) {
// ...
}
Use the brand parameter in your change handler if it is available or the selected value of the dropdown otherwise:
brand = brand || $('#brand').val();
Then use this for the .getJSON() call:
$.getJSON(
'fetch.php',
'brand=' + encodeURIComponent(brand),
...)
You can then trigger the event handler with any brand you want (e.g. $("#demo").text()) with:
$("#brand").triggerHandler("change", $("#demo").text())
An working example (with a fake getJSON() method)
$(function() {
$('#brand').change(function(ev, brand){
brand = brand || $('#brand').val();
getJSON(brand, function(result){
$('#brand').empty()
.append("<option></option>");
$.each(result, function(){
$('#brand').append('<option>'+this['brand']+'</option>');
});
});
});
// the "reset to default values" button
$("button").on("click", function() {
$("#brand").triggerHandler("change", $("#demo").text());
});
// initially fill the drop-down (I didn't want to write the default options also in the markup...)
$("#brand").triggerHandler("change", $("#demo").text());
});
function getJSON(brand, callback) {
const fakeResponse = {
A: [ { brand: "AA" }, { brand: "AB" } ],
B: [ { brand: "BA" }, { brand: "BB" } ],
default: [ { brand: "A" }, { brand: "B" } ]
}
callback(fakeResponse[brand]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="demo">default</div>
<select id="brand"></select>
<button>Reset to "default"</button>

Convert select2 dropdown from single select to multiple select

I have 10 to 12 select2 dropdowns in complex control, they need to be initialized as select2 dropdown.
On dropdown-open, I make ajax call to load data. The problem comes here, if there are specific data loaded from the server. the drop-down should became multiple select2.
Here is a part of the code:
$selectDropDown.select2({
ajax: {
url: '/GetValues',
dataType: 'json',
data: function (params) {
var query = {
title: name,
}
return query;
},
processResults: function (data) {
if (data.type === '10') {
// I need to make it multiple select here
return {results: data.results};
} else {
var values = getDefaultDataItems();
return {results: values };
}
}
},
allowClear: true,
placeholder: 'Select values'
width: '100%',
});
The data cannot be loaded before initialization of select2, because of optimization reasons.
Currently it works like:
processResults: function (data) {
if (data.type === '10') {
// The hacking way
$selectDropDown.select2({
multiple: 'multiple',
data: data.results
}).select2('open');
} else {
var values = getDefaultDataItems();
return {results: values };
}
}
I want to ask it it he best way to do it?
Is there a build-in functionality?
For me, the best way to do that is add attribute multiple to my select. And change attribute name become array. After that, call .select2(); on my select.
For example, I have select with sub-type class.
$('.sub-type').prop('multiple', true).attr('name', 'sub_type[]').select2();
If you want back to single select again, just write this:
$('.sub-type').prop('multiple', false).attr('name', 'sub_type').select2();
Sorry for the late reply, I just found your question today. I hope my answer can help others.

Select2 change input name if tag is being submited

So I have the following select2:
productFamilySelect.select2({
tags: true
});
By default the name of the associated select element is product_family_id, so is there a way to change the name of the input to lets say product_family_name, if selected value if one that user entered? This is so, that I could in the backend for sure distinguish between an already existing value, and one that user thought of. Checking by id in the database does not really suit, as this value could actually be numeric in on itself.
After some digging into select2 custom events I found a way:
firstly add createTag callback like so:
productFamilySelect.select2({
tags: true,
createTag: function (params) {
var term = $.trim(params.term);
if (term === '') {
return null;
}
return {
id: term,
text: term,
newTag: true
}
}
});
Then, add the following listener:
productFamilySelect.on('select2:select', function (e) {
if (e.params.data.newTag === true) {
$(this).attr('name', 'product_family_name');
} else {
$(this).attr('name', 'product_family_id');
}
});
Seems a bit hacky, since it is outside the config of the actual select2 config, but well it dos the job :)

Force a user to select from JQuery UI Autocomplete and populate a hidden field after selecting

I have a large HTML form that contains many fields that need an autocomplete for accounts. I tag these fields with the class AccountLookup and jQuery does the dirty work for the autocomplete:
$(".AccountLookup").autocomplete({
source: function (request, response) {
$.ajax({
url: "Lookup.asmx/GetAccounts",
data: "{ 'Search': '" + request.term + "' }",
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
dataFilter: function (data) { return data; },
success: function (data) {
response($.map(data.d, function (item) {
return {
value: item.Value
}
}))
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
},
minLength: 3
});
Now, when a user selects something from the autocomplete I need it to populate a hidden field just BEFORE the tagged input field; probably using something like:
$(this).prev().val(item.Key);
How do I incorporate this functionality? Also, how do I force a user to select from the auto complete? (All the values are pre-defined, the user cannot add new ones.)
EDIT:
As far as I understand from inspecting the DOM, the select option is currently filling in the hidden form field.
select: function (event, ui) {
$(this).prev().val(ui.item.key);
}
I know this is an old post--- but I ran into it in trying to solve a similar problem (forcing the user to select an item from the list)...
$("#ac").autocomplete({
source: function (req, resp) {
//add code here...
},
select: function (e, ui) {
$(this).next().val(ui.item.id);
},
change: function (ev, ui) {
if (!ui.item)
$(this).val("");
}
});
$(".AccountLookup").autocomplete({
/*...*/
}).result(function(event, item) {
$(this).prev().val(item.Key);
});
You could also use a jQuery validate to ensure that the field is populated.
for force selection, you can use "change" event of Autocomplete
var availableTags = [
"ActionScript",
"AppleScript"
];
$("#tags").autocomplete({
source: availableTags,
change: function (event, ui) {
if(!ui.item){
//http://api.jqueryui.com/autocomplete/#event-change -
// The item selected from the menu, if any. Otherwise the property is null
//so clear the item for force selection
$("#tags").val("");
}
}
});
For the selection action, try using the formatItem option. You can format each result to have an onclick event that will populate the other textbox.
For the forcing to select from autocomplete, you need to use the mustMatch option.
http://docs.jquery.com/Plugins/Autocomplete/autocomplete#url_or_dataoptions
I ran into this same problem quite awhile ago and some post helped me along with it. I have since modified the code as I found that there were cases I wanted one or more fields to fill in from the information returned. In the select option of the autocomplete I added a function.
select: function (e, ui) {ReSetField({'txtID':'id','txtPrice':'price' [,etc...]}, ui) }
The function "ResetFields" then takes in a JSON list of element names paired with fieldnames and uses the fieldnames to match the elements in the ui object. The value can then be pulled from the ui item and put into the html element.
function ReSetField(_flds, _vals) {
//Set up the flds to be reset with values passed in.
try {
if (_flds != undefined) {
if ($.type(_flds) == 'string') {
_flds = JSON.parse(_flds);
};
var _fld = null;
var _val = '';
$.each(_flds, function (index) {
if (index.length > 0) {
_fld = '#' + index; //Set the forms field name to set
_val = _flds[index]; //Pick up the field name to set the fields value
$fld = $(_fld);
$fld.val(_vals.item[_val]); //Set the fields value to the returned value
}
}
})
};
}
catch (e) {
alert('Cannot set field ' + _fld + '.');
}
}
By sticking the "fieldlist" into the HTML element as an attribute like "fieldlist" and using a class like "comboBox" I can then use a single function to find all ComboBox elements and set up the autocomplete on a form reducing the amount of code required to handle 2 or more lookups on a form.

jQuery: Referencing the calling object(this) when the bind/click event is for a class

Thanks for reading this.
I am dynamically generating some data which includes a select drop-down with a text box next to it. If the user clicks the select, I am dynamically populating it (code below). I have a class on the select and I was hoping the following code would work. I tested it with an ID on the select and putting the ONE on the ID I got it to work. However, in changing the code to reference a class (since there will be multiple data groups that include a select with a text box next to it) and $(this), I could not get it to work. Any ideas would be helpful. Thanks
The relevance of the text box next to the select is the second part of the code...to update the text box when an option is selected in the select
.one is so the select is updated only once, then the .bind allows any options selected to be placed in the adjacent text box.
$('.classSelect').one("click",
function() {
$.ajax({
type: "post",
url: myURL ,
dataType: "text",
data: {
'_service' : myService,
'_program' : myProgram ,
'param' : myParams
},
success:
function(request) {
$(this).html(request); // populate select box
} // End success
}); // End ajax method
$(this).bind("click",
function() {
$(this).next().val($(this).val());
}); // End BIND
}); // End One
<select id="mySelect" class="classSelect"></select>
<input type="text">
$(this) is only relevant within the scope of the function. outside of the function though, it loses that reference:
$('.classSelect').one("click", function() {
$(this); // refers to $('.classSelect')
$.ajax({
// content
$(this); // does not refer to $('.classSelect')
});
});
a better way to handle this may be:
$('.classSelect').one("click", function() {
var e = $(this);
$.ajax({
...
success : function(request) {
e.html(request);
}
}); // end ajax
$(this).bind('click', function() {
// bind stuff
}); // end bind
}); // end one
by the way, are you familiar with the load() method? i find it easier for basic ajax (as it acts on the wrapped set, instead of it being a standalone function like $.ajax(). here's how i would rewrite this using load():
$('.classSelect').one('click', function() {
var options = {
type : 'post',
dataType : 'text',
data : {
'_service' : myService,
'_program' : myProgram ,
'param' : myParams
}
} // end options
// load() will automatically load your .classSelect with the results
$(this).load(myUrl, options);
$(this).click(function() {
// etc...
}); // end click
}); // end one
I believe that this is because the function attached to the success event doesn't know what 'this' is as it is run independently of the object you're calling it within. (I'm not explaining it very well, but I think it's to do with closures.)
I think if you added the following line before the $.ajax call:
var _this = this;
and then in the success function used that variable:
success:
function(request) {
_this.html(request); // populate select box
}
it may well work
That is matching one select. You need to match multiple elements so you want
$("select[class='classSelect']") ...
The success() function does not know about this, as any other event callback (they are run outside the object scope).
You need to close the variable in the scope of the success function, but what you really need is not "this", but $(this)
So:
var that = $(this);
... some code ...
success: function(request) {
that.html(request)
}
Thanks Owen. Although there may be a better to write the code (with chaining)....my problem with this code was $(this) was not available in the .ajax and .bind calls..so storing it in a var and using that var was the solution.
Thanks again.
$('.classSelect').one("click",
function() {
var e = $(this) ;
$.ajax({
type: "post",
url: myURL ,
dataType: "text",
data: {
'_service' : myService,
'_program' : myProgram ,
'param' : myParams
},
success:
function(request) {
$(e).html(request); // populate select box
} // End success
}); // End ajax method
$(e).one("click",
function() {
$(e).next().val($(e).val());
}); // End BIND
}); // End One

Categories