Yeah, I know, likely been answered, but can't find it or figure out how to successfully search for it, so, 45 minutes later i am succumbing to the potential of flaming...so go easy on me, ok?
It is a simple problem, and my issue is timing. One select holds Countries, it is bound to State/Province select. Change Country, State/Province loads appropriately via a separate function. Use a mouse to select from State/Province, perfection. Use JavaScript ..uh oh. Problem is I need to force my JavaScript to wait for the browser to load the State/Province data before I can target and select it. SetTimeout or using a Promise just seems... inelegant? How many seconds does a browser need to load 50 states or 8 provinces - on a new rocket or an old turtle? Is there a way to just know when the second select finishes loading when the load is in a separate function? Example is jquery, but any flavor will do.
$('#country option[value=US]').prop('selected', 'selected').change();
$('#stp option[value=VT]').prop('selected', 'selected').change();
Adding more clarification based on the responses so far.
Whena user changes the Country, the State/Province loads in the time it takes them to move their mouse down the page allowing them to select.
Now I have implemented a helper that pulls the user's address from BigData using a phone number. This happens in a dialog box. When the user clicks "Accept" this code then fires
function setFormwithDF(){
d={};
// data from dialog
d.address=$('#oaddress').text();
d.city=$('#ocity').text();
d.state=$('#ostate').text();
d.zip=$('#ozip').text();
d.country=$('#ocountry').text();
$('#s_country option[value='+d.country+']').prop('selected', 'selected').trigger('change');
// doesn't matter if this is .change() or .trigger('change')
$('#s_addr1').val(d.address).change();
$('#s_addr2').val('').change();
$('#s_city').val(d.city).change();
$('#s_st option[value='+d.state+']').delay(3000).prop('selected', 'selected');console.log(d.state);//getting a good value here - delay is desperation
$('#s_zip').val(d.zip);
$('#s_phone').val($('#dfsearch').val());
$( "#dfsearchdialog" ).dialog('close');
}
And for completeness, here is the loading code. Bunch of extras in here that don't pertain to the issue though
$('#s_country,#b_country').change(function(e){
var st="";
var addrType="S";
var loadObj=$('#shipstp');
if( $(this).attr("id") == 'b_country'){
loadObj=$('#billstp');
addrType="B";
}
if( typeof(e.st) != 'undefined'){st=e.st;console.log(5)}// this data is passed during the trigger() code
uObj={c:$(this).val(),nc:Math.random(),t:addrType,p:st};
uParam=$.param(uObj);
loadObj.load('/stubs/state-n-province.cfm',uParam);
});
As per my understanding, you dont want user to select state until the state's are getting loaded. After loading only user should be able to select the state.
And I am assuming you are using AJAX to load the State.
If this is the issue :
you can use loading image, which will be displayed until the success has not been return and data has not been map to element.
In this case you can use below sample code :
function getData(p){
.......
$('#loadingmessage').show(); // show the loading message.
$.ajax({
url: "loadData.php?id=<? echo $id; ?>",
type: "POST",
cache: false,
data: "&page="+ page,
success : function(html){
$(".content").html(html);
$('#loadingmessage').hide(); // hide the loading message
}
});
I believe a Promise is what you need. It will allow you exactly
to just know when the second select finishes loading when the load is
in a separate function
$('#country').change(function() {
$("#stp").empty().append("<option>...</option>");
loadStates($(this).val())
.then(states => $("#stp")
.empty()
.append(states.reduce((acc, cur) => acc + `<option>${cur}</option>`, "")));
});
$('#country').change();
function loadStates(country) {
console.log(`Loading states for country: ${country}...`);
//setTimeout here just emulates your long loading process
return new Promise((res, rej) => setTimeout(() => {
console.log(`States for country: ${country} are loaded!`);
res(["state1", "state2", "state3"]);
}, 3000));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="country">
<option>US</option>
<option>UK</option>
<option>RU</option>
</select>
<select id="stp">
</select>
For your actual use case you would write something like:
return new Promise((resolve, reject) => {
var states = yourLoadingFunction();
resolve(states);
});
UPDATE: Given your latest example, I think I understand your problem now. I suggest you to put your loading code into a separate function, for example:
function countryChanged(e, callback) {
var st="";
var addrType="S";
var loadObj=$('#shipstp');
if( $(this).attr("id") == 'b_country'){
loadObj=$('#billstp');
addrType="B";
}
loadObj.prop("disabled", true);
// this data is passed during the trigger() code
if( typeof(e.st) != 'undefined'){st=e.st;console.log(5)}
uObj={c:$(this).val(),nc:Math.random(),t:addrType,p:st};
uParam=$.param(uObj);
loadObj.load('/stubs/state-n-province.cfm', uParam, function() {
// when the loading is complete, we enable the second select and
// call the callback function
loadObj.prop("disabled", false);
if (callback) callback();
});
}
Note that jQuery .load() method has a third argument which is a callback function that will be called when the loading is complete.
Then you can use this function in two ways:
1) when the user changes the country:
$('#s_country,#b_country').change(countryChanged);
2) in your setFormwithDF() function:
function setFormwithDF(){
d={};
// data from dialog
d.address=$('#oaddress').text();
d.city=$('#ocity').text();
d.state=$('#ostate').text();
d.zip=$('#ozip').text();
d.country=$('#ocountry').text();
$('#s_country option[value='+d.country+']').prop('selected', 'selected');
//instead of calling .trigger('change') just call countryChanged function
countryChanged({st: "whatever you pass during the trigger() code"}, function() {
//this will happen only after .load() is complete
$('#s_st option[value='+d.state+']').prop('selected', 'selected');
});
$('#s_addr1').val(d.address).change();
$('#s_addr2').val('').change();
$('#s_city').val(d.city).change();
$('#s_zip').val(d.zip);
$('#s_phone').val($('#dfsearch').val());
$( "#dfsearchdialog" ).dialog('close');
}
Related
I have a small MVC5/C# Web application. In short, users will select a location from a dropdown ("#UnitList"). This fires the jquery event, to populate the next dropdown ("#CheckItem") with a list of contacts associated to the location selected.
Being very inexperienced in Jscript and Jquery, I am lost.
Additionally, on the same event, I need to populate an element (hidden text box).
In order to fill the hidden elements, I use this code
$(function () {
$("[name='UnitList']").change(function () {
$("#DeliveryUnitID").val($(this).val());
});
});
To run the Jquery that populates the second dropdown:
$(document).ready(function() {
$("#UnitList").change(function() {
var batchId = $(this).val();
var urlForModesl = getURLBase() + "ICS_Requisitions/Details";
urlForModesl = urlForModesl + "/" + batchId;
var modelsHtml = "";
$('#CheckItem')
.find('option')
.remove()
.end()
.append('<option value="Select Contact"></option>')
.val('Contact')
$.ajax({
type: 'get',
url: urlForModesl,
contentType: 'application/json',
dataType: 'json',
success: function(jsonData) {
$.each(jsonData, function(key, value) {
modelsHtml += "<option value='" + value.LoginID + "'>" + value.ContactName + "</option>";
});
$("#CheckItem").html(modelsHtml);
$("#DeliveryUnitID").val($(this).val())
},
error: function(XMLHttpRequest, textStatus, errorThrown) {}
});
});
});
I am aware that I have two functions with the same name "$("#UnitList").change(function ()" and that this is very bad and causing conflicts. This is what I am trying to resolve.
I have tried to simply add the element update within the Jquery code, but that did not work.
I placed the following code
$("#DeliveryUnitID").val($(this).val())
Inside the Jquery, right after:
$("#CheckItem").html(modelsHtml);
But that does not work. The hidden elements are still empty.
I tried creating a function called foo, with the element update, and call that function from at the end of the jquery.
foo();
<script>
function foo() {
$("#DeliveryUnitID").val($(this).val());
}
That also left the element #DeliveryUnitID empty.
I know that I can't have two functions with the same name, but that's the only way I can get it working where the query populates the drop down, and then the hidden element is populated too. BUT . . . that's bad coding AND, for about 5% of the users, it fails.
I can't quite figure out how to make both happen with one onchange event.
It's been a while since I've used jQuery, so here's what I'm seeing:
On change Event:
Get the value of the #UnitList <select> (potential bug: these aren't being stored anywhere)
Clear the options
Re-populate with new ones from AJAX Request
Try to get value of new options (still within initial AJAX request, which might also be a bug? Like I said, it's been a while since I've used jQuery).
It looks like you might have a state-management issue?
Potential fixes:
Store values of first dropdown before you clear them.
Use the stored value to populate #DeliveryUnitID after the AJAX request
Use some if statements or remove the event listener to not constantly be running your code on change event once you get the data you need...unless you need it to be running constantly.
To me, it would seem beneficial (from a code pov and maybe also a UX perspective) to programmatically build a second dropdown to keep things clearer, but that might be overkill for what you're trying to accomplish.
If I am here asking it is because we are stuck on something that we do not know how to solve. I must admit, we already searched in StackOverflow and search engines about a solution.. but we didn't manage to implement it / solve the problem.
I am trying to create a JavaScript function that:
detects in my html page all the occurrences of an html tag: <alias>
replaces its content with the result of an Ajax call (sending the
content of the tag to the Ajax.php page) + localStorage management
at the end unwraps it from <alias> tag and leaves the content returned from ajax call
the only problem is that in both cases it skips some iterations.
We have made some researches and it seems that the "problem" is that Ajax is asynchronous, so it does not wait for the response before going on with the process. We even saw that "async: false" is not a good solution.
I leave the part of my script that is interested with some brief descriptions
// includes an icon in the page to display the correct change
function multilingual(msg,i) {
// code
}
// function to make an ajax call or a "cache call" if value is in localStorage for a variable
function sendRequest(o) {
console.log(o.variab+': running sendRequest function');
// check if value for that variable is stored and if stored for more than 1 hour
if(window.localStorage && window.localStorage.getItem(o.variab) && window.localStorage.getItem(o.variab+'_exp') > +new Date - 60*60*1000) {
console.log(o.variab+': value from localStorage');
// replace <alias> content with cached value
var cached = window.localStorage.getItem(o.variab);
elements[o.counter].innerHTML = cached;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(window.localStorage.getItem(o.variab),o.counter);
} else {
console.log(o.variab+': starting ajax call');
// not stored yet or older than a month
console.log('variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language);
$.ajax({
type: 'POST',
url: my_ajax_url,
data: 'variable='+o.variab+'&api_key='+o.api_key+'&lang='+o.language,
success: function(msg){
// ajax call, storing new value and expiration + replace <alias> inner html with new value
window.localStorage.setItem(o.variab, msg);
var content = window.localStorage.getItem(o.variab);
window.localStorage.setItem(o.variab+'_exp', +new Date);
console.log(o.variab+': replacement from ajax call');
elements[o.counter].innerHTML = content;
// including icon for multilingual post
console.log(o.variab+': calling multilingual function');
multilingual(msg,o.counter);
},
error: function(msg){
console.warn('an error occured during ajax call');
}
});
}
};
// loop for each <alias> element found
//initial settings
var elements = document.body.getElementsByTagName('alias'),
elem_n = elements.length,
counter = 0;
var i = 0;
for(; i < elem_n;i++) {
var flag = 0;
console.info('var i='+i+' - Now working on '+elements[i].innerHTML);
sendRequest({
variab : elements[i].innerHTML,
api_key : settings.api_key,
language : default_lang,
counter : i
});
$(elements[i]).contents().unwrap().parent();
console.log(elements[i].innerHTML+': wrap removed');
}
I hope that some of you may provide me some valid solutions and/or examples, because we are stuck on this problem :(
From our test, when the value is from cache, the 1st/3rd/5th ... values are replaced correctly
when the value is from ajax the 2nd/4th .. values are replaced
Thanks in advance for your help :)
Your elements array is a live NodeList. When you unwrap things in those <alias> tags, the element disappears from the list. So, you're looking at element 0, and you do the ajax call, and then you get rid of the <alias> tag around the contents. At that instant, element[0] becomes what used to be element[1]. However, your loop increments i, so you skip the new element[0].
There's no reason to use .getElementsByTagName() anyway; you're using jQuery, so use it consistently:
var elements = $("alias");
That'll give you a jQuery object that will (mostly) work like an array, so the rest of your code won't have to change much, if at all.
To solve issues like this in the past, I've done something like the code below, you actually send the target along with the function running the AJAX call, and don't use any global variables because those may change as the for loop runs. Try passing in everything you'll use in the parameters of the function, including the target like I've done:
function loadContent(target, info) {
//ajax call
//on success replace target with new data;
}
$('alias').each(function(){
loadContent($(this), info)
});
I have a search filter on my site whereby when a user unchecks a box, an ajax call is posted to the server, returning new, narrowed down search results.
now, however, the user has the ability to uncheck about 20 boxes at the same time, which forces me to make 20 different ajax calls -- this is very slow
any ideas on how to pass the 20 different ajax calls into one so as to speed up things?
here is my js:
// to allow us to process the "only" buttons, which don't register the click event we're looking for
$("input[type=checkbox]").change(function() {
// remove the original, unfiltered results
$('#original_results').css("display", "none");
// which filter section does the filter belong to
var filter_section = $(this).attr('name');
// should it be filtered out or left in
var remove = $(this).prop('checked');
// if needs to be filtered
if (!remove)
{
// add it to our filter list for the specified section
filters[filter_section].push(this.value);
}
else
{
// take it off the list for the specified section
var index = filters[filter_section].indexOf(this.value);
if (index > -1)
{
filters[filter_section].splice(index, 1);
}
}
doAjax();
});
// ajax function
function doAjax() {
// get slider values
var ranges = $('#pay_range').slider('values');
// define data to post
var data = {
min: ranges[0],
max: ranges[1],
filters: filters,
criteria: criteria
};
// post data
$.ajax({
type: 'POST',
url: '/results/search_filter',
data: data,
beforeSend: function() {
$('#search_results').html('Updating your query');
},
success: function(response) {
$('#search_results').html(response);
},
dataType: "html"
});
}
As I understand it, you want your AJAX call to only happen once per action, even if the action includes changing multiple checkboxes.
I've achieved this using javascript's setTimeout() to "buffer" events. When a checkbox is changed, a short timeout is set which will fire the AJAX. If another checkbox is changed within that time period, the timeout will be re-set (instead of firing the AJAX twice). The AJAX only fires once at the end of the timeout.
// initialize timer variable
var ajaxtimer;
// function to run AJAX
function runajax() {
alert('ajax');
}
// clicking a checkbox sets all checkboxes to the same state
$("input[type=checkbox]").on('click', function () {
$('input[type=checkbox]').prop('checked', this.checked).change();
});
// fires on checkbox change, sets short timeout
$("input[type=checkbox]").on('change', function () {
clearTimeout(ajaxtimer);
ajaxtimer = setTimeout(runajax, 50);
});
WORKING EXAMPLE (jsfiddle)
Edit:
I saw the link you posted and made the following adjustments:
I defined this variable at the top of the file:
var ajaxtimer;
On line 156 of results.js, I changed:
doAjax();
to
clearTimeout(ajaxtimer);
ajaxtimer=setTimeout(doAjax,50);
Here's a jsfiddle. The layout is butchered, but you can see that clicking an "only" link results in only one ajax call, rather than one call for every checkbox that was changed.
I have a input with an onchange event:
<input type="text" value="2014-01-01" class="datepicker dateDashboard" onchange="repartiteur('DatesDashboard',$(this));document.location.href='index.php?menu=17|30&options=opt';" name="DateDebutDashboard">
function appelAjax(data)
{$.ajax(
{type: "post",
data: data,
url: 'index_ajax.php'
});
}
function repartiteur(action,this)
{switch (action)
{case 'DatesDashboard':
data = (
{'action' : action,
'champ' : this.attr('name'),
'date' : this.val()
});
break;}
appelAjax(data);}
the desired goal is that when I change the date in the input, the date is sent via AJAX to change a value in the database, then the page is reloaded according to new values in database.
the problem is that sometimes, the ajax call by repartiteur takes longer to react and when the page reloads (with the second inline javascript call), the changes have not yet occured in the database.
the QUESTION: How can I, if it is in any way possible, delay the second javascript part (the reload document) to make sure the ajax call have finished updating the database before the page reloads?
checking this answer How to implement a lock in JavaScript I feel there some way to do that, but this answer applies to different events, while I want the delay to happen in the same event but between different javascript lines.
I know about event.stopPropagation but I don't want the event to stop, it must continue after the ajax call.
EDIT - here the php codes which receives the call:
$action = $_POST['action'];
switch ($action)
{case 'DatesDashboard':
$codeUSR = $_SESSION['usr'];
$champ = $_POST['champ'];
$date = $_POST['date'];
$env = unserialize(getValue('Preferences','ps_personnes',"codeUSR='$codeUSR'"));
$env['nav']['Contrat']['dates'][$champ] = $date;
sauverEnvironnement($env,$oDB,'Preferences');
break;}
the database definition:
$oDB = new PDO("mysql:host=$gSERV;dbname=$gBASE;charset=utf8",$gUSER,$gPASS);
$oDB->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
$oDB->setAttribute(PDO::ATTR_EMULATE_PREPARES,FALSE);
You want a nested callback function, something like this:
$.ajax({
url: 'blahblah'
}).done(function() {
$.ajax({
url: 'secondlayer'
}).done(function() {
innerstuff();
});
})
});
The explanation is, you only do your second function after your first function is done (or completes successfully, up to you). Conversely, you only call your final (innerstuff) function when the second function has fully completed as well.
Source: http://api.jquery.com/jquery.ajax/
so with timeout() I set the javascript to reload the page so that it "waits" before taking action:
<input type="text" value="2014-01-01" class="datepicker dateDashboard" onchange="repartiteur('DatesDashboard',$(this));setTimeout(document.location.href='index.php?menu=17|30&options=opt',500);" name="DateDebutDashboard">
hence the changes in the db are now done when the page reloads.
I have ddl(drop down list) which populates from database after change event of another ddl but I want to change the value of this ddl after it populated from database.
Example:
// This work very well
$("#ddlGroups").on('change',function(){
// Load data from database and populate ddlUsers
// response is loaded from database with $.ajax
// Query Example: SELECT User_ID, Username FROM tblUsers WHERE Group_ID = [Group_ID] (Just for undrestanding the question)
var Records = response.d;
$("#ddlUsers").empty();
$.each(Records, function(){
var _Key = this.User_ID;
_Value = this.Username;
$("#ddlUsers").append($("<option />").val(_Key).text(_Value));
});
});
// When button clicked then trigger ddlGroups change event and assign select option from ddlUsers
var _UserID = User_ID_From_Database; // Loaded from Database when button clicked
$("#ddlGroups").trigger('change'); // Users are loaded from database based on group ID
$("#ddlUsers").val(_UserID); // But this dosn't change
How do I check if ddlUsers is loaded or not, I tried while loop but it never stops.
With jquery there are two main ways (really the same underlying) to connect a ajax response from the database to a event or UI result. The first is promises and the second is a callback. Both these have 1 million examples and jquery documentation is quite robust.
In your case rather than "trigger", you callback calls the right function. The below is just junk code to show.
No: $("#ddlGroups").tigger('change');
Yes:
//Your code
$("#ddlGroups").on('change', updateDDL);
//I have just externalized this function to a scope/place where both the ajax and on change line of code can reference it.
function updateDDL(){
var Records = response.d;
$("#ddlUsers").empty();
$.each(Records, function(){
var _Key = this.User_ID;
_Value = this.Username;
$("#ddlUsers").append($("<option />").val(_Key).text(_Value));
}
};
$.ajax(...., function (data) {
// Update ddl, the existing function you have.
// You should already have something like this.
updateDDL(); //Like this
});
BTW: You could pass the data into the updateDDL directly use $(this) in the updateDDL to improve it etc but that is not key to your question/issue. It seems that you are new to jquery and some of the Javascript features it uses. Take a little time to learn about them and you will be WELL rewarded. I would start by reading examples around ajax/jquery and watch how then update the DOM.