how to access a variable from outside a function/instance - javascript

I have this very simple JavaScript code that should return a Fullname using parameters firstname and lastname All works fine when I alert out the fullname within the function. But I'm struggling how to make the fullname variable accessible from outside the function/instance?
here's my code:
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
success(fullName);
};
};
var test = new getName();
test.name("John", "Smith", function (output) {
var fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
var myName = fullname; //I need to create the variable here
alert(myName); //this does not work
Here's Fiddle
Thank you very much for your help.
Edit: I'm building an iPad app where I'm using cordova and javascript plugins.
One of the plugin gives me access to the file inside the device. Now, I need to be able to get the path and use it outside the callback so that I can use anywhere in the scope:
Here's the plugin code:
var FileManager = function(){
this.get_path = function(todir,tofilename, success){
fail = (typeof fail == 'undefined')? Log('FileManager','read file fail'): fail;
this.load_file(
todir,
tofilename,
function(fileEntry){
var sPath = fileEntry.toURL();
success(sPath);
},
Log('fail')
);
}
this.load_file = function(dir, file, success, fail, dont_repeat){
if(!dir || dir =='')
{
Log('error','msg')('No file should be created, without a folder, to prevent a mess');
fail();
return;
}
fail = (typeof fail == 'undefined')? Log('FileManager','load file fail'): fail;
var full_file_path = dir+'/'+file;
var object = this;
// get fileSystem
fileSystemSingleton.load(
function(fs){
var dont_repeat_inner = dont_repeat;
// get file handler
console.log(fs.root);
fs.root.getFile(
full_file_path,
{create: true, exclusive: false},
success,
function(error){
if(dont_repeat == true){
Log('FileManager','error')('recurring error, gettingout of here!');
return;
}
// if target folder does not exist, create it
if(error.code == 3){
Log('FileManager','msg')('folder does not exist, creating it');
var a = new DirManager();
a.create_r(
dir,
function(){
Log('FileManager','mesg')('trying to create the file again: '+file);
object.load_file(dir,file,success,fail,true);
},
fail
);
return;
}
fail(error);
}
);
}
);
};
}
and here's how I use it:
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
myPath = path;
console.log(myPath); //this gives me the path of the file in the console, which works fine
return myPath; //I return the path to be accessed outside
});
var filePath = myPath; //here I need to access the path variable
console.log(filePath)// here, the path is undefined and it does not work
//Since I'm using angular services to retrieve the data from json, I'd like to pass the filePath
//this is a fraction of code for retrieving data:
return $resource('data.json',{}, {'query': {method: 'GET', isArray: false}});
//passing the value does not work
return $resource(filePath,{}, {'query': {method: 'GET', isArray: false}});
//even wrapping resource around instance does not work, it breaks the whole app
b.get_path('Documents','data.json',function(path){
myPath = path;
console.log(myPath); //this gives me the path of the file in the console, which works fine
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
});
Factory services:
'use strict';
angular
.module ('myApp')
.factory('getMeData', function ($resource) {
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
myPath = path;
return myPath;
});
//below doesn't work when passing path (it is undefined)
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
//when wrapping it around, the app crashes
b.get_path('Documents','data.json',function(path){
myPath = path;
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
});
//none of the solution above work
});

If you have the name function return the result of success then you can use the return from the method.
I.e.
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
return success(fullName);
};
};
var test = new getName();
var myName = test.name("John", "Smith", function (output) {
var fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
alert(myName);

Define fullname outside the callback.
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
success(fullName);
};
};
var test = new getName();
var fullname;
test.name("John", "Smith", function (output) {
fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
alert(fullname);
Hope it helps. Fiddle

If async required:
Assuming the actual source of the code is an async one, now confirmed from your comment, you should simply process the result within the callback. The value is simply not available outside of the callback as the result arrives later.
The answers by #Jim and #bestmike007 will not work with an async operation as it returns a value from inside a callback which may occur long after the function is run. (note: #bestmike007 does have an improved answer linked in a comment).
e.g. Look at what happens here: http://fiddle.jshell.net/y1m36w9p/1/ or here http://jsfiddle.net/TrueBlueAussie/opczeuqw/1/
The only way to work with async code is in an async manner. That means you can only handle the result inside the callback:
// Get the fullname asyc and process the result
test.name("John", "Smith", function (fullname) {
alert(fullname); // you can only work with the result in here
});
An alternative is to return a jQuery promise from getName.name(), but the end result will still be a callback to do the work, but this time it would look like this:
test.name("John", "Smith").done(function(fullname){
alert(fullname); // you can only work with the result in here
});
but this is more code and complication for no extra benefit at this time.
For your specific updated example code:
I am not familiar enough with Angular to confirm this, and would need to see how getMeData is called, but you should use another callback as a parameter to the getMeData function you register:
'use strict';
angular
.module ('myApp')
.factory('getMeData', function ($resource, callback) {
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
callback($resource(path,{}, {'query': {method: 'GET', isArray: false}}));
});
});
If async not required (obsolete option)
If (as a previous contradictory comment below said) this was not mean to to be async, then do not use callbacks at all, just simple functions/methods returning values: http://fiddle.jshell.net/y1m36w9p/2/
e.g.
var getName = function () {
this.name = function (firstname, lastname) {
return firstname + " " + lastname;
};
};
// Create a instance of the getName class
var test = new getName();
// Get the fullname asyc and process the result
var fullname = test.name("John", "Smith");
// Do what you like with the value returned from the function
alert(fullname);

Related

Updating outside variables inside a javascript function in Node.js

I am developing a NodeJS application and encountered following problem:
I am using node module node-rest-client to issue REST requests using an API. I use post method provided by the module for my purpose. I need to fetch the json response and write it to seperate variables and return them.
I have defined returnData variable outside the client.post method. I need to update this returnData variable inside the function which is passed as a parameter to the client.post method.
But I have a scope issue here. Although I try to update the returnData variable inside that function, when execution returns from client.post function, I see that the same values I have set (in this case null, but may be any value) before calling client.post function still persists in the variable.
How can I define scope well so that I can update an outside variable inside the function which I pass as a parameter to another function? Any help would be greatly appreciated.
Following is my Code:
module.exports = function(){
require("../config");
var restClient = require('node-rest-client').Client;
var client = new restClient();
var sessionID = null,
reqID = 1;
var login = function(username, password){
var requestParams = {};
var apiParams = {};
requestParams.jsonrpc = "2.0";
requestParams.method = ZABBIX_API_METHODS.login;
apiParams.user = username;
apiParams.password = password;
requestParams.params = apiParams;
requestParams.id = reqID;
requestParams.auth = null;
var args = {
data: requestParams,
headers:{"Content-Type": "application/json-rpc"} // ask response type to be application/json-rpc
};
var returnData = {};
returnData.status = null;
returnData.data = null
client.post(ZABBIX_API, args, function(resData,rawRes){
if(rawRes.statusCode == 200){
returnData.status = rawRes.statusCode;
returnData.data = resData;
}
else{
returnData.status = rawRes.statusCode;
returnData.data = "Request Failed!";
}
reqID = reqID + 1;
console.log(returnData.data);
});
console.log("SessionID:"+getSessionID());
return returnData;
}
var functions = {
login: login
}
return functions;
}
Thank you.
.post is Async, you should do it like this,
var login = function(username, password, callback){
..................
client.post(ZABBIX_API, args, function(resData,rawRes){
if(rawRes.statusCode == 200){
returnData.status = rawRes.statusCode;
returnData.data = resData;
}
else{
returnData.status = rawRes.statusCode;
returnData.data = "Request Failed!";
}
reqID = reqID + 1;
return {login: returnData};
});
//remove all below return statements

jQuery updating class variable values not working

I am writing a class in JavaScript for the first time and I am having some trouble writing new data to a class variable. I've been trying all sorts for hours but nothing seems to work!
function ClassName(productId) {
//create variables
this.productId = productId;
this.shop = [];
this.product = [];
//method that calls for response. On success will return {"status" : "success", "shop" : "someshop.com"}
this.auth = function() {
$.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp",
success: function(data) {
authCallback(data); //use callback to handle response
},
error: function() {
console.log("bad auth");
}
});
}
var authCallback = function(r) {
//using console.log(r) output the response OK
this.shop = r; //this runs with no errors
}
}
Now, as yo can see in the authCallback method I'm setting this.shop = r; but then if i refer back to this variable its still at its default value of [] .
var class = new ClassName(1);
class.auth();
console.log(class.shop); //this outputs []
I've also tried this in the Javascript console writing each line after each stage had been completed(waited for a response from class.auth() and output from authCallback() before then calling console.log(class.shop);
So, what am I doing wrong? Why isn't the variable updating to its new value?
When you just write:
authCallback(data);
then within authCallback you will have the wrong value of this, it'll either be null or the global object (depending on whether you're in strict mode or not).
Use:
success: authCallback.bind(this)
to ensure that this inside the callback actually represents your object.
You should also note that you cannot access this.shop until after the callback has completed. A more idiomatic implementation using modern jQuery techniques would be this:
this.auth = function() {
return $.ajax({
url: "http://website.com/api/auth/",
dataType: "jsonp"
}).done(this.authCallback.bind(this)).fail(function() {
console.log("bad auth");
});
};
this.authCallback = function(r) {
this.shop = r;
return this;
}
followed by:
var clazz = new ClassName(1);
clazz.auth().then(function(c) {
console.log(c.shop);
});

Login Test with PhantomJs

I have problem with my phantom Js code, as you see below, I have code to test my friend web server (make with node Js). Actually, it seem simple and perfectly runs.
var page = require('webpage').create();
var address = "http://localhost:3333";
// Route "console.log()" calls from within the Page context to the
// main Phantom context (i.e. current "this")
page.onConsoleMessage = function(msg) {
console.log("Console.log: ", msg);
};
page.onAlert = function(msg) {
console.log("Alert:", msg);
};
page.open(address, function (s) {
page.evaluate(function () {
function click(el){
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent(
"click",
true /* bubble */,
true /* cancelable */,
window, null,
0, 0, 0, 0, /* coordinates */
false, false, false, false, /* modifier keys */
0 /*left*/, null
);
el.dispatchEvent(ev);
}
document.getElementById('username').value = 'MyName';
document.getElementById('password').value = 'MyWord';
click(document.querySelector('input[type=submit]'));
});
page.onNavigationRequested = function() {
// console.log("Moved", JSON.stringify(arguments))
// to check whether send or not
page.render("printscreen" + ".png");
};
setTimeout(function(){
page.render("nextprintscreen" + ".png");
phantom.exit();
}, 3000);
});
BUT
when I declare
var userName = 'MyName';
var passWord = 'MyWord';
then place it below
var address = "http://localhost:3333";
and exchange
document.getElementById('username').value = 'MyName';
document.getElementById('password').value = 'MyWord';
with
document.getElementById('username').value = userName;
document.getElementById('password').value = passWord;
it return invalid username or password from my friend web server. Can you help me how to figure it out and why it happen. It's my first 'javascript world' code.
I already read this question and another variation then a suggestion
but it's just make me more confuse.
Thanks,
Ahmad
The problem is that page.evaluate() is sandboxed, and so has no access to the variables of your phantom script.
Since PhantomJS 1.6, JSON-serializable arguments can be passed to the page.evaluate().
The arguments and the return value to the evaluate function must be a simple primitive object. However, an object can be serialized via JSON.
You can change your code to this :
page.evaluate(function (login, pwd) {
...
document.getElementById('username').value = login;
document.getElementById('password').value = pwd;
...
}, userName , passWord );

Javascript and jQuery: accessing class member variables from event callback

I am writing a Javascript SDK to interact with a web service. I am using jQuery to do my AJAX calls.
When an AJAX call fails, I have registered an event handler for the ajaxError that gets called at the top of my .js file. My problem, and I don't understand why, is that when it gets called I have no way of accessing class member variables for my Akamanda.Client.
I tried adding another method for Akamanda.Client as .prototype.logError, which got called by the jQuery Ajax handler, but even then a test for (this.logging) failed as well.
How can I access class member variables from jQuery callbacks? What am I failing to understand here? Akamanda.Client.logging is undefined from the ajaxError callback.
My code for the SDK:
$(document).ajaxError(function(event, jqxhr, settings, exception) {
// more robust error handling for different conditions
if (Akamanda.Client.logging) {
console.log('FAILED: ' + settings.type + ' ' + settings.url + ' => ' + exception);
}
});
Akamanda.Client = function(options) {
this.URL = options.URL || 'http://m-test.akamanda.com';
this.baseURL = this.URL + '/api/' + Akamanda.API_VERSION;
this.feedsURI = '/websyndication/feed/';
// who is the client? (iphone/android/web)
this.clientName = options.clientName;
// For development: Logging and buildcurl IS ON, for production: OFF
//this.logging = options.logging || true;
this.logging = true;
// called when a user is not authorised (Disabled)
// this.logoutCallback = options.logoutCallback || null;
}
Akamanda.Client.prototype.getFeeds = function(callback){
var feeds = [];
$.getJSON(this.baseURL + this.feedsURI, function(data) {
$.each(data, function(index, feed) {
feeds[index] = {
name: feed.name,
title: feed.title,
link: feed.link
};
})
callback(feeds);
});//.error(function(err) { (disabled at the moment in favour of ajaxError event)
// console.log('Error: ' + err.error);
// });
}
My code for the client (in another JS source file):
var options = { logging: true };
myAPI = new Akamanda.Client(options);
var feeds = [];
var articles = [];
function getFeeds()
{
myAPI.getFeeds(function(AkamandaFeeds) {
feeds = AkamandaFeeds;
showFeeds();
});
}
As far as I can see from the code you posted, you are never instantiating an object of type Akamanda.Client.
var Client = new Akamanda.Client();
or
var Akamanda.Client = {};
Akamanda.Client.logging = ....
JSBin Example: http://jsbin.com/ajidig/1/edit
Ok, here a little example(real code but very simplified):
//we wrap our code in a self invoking function so that we don't pollute the global namespace, see http://stackoverflow.com/questions/6715805/self-invoking-functions-javascript for further details
(function(){
//create your object that holds all your function, that are different ways to do this
var Akamanda = {};
//a private function
function ErrorHandler(clientObj) {
this.clientObj = clientObj;
//do whatever with clientObj
this.log = function(){..}
}
//private constructor for clientobj
function Client(options){
..
}
Akamanda.Client = function(){
var newClient = new Client({..});
//setup
Akamanda.ErrorLogging = new ErrorHandler(newClient);
return newClient;
}
//bind our service to the window object to make it accesible
window.Akamanda = Akamanda;
})()
//client
var myAPI = Akamanda.Client();
Akamanda.ErrorLogging.log();
I hope this basic examples helps. If you need to know more about Javascript Patterns, I can recommend this book http://jsninja.com/ by John Resig, the creator of jQuery.
Depending on what you want to do, there's also a lot of frameworks like http://backbonejs.org/ that help with this kind of stuff.

Uncaught TypeError: Object has no method ... Javascript

I'm having an issue where I get an error that says...
"Uncaught TypeError: Object f771b328ab06 has no method 'addLocation'"
I'm really not sure what's causing this. The 'f771b328ab06' is a user ID in the error. I can add a new user and prevent users from being duplicated, but when I try to add their location to the list, I get this error.
Does anybody see what's going wrong? The error occurs in the else statement of the initialize function as well (if the user ID exists, just append the location and do not create a new user). I have some notes in the code, and I'm pretty sure that this is partly due to how I have modified an example provided by another user.
function User(id) {
this.id = id;
this.locations = [];
this.getId = function() {
return this.id;
};
this.addLocation = function(latitude, longitude) {
this.locations[this.locations.length] = new google.maps.LatLng(latitude, longitude);
alert("User ID:" );
};
this.lastLocation = function() {
return this.locations[this.locations.length - 1];
};
this.removeLastLocation = function() {
return this.locations.pop();
};
}
function Users() {
this.users = {};
//this.generateId = function() { //I have omitted this section since I send
//return Math.random(); //an ID from the Android app. This is part of
//}; //the problem.
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
this.getUser = function(id) {
return this.users[id];
};
this.removeUser = function(id) {
var user = this.getUser(id);
delete this.users[id];
return user;
};
}
var users = new Users();
function initialize() {
alert("Start");
$.ajax({
url: 'api.php',
dataType: 'json',
success: function(data){
var user_id = data[0];
var latitude = data[1];
var longitude = data[2];
if (typeof users.users[user_id] === 'undefined') {
users.createUser(user_id);
users.users[user_id] = "1";
user_id.addLocation(latitude, longitude); // this is where the error occurs
}
else {
user_id.addLocation(latitude, longitude); //here too
alert(latitude);
}
}
})
}
setInterval(initialize, 1000);
Since I get the ID from the phone and do not need to generate it here (only receive it), I commented out the part that creates the random ID. In doing this, I had to add a parameter to the createUser method within Users() so that I can pass the ID as an argument from Initialize(). See the changes to createUser below:
Before, with the generated ID (the part where the number is generated is in the above code block with comments):
this.createUser = function() {
var id = this.generateId();
this.users[id] = new User(id);
return this.users[id];
};
After, with the ID passed as an argument:
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
If anyone has any suggestions I would really appreciate it. Thanks!
Here you're getting user_id by :
var user_id = data[0];
So it's a part of the json answer : maybe a string or another dictionnary, this can't be a user object. You should try to update your code in your success function inside the "if" block by :
user = users.createUser(user_id);
//The following line is a non sense for me you put an int inside
//an internal structure of your class that should contain object
//users.users[user_id] = "1";
user.addLocation(latitude, longitude);

Categories

Resources