2016-10-07 1 views
0

Я программирую веб-приложение, в котором координаты должны быть сохранены в службе AngularJS. Я использую Ionic Framework, JavaScript и HTML.Почему мой массив возвращает «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; 
    } 
}) 

Конкретные строки в моем примере кода являются

console.log(coords); 
console.log(coords[0]); 
console.log(JSON.stringify(coords, null, 4)); 

! (http://imgur.com/a/u33hg)

Это мои возвращаемые значения.

Как я уже сказал, console.log (coords) выводит правильный массив, но если я хочу вызвать console.log (coords [0]), он возвращает «undefined» (то же, что и console.log (JSON.stringify (coords, null, 4));)

Может кто-нибудь объяснить мне эту проблему или может (даже лучше) дать мне решение?

Edit после предложения @Jason ПАМ в:

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

И это результат:

! http://imgur.com/a/A5hAJ

я не мог использовать asyncPush для каждой координаты, так как они были неопределенными снова, так что я просто добавил весь обр массив в вар так что теперь это массив в массиве, и это, кажется, работает. Разумеется, я могу построить обход, чтобы сохранить трехмерный массив в двумерном, но это было то, что вы хотели сделать?

Редактировать: Попытка сохранить 3-тусклый массив в 2-тусклом один возвращает неопределенные переменные снова

Решение Edit: Так что я мог, наконец, решить мою проблему, следуя этому учебник по обещаниям: http://exploringjs.com/es6/ch_promises.html

хитрость в том, чтобы обернуть функцию geocodeAddress с обещанием и вызвать функцию .then на обещание в моем инициализации чтобы заставить две функции вызываться друг за другом. Вот мой код:

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

Во всяком случае, спасибо большое @Jason Cust за помощь в поисках решения для этого без особых знаний JS дал мне огромную головную боль.

многих отношениях Джулиан

+0

Вы можете разместить вывод 'console.log (coords); ' – Hackerman

+0

[] 0: 52.520006599999991: 13.404953999999975length: 2__proto__: Array [0] controlle rs.js: 68 undefined Не хорошо отформатирован, извините. Это пример вывода для двух координат. –

+0

То есть вывод 'console.log (coords);', вы уверены ... Мне просто нужно, пожалуйста, – Hackerman

ответ

2

Это связано с асинхронным характером программы и console.log сама.

Внутри обещания p призыв к функции geocodeAddress асинхронных, что никогда не проверяются, когда он завершает таким образом процесс продолжается до проверки на dataService.getLatLng() возвращает значение (которое он делает в виде пустого массива, который является правдой), которая вызывает выполнение resolve, который, в свою очередь, наконец попадает в функциональный блок, где находятся строки console.log.

Из-за поведения асинхронных из console.log и обращения, используя значение массива, возвращенное из dataService.getLatLng когда console.log(coords) наконец печатает STDOUT он имеет шанс напечатать значение. Но так как console.log(coords[0]) берет непосредственное значение undefined, которое снова будет напечатано в STDOUT. То же самое касается вывода JSON, поскольку эта операция синхронизирована.

По сути то, что код делает сейчас:

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

Проверка, чтобы убедиться, что ждет, прежде чем асинхронные продолжения:

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

+0

Спасибо за ваш ответ! Можете ли вы далее объяснить использование функции asyncPush (a, val, cb)? Я действительно не понимаю, почему он делает то, что он делает, и почему это помогает мне с моей проблемой. –

+0

@JuleWolf 'asyncPush' эквивалентен' geocodeAddress' тем, что оба они используют асинхронный вызов. Предоставляя механизм для того, чтобы узнать, когда операция async завершена (через аргумент 'cb'), код, вызывающий его, будет знать, когда продолжить его работу. –

+0

Спасибо за объяснение. Я попробую его как можно скорее и опубликую результат вашего решения. –

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