callback function removes two items instead of single one - javascript

What is wrong here:
function deluser() {
var id = $('.nameact').attr('data-id');
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact').remove();
$('.namesingle').eq(0).addClass('nameact');
});
}
users-pro-del.php - deletes a user from database.
So I want to remove the corresponding item from screen
$('.nameact').remove();
And give the active status to another (the first) item:
$('.namesingle').eq(0).addClass('nameact');
Sometimes it works fine.
But more often - $('.namesingle').eq(0) is also removed !
As I can see in console - there is only one single .nameact at each moment.
Any help?

Ajax has asynchronous nature. id = $('.nameact') before $.post may be differ from the $('.nameact') after ajax request. It's better to refer nameact by Id everywhere.
function deluser() {
var id = $('.nameact').attr('data-id');
console.log('id to delete: ' + id);
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact[data-id="' + id + '"]').remove(); // to be sure we delete the same object in php and JS
console.log('deleted id: ' + id);
$('.namesingle').eq(0).addClass('nameact');
});
}

Related

Javascript Issue with the order of printing to screen

I have a problem with my output of this. getYammerJSON contains a line that also appends. The issue is that when I run the script it puts all of the appends that are not inside the function first, and then the contents of the function, is it something to do with ready?
<div id="test"></div>
_
$(function(){
while(n<len){
$('#test').append("<br/><br/>Yammer Group ID: <b>" + groupIDs[n] + "<b/><br/><br/>");
getYammerJSON(page,n)
n++;
}
});
function getData(returnData){
$.each(returnData.users, function(key, value){
if(value.email != undefined){
$('#test').append(value.email + "<br/>");
}
});
}
function getYammerJSON(page,n){
$.get("https://www.yammer.com/api/v1/users/in_group/" + groupIDs[n] + ".json?page=" + page, function (returnData) {
getData(returnData);
if(!returnData.more_available === true){
return false;
}
else {
page++;
getYammerJSON(page,n);
}
});
}
an example (it should separate the emails by group ID, not put at the top) -
Yammer Group ID: 12069
Yammer Group ID: 46371
adetan#test.com
alanild#test.com
alexchi#test.com
alisoc#test.com
alwyn#test.com
...
This is a asynchronous problem. You expect the functions to fire after each other. They don't. The jQuery get is asynchronous. So it fires when the request is ready, not when the function is completed. This causes data to appear in the wrong place. You need to rethink the way #test is being populated.
The solution here will solve the asynchronous problem.
function getYammerJSON(page,n){
if (!$('#yg_'+n)[0]) //test if the div already exists, if not create.
{
$('#test').append("<div id='yg_"+n+"'>Yammer Group ID: <b>" + groupIDs[n] + "<b/></div><br/>");
}
$.get("https://www.yammer.com/api/v1/users/in_group/" + groupIDs[n] + ".json?page=" + page, function (returnData) {
getData(returnData, n);
if(!returnData.more_available === true){
return false;
}
else {
page++;
getYammerJSON(page,n);
}
});
}
function getData(returnData, n){
$.each(returnData.users, function(key, value){
if(value.email != undefined){
$('#yg_'+n).append(value.email + "<br/>");
}
});
Now everytime getYammerJSON is called it will create a new div inside #test if it doesn't already exists. The div is given an ID that refers to the n value. When getData is called it will append the addresses into the correct div always corresponding with the correct group id.
asynchronous forces you to rethink your strategy of data retrieval. It's relying more on callbacks. There are new techniques around the corner. They are mentioned in comments, but they are far from common practice. Till that day you need to apply call backs in order to make asynchronous work without faults.
Learn more here: Asynchronous vs synchronous execution, what does it really mean?

Javascript scope/hoisting OR promises/deferreds?

I am trying to make an external AJAX call to an API within a Jquery each loop.
Here is the code I have so far.
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).done(function(data){
var holder = [];
$.each(styles, function(index, value) {
var tempValue = value;
var temp = getNavigationInfo(value.id);
$.when(temp).done(function(){
if(arguments[0].equipmentCount == 1){
holder.push(tempValue);
console.log(holder);
}
});
});
});
console.log(holder);
function getStylesInfo(make, model, year, submodel){
return $.ajax({
type: "GET",
url: apiUrlBase + make + '/' + model + '/' + year + '/' + 'styles? fmt=json&' + 'submodel=' + submodel + '&api_key=' + edmundsApiKey + '&view=full',
dataType: "jsonp"
});
function getNavigationInfo(styleId){
return $.ajax({
type: "GET",
url: apiUrlBase + 'styles/' + styleId + '/equipment?availability=standard&name=NAVIGATION_SYSTEM&fmt=json&api_key=' + edmundsApiKey,
dataType: "jsonp"
});
The getStylesInfo() returns something similar to this. An array of objects with info about a car model.
var sampleReturnedData = [{'drivenWheels': 'front wheel drive', 'id': 234321}, {'drivenWheels': 'front wheel drive', 'id': 994301}, {'drivenWheels': 'rear wheel drive', 'id': 032021}, {'drivenWheels': 'all wheel drive', 'id': 184555}];
I am trying to loop through the sampleReturnedData and use each id as a parameter in a different AJAX call with the getNavigationInfo() function.
I want to loop through the results and make a check. If it is true then I want to push the entire object to the holder array.
The problem is the console.log(holder) outside the function returns an empty array. The console.log(holder) within the if statement works properly.
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
I have read this question and many like it. They suggest to use either
async:false
Or to rewrite the code better. I have tried and used console debugger numerous times. I don't want to set it to false. I'm just unsure what exactly is going on.
I've also read up on hoisting via this article.
I believe it has to do with deferreds but I don't have enough JS knowledge to figure it out.
Thanks!
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
In fact, it is both:
holder is declared only within the callback function (as a local variable), so it's undefined outside the function.
And the console.log is executed before the asynchronous callback function does fill the array with values, so even if holder was in scope it would still have been empty. See also Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
So what you should do is indeed to rewrite your code to use promises properly :-)
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).then(function(data) {
var holder = [];
var promises = $.map(data.styles, function(value, index) {
return getNavigationInfo(value.id).then(function(v){
if (v.equipmentCount == 1)
holder.push(value);
});
});
return $.when.apply($, promises).then(function() {
return holder;
}); // a promise for the `holder` array when all navigation requests are done
}).then(function(holder) {
console.log(holder); // use the array here, in an async callback
});

Cant get the current id of a data from local Storage using jquery

I am working on an app to store data offline. My problem is when I try to retrieve the data from local storage for update/edit, it keeps calling only the id of the first item, and not calling the id of the data in view.
Please what am I doing wrong?
Here is my code for loading employees:
// load cases from localStorage
var employees;
if (localStorage.getItem('employees')) {
employees = JSON.parse(localStorage.getItem('employees'));
} else {
// If no cases, create and save them
employees = [];
// offling storing of our cases
localStorage.setItem('employees', JSON.stringify(employees));
}
// show case listing in list view page
var showEmployees = function () {
//erase existing content
$('#employee_list').html('');
//insert each employee
for (var i = 0; i<employees.length; i++) {
addEmployees(employees[i]);
}
};
Here is my code to add an employee to list view:
//add an eliment to list view
var addEmployees = function (empData) {
//HTML content of one list element
var listElementHTML = '<li><a class="employee_list" ui-btn ui-btn-e ui-btn-icon-right ui-icon-carat-r" data-transition="fade" data-split-icon="delete" href="#item'+empData.id+'">' + empData.employeename + '<br> ' + empData.dateofbirth + '</br></a></li>';
//appending the HTML code to list view
$('#employee_list').append(listElementHTML);
};
Here is my code for Edit function:
//User input to edit form
$('#edit_employee_page').on('click' , function () {
var editEmployee = JSON.stringify({
id: employees.length+1,
employeeno: $('#employeeno').val(),
employeename:$('#employeename').val(),
stateoforigine:$('#stateoforigine').val(),
employeephone: $('#employeephone').val(),
dateofbirth:$('#dateofbirth').val()
});
//Alter the slected data
localStorage.setItem("employees", JSON.stringify(employees));
return true;
});
for (var i in employees) {
var id = JSON.parse(localStorage.getItem(employees[i]));
}
Here is my code for the Edit button:
//register Edit button
$('.edit_button').live('click', function (e) {
alert('I was Cliked!');
e.stopPropagation();
$.each(employees, function(a, b) {
//if(b.id == employees[i]){
$('#id').val(b.id);
$('#employeeno').val(b.employeeno);
$('#employeename').val(b.employeename);
$("#stateoforigine").val(i.stateoforigine);
$('#employeephone').val(b.employeephone);
$('#dateofbirth').val(b.dateofbirth);
$("#id").attr("readonly","readonly");
$('#employeeno').focus();
$.mobile.changePage('#edit_employee_page');
return false;
//}
});
});
Here is my local Storage:
[
{"id":1,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1965"},
{"id":2,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1966"},
{"id":3,
"employeeno":"DEF/234/20014",
"employeename":"Bill Gates",
"stateoforigine":"Osun",
"employeephone":"080765432",
"dateofbirth":"12/11/1966"},
{"id":4,
"employeeno":"DAST/003/2003",
"employeename":"Gold Base",
"stateoforigine":"",
"employeephone":"",
"dateofbirth":"12/03/1986"}
]
Thanks for helping me out
The way you are storing your employees into localStorage is correct, but the way you are getting them out is incorrect. You stored your employees by stating:
localStorage.setItem("employees", JSON.stringify(employees));
So, in order to retrieve them, you must use:
var employees = JSON.parse(localStorage.getItem("employees"));
You see, you stored the data as a string with a key of "employees"; therefore, you can only retrieve it by that key. Since all data stored in localStorage is saved as a string, you must use JSON.parse() to convert the data back into an object - an array in this case. Then you can iterate over your employees.
Update:
You should be running this code as soon as the page is rendered (see below). I'm not sure how you're doing that - if you're using an IIFE or jQuery's document.ready() function. I don't think it's necessary to store an empty array into localStorage if none were loaded initially, so, I took your else clause out.
var employees = [];
if (localStorage.getItem('employees') !== null) {
employees = JSON.parse(localStorage.getItem('employees'));
}
Debug this line-by-line when it runs and make positive your employees variable contains data. If it doesn't contain data, well then, there's nothing to edit.
If, however, there is data, then execute your showEmployees() function. Oddly, I'm not seeing in your code where you actually call this. Is it bound to a button or action in your UI? Also, what is that for loop doing after your $('#edit_employee_page') click event function? It's trying to read data from localStorage improperly and it does nothing.
I think if you simply stepped through your code one line at a time using breakpoints and desk-checking your inputs/outputs you'd find out where you're going wrong.
It also appears that there's a disconnect in your code. May be you left out some lines; you define a string editEmployee but out of the blues you store JSON.stringify(employees) whereas employees is not defined in your code:
$('#edit_employee_page').on('click' , function(){
var editEmployee = JSON.stringify({
id: employees.length+1,
//........
});
//Alter the slected data
localStorage.setItem("employees", JSON.stringify(employees));
return true;
});
I had a similar task to do . I did it this way.
I passed the dynamic Id to be passed as an id attribute
id="'+empData.id+'"
and then inside the
$('.edit_button').live('click', function (e) {
alert('I was Cliked!');
var empId=$(this).attr('id');
rest of the code is same.

Node.js : scraping with Request and Cheerio issue

first off : I'm new to node, and a relative programming beginner.
I'm trying to create a small web app with Express, whose only goal is to fetch and reformat data from a website that doesn't have an open API.
To do so, I've decided to learn about scraping, and that brought me to Cheerio and Request.
I'm using reddit as an example, to learn on. The end goal in this example is to gather the name and href of the posts on the front page as well as the url leading to the comments, then to go on that page to scrape the number of comments.
What follows is the route that is called on a GET request to / (please excuse the variable names, and the comments/console.logs, I got frustrated) :
/*
* GET home page.
*/
exports.index = function(req, res){
var request = require('request')
, cheerio =require('cheerio')
, mainArr = []
, test = "test"
, uI
, commentURL;
function first() {
request("http://www.reddit.com", function(err, resp, body) {
if (!err && resp.statusCode == 200) {
var $ = cheerio.load(body);
$('.thing', '#siteTable').each(function(){
var url = $('a.title', this).attr('href')
, title = $('a.title', this).html()
, commentsLink = $('a.comments', this).attr('href')
, arr = [];
arr.push(title);
arr.push(url);
arr.push(commentsLink);
mainArr.push(arr);
});
second();
};
});
}
function second() {
for (i = mainArr.length - 1; i >= 0; i--) {
uI = mainArr[i].length - 1;
commentURL = mainArr[i][uI];
console.log(commentURL + ", " + uI + ", " + i);
var foo = commentURL;
request(foo, function(err, resp, body) {
console.log("what the shit");
// var $ = cheerio.load(body);
// console.log(mainArr.length + ", " + commentURL + ", " + i + ", " + uI);
// var test = $('span.title', 'div.content').html();
console.log(test + ", "+ foo + ", " + commentURL + ", " + i + ", " + uI);
// mainArr[1][2] = test;
});
};
if (i<=0) {
res.render('index', {title: test});
};
}
first();
};
The function first(); works as intended. It puts the title, the href and url to the comments in an array, then pushes that array in a master array containing those data points for all of the posts on the front page. It then calls the function second();
Said function's goal is to loop through the master array (mainArr[]), then select all of the urls leading to comments (mainArr[i][uI]) and launch a request() with that url as first parameter.
The loop works, but during the second call of request() inside the second() function, everything breaks down. The variable i gets set permanently at -1, and commentURL (the variable that is set to the URL of the comments of the current post), is defined permanently as the first url in arrMain[]. There are also weird behaviors with arrMain.length. Depending on where I place it, it tells me that arrMain is undefined.
I have a feeling that I'm missing something obvious (probably to do with asynchronicity), but for the life of me, I can't find it.
I would be really greatful for any suggestions!
You are correct about your guess, it's the infamous "Javascript loop Gotcha". See here, for example, for an explanation:
Javascript infamous Loop issue?
Besides that, it seems that only your debug prints are affected. The commented code regarding var test ought to work.
Finally, the kind of language is frowned upon in SO, you would do well to take 2 minutes and change your variable names in this post.

How to make a variable accessible outside a function?

I am fetching data from a json file using $.getJSON to get an id of a character and then use $.getJSON to fetch it from another page using the id that I go previously.
However, in the console it says
https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/undefined/summary?api_key=API_KEY_HERE
It should show the id where it says unidentified - /by-summoner/undefined/summary
This is my current script:
var input = "netuetamundis";
var sID;
$(document).ready(function () {
// get json from this page to get the ID of the input
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/" + input + "?api_key=API_KEY_HERE", function (name) {
obj = name;
sID = obj.id;
console.log(sID);
});
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function (stats) {
console.log(stats);
});
});
When I googled, it said to declare the variable outside the function, which I did as you can see in the code, however it still doesn't work.
Your variable declarations and their scope are correct. The problem you are facing is that the first AJAX request may take a little bit time to finish. Therefore, the second URL will be filled with the value of sID before the its content has been set. You have to remember that AJAX request are normally asynchronous, i.e. the code execution goes on while the data is being fetched in the background.
You have to nest the requests:
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/"+input+"?api_key=API_KEY_HERE" , function(name){
obj = name;
// sID is only now available!
sID = obj.id;
console.log(sID);
});
Clean up your code!
Put the second request into a function
and let it accept sID as a parameter, so you don't have to declare it globally anymore!
(Global variables are almost always evil!)
Remove sID and obj variables - name.id is sufficient unless you really need the other variables outside the function.
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/"+input+"?api_key=API_KEY_HERE" , function(name){
// We don't need sID or obj here - name.id is sufficient
console.log(name.id);
doSecondRequest(name.id);
});
/// TODO Choose a better name
function doSecondRequest(sID) {
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function(stats){
console.log(stats);
});
}
Hapy New Year :)
$.getJSON is an asynchronous request, meaning the code will continue to run even though the request is not yet done. You should trigger the second request when the first one is done, one of the choices you seen already in ComFreek's answer.
Alternatively you could use jQuery's $.when/.then(), similar to this:
var input = "netuetamundis";
var sID;
$(document).ready(function () {
$.when($.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.1/summoner/by-name/" + input + "?api_key=API_KEY_HERE", function () {
obj = name;
sID = obj.id;
console.log(sID);
})).then(function () {
$.getJSON("https://prod.api.pvp.net/api/lol/eune/v1.2/stats/by-summoner/" + sID + "/summary?api_key=API_KEY_HERE", function (stats) {
console.log(stats);
});
});
});
This would be more open for future modification and separates out the responsibility for the first call to know about the second call.
The first call can simply complete and do it's own thing not having to be aware of any other logic you may want to add, leaving the coupling of the logic separated.

Categories

Resources