Show local weather API

Hello,
I’m working on the Show Weather Challenge. Ip-api.com is down, so I’m trying to get JSON data by zip code. My .getJSON isn’t returning the city name. I’m having trouble getting the city name into #city. Any help would be appreciated.

function getZip() {

 var zip = document.getElementById("zipcode").value;

 var api = 'api.openweathermap.org/data/2.5/weather?zip=' + zip + ',us' + '&appid=1800147fe71b1f960c693e7f65ee030a';

console.log(api); //tests api url is correct
   var city;
  $.getJSON(api,function(data){

 var city = data.name;
  })

}

$("#city").html(city);

Your

$("#city").html(city);

is in the wrong place (should be inside the getJSON callback).

Outside of the callback, the city variable you create is undefined.

Remember, getJSON is asynchronous, so the line where you attempt to insert city into the interface will execute prior to getJSON returning any data. It will only see that data from inside the callback.

Is this what you’re talking about?

function getZip() {

var zip = document.getElementById("zipcode").value;

var api = 'api.openweathermap.org/data/2.5/weather?zip=' + zip + ',us' + '&appid=1800147fe71b1f960c693e7f65ee030a';

console.log(api);
var city;
$.getJSON(api,function(data){ 

var city = data.name;
$("#city").html(city);
 })


}

This will return Reference Error because browser won’t be able to find city in the global name space, neither it’s deceleration nor it’s instantiation.

First, thank you for your assistance. I’m trying several things and honestly, I’m stuck.

$('#city').html();

function getZip(){

 var zip = document.getElementById("zipcode").value;
var api = 'api.openweathermap.org/data/2.5/weather?zip=' + zip + ',us' +    '&appid=1800147fe71b1f960c693e7f65ee030a';

console.log(api);  



$.getJSON(api,function(data){
  var city = data.name;  
});

  $('#city').html(city);
}

What issues are you getting? I can test it here as I’m getting problem with the token

My function seems to not be getting the city information from the JSON data that I’m trying to access. A user puts in their zip code. The user input is being passed into the API call but I’m having trouble accessing the JSON data. I would like to be able to print information to console first, then figure out passing it to html.

Hi @rmonastra,

No, you need to move the entire


$('#city').html(city);

into


$.getJSON(api,function(data){
  var city = data.name;  
});

That function being passed to $.getSJON is a callback - it is called back when the getJSON ajax call completes. The getJSON function calls back your callback function and passes in the data argument.

That’s the only place the data value will be accessible for use, no where else can see it.

So, assuming your API call is returning successfully, you need:


$.getJSON(api,function(data){

  var city = data.name; 
  $('#city').html(city);

  // YOU COULD ALSO console.log( data ); HERE TO INSPECT THE RESULTS

});

P.S. - Normal function scoping rules would allow you to declare your city variable outside getJSON and then access it later, like you had it before, but because getJSON is asynchronous, and Javascript keeps executing while waiting for a result, your statements after getJSON are executed before city has any value. That technique does not work with asynchronous calls. You could see that by doing something like:


console.log( 'First!' );

$.getJSON(api,function(data){
  
  console.log( 'Second!' );

});

console.log( 'Third!' );

And looking at the order in which they are logged to the console.

Hope that helps!
~Micheal

I just finished my Weather API project. What @michealhall michealhall said is mostly true, however there actually is a way to create a global object variable extracted from the $.getJSON API call. It took me three days to figure it out. haha I played around with your code to so you (and others) can see that it IS possible to work with a global object outside of $.getJSON I used your code and changed it to demonstrate what I’m talking about.

$(document).ready(function() {
	var JsonObjectAPI = {};   //Global object variable that the API will load into
	var city = "No City Name Yet";  //Global string variable for city name
	
	function getZip() {
		//var zip = document.getElementById("zipcode").value;
		var zip = 90210;
		
		var api = 'http://api.openweathermap.org/data/2.5/weather?zip=' + zip + ',us' + '&appid=1800147fe71b1f960c693e7f65ee030a';
		//console.log(api); //tests api url is correct
		
		//Must include the   'async':false   parameter or the object data won't get captured when loading
		JsonObjectAPI = JSON.parse($.ajax({'url': api, 'async': false}).responseText);
		
		city = JsonObjectAPI.name;
		//console.log(city);
	}
	
	setTimeout(getZip()); //runs the getzip function for testing
	
	console.log(JsonObjectAPI);
	console.log(city);
	
	$("#city").html(city);
});

If you set async to false, you are negating the entire point to ansynchronous requests which allow other processing to continue while you wait for your request to return. So, you really don’t want to do that. This is going to cause page freezing and un-responsive UI.

That is why you are able to, lower in the code, log the values from JsonObjectAPI and city.

There are other ways to pass around information to be used outside of callbacks - global variables and async: false I would not recommend.

Regards,
Micheal

I understand your points. Can you please explain to us what the other options we have of passing around information outside of callbacks?

Here is a simple method/function from a project I work on.

loadCollection is an asynchronous method which loads data from the server. Various methods may call it (some directly, others by triggering an event). The method itself does nothing other than load the data requested (this is specified by the provided collectionID).

The method may not actually need to load the data - if it finds it already loaded and it hasn’t been told to force_reload, it returns the data it has, saving a round trip to the server.

The data requested is JSON format, but not exactly the format we want (there are some links/relationships we’d like to establish and this collection may need to replace one already in memory). So, when the data is returned, the callback to the ajax request executes an update function, which does that data updating/replacement as needed.

At that point, the data is ready to use and function calls any callback provided, passing in the newly returned data. Additionally, it resolves the Promise which was created and returned, also passing in the data.

This way, the function can be called directly using the .then() syntax to process the results of the call after it is complete, or - when called via say an event triggering - the event can pass in a callback, which is executed.

Multiple collections can be loaded at once and all of this data is all maintained in a list (an Array) which is modified via methods like COLLECTIONS.update.collectionReference() and accessed via methods like the COLLECTIONS.get.collectionReference()

Written this way, the load function doesn’t care why you are loading data, it simply loads the data you requested and then executes any callback/resolves a promise. All of the handling of the data returned is done external to the load call and passed back out.

I’m not suggesting this method is perfect (I found a bug while I was writing this up!) But it’s a common pattern for me when handling data loaded from the server.

Anyway, hope this helps out a bit!


function loadCollection( collectionID, include_imagestores, force_reload, callback ) => {

	return new Promise( ( resolve ) => {

		const collection   = COLLECTIONS.get.collectionReference( collectionID );
		
		if ( collection && ( include_imagestores && collection.imageStores && collection.imageStores.length ) && !force_reload )
		{
			completeLoad( collection, resolve, callback );
			return;
		}
		
		COLLECTIONS.load.dataFromServer( 'collection', 
			{
				'collectionID'       : collectionID, 
				'includeImageStores' : ( include_imagestores ) ? 1 : 0, 
				'forceReload'        : ( force_reload ) ? 1 : 0 
			}) 
			.then( ( data ) => {
				
				COLLECTIONS.update.collectionList( data );
				completeLoad( COLLECTIONS.get.collectionReference( collectionID ), resolve, callback );

			});
		
	});

	function completeLoad( collection, resolve, callback ){
		
		( typeof callback === 'function' ) &&
			callback( collection );
			
		resolve( collection );
		
	}


}

Wow, that seems like a lot of code just to capture an json object! It will take me some time to digest how it works. But it seems like we should have a short and simple way to capture json data globally and asynchronously.

That’s not particularly a lot of code - there’s very little there and a decent amount going on.

It starts with a check to see if we even need to load the data, if not, it early returns and done.

If the data does need to be loaded, it calls the load function which returns a promise. When that promise resolves, the data is updated in an in memory cache. Then finally finally calls to execute callbacks and resolve a promise which allow external code to know that the data is available.

So … load, update/cache, callback/resolve.

Quite simple.

But, if that seems overly long and complex, it can be boiled down to …


const global_data = { 
	'dataFromServer': null
};

const url = '... some url that returns JSON data';

fetch( url )
	.then( ( response ) => response.json() )
	.then( ( json ) => global_data.dataFromServer = json );
	

Now your response is available globally. Of course other code won’t know the data has been loaded unless you create a function that returns a promise or executes a callback … which, if you look above, is all that the loadCollection function I posted is really doing.

I guess it’s all relative. But 17 lines of code compared to one line of code is a pretty big difference. But your last solution of 6 lines of code makes me feel a bit better. haha

I guess I’m just speaking out of my ignorance and lack of experience here. I mean, it seems like JavaScript should have something like your 17 lines of code already built into it and it would work nice and simple in one line of code. Maybe something like this:
var myJsonObject = JSON.parse('url': file.json);

Anyways, I’ll try your code out and probably end up using it from now on. I don’t fully understand it yet but seems like it’s pretty solid. Thanks!