0

У меня есть странная проблема с JavaScript Geocode Api из Google Maps в AngularJS.Google Maps Geocode AngularJS игнорирует обратный вызов

Прежде всего, я сделал некоторые исследования, и я выяснил, что мне нужно использовать функцию обратного вызова javascript для получения форматированного адреса из координаты.

Я реализовал функцию обратного вызова, как это:

"use strict"; 

var getParcelApp = angular.module("getParcelApp", []); 

function getStartAddress(latlng, callback) { 
    var geocoder = new google.maps.Geocoder; 

    geocoder.geocode({ 'location': latlng }, function(results, status) { 
     if (status === google.maps.GeocoderStatus.OK) { 
      callback(results[2].formatted_address); // HERE THE CALLBACK 
     } 
    }); 
}; 

var GetParcelAppController = function($scope, $http) { 
    $http.get("/umbraco/surface/ParcelSurface/GetLatLngParcel") 
     .success(function(result) { 
      $scope.coordinates = result; 
      $scope.getStartCoordinate(); 
     }) 
     .error(function(data) { 
      console.log(data); 
     }); 

    $scope.getStartCoordinate = function() { 
     var latlng; 

     $scope.listOfCoordinates = []; 

     for (var i = 0; i < $scope.coordinates.length; i++) { 
      latlng = { lat: $scope.coordinates[i].Latitude, lng: $scope.coordinates[i].Longitude }; 

      getStartAddress(latlng, function(formattedAddress) { 
       console.log(formattedAddress); // HERE formattedAddress WORKS 
       var coordinate = { 
        id: $scope.coordinates[i].Id, 
        waypointId: $scope.coordinates[i].WaypointId, 
        latitude: $scope.coordinates[i].Latitude, 
        longitude: $scope.coordinates[i].Longitude, 
        address: formattedAddress // HERE formattedAddress ISN'T WORKING 
       }; 
       $scope.listOfCoordinates.push(coordinate); 
      }); 
     } 
     console.log($scope.listOfCoordinates); 

    }; 
}; 

getParcelApp.controller("getParcelAppCtrl", GetParcelAppController); 

Как вы можете видеть, что я хочу массив координат объектов. Но я действительно не знаю, почему console.log показывает правильный результат, а массив заполняется пустым?

Консоль Firefox показывает следующее:

  • Массив:
    • Array [] getParcelController.js: 45: 9
  • отформатированные адреса:
    • Mitte, Berlin, Deutschland getParcelController.js: 34: 17
    • Иннерштадт, Кельн, Германия getParcelController.js: 34: 17
    • Альтштадт-Леэль, München, Deutschland getParcelController.js: 34: 17
    • Митте, Berlin, Deutschland getParcelController.js: 34: 17
    • Innenstadt, Köln, Deutschland getParcelController.js: 34: 17

У вас есть идея, почему журнал консоли работает правильно, а функция массива - нет? Если я напишу функцию массива вне функции обратного вызова и без адрес: массив создан.

Чтобы быть более детальными:

Я хочу показать отформатированные адреса координат в выбранном раскрывающемся списке. Это определяется в HTML файл, как

<select id="getParcel" class="form-control"></select> 

и я редактировал getStartAddress вызов вроде следующего:

$scope.getStartCoordinate = function() { 
    var latlng; 
    var selectBox = document.getElementById("getParcel"); 

    $scope.listOfCoordinates = []; 

    for (var i = 0; i < $scope.coordinates.length; i++) { 
     latlng = { lat: $scope.coordinates[i].Latitude, lng: $scope.coordinates[i].Longitude }; 

     getStartAddress(latlng, function(formattedAddress) { 
      var coordinate = { 
       id: $scope.coordinates[i].Id, 
       waypointId: $scope.coordinates[i].WaypointId, 
       latitude: $scope.coordinates[i].Latitude, 
       longitude: $scope.coordinates[i].Longitude, 
       address: formattedAddress 
      }; 
      $scope.listOfCoordinates.push(coordinate); 
      console.log($scope.listOfCoordinates); 

      var selectElement = document.createElement("option"); 
      selectElement.textContent = coordinate.address; 
      selectElement.value = coordinate.address; 

      selectBox.appendChild(selectElement); 
     }); 
    } 

}; 

Я действительно не специалист в JavaScript, но в моем понимании функции в getStartAddress тогда вызывается при получении данных обратного вызова? Таким образом, выпадающее окно выбора должно заполняться адресами, когда обратный вызов завершен, не так ли?

+1

Поскольку 'getStartAddress' является' функция asynchronous' и вы регистрируете '$ scope.listOfCoordinates', прежде чем она получает данные (и толкнул его в массив). Вы должны делать все, что вы делаете внутри обратного вызова. Почему Async JS-программирование «не работает», вероятно, является одним из наиболее распространенных вопросов о SO. – Adam

+0

Я знаю, что geocoder.geocode является асинхронной функцией, потому что я использую функцию обратного вызова в 'getStartAddress'. Но я не ожидал, что 'function (formattedAddress)' также является асинхронным? – Hardstyl3R

+0

'function (formattedAddress)' является обратным вызовом функции async - он запускается только после того, как данные были извлечены. – Adam

ответ

0

Хорошо, я решил эту проблему с помощью forEach вместо простого цикла.

Таким образом, результат состоит в следующем:

"use strict"; 

var getParcelApp = angular.module("getParcelApp", []); 

function getStartAddress(latlng, callback) { 
    var geocoder = new google.maps.Geocoder; 

    geocoder.geocode({ 'location': latlng }, function(results, status) { 
     if (status === google.maps.GeocoderStatus.OK) { 
      callback(results[2].formatted_address); 
     } 
    }); 
}; 

var GetParcelAppController = function($scope, $http) { 
    $http.get("/umbraco/surface/ParcelSurface/GetLatLngParcel") 
     .success(function(result) { 
      $scope.coordinates = result; 
      $scope.getStartCoordinate(); 
     }) 
     .error(function(data) { 
      console.log(data); 
     }); 

    $scope.getStartCoordinate = function() { 
     var latlng; 
     var selectBox = document.getElementById("getParcel"); 

     $scope.listOfCoordinates = []; 

     $scope.coordinates.forEach(function(point) { 
      latlng = { lat: point.Latitude, lng: point.Longitude }; 

      getStartAddress(latlng, function (formattedAddress) { 
       var coordinate = { 
        id: point.Id, 
        waypointId: point.WaypointId, 
        latitude: point.Latitude, 
        longitude: point.Longitude, 
        address: formattedAddress 
       }; 
       $scope.listOfCoordinates.push(coordinate); 

       var selectElement = document.createElement("option"); 
       selectElement.textContent = formattedAddress; 
       selectElement.value = formattedAddress; 

       selectBox.appendChild(selectElement); 
      }); 
     }); 
    }; 
}; 

getParcelApp.controller("getParcelAppCtrl", GetParcelAppController); 
1

Вы имеете дело с асинхронномgoogle.maps.Geocoder.geocode функция внутри цикла. Для этой цели вы можете использовать $q.all, который объединяет ряд обещаний в один, который разрешается только при решении всех обещаний..

В вашем случае давайте первым ввести обещание для получения адреса:

$scope.getStartAddressPromise = function (latlng) { 
    var deferred = $q.defer(); 
    getStartAddress(latlng, function (formattedAddress) { 
     console.log(formattedAddress); // HERE formattedAddress WORKS 
     var coordinate = { 
      id: latlng.Id, 
      waypointId: latlng.WaypointId, 
      latitude: latlng.Latitude, 
      longitude: latlng.Longitude, 
      address: formattedAddress // HERE formattedAddress ISN'T WORKING 
     }; 
     $scope.listOfCoordinates.push(coordinate); 
     deferred.resolve(); 
    }); 
    return deferred.promise; 
}; 

и тогда вы могли бы заменить:

for (var i = 0; i < $scope.coordinates.length; i++) { 
    latlng = { lat: $scope.coordinates[i].Latitude, lng: $scope.coordinates[i].Longitude }; 

    getStartAddress(latlng, function (formattedAddress) { 
     console.log(formattedAddress); // HERE formattedAddress WORKS 
     var coordinate = { 
      //id: $scope.coordinates[i].Id, 
      //waypointId: $scope.coordinates[i].WaypointId, 
      latitude: $scope.coordinates[i].Latitude, 
      longitude: $scope.coordinates[i].Longitude, 
      address: formattedAddress // HERE formattedAddress ISN'T WORKING 
     }; 
     $scope.listOfCoordinates.push(coordinate); 
    }); 
} 
console.log($scope.listOfCoordinates); 

с:

var promises = []; 

$scope.listOfCoordinates = []; 
angular.forEach($scope.coordinates, function (coordinate) { 
    var latlng = { lat: coordinate.Latitude, lng: coordinate.Longitude }; 
    promises.push($scope.getStartAddressPromise(latlng)); 
}); 

$q.all(promises).then(function() { 
    console.log($scope.listOfCoordinates); 
}); 

Complete пример

Plunker

+0

Спасибо, это тоже возможность. Но немного сложнее, чем просто изменить цикл for на функцию forEach. – Hardstyl3R

+1

Отличный пример того, как решить несколько обещаний после завершения - именно то, что я искал. – BatteryAcid

Смежные вопросы