Cant define data. why is this? - javascript

I'm working on a small funny bit of code and i'm stuck.
on line 66 in the JS file. it cant define data... it is in the scope right?
please help me.
http://codepen.io/shiva112/pen/JGXoVJ
function PlaceMarkers(data){
for (i = 0; i < data.length - 1; ++i) {
if (data[i].country == "NL"){
map.addMarker({
lat: data[i].lat,
lng: data[i].lng,
title: 'Marker with InfoWindow',
infoWindow: {content: "Station: " + data[i].name},
click: function(e) {
$("#info h2").remove();
$("#info").append( "<h2>U hebt station " + data[i].name + " geselecteerd.</h2>" );
}
});
}
}
the append line is not working. data[i].name 2 lines above is in the scope!! but why not the data[i].name in the append line..

data[i].name in the click handler isn't evaluated until after the for loop has finished, and the values have changed. You can create an immediately invoked function expression to get around this:
function PlaceMarkers(data) {
for (i = 0; i < data.length; ++i) {
if (data[i].country == "NL") {
(function(station) {
map.addMarker({
lat: station.lat,
lng: station.lng,
title: 'Marker with InfoWindow',
infoWindow: {
content: "Station: " + station.name
},
click: function(e) {
$("#info h2").remove();
$("#info").append("<h2>U hebt station " + station.name + " geselecteerd.</h2>");
}
});
})(data[i]);
}
}
}
http://codepen.io/anon/pen/obxzmG

Like #JasonP said, in the click handler i isn't evaluated until after the for loop has finished.
You could set a custom key name and then access it by using this.name like this:
map.addMarker({
lat: data[i].lat,
lng: data[i].lng,
name: data[i].name,
title: 'Marker with InfoWindow',
infoWindow: {content: "Station: " + data[i].name},
click: function(e) {
$("#info h2").remove();
$("#info").append( "<h2>U hebt station " + this.name + " geselecteerd.</h2>" );
}
});
http://codepen.io/pespantelis/pen/LGNRgL

Related

JavaScript for loop inside async function

I am running into promise error inside for loop which is inside async function.
var circles=[];
async function displayMarkersOnGoogleMap(locations) {
try {
for (i = 0; i < locations.length; i++) {
var latlong = new google.maps.LatLng(locations[i].latitude, locations[i].longitude);
var marker = new google.maps.Marker({
position: latlong,
title: ""
icon: {
url: "https://maps.google.com/mapfiles/ms/icons/red-dot.png",
labelOrigin: { x: 12, y: -10 }
},
map: map,
label: {
text: "",
color: "red",
fontWeight: "bold",
fontsize:"16px"
}
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
console.warn("Business Marker clicked");
var distanceInMeters = google.maps.geometry.spherical.computeDistanceBetween(
currentPosMarker.getPosition(),
marker.getPosition()
);
try {
circles.forEach(async (circle) => {
await circle.setMap(null);
})
} catch (e) {
}
if (i == undefined)
i = 0;
setTimeout(() => {
var circle = drawCircle(map, latlong, locations[i].rangeLimitInMeters);
circles.push(circle);
}, 5000);
var content = "<span style='font-weight: bold;'>" + locations[i].locationName + "</span>"
content = content + "<br/> " + locations[i].address + "<br/> " + locations[i].city + ", " + locations[i].state;
if (locations[i].locationOpenStatus == "CLOSE") {
content = content + "<br/><span class='badge badge-danger'>" + locations[i].locationOpenStatus + "</span>";
}
else {
content = content + "<br/><span class='badge badge-success'>" + locations[i].locationOpenStatus + "</span>";
}
content = content + "<br/><span style='font-weight: bold;'> Time : </span> " + locations[i].locationStartTime + " To " + locations[i].locationStopTime;
infowindow.marker = marker;
infowindow.setContent(content);
infowindow.open(map, marker);
}
})(marker, i));
markersArrray.push(marker);
}
} catch (e) {
console.error("Javascript:displayMarkersOnGoogleMap:-" + e.errorMessage );
}
}
How do I get around this ?
All I am trying to do is clear all the previous Circles that I might have drawn , before drawing the new one.
circles.forEach(async (circle) => {
await circle.setMap(null);
})
This right here, wont work as you expect it to. forEach loop doesn't work with async callbacks. Even if you explicitly mark your callback function as async, forEach loop wont wait for it to complete the promise.
Instead try with a for...of loop.This would execute whatever is inside synchronously, in the order you normally read the code.
for(const circle of circles) {
await circle.setMap(null);
}

Ionic 2 click event inside infowindow googlemaps

Hi i'm trying to fire an event from a button inside a google maps infowindow.
the problem is that i canĀ“t make it fire by any mean.
addMarket(latitud, longitud, sensorData) {
let marker = new google.maps.Marker({
draggable: false,
position: { lat: latitud, lng: longitud },
map: this.map,//set map created here
title: sensorData.sensor
});
marker.addListener('click', function () {
infowindow.open(this.map, marker);
});
let bodyMessege = 'Nombre del Sensor: ' + sensorData.sensor + '<br>' +
'Componente Descripcion: ' + sensorData.componentDesc + '<br>' +
' description: ' + sensorData.description + '<br>' +
' Tipo de sensor: ' + sensorData.type + '<br>' +
' Unidad de sensor: ' + sensorData.unit + '<br>' +
'Tipo de dato: ' + sensorData.dataType + '<br>' +
' <button ion-button (click)="this.navCtrl.push(DetalleSensorPage)" >Default</button>';
// ' <button ion-button="" onclick=console.log("log");' anda
//this.pushPage=DetalleSensorPage;
var infowindow = new google.maps.InfoWindow({
content: bodyMessege
});
Any tip will be welcome thanks. :)
I have found this solution for my own project: ( https://forum.ionicframework.com/t/ng-click-in-google-maps-infowindow/5537/6)
The key element is the id="tap" that you look for at the getElementbyId below.
let content = '<div class="infowindow"><p id="tap">your text here</p></div>';
let infoWindow = new google.maps.InfoWindow(
{
closeBoxURL: "",
content: content
}
);
infoWindow.open(this.map, marker);
google.maps.event.addListenerOnce(infoWindow, 'domready', () => {
document.getElementById('tap').addEventListener('click', () => {
//alert('Clicked');
console.log("touch");
this.closeInfoViewWindow(infoWindow);
this.openEventDetailModal(event);
});
});
hope this will help you and others.

Google Map Geocoded TypeError in Callback function

I have the following 2 functions to pull in, geocode, and place markers in a google map.
I keep getting a TypeError: adds[i] is undefined, which of course is causing the rest of the map to bomb.
Here is my code:
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var image = {url: "http://meatmysite.com/Images/star2.png", size: new google.maps.Size(24, 24)};
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i].address, function(pos) {
if(pos) {
var ipop = '<h1>' + adds[i].title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(adds[i].url)){
ipop += '' + adds[i].url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + adds[i].content + '</div>';
if(!isBlank(adds[i].mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + adds[i].mainphone + '';
}
if(!isBlank(adds[i].mainemail)){
ipop += '<br /><strong>Email:</strong> ' + adds[i].mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: adds[i].title, position: pos, map: map, icon: image, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
DEMO RETURNED DATA FOR adds
[
{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
},
{
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}
]
One option, pass the complete "address" object into the GetLatLng function, and from there into its callback (so you get function closure on it):
// Get Lat/Lng Location
var GetLatLng = function (gc, add, f) {
gc.geocode({
'address': add.address
}, function (res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
Then use it like this inside the callback (you could pass just the index into the array also):
GetLatLng(gc, adds[i], function (pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>';
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
icon: image,
html: ipop
});
google.maps.event.addListener(mark, 'click', function () {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
proof of concept fiddle
code snippet:
var geocoder = new google.maps.Geocoder();
var map;
var infoWindow = new google.maps.InfoWindow();
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
PlaceMarkers(infoWindow, adds, geocoder);
}
google.maps.event.addDomListener(window, "load", initialize);
// Place Markers on the Map
var PlaceMarkers = function(iw, adds, gc) {
var bounds = new google.maps.LatLngBounds();
var image = {
url: "http://meatmysite.com/Images/star2.png",
size: new google.maps.Size(24, 24)
};
var aCt = adds.length;
for (var i = 0; i < aCt; ++i) {
GetLatLng(gc, adds[i], function(pos, add) {
if (pos) {
var ipop = '<h1>' + add.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if (!isBlank(add.url)) {
ipop += '' + add.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content' + i + '">' + add.content + '</div>';
if (!isBlank(add.mainphone)) {
ipop += '<br /><strong>Phone:</strong> ' + add.mainphone + '';
}
if (!isBlank(add.mainemail)) {
ipop += '<br /><strong>Email:</strong> ' + add.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({
title: add.title,
position: pos,
map: map,
// icon: image,
html: ipop
});
bounds.extend(mark.getPosition());
map.fitBounds(bounds);
google.maps.event.addListener(mark, 'click', function() {
iw.setContent(this.html);
iw.open(map, this);
});
}
});
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
gc.geocode({
'address': add.address
}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
}
});
};
var adds = [{
"address": "1 My Street Gilbert, AZ 85234",
"title": "My Title 1",
"url": "http://www.myurl.com/",
"mainphone": null,
"mainemail": null,
"content": "1 My Street<br />Gilbert, AZ 85234"
}, {
"address": "2 My Street North Richland Hills, TX 76182",
"title": "My Title 2",
"url": null,
"mainphone": null,
"mainemail": null,
"content": "2 My Street<br />North Richland Hills, TX 76182"
}];
function isBlank(str) {
return (!str || /^\s*$/.test(str));
}
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas"></div>
This looks like a typical binding issue. By the time your callback is called, the value of adds[i] will have changed. It is likely that the loop terminated and i has now a value of last index + 1, which is pointing to nothing. Note that it could also point to the wrong index, that would not fail but use the wrong data.
You must bind the value of adds[i] locally for each iteration or the callback will just use a reference to a global value. There a multiple ways to go about this, here is a simple one where we keep passing adds[i] along as a function argument.
Replace adds[i].address with adds[i] when calling GetLatLng and add a second parameter add to the callback:
GetLatLng(gc, adds[i], function(pos, add) {
...
});
Then modify GetLatLng to use add.address instead of just add and add add to the callback call:
// Get Lat/Lng Location
var GetLatLng = function(gc, add, f) {
var ret = '';
gc.geocode({'address': add.address}, function(res, status) {
if (status == 'OK') {
f(res[0].geometry.location, add);
console.log('Found Here: ' + ret.toString());
}
});
return -1;
};
Then in the callback function, replace all instances of adds[i] with add to use the local variable.
I didn't set up a test but it should theoretically work.
you appear to be overcomplicating things. Any reason why you can't do this?
// Place Markers on the Map
var PlaceMarkers = function (iw, adds, gc) {
var aCt = adds.length;
for(var i = 0; i < aCt; ++i) {
var obj=adds[i];
GetLatLng(gc, obj)
}
};
// Get Lat/Lng Location
var GetLatLng = function(gc, obj) {
var ret = '';
gc.geocode({'address': obj.address}, function(res, status) {
if (status == 'OK') {
var pos=res[0].geometry.location;
var ipop = '<h1>' + obj.title + '</h1>'; // <----- TypeError: adds[i] is undefined
if(!isBlank(obj.url)){
ipop += '' + obj.url + '<br />';
}
ipop += '<div class="map_item_content" id="mi_content">' + obj.content + '</div>';
if(!isBlank(obj.mainphone)){
ipop += '<br /><strong>Phone:</strong> ' + obj.mainphone + '';
}
if(!isBlank(obj.mainemail)){
ipop += '<br /><strong>Email:</strong> ' + obj.mainemail + '';
}
console.log('HEY NOW: ' + pos.toString() + ' - Location Found!');
var mark = new google.maps.Marker({title: obj.title, position: pos, map: map, html: ipop});
google.maps.event.addListener(mark, 'click', function(){
iw.setContent(this.html);
iw.open(map, this);
});
} else {
console.log("geocoder problem!")
}
});
};
for(var i = 0; i < aCt - 1; ++i). You need to add "-1" in you for-loop. The array starts at index 0 and not 1. You also need to be careful with using functions in a for loop. Within javascript a for-loop does not have a scope from itself. Only functions create new scopes.

jquery string value not getting passed into google map object inside loop

Why is the string lost inside the object within a loop?
for (var i = 0; i < nrow.length - 1; i++) {
displayNote = "<b>" + nfield[0] + "</b><br />" + nfield[1] + " " + nfield[2] + "<br /> " + nfield[7];
$('#googleMap').gmap3({
action: 'addMarker',
lat: parseFloat(nfield[5]),
lng: parseFloat(nfield[6]),
events: {
mouseover: function (marker, event) {
var map = $(this).gmap3('get'),
infowindow = $(this).gmap3({ action: 'get', name: 'infowindow' });
if (infowindow) {
infowindow.open(map, marker);
infowindow.setContent(displayNote);
displayNote only displays the first increment for all the other infowindow
at the end of for loop execution displayNote will contain last value. And InfoWindow will show last displayNote on mouseover.
You can save displayNote for each iteration by creating new function
function attachEvent( displayNote, nfield ){
$('#googleMap').gmap3({
action: 'addMarker',
lat: parseFloat(nfield[5]),
lng: parseFloat(nfield[6]),
events: {
mouseover: function (marker, event) {
var map = $(this).gmap3('get'),
infowindow = $(this).gmap3({ action: 'get', name: 'infowindow' });
if (infowindow) {
infowindow.open(map, marker);
infowindow.setContent(displayNote);
}
for (var i = 0; i < nrow.length - 1; i++) {
displayNote = "<b>" + nfield[0] + "</b><br />" + nfield[1] + " " + nfield[2] + "<br /> " + nfield[7];
attachEvent( displayNote, nfield );
}

Problems with javascript loop adding GMarker to GMap

I am having problems adding GMarkers when using a loop. The best way to explain the problem is to show the code, I guess :)
This works:
htmls[0] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[0].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[0].Username + "</strong> (" + result[1].Age + ")<br/>" + result[0].Country + "<br/>" + result[0].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[0] = new GLatLng(result[0].Latitude, result[0].Longitude);
if (result[0].Gender == "F") {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: fIcon });
} else {
markers[0] = new GMarker(latlngs[0], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[0], "click", function () {
markers[0].openInfoWindowHtml(htmls[0]);
});
map.addOverlay(markers[0]);
htmls[1] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[1].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[1].Username + "</strong> (" + result[1].Age + ")<br/>" + result[1].Country + "<br/>" + result[1].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[1] = new GLatLng(result[1].Latitude, result[1].Longitude);
if (result[1].Gender == "F") {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: fIcon });
} else {
markers[1] = new GMarker(latlngs[1], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[1], "click", function () {
markers[1].openInfoWindowHtml(htmls[1]);
});
map.addOverlay(markers[1]);
But when I put it in a loop, it doesn't work...
for (i = 0; i < result.length; i++) {
htmls[i] = "<div style=\"margin-bottom:10px; \"><table><tr><td><img src=\"" + result[i].UserImageURI + "\" width=\"80\" height=\"80\" /></td><td style=\"vertical-align:top; \"><strong>" + result[i].Username + "</strong> (" + result[i].Age + ")<br/>" + result[i].Country + "<br/>" + result[i].Distance + " KMs away<br/>View Profile</td></tr></table></div>";
latlngs[i] = new GLatLng(result[i].Latitude, result[i].Longitude);
if (result[i].Gender == "F") {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: fIcon });
} else {
markers[i] = new GMarker(latlngs[i], { draggable: false, icon: mIcon });
}
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
map.addOverlay(markers[i]);
}
When using the loop, clicking on a marker breaks the script. It points to the line
markers[i].openInfoWindowHtml(htmls[i]);
And says that object is undefined. It also says that i = 10 at that point which is "impossible" as results.length is only 10
The problem is the classic function-in-a-loop. Here's one of the two typical ways to fix it:
function callback(i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
};
}
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", callback(i));
// snip...
}
JSLint can easily catch these common errors.
Edit
#Alex's answer shows roughly the other typical way that this problem is fixed, but with a few errors. This should work, though:
for (i = 0; i < result.length; i++) {
// snip...
GEvent.addListener(markers[i], "click", (function (i) {
return function () {
markers[i].openInfoWindowHtml(htmls[i]);
}
})(i));
// snip...
}
In this piece of code...
GEvent.addListener(markers[i], "click", function () {
markers[i].openInfoWindowHtml(htmls[i]);
});
...the function has closure to the i in the parent scope. So it is accessing the variable itself, not a copy of it.
At the end of the loop, when your function accesses the i variable, it will be equal to whatever condition stopped the loop, 10 in your example.
You can fix it with a self invoking anonymous function which passes the value to a new variable with a limited lifespan...
(function(j) {
GEvent.addListener(markers[j], "click", function () {
markers[j].openInfoWindowHtml(htmls[j]);
});
})(i);
Here is an example of similar code working.

Categories

Resources