Hardstyl3R
Hardstyl3R

Reputation: 23

Google Maps Geocode AngularJS ignores callback

I have got a strange problem with the JavaScript Geocode Api of Google Maps in AngularJS.

First of all I did some researches and I find out that I have to use the javascript callback function to get the formatted address out of a coordinate.

I implemented the callback function like this:

"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);

As you may see I want an array of coordinate objects. But I really don't know why the console.log shows the right result and array is complete empty?

The Firefox console shows the following:

Do you have an idea why the console log is working properly and the array function isn't? If I write the array function outside of the callback function and without address: the array is created.

To be more detailed:

I want to show the formatted addresses of the coordinates in a select drop down box. This is defined in a html file as

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

and i edited the getStartAddress call like the following:

$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);
        });
    }

};

I am really not an expert in javascript but in my understanding the function in getStartAddress is then called when the callback data are fetched? So the select drop down box should be filled with the addresses when the callback is finished, isn't it?

Upvotes: 0

Views: 1043

Answers (2)

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59318

You are dealing with async google.maps.Geocoder.geocode function inside a loop. For that purpose you might want to utilize $q.all which combines a number of promises into one which is only resolved when all the promises are resolved.

In your case let's first introduce a promise for getting an address:

$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;
};

and then you could replace:

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);

with:

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 example

Plunker

Upvotes: 1

Hardstyl3R
Hardstyl3R

Reputation: 23

Okay I solved that problem with a forEach instead of the simple for loop.

So the result is the following:

"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);

Upvotes: 0

Related Questions