about java script variable scope and facebook api - javascript

I am using facebook login api to get user information, code can exceed correctly and FirstName can get from facebook api, but the first alert has correct value, the second value is still "". First I think it is because remote call time cause the second alert is before the first alert, after I using a delay function before second alert, also I can not get value in the second alert.
Part of code like below.
if (response.status === 'connected') {
FirstName="";
LastName="";
FB.api('/me', function(response) {
FirstName = response.first_name;
LastName = response.last_name;
Email = response.email;
alert(FirstName);
});
alert(FirstName);
}

FB.Api is asynchronies method, which will post/get to a remote server.
The execution don’t wait for it to finish before your second “alert”
The only way you can be sure your FirstName is initialized, is using callbacks or MVVM pattern.
Here MVVM with knockout.js code:
var fbModel = function () {
var self = this;
self.FirstName = ko.observable("");
self.FirstName.subscribe(function () {
//DO what you want, first name just been changed from FB
});
self.load = function () {
FB.api('/me', function (response) {
self.FirstName(response.first_name); // WILL TRIGGER self.FirstName.subscribe
});
};
};
And don’t forget, applying this model is easy, I can give you some links if you want. (But just go and look on examples on their site)
Edit : callback version
var FirstName = ""; // Global
function callback(name) {
//your name has been loaded
FirstName = name; // Global is initialized
};
function load (callback) {
FB.api('/me', function (response) {
callback(response.first_name);
});
};
//Now just call the load :
load(callback);

Related

Cannot pass the object into the javascript function as a parameter

I'm building a user signup for a web site. It uses AJAX to send data. I have created an object to store user data and send the object to the server via function.
sendUserData(passenger, "user_registration.php");
Note :
signupUser(); is called with a button click in HTML document.
sendUserData() is the function for sending the user details.
passenger is the object which stores the user data.
sendUserData(); is in a separate Script linked to the HTML page.(Global Scope)
This is the code I used to do this
//Sign Up A User
var signupUser = function () {
// User Object
var passenger = {
userName : document.getElementById("userFirstName").value,
userEmail : document.getElementById("userEmail").value,
country : document.getElementById("country").value,
password : document.getElementById("userPassword").value,
passwordVerify : document.getElementById("userPasswordVerify").value,
passwordStatus : Boolean
};
// Verifies the password ("password" and "re-enter password")
verifyPassword(passenger.password,passenger.passwordVerify,passenger.passwordStatus);
if (passenger.passwordStatus == true) {
sendUserData(passenger, "user_registration.php");
console.log(passenger);
}
};
The problem is with invoking the function signupUser() on the button,
the function stops after verifyPassword(); .
sendUserData(passenger, "user_registration.php"); invoked in the chrome debugging tool, it gives an error "passenger is not defined at : 1:13"
Please help me to find an answer.
Thank you.
Edit:
sendUserData(); function
This is in a separate javascript file linked to the HTML page.
function sendUserData (userDetails, destinationPage, functionToExecute) {
var userDetailsJson = JSON.stringify(userDetails);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4 && xhr.status == 200 ) {
console.log(userDetailsJson);
window.alert("Successfully Posted");
console.log(xhr.responseText);
if(functionToExecute) {
functionToExecute();
}
}
};
xhr.open("POST", destinationPage, false);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send("m=" +userDetailsJson);
}
So this hinges on passenger.passwordStatus == true not working out.
You initialised it as passwordStatus: Boolean which already doesn't do what you think it does.
Then you pass the parameter to the verifyPassword function, which presumably looks something like:
function verifyPassword( password, retype, status) {
// verify...
status = true;
}
This reassigns status and does not affect passenger.passwordStatus in the outer scope at all.
Therefore, when it comes time to check the status, you're still comparing Boolean == true, which is never going to work.
You probably want something more like:
if( verifyPassword( passenger.password, passenger.passwordVerify)) {
sendUserData(...);
}
Have verifyPassword do either return true; or return false; as required.
And get rid of this passenger.passwordStatus thing entirely.

Passing a variable into a JavaScript Function

I have written a Javascript Function
jQuery(document).ready( function newbie($) {
//var email = 'emailaddress'
var data = {
action: 'test_response',
post_var: email
};
// the_ajax_script.ajaxurl is a variable that will contain the url to the ajax processing file
$.post(the_ajax_script.ajaxurl, data, function(response) {
alert(response);
});
return false;
});
Which I will call using
newbie();
But I want to pass in a variable (the email address) when I call the function but I am not sure how to do this. That $ sign seems to get in my way! Any thoughts much appreciated.
jQuery(document).ready(function(){
var email = 'emailaddress';
newbie(email);
});
function newbie(email) {
var data = {
action: 'test_response',
post_var: email
};
// the_ajax_script.ajaxurl is a variable that will contain the url to the ajax processing file
$.post(the_ajax_script.ajaxurl, data, function(response) {
alert(response);
});
return false;
}
OR
jQuery(document).ready(function(){
var newbie = function(email) {
var data = {
action: 'test_response',
post_var: email
};
// the_ajax_script.ajaxurl is a variable that will contain the url to the ajax processing file
$.post(the_ajax_script.ajaxurl, data, function(response) {
alert(response);
});
return false;
}
var email = 'emailaddress';
newbie(email);
});
Functions in javascript take 'arguments'. You can pass in as many arguments you want and define their name space in the function declaration. ie
function foo(bar,baz,etc){
console.log(bar,baz,etc);
}
foo(1,2,3)
//logs out 1 2 3
Sometimes you don't always know what's going to be passed in or how many arguments there are going to be, in this case inside of the function declaration we can use the 'arguments' object to pick out certain arguments passed into the function.
function foo(){
console.log(arguments);
}
foo(1,2,3)
//logs out an array object that looks like this [1,2,3]
jQuery(document).ready( function newbie($, email) {
//var email = 'emailaddress'
var data = {
action: 'test_response',
post_var: email
};
// the_ajax_script.ajaxurl is a variable that will contain the url to the ajax processing file
$.post(the_ajax_script.ajaxurl, data, function(response) {
alert(response);
});
return false;
});
you simply call the function by passing the values

How to keep "response" out of FB.api

I am just trying FB JS api and want to know whether or how I can still use "response" out of FB.api. For example:
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
console.log(picture);
});
alert(picture);
The above code will show "undefined" in alert window.
Is there a way to use "response.data.url" out of FB.api?
Thanks
Update:
Here is the big picture: I need retrieve some information from FB user account, such as /me/name, /me/address/city, /me/picture.data.url and group them together and then send the information to server through AJAX.
var name;
var city;
var picture;
FB.api('/me', function (response) {
name = response.name;
FB.api('/me/address', function (adrresponse) {
city = adrresponse.city;
}
FB.api('/me/picture', function (imgresponse) {
picture = imgresponse.data.url;
}
//since FB API is async, the following is not correct!!!
var params = "name="+name+"&city="+city+"&picture="+picture;
//send out through AJAX.
var http = new XMLHttpRequest();
http.open("POST", url, true);
}
Is there a better way to finish the above job?
Update 2:
The best way is to use fields expansion
https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3#fieldexpansion, as shown by the answer of this question.
Thanks
Derek
The problem is the picture variable is not populated at the time that the alert fires. It will only be populated after the FB.api callback completes.
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
// this would work correctly
alert(picture);
});
What are you attempting to do with the picture variable? Perhaps you should call a function do something with the picture inside your callback:
var picture;
FB.api('/me/picture?width=180&height=180', function (response) {
picture = response.data.url;
doSomethingWithPicture(picture);
});
Update
The simple way to achieve what you are after is this:
FB.api('/me', function (response) {
var name = response.name;
FB.api('/me/address', function (adrresponse) {
var city = adrresponse.city;
FB.api('/me/picture', function (imgresponse) {
var picture = imgresponse.data.url;
doAjax(name, city, picture);
}
}
}
function doAjax(name, city, picture) {
//since FB API is async, the following is not correct!!!
var params = "name="+name+"&city="+city+"&picture="+picture;
//send out through AJAX.
var http = new XMLHttpRequest();
http.open("POST", url, true);
}
However, this is not ideal as you have to wait for /me/address before you can call /me/picture.
Here are some other options
you need to call /me first.
you fire off both api calls and execute code when the both complete
Ways to accomplish #2
You could then use a promise library to chain the /me/address and /me/picture/. See: https://github.com/kriskowal/q or https://api.jquery.com/category/deferred-object/ to get started
Call a callback after each that conditionally fires the ajax if both address and picture are set
I am sure there are a number of other ways:
How to chain ajax requests?
How to chain ajax calls using jquery
Update #2
This is the best way to accomplish what you are after (no additional callbacks required)
FB.api('/me', {fields: ['first_name', 'last_name', 'picture', 'address']}, function(response) {
// response will now have everything you need
console.log(response);
});
I did not give this answer originally as it was not the topic of the question which seemed to be scoping.

Accessing outer scope

I'm working on creating a Users collection with the ability to then grab single users inside. This will be used to match from another system, so my desire is to load the users once, and then be able to fine/match later. However, I'm having a problem accessing the outer users collection from an inner method.
function Users(){
var allUsers;
this.getUsers = function () {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
allUsers = data;
}
});
return allUsers;
};
this.SingleUser = function (name) {
var rate = 0.0;
var position;
this.getRate = function () {
if(position === undefined){
console.log('>>info: getting user position to then find rate');
this.getPosition();
}
$.ajax({
url: '../app/data/rates.json',
async: false,
dataType: 'json',
success: function(data) {
rate = data[position];
}
});
return rate;
};
this.getPosition = function () {
console.log(allUsers);
//position = allUsers[name];
return position;
};
//set name prop for use later I guess.
this.name = name;
};
}
and the test that's starting all of this:
it("get single user's position", function(){
var users = new Users();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
The getPosition method is the issue (which might be obvious) as allUsers is always undefined. What I have here is yet another attempt, I've tried a few ways. I think the problem is how the Users.getUsers is being called to start with, but I'm also unsure if I'm using the outer and inner vars is correct.
Though the others are correct in that this won't work as you have it typed out, I see the use case is a jasmine test case. So, there is a way to make your test succeed. And by doing something like the following you remove the need to actually be running any kind of server to do your test.
var dataThatYouWouldExpectFromServer = {
bgrimes: {
username: 'bgrimes',
show: 'chuck',
position: 'mgr'
}
};
it("get single user's position", function(){
var users = new Users();
spyOn($, 'ajax').andCallFake(function (ajaxOptions) {
ajaxOptions.success(dataThatYouWouldExpectFromServer);
});
users.getUsers();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
This will make the ajax call return whatever it is that you want it to return, which also allows you to mock out tests for failures, unexpected data, etc. You can set 'dataThatYouWouldExpectFromServer' to anything you want at any time.. which can help with cases where you want to test out a few different results but don't want a JSON file for each result.
Sorta-edit - this would fix the test case, but probably not the code. My recommendation is that any time you rely on an ajax call return, make sure the method you are calling has a 'callback' argument. For example:
var users = new Users();
users.getUsers(function () {
//continue doing stuff
});
You can nest them, or you can (preferably) create the callbacks and then use them as arguments for eachother.
var users = new Users(), currentUser;
var showUserRate = function () {
//show his rate
//this won't require a callback because we know it's loaded.
var rate = currentUser.getRate();
}
var usersLoaded = function () {
//going to load up the user 'bgrimes'
currentUser = new users.SingleUser('bgrimes');
currentUser.getRate(showUserRate);
}
users.getUsers(usersLoaded);
your approach to fill the data in allUsers is flawed
the ajax call in jquery is async so every call to users.getAllUsers would be returned with nothing and when later the success function of the jquery ajax is called then allUsers would get filled
this.getUsers() won't work. Its returning of allUsers is independent from the ajax request that fetches the data, because, well, the ajax is asynchronous. Same with getRate().
You'll have to use a callback approach, where you call getUsers() with a callback reference, and when the ajax request completes, it passes the data to the callback function.
Something like:
this.getUsers = function (callback) {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
callback(data);
}
});
};
And the call would be along the lines of:
var user_data = null;
Users.getUsers(function(data) {
user_data = data;
});

Javascript object properties visible in console, but undefined?

I'm having trouble figuring out how to access object properties in Javascript. I have a function that returns an object, and I can see that object and all of its properties when it is logged to the console in Safari, but I can't get the property values for other functions. For example trying to alert out one of the properties returns 'undefined'.
The function that generates a object
getProfile : function() {
FB.api('/me', function(response) {
facebook.profile.user_id = response.id;
facebook.profile.name = response.name;
facebook.profile.firstName = response.first_name;
facebook.profile.lastName = response.last_name;
facebook.profile.gender = response.gender;
});
FB.api('/me/photos', {limit: 8}, function(response) {
facebook.profile.numPhotos = response.data.length;
for (key in response.data) {
var photoUrl = response.data[key].source;
eval('facebook.profile.photo' + key + '= photoUrl');
}
});
return facebook.profile;
}
Trying to use that function in another script
function loadProfile() {
var profile = facebook.getProfile();
console.log(profile);
alert(profile.name);
}
The function getProfile invokes FB API function FB.api which executes an asynchoronous HTTP request. In your loadProfile function call you call getProfile which immediately returns facebook.profile object which is not populated with data yet since the HTTP request is not finished yet.
Consider following change:
getProfile : function(fCallback) {
var bInfo = false,
bPhotos = false;
FB.api('/me', function(response) {
facebook.profile.user_id = response.id;
facebook.profile.name = response.name;
facebook.profile.firstName = response.first_name;
facebook.profile.lastName = response.last_name;
facebook.profile.gender = response.gender;
bInfo = true;
if (bPhotos)
fCallback(facebook.profile);
});
FB.api('/me/photos', {limit: 8}, function(response) {
facebook.profile.numPhotos = response.data.length;
for (key in response.data) {
var photoUrl = response.data[key].source;
eval('facebook.profile.photo' + key + '= photoUrl');
}
bPhotos = true;
if (bInfo)
fCallback(facebook.profile);
});
}
and call this function the following way now:
function loadProfile() {
facebook.getProfile(function (profile) {
alert(profile.name);
});
}
The reason why ou could see fields in the console is because you introspected the object after the asynch call was successfully executed. The alert call however executed immediately in the same thread on a not yet populated object.
Though 'Sergey Ilinsky' might have considered a right ground, but there are more things, that can be taken into consideration. Usually comes by mistaken, and hard to debug.
Sometime its so happens, that your object-keys contains spaces, let say:
var myObj = new Object();
myObj['key1'] = 'val1';
myObj['key2'] = 'val2';
myObj['key3 '] = 'val3'; //the key contains spaces here
myObj['key4 '] = 'val4'; // the key contains spaces here
so, when you log it to console by console.log(myObj), you will get:
Object { key1="val1", key2="val2", key3 ="val3", key4 ="val4"}
But when you access:
alert(myObj.key1); //Ok: val1
alert(myObj.key2); //Ok: val2
alert(myObj.key3); //undefined
alert(myObj.key4); //undefined
alert(myObj['key3']; //undefined
alert(myObj['key4']; //undefined
These are the common areas of mistakes, where one mistakenly puts the space, may be while doing copy-paste, and it so happens, that one says, console log is able to show it, but I cannot access it.

Categories