views:

438

answers:

5

Here is my issue. I have an array containing the name of cities that I need to lookup the weather for. So I'm looping through each city and performing an AJAX request to retrieve the weather.

var LOCATION = 'http://www.google.com/ig/api?weather=';

$( document ).ready( function() {
    for( var cityIdx = 0; cityIdx < cities.length; cityIdx++ ) {
     $.ajax({
      type: 'GET',
      url: LOCATION + cities[ cityIdx ],
      dataType: 'xml',
      success: function( xml ) {
       if( $( xml ).find( 'problem_cause' ) != 0 ) {
        // Do what I want with the data returned
                    var weather = $( xml ).find( 'temp_c' ).attr( 'data' );
       }
      }
     });
    }
});

The issue I'm encountering is that in the success function, I can't access the city name (via cities[cityIdx]). I inserted an alert() in the for loop and the success function and it seems like the loop gets executed cities.length times, then I get the success function alerts. My goal is to simply loop through each city getting the weather and showing it on my page along with the associated city name.

Also, what would you suggest I should do to separate content with presentation?

Thank you. :)

+1  A: 

The simplest thing to do would have your AJAX request return the name of the city.

Adam Peck
+1  A: 

For a variety of reasons I would try pulling out the success function into a separately defined function, and then create a closure to it within the ajax call that includes the city name. So first, a separate success handler:

function city_success(xml, name) {
  // your success handling code here
}

And then change the success binding in the ajax call:

success: function (xml) { city_success(xml, cities[ cityIdx ]); },
nezroy
+4  A: 

I suspect that your problem is similar to the example at http://ejohn.org/apps/learn/. The index variable cityIdx is updated in the closure you create as the for loop is processed, so by the time your function on success is run cityIdx will point to the last element in the array. The solution is to use an evaluated anonymous function to create an independent context, where the index value doesn't get updated.

//...
success: (function(cities, idx) {
    return function( xml ) {
      if( $( xml ).find( 'problem_cause' ) != 0 ) {
        // Do what I want with the data returned
        // use cities[idx]
        var weather = $( xml ).find( 'temp_c' ).attr( 'data' );
      }
    };
  })(cities, cityIdx)
//...
bandi
+3  A: 

Since Javascript uses functions for closure, I found the simplest way for me was to just wrap the contents of the for loop in an inline function that copies the current city name to a variable it will always have access to.

$(document).ready(function() {
    for (var cityIdx = 0; cityIdx < cities.length; cityIdx++) {
        new function() {
            var currentCity = cities[cityIdx];
            $.ajax({
                type: 'GET',
                url: LOCATION + currentCity,
                dataType: 'xml',
                success: function(xml) {
                    alert(currentCity);
                    if ($(xml).find('problem_cause') != 0) {
                        // Do what I want with the data returned
                        var weather = $(xml).find('temp_c').attr('data');
                    }
                }
            });
        }(); // the "();" calls the function we just created inline
    }
});
patridge
Just about to post the same answer... good call
Pablo Fernandez
i think you should remove the 'new'
Javier
Trying to remove the "new" causes a syntax error. I believe it is because you are creating an instance of the function for immediate use, not passing it to the .ready() function. I think the .ready() function puts the passed function object into a queue for it to instantiate and execute later.
patridge
+3  A: 

Why not use jQuery to iterate through your array? Use jQuery's each function:

    var LOCATION = 'http://www.google.com/ig/api?weather=';

$( document ).ready( function() {

    $.each(cities, function()  {
 //assign the current city name to a variable
     var city = this;
        $.ajax({
                type: 'GET',
                url: LOCATION + city,
                dataType: 'xml',
                success: function( xml ) {
                    if( $( xml ).find( 'problem_cause' ) != 0 ) {
         alert(city);
          // Do what I want with the data returned
         var weather = $( xml ).find( 'temp_c' ).attr( 'data' ); 
                    }
                }
        });
    });
});

The alert in the success function displays the correct city.

Ben Koehler
Thanks for that. I've updated my function to use the for each. :)
Mike