Pink Jazz
Pink Jazz

Reputation: 802

AngularJS REST calls - Property 'name' is undefined

I am making calls to the OpenWeatherMap API like this:

$scope.cities = [
    {name: 'Phoenix,US'},
    {name: 'Atlanta,US'},
    {name: 'Honolulu,US'}
];

$scope.reload = function() {
    $scope.items = [];
    for (var i = 0; i < $scope.cities.length; i++) {
        delete $http.defaults.headers.common['X-Requested-With'];
        $http.get('http://api.openweathermap.org/data/2.5/weather?q=' + $scope.cities[i].name).success(function(data, status, headers, config) {
            $scope. items.push(data);
        });
    }
$scope.name0 = $scope.items[0].name //error occurs here
};

I am getting the error "Property 'name' is undefined. I don't know why this error is occurring, since 'name' is a valid id in the OpenWeatherMap API.

Upvotes: 2

Views: 841

Answers (3)

Marc Kline
Marc Kline

Reputation: 9409

I started on this before @NitishKumar posted their answer. Mine is similar in many respects, although they use a factory and Array.map(), which is probably better form overall. Meanwhile, I kept everything in the controller as you had it and used angular.forEach(). They also help you handle returned errors, which I didn't do in my code.

I still provide my answer (along with this Plunk) in case looking at your problem along with a few different solutions is helpful to you.

$scope.cities = [
  {name: 'Phoenix,US'},
  {name: 'Atlanta,US'},
  {name: 'Honolulu,US'}
];

$scope.reload = function() {

  var promises = [];
  $scope.items = [];

  angular.forEach($scope.cities, function(city) {
      var deferred = $q.defer();

      delete $http.defaults.headers.common['X-Requested-With'];
      $http.get('http://api.openweathermap.org/data/2.5/weather?q=' + city.name).success(function(data, status, headers, config) {
          deferred.resolve(data);
      }); 

      promises.push(deferred.promise);
  });

  return $q.all(promises);

}; 

$scope.reload().then(function(data){
  $scope.name0 = data[0].name;
}); 

As mentioned in my initial comment on your question, your problem was that you were trying to set a scope variable to a value that had yet to be defined due to the asynchronous nature of AJAX requests / responses.

A good solution is to use promises, which help you to defer the setting of the scope variable until all AJAX requests have returned. AngularJS comes packaged with "a promise/deferred implementation inspired by Kris Kowal's Q", and the $q docs are worth a read.

Upvotes: 1

khakiout
khakiout

Reputation: 2400

The error occurs because the $scope.items has not yet been populated. You can fix this by moving the assignment class inside the $http.get function.

$scope.reload = function() {
    $scope.items = [];

    for (var i = 0; i < $scope.cities.length; i++) {
       delete $http.defaults.headers.common['X-Requested-With'];
       $http.get('http://api.openweathermap.org/data/2.5/weather?q=' + $scope.cities[i].name).
            success(function(data, status, headers, config) {
                $scope.items.push(data);
                $scope.name0 = $scope.items[0].name //move this inside the $http.get
            }
        );
     }
};

Upvotes: 0

Nitish Kumar
Nitish Kumar

Reputation: 4870

First Create a Factory for $http Request

  myApp.factory('cityData',function($q,$http){
      return function(cities){

        var promises = cities.map( function(city){
        var deffered  = $q.defer();

       delete $http.defaults.headers.common['X-Requested-With'];

       $http({
         url : 'http://api.openweathermap.org/data/2.5/weather?q=' + city.name,
         method: 'GET'
       })
       .success(function(data){
          deffered.resolve(data);
       }).
       error(function(error){
          deffered.reject();
       });

       return deffered.promise;

     });

    return $q.all(promises);
  }

});

Then Call it in Controller:

 cityData($scope.cities).then(function(datas){
    $scope.name0 = datas[0].name;
    console.log($scope.name0);
 });

See Demo Here

Upvotes: 3

Related Questions