Reputation: 221
I'm programming a Webapplication in which coordinates have to be saved into a AngularJS service. I'm using Ionic Framework, JavaScript and HTML.
My problem is, that the array itself is getting displayed correctly while the single elements get put out as "undefined".
//Controller für die Map und ihre Funktionen
mapApp.controller('MapCtrl', function($scope, $ionicLoading, dataService) {
//Funktion zur Geocodierung von Adressen Eingabe String
var geocodeAddress = function(geocoder, resultsMap) {
//Hole Textfeld Input aus Service
var address = dataService.getAddress();
//Geocode Funktion analog zu Google Maps API Beispiel
geocoder.geocode({'address' : address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resultsMap.setCenter(results[0].geometry.location);
resultsMap.setZoom(14);
dataService.setLatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
var marker = new google.maps.Marker({
map: resultsMap,
position: results[0].geometry.location,
});
//onClick Info Fenster mit Adresse
var infoString = "Dein Startpunkt: <br>" + results[0].formatted_address;
var infoWindow = new google.maps.InfoWindow({
content: infoString
});
marker.addListener("click", function() {
infoWindow.open(resultsMap, marker);
});
}
else {
alert("Ups, da ist etwas schief gegangen ! Error: " + status);
}
});
}
//Funktion zur Initialisierung der Map
//inklusive Geocoding und Erreichbarkeitspolygonen
var initialize = function() {
//Route360° stuff
r360.config.serviceKey = 'KADLJY0DAVQUDEZMYYIM';
r360.config.serviceUrl = 'https://service.route360.net/germany/';
var myLatlng = new google.maps.LatLng(48.383, 10.883);
var mapOptions = {
center: myLatlng,
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
var geocoder = new google.maps.Geocoder();
var p = new Promise(function (resolve, reject) {
geocodeAddress(geocoder, map);
if(dataService.getLatLng()) {
resolve("Success!");
}
else {
reject("Failure!");
}
});
//console.log(dataService.getLatLng());
p.then(function() {
//console.log(dataService.getLatLng());
var coords = dataService.getLatLng();
console.log(coords);
console.log(coords[0]);
console.log(JSON.stringify(coords, null, 4));
var time = dataService.getTime();
var move = dataService.getMove();
var colorPolygonLayer = new GoogleMapsPolygonLayer(map);
showPolygons(48.4010822, 9.987607600000047, colorPolygonLayer, time, move);
});
$scope.map = map;
}
ionic.Platform.ready(initialize);
});
//Funtkion zum Erstellen und Anzeigen der Erreichbarkeitspolygone
var showPolygons = function(lat, lng, polygonLayer, time, move) {
//Setzen der Optionen für die Berechnung der Polygone
var travelOptions = r360.travelOptions();
//Lat-Lng bestimmen
travelOptions.addSource({ lat : lat, lng : lng });
//Zeitintervalle bestimmen
travelOptions.setTravelTimes(time*60);
//Fortbewegungsmittel bestimmen
travelOptions.setTravelType(move);
r360.PolygonService.getTravelTimePolygons(travelOptions, function(polygons) {
polygonLayer.update(polygons);
});
}
//Controller für die Daten
//Eigentlich nur Daten in Service schreiben onClick
mapApp.controller("DataCtrl", function($scope, dataService) {
$scope.setData = function() {
var address = document.getElementById("address").value;
var time = document.getElementById("time").value;
var move = $scope.move;
dataService.setData(address,time,move);
};
});
//Service um Daten zwischen Controllern zu benutzen
mapApp.service("dataService", function() {
var address;
var time;
var move;
var latlng = [];
//Adresse, Zeit und Fortbewegungsmittel setzen
this.setData = function(passed_address, passed_time, passed_move) {
address = passed_address;
time = passed_time;
move = passed_move
};
this.setLatLng = function (lat, lng) {
latlng.push(lat);
latlng.push(lng);
};
//Getter
this.getAddress = function() {
return address;
};
this.getTime = function() {
return time;
};
this.getMove = function () {
return move;
};
this.getLatLng = function(){
return latlng;
}
})
The particular lines in my code sample are
console.log(coords);
console.log(coords[0]);
console.log(JSON.stringify(coords, null, 4));
!(https://i.sstatic.net/w0pUI.jpg)
Those are my return values.
As I said, console.log(coords) prints out the correct array but if I want to call console.log(coords[0]) it returns "undefined" (same as console.log(JSON.stringify(coords,null,4));)
Can someone explain me that issue or can (even better) give me a solution to it ?
Edit after @Jason Cust's suggestion:
var arr = [];
var p = new Promise(function (resolve, reject) {
geocodeAddress(geocoder, map);
asyncPush(arr, dataService.getLatLng(), resolve);
});
p.then(function() {
var a = getArr();
console.log(a);
console.log(a[0]);
var time = dataService.getTime();
var move = dataService.getMove();
var colorPolygonLayer = new GoogleMapsPolygonLayer(map);
showPolygons(48.4010822, 9.987607600000047, colorPolygonLayer, time, move);
});
function asyncPush(a, val, cb) {
setTimeout(function() {
a.push(val);
cb();
} , 0);
}
function getArr() {return arr; }
And this is the result:
! https://i.sstatic.net/oAhYq.jpg
I could not use asyncPush for each coordinate since they were undefined again, so I just added the entire arr Array to var a so now it is a array in an array and it seems to work. I can of course build a work-around to save the 3-dimensional array in a 2-dimensional one, but was that what you meant to do ?
Edit: Trying to save the 3-dim array into a 2-dim one returns undefined variables again
Solution Edit: So I could finally solve my problem by following this tutorial about promises: http://exploringjs.com/es6/ch_promises.html
The trick was to wrap the geocodeAddress function with a promise and call the .then function on the promise in my initialize function to make the two functions get called after each other. Here is my code:
var geocodeAddress = function(geocoder, resultsMap) {
return new Promise(function(resolve, reject) {
//Hole Textfeld Input aus Service
var address = dataService.getAddress();
//Geocode Funktion analog zu Google Maps API Beispiel
geocoder.geocode({'address' : address}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resultsMap.setCenter(results[0].geometry.location);
resultsMap.setZoom(14);
dataService.setLatLng(results[0].geometry.location.lat(), results[0].geometry.location.lng());
var marker = new google.maps.Marker({
map: resultsMap,
position: results[0].geometry.location,
});
//onClick Info Fenster mit Adresse
var infoString = "Dein Startpunkt: <br>" + results[0].formatted_address;
var infoWindow = new google.maps.InfoWindow({
content: infoString
});
marker.addListener("click", function() {
infoWindow.open(resultsMap, marker);
});
}
if(dataService.getLatLng()) {
resolve("Success");
}
else {
alert("Ups, da ist etwas schief gegangen ! Error: " + status);
reject("Failed");
}
});
});
}
//Funktion zur Initialisierung der Map
//inklusive Geocoding und Erreichbarkeitspolygonen
var initialize = function() {
//Route360° stuff
r360.config.serviceKey = 'KADLJY0DAVQUDEZMYYIM';
r360.config.serviceUrl = 'https://service.route360.net/germany/';
var myLatlng = new google.maps.LatLng(48.383, 10.883);
var mapOptions = {
center: myLatlng,
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map"), mapOptions);
var geocoder = new google.maps.Geocoder();
geocodeAddress(geocoder, map)
.then(function() {
var coords = dataService.getLatLng();
var time = dataService.getTime();
var move = dataService.getMove();
var colorPolygonLayer = new GoogleMapsPolygonLayer(map);
showPolygons(coords[0], coords[1], colorPolygonLayer, time, move);
});
$scope.map = map;
}
ionic.Platform.ready(initialize);
});
Anyway, thank you very much @Jason Cust for your help, searching a solution for this without much JS knowledge gave me a huge headache.
Many regards, Julian
Upvotes: 3
Views: 11164
Reputation: 10899
This is due to the async nature of the program and for console.log
itself.
Inside the promise p
is a call to an async function geocodeAddress
that is never checked for when it completes so the process continues on to the check for dataService.getLatLng()
returning a value (which it does in the form of the empty array, which is truthy) which causes the resolve
to be executed which in turn finally gets into the function block where the console.log
lines are.
Due to the async behavior of console.log
and the reference of using the array value returned from dataService.getLatLng
when console.log(coords)
finally prints to STDOUT
it has the chance to print the values. But since console.log(coords[0])
grabs the immediate value it is undefined
, which again is later printed to STDOUT. The same goes for the JSON
output since that operation is sync.
Essentially what the code does now:
var arr = [];
var p = new Promise(function(resolve, reject) {
asyncPush(arr, 'foo');
if (getArr()) {
resolve();
}
});
p.then(function() {
var a = getArr();
console.log(a);
console.log(a[0]);
console.log(JSON.stringify(a, null, 2));
});
function asyncPush(a, val) {
setTimeout(function() { a.push(val) }, 0);
}
function getArr() { return arr; }
Checking to make sure the async waits before continuing:
var arr = [];
var p = new Promise(function(resolve, reject) {
asyncPush(arr, 'foo', resolve);
});
p.then(function() {
var a = getArr();
console.log(a);
console.log(a[0]);
console.log(JSON.stringify(a, null, 2));
});
function asyncPush(a, val, cb) {
setTimeout(function() { a.push(val); cb(); }, 0);
}
function getArr() { return arr; }
Upvotes: 3