Loop bind objects - javascript

I have read this answer but still have a headache on making the code work.
I have a slightly different needs. Instead of alerting I have to bind each object.
My code is:
for (var i = 0; i < markers_length; i++) {
events_number = data.markers[i].events_number //data.markers is a multidimentional array
marker = L.marker([ data.markers[i].latitude , data.markers[i].longitude ]); //just create the new object
marker.on('mouseover', function(){
return function(){
this.bindPopup(" Found"+events_number+" event(s)").openPopup();
}
}(i) );
}
I am using leaflet if you ask. For a single object the bindPopup would work like:
marker.on('mouseover', this.bindPopup('hi').openPopup());
The trouble is that the above code gives the last object for all the. I assume that there is a problem with the this and the level of the functions. So how can I bind each marker with a separate text?

it should be:
marker.on('mouseover', function(en){
return function(){
this.bindPopup(" Found"+en+" event(s)").openPopup();
}
}(events_number) );
You have to pass the value that you want to be saved in the closure (events_number in this case), and the function has to take a parameter to receive that value and use it in the closure (en in my code).

Related

Existing Array with data: Cannot read property length of undefined. JavaScript

I have the weirdest problem with my code and I don't know how to solve it. I have an array of objects which I would like to loop through and match the variable dossiernummer to the selected dossiernummer to extract the zipcode of the object. This is all a bit too much for one question and I am certain I can code this, but my array seems empty whenever I add a loop!
My code:
$('select[name=company]').on('change', function() {
// The selected `dossiernummer`
console.log($(this).val());
// The array of `Objects`
console.log(results);
geocodeAddress(geocoder, map, address);
});
When using the Chrome console, I can see the filled array of objects, so I am 100% sure that the data is there! I also checked the variables, and it all matches:
But! Now I would like to loop through the array of objects:
$('select[name=company]').on('change', function() {
for (var j = 0; j < results.length; j++){
if (results[j]['dossiernummer'] == $(this).val()){
var address = results[j]['postcode']
}
}
geocodeAddress(geocoder, map, address);
});
This gives the following error in my console:
I am not advanced with JavaScript, so this error is probably very silly but I can't figure it out. Help is much appriciated!
Edit:
The data is coming from an API to fill a Select2
params.page = params.page || 1;
var results = [];
if(data.totalItemCount > 0){
$.each(data._embedded.rechtspersoon, function(i, v) {
v.id = v.dossiernummer ;
// console.log(v);
if(v.actief == 1){
results.push(v);
}
});
// the code function which i described ->
KvkGoogleMaps(results);
}
return {
results: results,
pagination: {
more: (params.page * 30) < data.totalItemCount
}
};
The problem is your results array is undefined. I cannot tell you exactly how to fix this, since you've only given us a snippet without context.
This problem of yours seems to be due to the fact that JavaScript has lexical scope, something similar to dynamic scope. If you call a function, the scope in that function is different than the scope in the parent from which it was called from. Therefore, it could be possible that scope from the called function (the anonymous one in your example) cannot access the results array from the parent.
It would be helpful if you posted more of your code in order to be able to tell you the exact problem.
More about JavaScript lexical scope.

Access value of object array in javascript

I have managed to create an object connecting to an API. So I have a function loadColors()
loadColors = function() {
var Colors = [];
for (var i =0; i < array.length; i++) {
Card.get({cardName: array[i].object_name}, function(data) {
Colors.push(data.data[0].color);
});
}
return {Colors};
};
var Colors = loadColor();
and inside there I was able to see the result with console.log(Colors) which is:
Object {Colors: Array[0]}
Colors: Array[4]
0: "green"
1: "red"
2: "yellow"
3: "blue"
length: 4
__proto__: Array[0]
__proto__: Object
When I try to access a value like console.log[Colors[0]]; I get undefined.
What am I doing wring?
Why are you wrapping Colors in {} in your return statements?
Just do return Colors; and you would be able to access the indexed array items directly from the return result.
Since you're currently returning an object containing the array, you would have to access it by Colors.Colors[0].
Update
I realize that besides wrapping your result in an object before returning it, you are also returning an array that is not populated with items at the return point, due to the asynchronous nature of Card.get.
This problem is actually rather well suited for a concept called Promises, but rather than introducing another new concept at this point, I will illustrate how you can solve this manually. (IE doensn't have native Promise support, there are frameworks that will solve this but you may not want an entire new framework just for this. jQuery has something called Deferreds, but they're subtly different from the Promise specs).
Instead of returning a result from the function, the callback from Card.get should call the function that moves your code forward. It should only do this once the array is filled, however, and not once for every callback.
Card.get({ your options }, function(data) {
Colors.push(data.data[0].color);
if(Colors.length == array.length) {
allColorsLoaded(Colors);
}
});
So if your logic is currently:
var Colors = loadColors();
alert(Colors.length);
It would need to be updated so that everything that relies on Colors to be populated is initiated by the callback:
var allColorsLoaded = function(Colors) {
alert(Colors.length);
};
loadColors();
It is isn't so clear from the question what is going on but from what I understood, when you try
console.log(Colors[0])
outside the function it returns undefined while inside the function it returns 'green'?
If so you should probably just change:
return {Colors};
to be:
return Colors;

JavaScript - Instantiate a object and assign it to another existing variable?

Been trying to instantiate an object (called "isSize" below) and assign it to another existing variable (called "sizeObject"), here is the first object ("isSize") within a function:
function Size(isSize) {
this.isSize = 80;
setSize(this.isSize);
}
And here's the second variable of which I want to assign the previous variable to:
var sizeObject;
I've been trying various ways such as the following:
function createSize(isSize){
var isSize = new sizeObject();
}
Anyone got any ideas? Many thanks
If I understand your comment correctly, here is what you are looking for:
// this is the constructor of the `Size` class
function Size() {
this.isSize = 80;
}
function createSize() {
// add this line:
var sizeObject = new Size();
}
It's very hard to understand quite what you're asking, but this:
var sizeObject = new Size(20);
...will create a new object with a isSize property on it with the value 20 if you change Size to:
function Size(initialSize) {
this.isSize = initialSize;
}
(E.g., so it actually uses the argument you give it, rather than using an hardcoded 80.)
Size in the above is a constructor function. You use constructor functions via new.

Javascript class initialization and jQuery DOM in memory

Which is the best way between:
var myClass = function() {
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
AND
var myClass = function() {
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Is there any concrete differences?
The memory problem arose when I have seen that my web page, that has enough javascript ( about 150KB of mine + libs ) takes more then 300-400MB of RAM. I'm trying to find out the problem and I don't know if this could be one of them.
function myClass{
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
Here you will need to call it something like myClassInstance.myContainer() and that means jquery will search for .container element(s) any time you are using that function. Plus, it will create 2 additional closures any time you will create new instance of your class. And that will take some additional memory.
function myClass{
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Here you will have myContainer pointing to an object which already contains all links to DOM nodes. jQuery will not search DOM any time you use myClassInstance.myContainer
So, second approach is better if you do not want to slow down your app. But first approach could be usefull if your DOM is frequently modified. But I do not beleave you have it modified so frequently that you may need to use second approach.
If there is a single variable you are trying to assign , then the second approach looks cleaner..
Yes they are different.
MODEL 1:
In the first model, myContainer is a function variable.It does not contain the jQuery object.You cannot call any of jQuery's methods on the objet. To actually get the jQuery object you will have to say
var obj = this.myContainer() or this.myContainer.call()
MODEL 2:
The second model stores the actual jQuery object.
try alerting this.myContainer in both models, u will seee the difference.
Yes this is different. after you fix your syntax error :
function myClass(){... // the parentheses
1st
When you create a new object var obj = new myClass(), you are creating two functions, and the jquery object is not returned until you call it
var container = obj.myContainer();
2nd
As soon as the object is initialized the dom is accessed and you have your objects cached for later use;
var container = obj.myContainer;

Pass extra parameter to jQuery getJSON() success callback function [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 1 year ago.
I've never had to use callback functions before, so I may have made a completely stupid mistake. I think I somewhat understand the problem here, but not how to solve it.
My code (a bit simplified) is:
for (var i = 0; i < some_array.length; i++) {
var title = some_array[i];
$.getJSON('some.url/' + title, function(data) {
do_something_with_data(data, i);
}
Now as far as I understand, this anonymous function will only be called if getJSON() has received the data. But by this point, i does not have the value I would require. Or, as far as my observation goes, it has the last value it would have after the loop is done (shouldn't it be out of bounds?).
As a result, if the array had a size of 6, do_something_with_data() would be called five times with the value 5.
Now I thought, just pass i to the anonymous function
function(data, i) { }
but this does not seem to be possible. i is undefined now.
You need to understand what a closure is. In JavaScript, there are certain rules about the scope of each variable.
The scope for variables declared implicitly or with var is the nearest/current function (including "arrow functions"), or if not in a function, then the window or other global object appropriate for the execution context (e.g., in Node, global).
The scope for variables declared with let or const (in ES5 and up) is the nearest statement block { /* not an object, but any place that will take executable statements here */ }.
If any code can access a variable in the current scope or in any parent scope, this creates a closure around that variable, keeping the variable live and keeping any object referred to by the variable instantiated, so that these parent or inner functions or blocks can continue to refer to the variable and access the value.
Because the original variable is still active, if you later change the value of that variable anywhere in the code, then when code with a closure over that variable runs later it will have the updated/changed value, not the value when the function or scope was first created.
Now, before we address making the closure work right, note that declaring the title variable without let or const repeatedly in the loop doesn't work. var variables are hoisted into the nearest function's scope, and variables assigned without var that don't refer to any function scope get implicitly attached to the global scope, which is window in a browser. Before const and let existed, for loops in JavaScript had no scope, therefore variables declared within them are actually declared only once despite seeming to be (re)declared inside the loop. Declaring the variable outside the loop should help clarify for you why your code isn't working as you'd expect.
As is, when the callbacks run, because they have a closure over the same variable i, they are all affected when i increments and they will all use the current value of i when they run (which will as you discovered be incorrect, because the callbacks all run after the loop has completely finished creating them). Asynchronous code (such as the JSON call response) does not and cannot run until all synchronous code finishes executing--so the loop is guaranteed to complete before any callback is ever executed.
To get around this you need a new function to run that has its own scope so that in the callbacks declared inside of the loop, there is a new closure over each different value. You could do that with a separate function, or just use an invoked anonymous function in the callback parameter. Here's an example:
var title, i;
for (i = 0; i < some_array.length; i += 1) {
title = some_array[i];
$.getJSON(
'some.url/' + title,
(function(thisi) {
return function(data) {
do_something_with_data(data, thisi);
// Break the closure over `i` via the parameter `thisi`,
// which will hold the correct value from *invocation* time.
};
}(i)) // calling the function with the current value
);
}
For clarity I'll break it out into a separate function so you can see what's going on:
function createCallback(item) {
return function(data) {
do_something_with_data(data, item);
// This reference to the `item` parameter does create a closure on it.
// However, its scope means that no caller function can change its value.
// Thus, since we don't change `item` anywhere inside `createCallback`, it
// will have the value as it was at the time the createCallback function
// was invoked.
};
}
var title, i, l = some_array.length;
for (i = 0; i < l; i += 1) {
title = some_array[i];
$.getJSON('some.url/' + title, createCallback(i));
// Note how this parameter is not a *reference* to the createCallback function,
// but the *value that invoking createCallback() returns*, which is a function taking one `data` parameter.
}
Note: since your array apparently only has titles in it, you could consider using the title variable instead of i which requires you to go back to some_array. But either way works, you know what you want.
One potentially useful way to think about this that the callback-creating function (either the anonymous one or the createCallback one) in essence converts the value of the i variable into separate thisi variables, via each time introducing a new function with its own scope. Perhaps it could be said that "parameters break values out of closures".
Just be careful: this technique will not work on objects without copying them, since objects are reference types. Merely passing them as parameters will not yield something that cannot be changed after the fact. You can duplicate a street address all you like, but this doesn't create a new house. You must build a new house if you want an address that leads to something different.
You could create a closure using an immediate function (one that executes right away) that returns another function:
for (var i = 0; i < some_array.length; i++) {
var title = some_array[i];
$.getJSON('some.url/' + title, (function() {
var ii = i;
return function(data) {
do_something_with_data(data, ii);
};
})());
}
If you can modify the service at some.url, it would be much better if rather than making a separate HTTP request for each item in some_array, you simply passed every item in the array in a single HTTP request.
$.getJSON('some.url', { items: some_array }, callback);
Your array will be JSON serialized and POSTed to the server. Assuming some_array is an array of strings, the request will look like this:
POST some.url HTTP/1.1
...
{'items':['a','b','c', ... ]}
Your server script should then deserialize the JSON request from the request body and loop over each item in the items array, returning a JSON-serialized array of responses.
HTTP/1.1 200 OK
...
{'items':[{id:0, ... }, {id:1, ... }, ... ]}
(Or whatever data it is you're returning.) If your response items are in the same order as the request items, it is easy to piece things back together. In your success callback, simply match the item index with some_array's index. Putting it all together:
$.getJSON('some.url', { items: some_array }, function(data) {
for (var i = 0; i < data.items.length; i++) {
do_something_with_data(data.items[i], i);
}
});
By 'batching up' your requests into a single HTTP request like this, you'll significantly improve performance. Consider that if each network round-trip takes at least 200ms, with 5 items, you're looking at a minimum 1 second delay. By requesting them all at once, network delay stays a constant 200ms. (Obviously with larger requests, server script execution and network transfer times will come in to play, but performance will still be an order of a magnitude better than if you issue a separate HTTP request for each item.)
Create N closures and pass in the value of 'i' each time, like so:
var i, title;
for (i = 0; i < some_array.length; i++) {
title = some_array[i];
$.getJSON('some.url/' + title, (function(i_copy) {
return function(data) {
do_something_with_data(data, i_copy);
};
})(i));
}
I think some browsers have trouble with making multiple asynchronous calls at the same time, so you could make them one at a time:
var i;
function DoOne(data)
{
if (i >= 0)
do_something_with_data(data, i);
if (++i >= some_array.length)
return;
var title = some_array[i];
$.getJSON('some.url/' + title, DoOne);
}
// to start the chain:
i = -1;
DoOne(null);
I had exactly the same issue as the OP but solved it a different way. I replaced my JavaScript 'for' loop with a jQuery $.each which for each iteration calls a function which I think gets over the callback 'timing' issue. And I combined my external data arrays into a JavaScript object so that I could reference both the parameter I was passing on the JSON URL and the other field in the same element of that object. My object elements came out of a mySQL database table using PHP.
var persons = [
{ Location: 'MK6', Bio: 'System administrator' },
{ Location: 'LU4', Bio: 'Project officer' },
{ Location: 'B37', Bio: 'Renewable energy hardware installer' },
{ Location: 'S23', Bio: 'Associate lecturer and first hardware triallist' },
{ Location: 'EH12', Bio: 'Associate lecturer with a solar PV installation' }
];
function initMap() {
var map = new google.maps.Map(document.getElementById('map_canvas'), {
center: startLatLon,
minZoom: 5,
maxZoom: 11,
zoom: 5
});
$.each(persons, function(x, person) {
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address=' + person.Location, null, function (data) {
var p = data.results[0].geometry.location;
var latlng = new google.maps.LatLng(p.lat, p.lng);
var image = 'images/solarenergy.png';
var marker = new google.maps.Marker({
position: latlng,
map: map,
icon: image,
title: person.Bio
});
google.maps.event.addListener(marker, "click", function (e) {
document.getElementById('info').value = person.Bio;
});
});
});
}

Categories