I have the following code
this.userInfo = 'bla';
request({
url: 'https://api.api.ai/v1/entities?v=20150910',
headers : {
Authorization: "Bearer " + process.env.APIAI_ACCESS_TOKEN
},
method: 'GET'
}, function (error, response, body) {
if (error) {
console.log('Error sending message: ', error);
} else if (response.body.error) {
console.log('Error: ', response.body.error);
}
console.log(this.userInfo);
}.bind(this));
When I try to print the this.userInfo variable I get undefined but I did the bind() on this. Could someone explain me what's going on ?
In your code scope of this is override by other functions so the value which you have set is not available.
When you call bind with this which is available out side function then it have same value as you have set, see below corrected code.
let self= this;
self.userInfo = 'bla';
request({
url: 'https://api.api.ai/v1/entities?v=20150910',
headers : {
Authorization: "Bearer " + process.env.APIAI_ACCESS_TOKEN
},
method: 'GET'
}, function (error, response, body) {
if (error) {
console.log('Error sending message: ', error);
} else if (response.body.error) {
console.log('Error: ', response.body.error);
}
//scope of 'this' in callback function are removed, so have set value to self variable
console.log(self.userInfo);
}.bind(this));
I'd highly recommend checking out the MDN article on the this keyword in JavaScript, to me it looks like you're misunderstanding the purpose of the this keyword in JavaScript.
In Node.js it's slightly different too, in the browser in the global scope, this is always window.
In Node.js all modules are executed in their own closure, whereas by default browsers run the global scope.
TL;DR as far as I know you can't write code like this in JS. Check out this StackOverflow answer for more detail.
Instead of using this you may want to setup a variable within that module, and store userInfo into that.
Related
I've searched around for and answer to my problem, but I can't seem to find anything like it.
My code is returning a value as expected!
I'm building an app in React-Native and am currently working on the login. I need to save the userId, userEmail, and userToken, and came up with AsyncStorage and Expo's SecureStore as options, but I've got a really odd problem that I've never seen before.
I can set and read the required data, but when I try and use it, it just doesn't work, but when I hardcode it, it does? (it's a little hard to explain).
This is where the problem lies. For ease of reading I have left out the rest of the code, it works just fine if I hardcode the values below...
// Set the email and token
// Before setting these, I logged the key's values and (of course), they were null
// After setting them I logged them out again and they're set
/** from promise (no problems) **/
SecureStore.setItemAsync('shifterEmail', responseJson.email);
SecureStore.setItemAsync('shifterToken', responseJson.token);
/** returns response (no problems) **/
...meanwhile, further down the page...
/** another function **/
let shifterEmail = '';
let shifterToken = '';
SecureStore.getItemAsync('shifterEmail')
.then((email) => {
shifterEmail = email;
console.log('API email: ', shifterEmail); // my#email.com <- good!
})
.catch((error) => {
console.log('API email error: ', error);
});
SecureStore.getItemAsync('shifterToken')
.then((token) => {
shifterToken = token;
console.log('API token: ', shifterToken); // random token <- good!
})
.catch((error) => {
console.log('API token error: ', error);
});
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Token' +
' token=' + shifterToken + // WHAAAT <- doesn't work
', email=' + shifterEmail, // WHAAAT <- doesn't work
};
I had a brainwave and though it might've been good old asynchronicity, and tried nesting them in their callbacks, but it makes no difference.
I've shown the code for SecureStore here, but it's the same with AsyncStorage, i can get the expected values for the email and token, but it just doesn't work.
But, here's the odd bit, that I don't understand...
if I hardcode the token and email in the return, it works just fine!
mind = blown
I can use typeof to ensure its a string.
I've tried JSON.stringify() and JSON.parse() on set and get.
I've tried setting and getting it in-line, and as callable functions.
I've tried nesting the callbacks, and leaving them sequential.
I even tried adding a timeout to the return to make sure everything's been set.
...nothing works :(
On a side note: These are both strings, but I had the same problem with a number, and the only way I got it to work was to bring the 'get' (from both AsyncStorage and SecureStore) out of their callable functions and have the code in-line.
Also, if, instead of saving the email and token I just add it to a variable and use that in the return, it works just fine, but this is no good as the user would have to log in every time they open the app.
Totally baffled. Happy to answer any questions, I've tried loads, so we can have a good old chat, or, if you can suggest a different way of handling logging in, I'd love to hear that too!
Why can't I use the value when it's RIGHT THERE, but can hardcode the same thing ???
Thanks!
It looks like it's an async issue. Based on the code snippet you have provided the return statement could possibly run before the .then callback is called.
An easy fix could be to use async/await or Promise.all
For example:
Async/Await
const getToken = async () => {
try{
const email = await SecureStore.getItemAsync('shifterEmail')
const token = await SecureStore.getItemAsync('shifterToken')
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Token' +
' token=' + token +
', email=' + email
};
} catch(error) {
console.error(error);
throw error;
}
}
Promise.all
const getToken = () => {
Promise.all([
SecureStore.getItemAsync('shifterEmail'),
SecureStore.getItemAsync('shifterToken')
])
.then((response) => {
const [email, token] = response
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Token' +
' token=' + token +
', email=' + email
};
}
).catch((error) => {
console.error(error);
throw error;
});
}
The above code is run asynchronously and, as such, the below code in the return statement has not been set yet.
You haven't posted the entire function, but a return statement probably won't work here and you'll need to pass a callback function.
const doSomethingWithLogin = headers =>
console.log(headers)
Promise.all([
SecureStore.getItemAsync('shifterEmail'),
SecureStore.getItemAsync('shifterToken')
])
.then(([shifterEmail, shifterToken]) =>
Promise.resolve({
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Token' +
' token=' + shifterToken +
', email=' + shifterEmail,
})
)
.then(doSomethingWithLogin)
Really old question, but I just ran into the same issue and think I know the answer.
after getting token from storage, when doing something like:
'token=' + token
what actually comes out is:
token="sadfaswerw"
In other words, the token is wrapped in quotes for some reason.
Simple enough to replace the quotes, but a bit odd.
I'm trying to write a plugin for HomeBridge and have run in to a problem. I started out by just writing some code, start up Homebridge and test it. That worked out find in the beginning but after a while as I added more functionality every time I start HomeBridge there's a lot of testing just to ensure that I haven't broken anything. I mainly work with Java and have just started out with JavaScript. I have copied the patten on how the plugin should be designed. Theres very little documentation about how to write a plugin so I'm kind of in the dark here when it comes to best practice and so on. I have simplified the code so it won't take that much space but the structure is intact. So my question is: How do I test this code?
index.js
let Service, Characteristic;
let request = require('request');
module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerAccessory("homebridge-myplugin", "MyPlugin", MyPluginDevice);
};
function MyPluginDevice(log, config) {
this.log = log;
this.url = config['url'];
this.id = config['id'];
}
MyPluginDevice.prototype = {
getSwitchOnCharacteristic: function(callback) {
this.httpRequest(this.url, null, 'GET', function(error, response, body) {
if (error) {
this.log("Fail: %s", error.message);
callback(error);
} else {
const newState = body['state'];
return callback(null, newState);
}
}.bind(this)
);
},
setSwitchOnCharacteristic: function(state, callback) {
this.httpRequest(this.url, requestBody, 'POST', function(error, response, body) {
if (error) {
this.log("Fail: %s", error.message);
callback(error);
} else {
callback();
}
}.bind(this));
},
httpRequest: function(url, body, theMethod, callback) {
request(
{
url: url,
body: body,
method: theMethod,
auth: {
user: 'nexa',
pass: 'nexa',
sendImmediately: false
},
json: true
},
function(error, response, body) {
callback(error, response, body)
})
},
getServices: function() {
let informationService = new Service.AccessoryInformation();
informationService.setCharacteristic(Characteristic.Manufacturer, "My Nexa plugin").setCharacteristic(Characteristic.Model, "My Nexa Plugin Model").setCharacteristic(Characteristic.SerialNumber, "A very special number");
this.switchService = new Service.Switch(this.name);
this.switchService
.getCharacteristic(Characteristic.On)
.on("get", this.getSwitchOnCharacteristic.bind(this))
.on("set", this.setSwitchOnCharacteristic.bind(this));
return [this.switchService];
}
};
After having been at it for a while I can't write a test for this code. I'm having problem with variables being null and however I try to get around it I always end up with some part of the code not initiated. I have tried:
let MyPluginDevice = require('./index');
let myDevice = new MyPluginDevice(homebridgeMock);
but that leaves me with a problem with getSwitchOnCharacteristic and setSwitchOnCharacteristic. My other approach is to access the MyPluginDevice through my homebridgeMock. But that leaves me with getSwitchOnCharacteristic and setSwitchOnCharacteristicas null or not a function.
I'm kind of out of ideas and my skills is not that good so I can spot the problem or of I have implemented the code in a way that it can't be tested. I got no idea how other developers have done when writing a plugin but I would feel much safer if I could have a few tests running.
Help me Stackoverflow, you are my only hope!
So I'm having trouble getting one javascript function to finish before the next one starting. I've spent quite a lot of time trying to use callback methods described on other stackoverflow posts. I could get simple examples that used timeouts to work but couldn't get it to work with my API request. I stumbled upon async.js and thought that perhaps using async.series would be a good idea to get my two functions to perform one after another. So I tried this approach, however I still seem to be having the problem where the first function takes a bit longer to execute (which is fine) but the execution process moves past this function instead of waiting for it to end. I feel I have a misconception of some sort since I have tried several methods but to no avail.
What is strange is, is that that when running server.js, it goes into the first function but then it leaves the async.series() function even before the request is finished. When I print inside of tokenReq(), I can see that the request was successful as a token code is returned successfully however this happens to late as execution has moved on. The output is shown below.
server.js:
var access_code;
async.series([
function() {
access_code = queries.data.tokenReq(code);
console.log("Finished inside function 1");
},
function() {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
}
],
function(err, access_code) {
});
console.log("Outside");
queries.js:
tokenReq: function(code) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return access_token;
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return "error";
}
});
},
Output:
Finished inside function 1
Outside
INSIDE FUNCTION REQUEST, Token: 8Swhd.......
You missed a major point here. Since node.js is asynchronous there should not be a way to know when a function completes its execution. That is why we specify callbacks so that the invoking function knows whom to call when it finishes its execution. Once you have functions with callbacks, you can enforce series/parallel/waterfall behavior with async module.
tokenReq: function(code, cb) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return cb(null, access_token);
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return cb(new Error("whatever"));
}
});
},
Now, you can use the callback inside server.js
var access_code;
async.series([
function(cb) {
return queries.data.tokenReq(code, cb);
},
function(access_code, cb) {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
// do whatever you want after this
return cb();
}
],
function(err, access_code) {
if (err) {
console.log(err);
}
// wrap your logic around a function and call the correspoding callback here
});
I'm attempting to make a Web 2.0 API call via AngularJS using $http.post that returns JSON and as weird as this may sound, it worked an hour ago, now it doesn't and I haven't changed a thing. The application continues to work in all other browsers, it just fails in Edge.
var _login = function (loginData) {
var data = "";
var grant = [insert data to authorise user on server];
var deferred = $q.defer();
$http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
_userlogin(response);
deferred.resolve(response);
}).error(function (err, status) {
console.log(err + " " + status);
_logOut();
deferred.reject(err);
});
return deferred.promise;
};
I've had to take some info out there because it's security info, but the functionality should be the same, so, from debugging I know that this is where the application stumbles. I also know that the logindata passed in is valid and I've tried the same call with the same grant on a REST client and that works fine also like I mentioned before the same call with no alterations runs in any other major browser (including IE weirdly).
When that call is run, the front end angular does the following:
$scope.authUser = function (username, password) {
var loginData = { userName: username, password: password, useRefreshTokens: login.useRefreshTokens}
authService.login(loginData).then(function (response) {
console.log(response);
sessionStorage.setItem("status", "User Logged In As: ");
sessionStorage.setItem("username", username);
global.template = "templates/dashboard.html";
login.password = "";
},
function (err) {
console.log(err);
login.message = err.error_description;
$("#passwordError").modal();
});
};
The application stops at login.message = err.error_description;, because it's not returned from the API call. The error is: Network Error 0x2efd, Could not complete the operation due to error 00002efd. and Unable to get property 'error_description' of undefined or null reference.
Edit: Forgot to mention - the application works when Fiddler is open and capturing traffic. That's the strangest part.
Take a look at this post which used the solution mentioned here. They're saying the issue was with interacting between localhost to localhost.
I'm glad I was able to help.
Im trying to call my nodejs server and show the results from an angularjs app. ive been following an example, but when i change the example code for my code, it always calls the error callback. i have a feeling its something wrong with my server, but i cant make it work, its nodejs without express or other libraries, and all other answers involve using express. how can i make it work without using express?
the code in angularjs, where i call the server:
app.controller('Main', function ($scope, $http) {
$scope.init = function() {
//var api = 'http://api.trakt.tv/calendar/premieres.json/7c989229f2fa626832e7576eb59820e9/20131103/30/?callback=JSON_CALLBACK';
$http.jsonp('http://localhost:8888/selectAllLocations?callback=JSON_CALLBACK').success(function(data) {
console.log(data);
}).error(function(error) {
console.log('My error: ' + error);
});
};
});
if i use the value in the var api, the result is ok and success is called, not with mine.
This is the code in my nodejs server.
var result = sequelize.query(query, null, options, parameters)
.success(function (rows) {
if (type == 'select') {
response.writeHead(200, { "Content-Type": "application/json" });
response.write(JSON.stringify(rows));
response.end();
} else if (type == 'modify') {
response.writeHead(200, { "Content-Type": "text/html" });
response.write("Query was successful");
response.end();
}
}
).error(function (e) {
console.log("An error occured: ", e);
response.writeHead(404, { "Content-Type": "text/html" });
response.write("There was an error man, shits on fire yo ---> " + e);
response.end();
}
);
Im pretty sure i have to wrap the json result in a function, but i dont know how, and i cant even know the function name, as this is all i get in my server:
path: /selectAllLocations
params: {"callback":"angular.callbacks._0"}
I understand im returning the pure json, and i can see it in chrome in my response, so, there is something preventing it from calling the success function. Can somebody point me in the right direction?
The function name is specified as the callback parameter.
response.write(params.callback + '(' + JSON.stringify(rows) + ')');