How to set array value as function parameter, when iterating - javascript

I use google maps API to display markers on a map.
Each marker points to a shop on the map.
My problem is with creating the event handler for each mark, which will display the tooltip with information about the shop when the given mark will be clicked.
I use the text input to let the user type the city, and with jquery I handle the click event on the nearby submit button. Then I send the query to the php site, get following object back:
d: Object
error: false
lat: "52.3744440000"
lng: "9.7386110000"
shop: Array[2]
0: Object
address: "Addressstreet 12"
lat: "52.3761209000"
lng: "9.7387242000"
name: "Shop 1"
tel: "1234"
__proto__: Object
1: Object
length: 2
etc.
Now I use the lat and lng values which are coordinates of the city to center the map.
Then I send the shop array to the paintShops(shops) method:
function paintShops(shops){
for (var i = 0; i < shops.length; i++){
kl = shops[i];
var ka = parseFloat(kl.lat);
var kb = parseFloat(kl.lng);
tel = kl.tel;
address = kl.address;
var son = new google.maps.Marker({
position: new google.maps.LatLng(ka, kb),
map: map,
title: kl.name,
clickable: true
});
google.maps.event.addListener(son, 'click', function(son, tel, address) {
displayShopTooltip(son, tel, address);
}
My problem is in the last line of the above snippet: when I click on the mark, the parameters tel and address of the displayShopTooltip are undefined.
I'd like to know what to do to do not have this problem, or alternatively another approach.
Thanks in advance.

The problem you have is that by declaring tel and address within the listener you are setting them to undefined within the event handler callback (as there is no mechanism for passing in arbitrary parameters). The solution is to make sure the handler is within the same scope as (i.e. wrapped inside a function with) the marker. I'd recommend reading up about scope in javascript as there were one or two other errors you made (e.g. not properly declaring variables) - scope in javascript is handled very differently to other programming languages and takes a bit of getting used to.
function paintShops(shops){
for (var i = 0; i < shops.length; i++){
makeMarker(shops[i]);
}
function makeMarker(kl) {
var ka = parseFloat(kl.lat);
var kb = parseFloat(kl.lng);
var tel = kl.tel;
var address = kl.address;
var son = new google.maps.Marker({
position: new google.maps.LatLng(ka, kb),
map: map,
title: kl.name,
clickable: true
});
google.maps.event.addListener(son, 'click', function(son) {
displayShopTooltip(son, tel, address);
}
}

You have defined tel and address as parameters of your click handler, which doesn't make sense. You probably wanted to access your global variables instead:
google.maps.event.addListener(son, 'click', function(son) {
displayShopTooltip(son, tel, address);
}
Still, this way there can be only one shop (there is only one global tel and address). Use local variables and a closure instead:
function paintShops(shops) {
for (var i = 0; i < shops.length; i++) {
(function() {
var kl = shops[i];
var ka = parseFloat(kl.lat);
var kb = parseFloat(kl.lng);
var tel = kl.tel;
var address = kl.address;
var son = new google.maps.Marker({
position: new google.maps.LatLng(ka, kb),
map: map,
title: kl.name,
clickable: true
});
google.maps.event.addListener(son, 'click', function(son) {
displayShopTooltip(son, tel, address);
}
})();
}
}

The $.each() function is not the same as $(selector).each(), which is used to iterate, exclusively, over a jQuery object. The $.each() function can be used to iterate over any collection, whether it is a map (JavaScript object) or an array. In the case of an array, the callback is passed an array index and a corresponding array value each time. (The value can also be accessed through the this keyword, but Javascript will always wrap the this value as an Object even if it is a simple string or number value.) The method returns its first argument, the object that was iterated.

Related

Why can't I access variable inside a nested function?

So I have been using the geolocation to find the user location and then find the distance to it and a csv list of locations. I want to save the distance to the json object, but can't access it in my nested function.
function onLocationFound(e) {
console.log(e);
L.marker([e.latitude, e.longitude], {icon: home_marker}).bindPopup("You are here!").addTo(m);
console.log(typeof(e.latitude));
console.log(typeof(e.longitude));
$.get('vac_sites.csv', function (csvString) {
var data = Papa.parse(csvString, { header: true, dynamicTyping: true }).data;
console.log(data)
for (let i = 0; i < data.length; i++) {
//get distance and then use dynamic variable names, make them into js objects, then store in dic with distance
var row = data[i];
console.log(row)
var myRoute = L.Routing.osrmv1({
serviceUrl: 'https://xxx.xxx.xxx:443/route/v1'
});
var self_loc = new L.Routing.Waypoint;
self_loc.latLng = L.latLng([e.latitude, e.longitude]);
var site_loc = new L.Routing.Waypoint;
site_loc.latLng = L.latLng([row.Latitude, row.Longitude]);
myRoute.route([self_loc, site_loc], function(err, routes) {
distance = routes[0].summary.totalDistance;
console.log('routing distance: ' + distance);
row.distance = distance
console.log(row)
});
When I open the console, it appears to have created a new json object and added row to it.
How can I access the original row variable and add the distance to it? Is it a problem with function scope?
EDIT:
When I open console I get this for the first console.log(row):
...
{Name: "Cate Pharmacy", Address: "500 N Missouri Ave, Corning, Arkansas", Telephone: "(870) 857-6766", Website: "www.catepharmacy.com/", Latitude: 36.4155144, …}
...
I want to add a key and value pair for this that is the distance of the route in the form distance: xxxxx.
Desired result is:
...
{Name: "Cate Pharmacy", Address: "500 N Missouri Ave, Corning, Arkansas", Telephone: "(870) 857-6766", Website: "www.catepharmacy.com/", Latitude: 36.4155144, distance: xxxxxx, …}
...
But instead at the second console.log(row) I get this:
{Name: null, distance: 265184.8}
Talking on the chat we solved the problem.
It was narrowed down to changing var row; to let row;
It sounds like row is leaked into your function somehow. You should read this to understand the differences between the two variable declaration keywords.
In a nutshell, var is bound to the immediate function body while let is bound to the immediate closing block. That could be what caused it. Otherwise, I don't know.
It's best to use let because var is almost always unnecessary and can cause problems.

Assign variable value as variable name

the problem is i want to shorten my code by calling a variable using other variable's value
long working version:
var russia = new Array('15')
var switzerland = new Array('5')
$('.country').mouseover(function(){
switch(this.id){
case 'russia':
active_country_lift(this.id,russia[0])
break
case 'switzerland':
active_country_lift(this.id,switzerland[0])
break
}
})
it will get the id of mouseovered then check if it matched one of the variable by using switch
what i want to obtain is something like this:
var russia = new Array('15')
var switzerland = new Array('5')
$('.country').mouseover(function(){
active_country_lift(this.id,this.id[0])
})
of course the above code wouldn't work but is there a workaround for this?
UPDATE: Arun's answer worked and ill accept it soon and as for the comments requesting for the full code, here's a chunk of it after i applied Arun's
var countries = {
russia: ['-15px'],
switzerland: ['-5px']
}
$('.country_inactive').mouseover(function(){
active_country_lift(this.id, countries[this.id][0])
})
function active_country_lift(country, country_top){
if(!$('#'+country+'_active').hasClass('active')){
$('#'+country+'_active').stop().fadeIn(100).animate({
'top' : country_top
}, 200)
$('#'+country).stop().fadeOut(100)
}
}
it will be used for a world map, feel free to make any suggestions for making it more dynamic
You can store the country info in an object like a key value pair, then use bracket notation to access it dynamically
var countries = {
russia: new Array('-15px'),
switzerland: new Array('-5px')
}
$('.country').mouseover(function() {
active_country_lift(this.id, countries[this.id][0])
})
If you don't have multiple values then
var countries = {
russia: '-15px',
switzerland: '-5px'
}
$('.country').mouseover(function() {
active_country_lift(this.id, countries[this.id])
})
try using eval() function
var russia = new Array('-15px')
var switzerland = new Array('-5px')
$('.country').mouseover(function(){
active_country_lift(this.id,eval(this.id)[0])
})

Get Firebase unique ID from filtered object

I’m building an Angular app that searches geographic locations through the 500px API and returns photos based on the search. If someone searches the same location multiple times, I’ll need to increment the search for page=x on the API request, so that fresh results are returned.
My current way of handling this is to query all my locations in Firebase, and filter through them using Undescore.js _findWhere feature. If a location name matches the searched term, I increment it, otherwise I create a new one.
Currently I have it working so that my object is being returned when it matches, but in order to increment it, I need the unique ID of that object that Firebase assigned to it.
Here's my code (converted from CoffeeScript; apologies):
getSearchCount = function(result) {
var data;
// fetch the data from firebase as an object
data = $firebase(ref).$asObject();
return data.$loaded().then(function() {
var passed_data, plucked_result;
// search the object to see if a location matches the currently searched term
plucked_result = _.findWhere(data.locations, {
name: result.formattedAddress
});
// this is where I want to return the unique ID of the plucked result
return passed_data = [result, plucked_result];
});
};
saveLocation = function(passed_data) {
var plucked_result, result, search_count;
result = passed_data[0];
plucked_result = passed_data[1];
// if the search term doesn't exist, create a new one
if (plucked_result === null) {
search_count = 1;
return locationsRef.push({
name: result.formattedAddress,
lat: result.lat,
lng: result.lng,
search_count: search_count
});
} else {
// increment the search count on the query
// search_count = plucked_result.search_count + 1
// plucked_result.search_count = search_count
}
};
Here's the object I’m getting returned for console.log(data):
d {$$conf: Object, $id: null, $priority: null, foo: "bar", locations: Object…}
$$conf: Object
$id: null
$priority: null
locations: Object
-JUBNhmr_0kwSmHLw4FF: Object
lat: 51.5073509
lng: -0.12775829999998223
name: "London, UK"
search_count: 1
__proto__: Object
-JUBQREGJpQnXxiMIaKm: Object
lat: 48.856614
lng: 2.3522219000000177
name: "Paris, France"
search_count: 1
__proto__: Object
__proto__: Object
photos: Object
__proto__: Object
Here's the object I’m getting return for console.log(plucked_result):
Object {lat: 51.5073509, lng: -0.12775829999998223, name: "London, UK", search_count: 1}
So, to summarise, I want the Firebase unique ID (-JUBNhmr_0kwSmHLw4FF).
Or perhaps I’m doing this in a completely convoluted way that could be simplified? All I essentially need to do it create a way of paginating my API request so that I’m not pulling in all the same page of results twice.
Short answer:
You can replace the underscore.js findWhere() call with a loop like this:
var key;
var location;
for(key in data.locations) {
location = data.locations[key];
if(location.name == result.formattedAddress) {
break;
}
}
This will yield your object of interest stored in location and its name in key.
Why this is the case
The unique id that you're looking for (which the Firebase docs call the object name) is the parent of your location object in your firebase. One object in data.locations might look something like this:
{ '-JUBNhmr_0kwSmHLw4FF' :
{ lat: 51.5073509, lng: -0.12775829999998223,
name: "London, UK", search_count: 1 }
}
And the findWhere() function can locate it, but it only returns the matched object and does not provide a way to step up the tree one to get the parent node.

Getting an id array to pass to onclick function

I have a jQuery code that parses a JSON encoded string like this:
var results = JSON.parse(msg);
Then it does a foreach statement as such:
jQuery.each( results.stores,function(k,v){
var store_id = v.id;
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(v.lat,v.lng),
icon: image,
shadow: shadow,
shape: shape,
title: v.name+' : '+v.address
});
I need to be able to pass the id of the item that has been clicked to the onclick event so it can be passed to the PHP script to do the query properly. I am guessing I will have to create a function and then make that function call inside this area.
Here is the working onclick function, but it will only pass the first id within the JSON string:
Updated code
$("#list li").click(function(){
//var store_id = $(this).store_id;
var pathname = "ajax=1&store_id="+store_id+"&action=get_nearby_stores&distance="+distance+"&lat="+lat+"&lng="+lng+"&products="+$('#edit-products').val();
$("#pocp_content").load("file1.php?" + pathname)
});

Javascript API - Error 401 - Missing api_id when clicking on SearchBox

I have two symptoms that make me think there's something wrong with my Nokia appId/token. One is that when I try to use the searchbox, I get a javascript error "Error 401 - Missing api_id". The second is when I go to developer.here.com and to My Apps, and look at the hit history of my app, I see zero hits. What am I doing wrong?
nokia.Settings.set("appId", "[My ID]");
nokia.Settings.set("authenticationToken", "[MY TOKEN]");
var my_lat = 41.88
var my_lon = -87.63
map = new nokia.maps.map.Display(document.getElementById('mapcanvas'), {
'components': [
// Behavior collection
new nokia.maps.map.component.Behavior(),
new nokia.maps.map.component.ZoomBar(),
new nokia.maps.map.component.Overview(),
new nokia.maps.map.component.TypeSelector(),
new nokia.maps.map.component.ScaleBar()
],
'zoomLevel': 11, // Zoom level for the map
'center': [my_lat, my_lon] // Center coordinates
});
// Initialize search box:
var searchBox = new nokia.places.widgets.SearchBox ({
targetNode: 'searchbox',
searchCenter: function () {
return {
latitude: my_lat,
longitude: my_lon
}
},
onResults: function (data) {
renderResults (data);
}
});
// Handle the results of the search. This callback function
// receives the raw places data as an array. For each element
// in this array, it creates an HTML list element and fills
// it with the name of the place:
function renderResults (data) {
var previewList = document.getElementById ('results');
previewList.innerHTML = '';
var results = data.results.items;
for (var i = 0, l = results.length; i < l; i++) {
var result = results[i];
var resultLi = document.createElement ('li');
resultLi.innerHTML = result.title;
previewList.appendChild (resultLi);
}
}
The problem was how I was getting the javascript lib.
This is what worked. Note the "with=maps,places".
<script type="text/javascript" src="http://api.maps.nokia.com/2.2.4/jsl.js?with=maps,places"></script>
Here's what did not work
<script type="text/javascript" src="http://api.maps.nokia.com/2.2.4/jsl.js"></script>
<script type="text/javascript" src="http://api.maps.nokia.com/2.2.4/jsl.js?with=places"></script>
Your code looks correct - my assumption would be that the appId and token you are using are not valid.
Firstly to prove this is the case start with the code behind the Places Widget example. Copy the code to your PC or local server and see if you can get a local copy working for you - if you type the word Airport into the search box you should get suggestions appearing after the first three letters, and markers on the map once you click search.
Now try replacing the app id and token wih blanks:
nokia.Settings.set("appId", "");
nokia.Settings.set("authenticationToken", "");
The application should no longer work. If this is also the case when you enter your appId and token then it looks like you have incorrectly copied them, used the app name by mistake (or something is up with the back-end).
The most comprehensive guide to creating a new appId and token can be found here - I would follow it from scratch and create a new one if necessary. When logged in and you click on Manage Apps the appId and token can be found in the boxes on the right. Double click on the box to select all and ensure that you don't inadvertently miss the final character which may not quite fit in the box.
Go back to your working demo example and replace the demo appId and token with the ones associated with your account. Hopefully it should now work for you.

Categories

Resources