Variable in JQuery changes - javascript

I post here a snippet of code where I get a runtime error. The variable x changes is value inside an ajax call:
$("#acquisto").click(function(){
var x = 0;
for(x = 0; x < numRighe; x++){
if( $("#ch"+x).prop("checked") == true){
alert("#tr"+x);
$.ajax({
url:"eliminazioneRecord.php",
type: "GET",
data: { Codice: $("#ch"+x).val() },
success:function(result){
//$("#tr"+x).fadeOut("slow");
alert("#tr"+x);
},
error:function(richiesta,stato,errori){
alert("<strong>Chiamata fallita:</strong>"+stato+" "+errori);
}
});
}
}
});
I realized that because the in the alert before the ajax call x has a value that is different from the one showed in the alert inside the success function. Where I am wrong?

All the anonymous functions passed to success in $.ajax reference the same x variable from the outer scope which is incremented by the for structure. You need each function to have it's own copy of x.
success:function(copy_x) {
return function(result){
//$("#tr"+copy_x).fadeOut("slow");
alert("#tr"+copy_x);
}
}(x),

It is because your are using the loop variable x in a closure within the loop.
$("#acquisto").click(function () {
for (var x = 0; x < numRighe; x++) {
(function (x) {
if ($("#ch" + x).prop("checked") == true) {
alert("#tr" + x);
$.ajax({
url: "eliminazioneRecord.php",
type: "GET",
data: {
Codice: $("#ch" + x).val()
},
success: function (result) {
//$("#tr"+x).fadeOut("slow");
alert("#tr" + x);
},
error: function (richiesta, stato, errori) {
alert("<strong>Chiamata fallita:</strong>" + stato + " " + errori);
}
});
}
})(x)
}
});
Read:
Creating closures in loops: A common mistake
Javascript closure inside loops - simple practical example

Related

Loop Function Javascript

I am newbie in javascript, I want to do looping normally use "for". I would like to duplicate this script about 10 ...
how loop this script?
function getComboMotif1() {
$.get("file.php?opt1=" + $("#id1"), function (data) {
$("#asd1").html(data);
});
}
The manual Loop script like this !!
function getww1() {
$.get("file.php?opt1=" + $("#id1"), function (data) {
$("#asd1").html(data);
});
}
function getww2() {
$.get("file.php?opt1=" + $("#id2"), function (data) {
$("#asd1").html(data);
});
}
function getww3() {
$.get("file.php?opt1=" + $("#id3"), function (data) {
$("#asd1").html(data);
});
} //and further
Something like that :
function getResource(which) {
$.get('file.php?opt1=' + $('#id' + which), function (data) {
$('#asd' + which).html(data);
}
}
for (var i = 0, max = 3; i < max; i += 1) {
getResource(i);
}
But your code contains a few oddities.
$('#id1') is a jquery object, so it can't be sent to the server as a string.
If you always replace the $('#asd1').html(data) in each callback, it will get overwritten each time you get an answer from the server. That's why I made it dynamic also.
If you need it to just run through a forloop, then start at 1 in order to accommodate your name and id.
for(var i = 1; i <= 10; ++i)
$.get("file.php?opt1=" + $("#id" + i), function (data) {
$("#asd1").html(data);
});
Now since get is asynchronous, each one will not wait for the other to complete.
If you need each function to be created, getww1 and such, then I recommend using eval to create those functions for you. But, that is very inefficient to do and should be avoided unless there is a specific requirement. Ex:
...
eval("(function () { return function "
+fname+" (){"+
codeAsString
+"};})()"));
...
That will return the newly created function.
I hope this helps to some degree.

jQuery pass argument to $.get() callback function

I am using this piece of code to retrieve some JSONs from twitch:
for (var i = 0; i < streamList.length; i++) {
$.get(baseURL + streamList[i], getStreamInfo, "json");
}
where getStreamInfo is the callback function. I would like to know if it is possible to pass the value of "i" to the callback function somehow, along with the JSON.
Yes, you can pass the default parameter that receive the data of the ajax query and then add the i var.
for (var i = 0; i < streamList.length; i++) {
$.get(baseURL + streamList[i],
function(data) { getStreamInfo(data, i) },
"json");
}
Note that you need to receive it in getStreamInfo function
Hope it helps.
You can add any variables you want to the anonymous object. Be sure those variables are not used by the get function.
For exemple, I added the variable foo to the anonymous object and used it with this.foo in the callback function :
for (var i = 0; i < streamList.length; i++) {
$.get({
url: baseURL + streamList[i],
success: getStreamInfo,
dataType: "json",
foo:i
});
}
function getStreamInfo()
{
var i = this.foo;
}
You can use Closures.
for (var i = 0; i < streamList.length; i++) {
(function(index){
$.get(baseURL + streamList[i], function(data){
getStreamInfo(data, index);
}, "json");
})(i);
}
Note: Modify your function getStreamInfo to accept index.
Read How do JavaScript closures work?

Getting TypeError: Cannot set property '0' of undefined when trying to add to array

I'm trying to learn typescript. In this attempt, I am trying to get an array of numbers, from a webservice I have created, into a javascript array.
I have the following Typescript class:
class GameMap2 {
Grid: Uint8Array;
width: number;
height: number;
constructor(height: number, width: number) {
this.height = height;
this.width = width;
this.Grid = new Uint8Array(height * width);
}
generateMap() {
alert("calling generate");
$.ajax({
url: "/api/Maps/" + this.width + "/" + this.height,
async: false,
dataType: 'json',
success: function(data) {
alert("Ajax success");
for (var idx = 0; idx < data.length; idx++) {
this.Grid[idx] = data[idx];
}
}
});
}
}
From the webservice, I will get something like: [1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,1,1,1,1,0,1,0,1,1,1,0,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1]
Now, when I try to loop through this result, and put it into the Grid array, I get the error:
TypeError: Cannot set property '0' of undefined
If I change it to This.Grid.Push(data[idx]); I get undefined errors.
It seems to me, that my Grid array is not actually within reach of my ajax callback. Can that really be true, or am I doing something wrong here?
Is there another way I can get my array into the javascript array?
Well in fact, this issue is solved directly by TypeScript. The feature is called arrow function and would change your code defintion like this
class GameMap2 {
...
constructor(height: number, width: number) { ... }
generateMap() {
alert("calling generate");
$.ajax({
// instead of this
// success: function(data) {
// use this style, and the compiler will do the magic
// of keeping correct "this" for us
success: (data) => {
alert("Ajax success");
for (var idx = 0; idx < data.length; idx++) {
this.Grid[idx] = data[idx];
}
}
....
See TypeScript Arrow Function Tutorial (small cite)
... “Arrow function expressions are a compact form of function expressions that omit the function keyword and have lexical scoping of this.” Basically the Arrow Function helps you retain a certain scope automatically. If you look at the outputted code from the compiler, it just creates a var _this = this; and it is used inside the function.
It's because this has changed context in the success callback. Make a reference to this and use that instead:
generateMap() {
alert("calling generate");
var _this = this;
$.ajax({
url: "/api/Maps/" + this.width + "/" + this.height,
async: false,
dataType: 'json',
success: function(data) {
alert("Ajax success");
for (var idx = 0; idx < data.length; idx++) {
_this.Grid[idx] = data[idx];
}
}
});
}

$getJSON and for loop issue

This is to populate a table with the amount of results that are returned from the MediaWiki API query /api.php?action=query&list=querypage&qppage=BrokenRedirects. The number of results is then added to the id, for example:
// BrokenRedirects
$.getJSON('/api.php?action=query&list=querypage&qppage=BrokenRedirects&format=json', function (data) {
$('#BrokenRedirects').text(data.query.querypage.results.length);
});
But as it's being repeated another 7 times I made the arguments for qppage into an array and used a for loop to shorten overall code.
var array = ['BrokenRedirects',
'DoubleRedirects',
'Unusedcategories',
'Unusedimages',
'Wantedcategories',
'Wantedfiles',
'Wantedpages',
'Wantedtemplates'];
for (var i = 0; i < array.length; i++) {
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
}
The first, unlooped, version works. But when I added a loop it didn't. The $getJSON part executes, but it then fails to add the resultant data to the id. I ran it through JSLint which apart from complaining about functions in a loop and declaring var i with var array returned little help. I'm relatively inexperienced with javascript so thought perhaps a variable can't be used twice within a loop? Other than that, maybe something to do with using an id within a loop?
That's a classical problem : i has the value of end of loop when the callback is called.
You can fix it like this :
for (var i = 0; i < array.length; i++) {
(function(i) { // protects i in an immediately called function
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
})(i);
}
2018 addendum:
There's now another cleaner solution in today's browsers: use let instead of var:
for (let i = 0; i < array.length; i++) {
$.getJSON('/api.php?action=query&list=querypage&qppage=' + array[i] + '&format=json', function (data) {
$('#' + array[i]).text(data.query.querypage.results.length);
});
}
getJSON is an asynchronous ajax call
REFER: use synchronous ajax calls
Use Jquery $.each() to iterate over the array instead of a for loop.
For example:
$.each(array, function(_, value) {
var url = '/api.php?action=query&list=querypage&qppage=' + value + '&format=json';
$.getJSON(url, function (data) {
$('#' + value).text(data.query.querypage.results.length);
});
});
You should write a function like -
function callUrl(value)
{
$.getJSON('/api.php?action=query&list=querypage&qppage=' + value + '&format=json', function (data) {
$('#' + value).text(data.query.querypage.results.length);
});
}
and then call it with some timeout option like -
setTimeout('callUrl(+ array[i] +)',500); within the loop -
i.e.
for (var i = 0; i < array.length; i++) {
setTimeout('callUrl(+ array[i] +)',500);
}
Some delay for each call will be required here.

How to get an anonymous function to keep the scoping it had originally when called in an event handler

I have a setup where I get some information, in an ajax call, then I immediately use some of that information for another call, and then I populate some fields.
The problem is that I am not certain how to create the anonymous function so that it can call this.plantName.
Right now the value of this.plantName is undefined.
I know that there are missing close braces, but I am not going to try to line them up as that will cause confusion, so please ignore the missing close braces.
TabLinks.prototype.plantName = '';
TabLinks.prototype.init = function() {
this.updateTab('PlantDropDown', 'retrievePlants', 'retrieveAllPlants', 'tab3', function() { return this.plantName; });
};
function TabLinks() {
this.updateTab = function(entityname, wsbyid, webservicename, parentContainer, defaultValue) {
$("#" + parentContainer + "link").bind('click', function() {
$.ajax({
type: "POST",
...
success: function(result) {
myData = JSON.parse(result.d);
$.ajax({
type: "POST",
...
success: function(result2) {
...
myelem.value = JHEUtilities.testIsValidObject(defaultValue) ?
defaultValue() :
'';
Update:
Here is the solution that worked, I didn't realize the two returns of functions:
this.updateTab('PlantDropDown', 'retrievePlants', 'retrieveAllPlants', 'tab3',
function() {
var app = this;
return function() {
return function() {
return app.plantName;
}
}()
}
);
So I had to call it thusly:
defaultValue()();
The way I program my way out of this is to make sure that the closure is returned from a function in the scope of the captured variables. Something like so:
function foo(){
var myFoo = 1;
return function (){return function() { myFoo += 1; return myFoo }}()
} // ^^

Categories

Resources