I've got a parent page that houses a series of iframes. These all reside on the same domain.
I've got an AJAX call that runs within each iframe when it loads and it retrieves some JSON for me that I'm using to populate inputs that reside within it. It's the same information being retrieved by each iframe.
Currently, I'm running that AJAX call inside of each iframe. This doesn't strike me as particularly performant and I'm encountering issues with this. In some cases depending on the order the DOM loads, one iframe will have the correct data available while another won't have any at all.
My thought now (and open to suggestion) is to load the AJAX once within the parent page, store the data I need as local storage variables and then call those from within each iframe. Idea being it loads once (before the iframes are called) so that the data is there and defined, every time.
As a rough proof of concept I've got this;
Parent page
$.ajax({
url: 'https://www.urlofsite.com/mylookupfile.php',
type: 'POST',
dataType : 'text',
data: {Finder: finderID},
success: finderAccess,
error: finderDecline
});
function finderAccess(data) {
console.log("sucessful send:");
// Parsing the returned data from the DB into a JSON object
var useableData = $.parseJSON(data);
console.log(useableData);
// Set the session variables that will be used by each form
localStorage.setItem('fName', useableData.fname);
const sessfName = localStorage.getItem('fName');
localStorage.setItem('lName', useableData.lname);
const sesslName = localStorage.getItem('lName');
}
//error function and some other non-related scripts follow
So now I've got my session vars set and it works within the parent page. If I call something like
$(".class-name-of-a-p").html(sessfName);
The paragraph with that class properly displays the value stored in the variable.
Moving onto the iframe now...
Iframe
$("#person_first_name").val(sessfName);
$("#person_last_name").val(sesslName);
My understanding of the HTML Local Storage method of creating local storage variables is that they are available as long as the domain is the same. That's the case here, but, in the iframe the console tells me the variables don't exist and throws an error.
Store the Ajax request in the window object (in your top-level frame):
window.lookup = $.ajax({
url: 'https://www.urlofsite.com/mylookupfile.php',
type: 'POST',
dataType : 'text',
data: {Finder: finderID},
});
Now, in each of your child frames, you can do
window.parent.lookup.done(function (data) {
// work with data
});
and all iFrames will receive the request as soon as it's there.
This works because $.ajax returns a promise. This promise is stored in window.lookup. You register as many .done() callbacks with a promise as you like - all of them will be notified when the promised result (the Ajax request in this case) is available. It also caches the data - frames that load later (or are created later) will receive the result immediately.
If you want to do some pre-processing before you hand down the data to your child frames, use .then() in the main top frame:
window.lookup = $.ajax({
url: 'https://www.urlofsite.com/mylookupfile.php',
type: 'POST',
dataType : 'text',
data: {Finder: finderID},
}).then(function (data) {
var modifiedData = doSomethingWith(data);
return modifiedData;
});
Now all child frames will receive modifiedData.
Error handling can be done in the top frame (this error handler will be called once):
window.parent.lookup.done(function (data) {
// work with data
}).fail();
url: 'https://www.urlofsite.com/mylookupfile.php',
type: 'POST',
dataType : 'text',
data: {Finder: finderID},
}).fail(function (jqXHR, textStatus, errorThrown) {
finderDecline();
});
or in the child frame (this will be called once per frame):
window.parent.lookup.done(function (data) {
// work with data
}).fail(function (jqXHR, textStatus, errorThrown) {
// do something appropriate
});
or in both.
Related
I have a node.js project with frontend written in Pug. On the pug side I have written a Javascript function that is making an Ajax call and returning a string, I am trying to call this function inside that same pug file but it gives me a function not defined error. Can anyone please help with this?
header(class="global-header")
p Current Status: #{getCurrentAvail()}
script.
function getCurrentAvail(){
$.ajax({
url: "/admin/account/accountAvailability",
type: "GET",
contentType: "application/json; charset=utf-8",
async: false,
success: function(data){
console.log("===1")
currentAvail = data.message
console.log(currentAvail)
return data.message
},
error: function(data){
console.log("Error function avail");
}
});
}```
It appears you have some piece of external data and you want that data to show in a rendered web page. You have two basic choices here:
1. Server-side fetching and rendering of the data. You can have your server get the data before rendering the page and pass that data to your template engine so your template engine can insert the data into the page at the appropriate place.
2. Client-side fetching and insertion of the data. You can call the getCurrentAvail() function from within a <script> tag in the web page. This will send the rendered web page to the browser. As it loads, the browser will then execute your script, fetch the data from your server and then you would use DOM APIs in the browser to insert the result of that ajax call into the web page to make it visible to the user in the page.
Also, please note that your function getCurrentAvail() has no return value at all. You aren't returning anything from the outer function. Your return data.message is inside the asynchronous success callback so that just go back to nowhere. If you're going to go with the client-side option #2, then you can just put the DOM manipulation code right into the success callback function.
Your current code is attempting to do 1/2 server and 1/2 client and that is not something that will work.
At all times, have total clarity on what is in the server and what is in the client. Ajax methods run in the browser. A #{variable} construct exists only in Pug, and the substitution occurs on the server.
I think this is what you need:
header(class="global-header")
p Current Status:
span#status
script.
function getCurrentAvail(){
$.ajax({
url: "/admin/account/accountAvailability",
type: "GET",
contentType: "application/json; charset=utf-8",
async: false,
success: function(data) {
document.getElementById("status").innerText = data.message
},
error: function(data){
console.log("Error function avail");
}
});
}
getCurrentAvail();
I am trying to get use an object from a script loaded synchronously using Ajax via jQuery.
From this script I am trying to load an object which looks like this from a script called map_dropdowns.js which returns the object options:
{curr_cat: "RELATIONSHIP"
curr_subcat: " Population in households"
curr_total: "Total"}
My code for the script with the ajax is here:
<script>
$.ajax({
type: "GET",
url: "../scripts/map_dropdowns.js",
dataType: "script",
async: false,
success: function(data){
console.log(data);
}
});
console.log(options); //returns `Object{}` in the console, and only shows values when expanded
options["curr_cat"]; //returns undefined
console.log(Object.keys(options)); //returns an empty array []
</script>
In the original script, the keys and values within options can be accessed perfectly fine. console.log in Chrome shows its contents fully without needing to be expanded (Object {curr_cat: "RELATIONSHIP", curr_subcat: " Population in households", curr_total: "Total"}), and Object.keys() works just fine.
After it is loaded onto the page with the Ajax function, however, trying to access the values using the keys comes up undefined, Object.keys turns up an empty array [], and the key:value pairs are only shown in the console when I click on the object, with it otherwise showing only Object {}.
I am pretty sure that I need to do something in the success function of the Ajax, but I am not sure what after a lot of trial and error.
Thanks!
Loading JS code via AJAX is always a little hit and miss. It's usually a much better idea to load the data either as HTML, XML or JSON, and then deal with it as required once the AJAX request completes.
In your case, as you're attempting to load an object, JSON would be the most appropriate. If you change your map_dropdowns.js file to return data in this format:
'{"curr_cat":"RELATIONSHIP","curr_subcat":"Population in households","curr_total":"Total"}'
You can then make your async request to get this information from this file:
$.ajax({
type: "GET",
url: "../scripts/map_dropdowns.js",
dataType: "json",
success: function(data){
console.log(data.curr_cat); // = 'RELATIONSHIP'
console.log(data.curr_subcat); // = 'Population in households'
console.log(data.curr_total); // = 'Total'
}
});
I work on a Backbone app with a collection for data sources. Whenever a new data source is added, its model is added to the collection, and a jQuery Ajax call is made for it like this:
fetch: function() {
var model = this,
url = model.get("url");
function testCallback(parObj) {
return function(data, textStatus, jqXHR) {
alert("test - "+parObj.url+" : "+data.sourceurl);
}
}
$.ajax({
url: url,
type: "GET",
dataType: "jsonp",
jsonpCallback: "data",
success: testCallback({ model: model, url: url })
})
.done(function (data) {
alert("done - "+model.get("url")+" : "+data.sourceurl);
});
}
The fetch() is called in rapid succession, and debugging it I can see everything is ok when I initiate the Ajax request.
Everything works great if I only add two data sources.
But both the done() and testCallBack() functions mixes up the data when I have three requests running simultaneously on three different domains (same happens in both Chrome and Safari).
For instance:
URL 1 gets the data from URL 1.
URL 2 gets the data from URL 3.
URL 3 gets the data from URL 2.
Am I doing something wrong? Any help is greatly appreciated.
That's because you're setting the jsonpCallback parameter to the same thing for each request. Just remove that line entirely as jQuery will automatically create unique ones for you.
I'm trying to retrieve the data I submitted to an asynchronous ajax request should the back-end fail in some way. The data in 'myJSONData' is actually pulled off a queue (array) in memory and I need to put it back into the queue if any kind of error occurs.
e.g.
var myJSONData = {"parm1":"value1","parm2":"value"};
$.ajax({
type: "POST",
url: "/postData.ajax",
dataType: "json",
data: myJSONData,
success: function(jsonReply) {
// I need to refer to the posted data here (i.e. myJSONData)
},
error: function(xhr,ajaxOptions,thrownError) {
// I need to refer to the posted data here (i.e. myJSONData)
}
});
My code fires off a number of calls at various times, the trouble is that if I refer to myJSONData within the success or error blocks it contains the most recent value of that variable in memory, and not what was in the variable at the time of the ajax call.
Is there some other way to access the data associated with the particular instance of ajax call - something like $.ajax.data ?
You should be able to access it in your success and error functions :
success: function(jsonReply) {
var p1 = myJSONData.param1;
}
I am experiencing an issue in jQuery when I do multiple jsonp requests, all with the same jsonpCallback function. It seems that only for the one of those the callback function is triggered. Are JSONP requests somehow overwriting each other?
Below an example of doing 2 jsonp request to github, and even though both firebug shows that both of them return, the callback function getName is only called for one of them:
function getName(response){
alert(response.data.name);
}
function userinfo(username){
$.ajax({
url: "https://api.github.com/users/" + username,
jsonpCallback: 'getName',
dataType: "jsonp"
});
}
users = ["torvalds", "twitter", "jquery"]
for(var i = 0; i < users.length; i++){
userinfo(users[i]);
}
Your request fired only once because of how jsonp works.
Jsonp means adding a script tag to the page from an outside domain to get around Cross-Site Scripting protections built into modern browsers (and now IE6 and 7 as of April 2011). In order to have that script interact with the rest of the script on the page, the script being loaded in needs to call a function on the page. That function has to exist in the global namespace, meaning there can only be one function by that name. In other words, without JQuery a single jsonp request would look like this:
<script>
function loadJson(json) {
// Read the json
}
</script>
<script src="//outsidedomain.com/something.js"></script>
Where something.js would look like this:
loadJson({name:'Joe'})
something.js in this case has a hard-coded callback to load the JSON it carries, and the page has a hard-coded loadJson function waiting for scripts like this one to load and call it.
Now suppose you want to be able to load json from multiple sources and tell when each finishes, or even load JSON from the same source multiple times, and be able to tell when each call finishes - even if one call is delayed so long it completes after a later call. This hard-coded approach isn't going to work anymore, for 2 reasons:
Every load of something.js calls the same loadJson() callback - you have no way of knowing which request goes with which reply.
Caching - once you load something.js once, the browser isn't going to ask the server for it again - it's going to just bring it back in from the cache, ruining your plan.
You can resolve both of these by telling the server to wrap the JSON differently each time, and the simple way is to pass that information in a querystring parameter like ?callback=loadJson12345. It's as though your page looked like this:
<script>
function loadJson1(json) {
// Read the json
}
function loadJson2(json) {
// Read the json
}
</script>
<script src="//outsidedomain.com/something.js?callback=loadJson1"></script>
<script src="//outsidedomain.com/somethingelse.js?callback=loadJson2"></script>
With JQuery, this is all abstracted for you to look like a normal call to $.ajax, meaning you're expecting the success function to fire. In order to ensure the right success function fires for each jsonp load, JQuery creates a long random callback function name in the global namespace like JQuery1233432432432432, passes that as the callback parameter in the querystring, then waits for the script to load. If everything works properly the script that loads calls the callback function JQuery requested, which in turn fires the success handler from the $.ajax call.
Note that "works properly" requires that the server-side reads the ?callback querystring parameter and includes that in the response, like ?callback=joe -> joe({.... If it's a static file or the server doesn't play this way, you likely need to treat the file as cacheable - see below.
Caching
If you wanted your json to cache, you can get JQuery to do something closer to my first example by setting cache: true and setting the jsonpCallback property to a string that is hardcoded into the cacheable json file. For example this static json:
loadJoe({name:'Joe'})
Could be loaded and cached in JQuery like so:
$.ajax({
url: '//outsidedomain.com/loadjoe.js',
dataType: 'jsonp',
cache: true,
jsonpCallback: 'loadJoe',
success: function(json) { ... }
});
Use the success callback instead..
function userinfo(username){
$.ajax({
url: "https://api.github.com/users/" + username,
success: getName,
dataType: "jsonp"
});
}
$(function() {
function userinfo(username){
var XHR = $.ajax({
url: "https://api.github.com/users/" + username,
dataType: "jsonp"
}).done(function(data) {
console.log(data.data.name);
});
}
users = ["torvalds", "twitter", "jquery"];
for(var i = 0; i < users.length; i++){
userinfo(users[i]);
}
});
Not sure but the response I get from that call to the github API does not include gravatar_id.
This worked for me:
function getGravatar(response){
var link = response.data.avatar_url;
$('#list').append('<div><img src="' + link + '"></div>');
}