I'm trying to read data from a json file into JavaScript so it can be used by other functions that are called when a user interacts with the page. I've tried, using jQuery and JS:
var products = null;
$.getJSON("products.json", function(data) {
products = data;
console.log(data);
});
console.log(products);
Which produces, as expected:
null
Array [ {…}, {…} ]
I understand this is because of the asynchronous nature of the jQuery code execution. I've read into it a bit, but I'm just struggling to wrap my head around how to re-structure my code (coming from pretty much exclusively Python).
This is how I'm seeing it:
var products = null;
$.getJSON("products.json", function(data) {
products = data;
console.log(data);
});
function do_stuff(){
// Access the attributes of data and do stuff
}
function do_more_stuff(){
// Do some more stuff with the data
}
I would probably have do_stuff() execute when the page body loads, and let's say do_more_stuff executes when the user selects something in a dropdown menu. Both need to access the data in products.json. I know there are many examples out there, but I'm just not getting it. How can I re-structure the code to actually work?
I believe you are looking for something like this:
function get_json(cb) {
$.getJSON("products.json", function(data) {
cb(data);
});
}
function cb(data) {
// do stuff here if you want
console.log(data)
}
get_json(cb)
Create a callback function cb (or call it do_stuff if you'd like). Pass a reference to that function to your async function (get_json). When the async operation is complete, call your callback function with the data you received.
Related
I want to get data in global variable by using the following code:
var data;
d3.json ( "file.json" , function(json) {
data = json;
console.log(data); //defined
});
console.log(data); //undefined
But the problem is that i just have data variable defined in d3.json function but out it is undefined.
how can I solve this issue?
Thanks
Because d3 requests (like d3.json) are asynchronous, it's best practice to wrap all of the code dependent on your external request within the request callback, ensuring that this code has access to the data before executing. From the D3 docs: "When loading data asynchronously, code that depends on the loaded data should generally exist within the callback function."
So one option is to put all of your code within the callback function. If you'd like to separate the code into parts, you can also pass the response from your request to a separate function, something like this:
function myFunc(data) {
console.log(data);
}
d3.json('file.json', function (data) {
var json = data;
myFunc(json);
});
I have 2 functions in jQuery and one needs data the other is loading from a DB via php and json(this part works).
My problem is that the first one starts berfore the data is loaded.
How con I fix this?
Here a little code to understand:
function loadBody(){
//Create an calendar of selectet month with classes to Events
}
function loadEvents(){
//loads Events from DB with php and json and puts them into an array
}
I tryed e.g. the following:
function loadCal(){
$.when(loadEvents()).done(loadBody());
}
loadBody not loadBody() : you were executing loadBody instead of giving the callback.
function loadCal(){
$.when(loadEvents()).done(loadBody);
}
$when expects a promise to be returned by the method passed into it. You need to make sure loadEvents method returns a promise.
function loadEvents(){
return $.ajax({
//Make ajax call to loads Events from DB
});
}
function loadBody(){
//Create an calendar of selectet month with classes to Events
}
function loadEvents(){
//loads Events from DB with php and json and puts them into an array
loadBody();//call here after load complete
}
function loadCal(){
loadEvents();
}
the problem is that loadEvents immediatelly returns, so loadBody is called before it finishes to load the file asynchronously. Can you post the full loadCal function? In it you'll have some callback, that saves the data to the array. There you could call loadBody(). I know it's not as nice as you tried, but it should work :)
You can try this:
var loadEvents = function (callback) {
// do stuff...
callback()
};
var loadBody = function () {
loadEvents(function () {
// loadEvents function is already done here...
// continue with loadBody function
})
};
I'm developing a webpage that retrieves some database info via a (jquery) ajax call and then manipulates the data and displays it on the page via various javascript functions. I use a module pattern ('userapp' in the code example below) to avoid globals and to use the data by the various functions.
The simplified code below works, but only if I include all the functions (such test() in the code below) within the ajax call, and I think that's going to result in ugly complex code (also in line with some best javascript practises I read on the web). When I tried to call the example function test() outside /after the ajax call (embedded in init()), test() does not work as I think the ajax call has not completed setting the variables ('products[]' in example) yet.
My question: is it possible to call other functions outside the ajax call, and if so, how? I looked at callback examples but I'm not sure whether/how that would solve my problem...
simplified code:
userapp = function(){
//userapp properties
var today = new Date();
var products = [];
var copyItems = function(source_items, target_items){
//do something
};//var copyItems
var init = function(){
$.ajax({
url: "user_admin_get.php",
data: { command: "getuserbaseinfo", val1: "", val2: "" },
success: function (msg) {
copyItems(msg.eproducts,products); //set values for 'products' used by test()
test(); //calling test() here works as the the values has been set()
},//success: function(msg)
error: function(msg) {
console.log('error result from php file:');
},//error:
dataType: "json"
});//$.ajax({
};//var init = function(){
var test = function(){
//do something
};//test()
return{init:init, test:test} //return (only) public functions and vars
}(); //userapp()
//main function document ready
$(document).ready(function(){
userapp.init();
//userapp.test(); //test() is not working here as init() has not set the requirement parameters tey
}); //$(document).ready
You want to pass a callback to init and call test inside this callback.
var init = function(callback){
$.ajax({
....
success: function (msg) {
....
callback();
}
...
};
...
userapp.init(function() {
// user app is ready!
userapp.test();
});
So I have a common scenario where everything depends on AJAX responses, followed by possibly more AJAX responses.
What ends up happening is lots and lots of presentation (page-specific) code gets thrown inside the success() callback:
$.ajax({
...
success: function (response) {
// too much $('#something').html() crap goes in here!
}
});
What is the best practice for removing this "data access" code completely from the presentation code but maintaining the loading sequence?
I've used deferred calls like $.when().then() but that still seems sloppy to me. Is there anything better?
To make an answer even simpler, let's say I want to retrieve information about a Person object and segregate all that logic into its own area. Example:
note: this code will not work - I am aware of that
Person.js:
var Person = {
getByID: function(id) {
// ajax call to return a person object (format is arbitrary)
// { id: 12345, name: 'Joe Smith', email: 'joe#smith.com }
}
};
SomePage.html
var myID = 12345; // get ID from wherever
var person = Person.getByID(myID);
$('#person .name').html(person.name);
$('#person .email').html(person.email);
EDIT: My solution
Although many of the answers were helpful, I chose to pass callback functions that separate all the various pieces of logic from each other. Example code:
JS:
Person = {
get: function(params, callback) {
$.ajax({
url: '/person/get',
data: params,
success: callback
});
}
};
Pages = {
Person: {
render: function(person) {
// in reality I'm using templates, this is for simplicity
$('#person .name').html(person.name);
$('#person .email').html(person.email);
}
}
};
SomePage.html
$('#someElement').someEvent(function() {
var params = {
id: 12345
};
Person.get(params, Pages.Person.render);
}
I should also add I found this slide deck extremely informative:
http://speakerdeck.com/u/addyosmani/p/large-scale-javascript-application-architecture
You'll have to have something in the success callback, but you don't need presentation details there. The success call could simply call:
Person.render(attributes, element)
And the details would be in the render function.
As SLaks says, what you want to do is impossible in the asynchronous context of AJAX.
However, nothing speaks against keeping the majority of the code in separate objects that do all the grunt work. You would then call those objects and their methods from your success callbacks and pass them all the necessary data. That would ensure that your callbacks contain only the bare minimum of code.
It sounds to me that you may wish to introduce the Model-View-Controller design pattern into your application. In the simplest form the Model would be responsible for fetching the required data which is then passed through to the View to be rendered; the Controller's job is to process incoming requests, invoke the Model and shuffle the data into the View in the expected format.
As others have mentioned there are plenty of lightweight MVC frameworks out there for JavaScript; but you should be able get the basic concept up and running just by using jQuery and a templating engine (such as Mustache.js) so you can get some separation between Model (fetching the data and building a common object graph) and View (rendering a template and adding the rendered HTML to the DOM).
As for you proposed Person example, you could make use of jQuery's Deferred to allow Person.getById to return a Promise, eg:
var Person = {
getByID: function(id) {
var result;
// Return a Promise which we will resolve once the AJAX call completes.
return $.Deferred(function (dfd) {
$.ajax(...)
.done(function (response) {
// Parse the AJAX response into your app's model data.
result = {
id: response.id,
name: response.firstName,
email: response.email
};
// Resolve the Promise and supply the Person object.
dfd.resolve(person);
});
}).promise();
}
};
Your Controller can then invoke the Model and then supply the result to the View (this is where you could make use of templates).
Person.getByID(myID)
.done(function(person) {
$('#person .name').html(person.name);
$('#person .email').html(person.email);
});
You can slightly change your example to make it work asynchronously - still cleaner code than shoving everything in to the success callback function:
var myID = 12345; // get ID from wherever
Person.getByID(myID, function(person) {
$('#person .name').html(person.name);
$('#person .email').html(person.email);
});
I now know the correct answer is:
There are over 9000 libraries created specifically for this, a common problem known as AMD.
-TerryR
http://speakerdeck.com/u/addyosmani/p/large-scale-javascript-application-architecture
Can someone tell me why this alert is empty?
var pending_dates = [];
$.getJSON('/ajax/event-json-output.php', function(data) {
$.each(data, function(key, val) {
pending_dates.push({'event_date' : val.event_date});
});
});
alert(pending_dates);
I can't get my head around this. Am I not declaring pending_dates as a global variable, accessible within the each loop? How would one solve this?
Please note that the JSON output is working well. If I would declare pending dates within the getJSON function (and alert within that function), it works, but I need to store the data in an array outside of that getJSON function.
Thanks for your contributions.
EDIT
Thanks to your comments this code is working:
pending_dates = [];
$.getJSON('/ajax/event-json-output.php', function(data) {
$.each(data, function(key, val) {
pending_dates.push({'event_date' : val.event_date});
});
}).success(function() { alert(pending_dates); })
Thanks a lot for your contributions!
I think the problem is $.getJSON is an asynchronous call - it returns immediately and then alert(pending_dates) is invoked.
However, when that happens, the response from the asynchronous call may not have been received, and hence pending_dates may not have been populated.
That is probably why it is empty at the time alert(pending_dates) is called.
Your alert is executing before the JSON call has finished. Remember this JSON it fetched and processed asynchronously, but your alert comes right after it's started. If you want an alert, then you need to put it at the completion of the getJSON call.
$.getJSON is working asynchronously meaning that whatever you have specified in the callback will be executed eventually but there is no guarantee that will happen by the time you reach alert('pending_dates').
You can verify this by moving alert('pending_dates') right after
pending_dates.push() (this would result in one alert being displayed for each item it is retrieving).
You can write a function to start working with the data you're retrieving as soon as it is available:
var pending_dates = [];
$.getJSON('/ajax/event-json-output.php', function(data) {
$.each(data, function(key, val) {
pending_dates.push({'event_date' : val.event_date});
doSomething(val.event_date);
});
});
function doSomething(date) {
// do something with date
// like writing it to the page
// or displaying an alert
}
With this you'll be able to work with all the data you get as it becomes available.
Variables are global by default in Javascript - having var actually introduces scope. Remove that and see if it helps.
It's more likely that the AJAX response isn't returning any data. Can you try pushing just 'foo' onto the array, and see if the alert shows anything different?