Make a scope inside a function global - javascript

Im tryin to access my $rootScope.newBloodneeded but I cant access it outside the function, I tried rootscope so I can call it as global but still it gives me undefined
.controller('editbloodrequestCtrl', function($scope,Bloodrequest,$rootScope,$routeParams) {
$rootScope.newBloodneeded;
Bloodrequest.getBloodrequest($routeParams.id).then(function(data) {
if (data.data.success) {
$scope.newBloodneeded = data.data.bloodrequest.blood_component;
$rootScope.newBloodneeded = $scope.newBloodneeded;
//gives me output when I console here
} else {
app.errorMsg = data.data.message; // Set error message
}
});
console.log($rootScope.newBloodneeded); //gives me undefined
}

Assuming $rootScope is working correctly, this is a problem of asynchronicity NOT scope—when you try to run:
console.log($rootScope.newBloodneeded); //gives me undefined
...getBloodRequest has not necessarily finished. You set the $rootScope.newBloodneeded in the then, which is ONLY run after getBloodRequest resolves, which may be far, far after your console log finishes.
Bloodrequest.getBloodrequest($routeParams.id).then(function(data) {
...
$rootScope.newBloodneeded = $scope.newBloodneeded;
...
One fun test you can try is to wrap that console log in setTimeout for a LONG time (when you are guaranteed/sure that getBloodRequest has finished). That should prove to you that timing is the issue, not one of function scoping.
Basically:
setTimeout(() => console.log($rootScope.newBloodneeded), 10000000000) // or whatever timing guarantees completion
The solution here is to also chain whatever logic that requires $rootScope.newBloodneeded in .then. If THAT doesn't work, you might want to create a Promise that you access from elsewhere. (That's beyond the scope of this question, and we'd need more detail to figure out best implementation).

Related

Passing context to Sinon stub's fake function

I'm using the mochawesome test reporter for my mocha tests. I'd like it to record my logging as well, and attach it to whatever test was running when the log was written. This can be achieved with addContext(). However, I also want the logs to output to the console, so I can monitor them as the test is running, rather than wait til it all finishes and the report is generated.
I've got 98% of a solution, based on this answer, but am struggling based on the fact that mochawesome's addContext() requires you to pass it a test object. Here's what I've got:
beforeEach(`Spy on logger`, function() {
const origLogInfo = logger.info.bind(logger),
origLogError = logger.error.bind(logger),
testContext = this
sinon.stub(logger, 'info').callsFake(function(message) {
addContext(testContext, `INFO: ${message}`)
origLogInfo.call(testContext, message)
})
sinon.stub(logger, 'error').callsFake(function(message) {
addContext(testContext, `ERROR: ${message}`)
origLogError.call(testContext, message)
})
})
afterEach(`Remove stubs`, function() {
logger.info.restore()
logger.error.restore()
})
it('counts approved hours', async function() {
logger.info(`Approving timesheets...`)
...
So before each it(), I get a reference to the original logger.info() and logger.error() functions, then I stub them. The stub calls a function which calls mochawesome's addContext() function, passing it a reference to the beforeEach() and whatever string I've sent to logger.info(). Then the original is called.
My problem is that every time addContext() is called, it attaches the logs to the beforeEach() hook. I can see why it's happening, too. Stepping through the code reveals that when beforeEach() is executing, this has two properties: currentTest, and test. The former refers to the it() which is about to be called, and the latter is the beforeEach() hook itself. This is great! If it stayed like this, addContext() would pick up currentTest and attach the logs to it (link to source).
By the time the argument to callsFake() is called, however, that same object has lost its currentTest property, so addContext() instead attaches the logs to the beforeEach() hook itself. Resulting in a test report with all the logs attached to the beforeEach hook instead of the relevant tests.
Is there any way I can write this so the callsFake() argument has a reference to the test that logger.info was called from? The following works, but requires extra code inside each it():
boundLoggerInfo = logger.info.bind(this);
boundLoggerInfo(`Approving timesheets...`)
Cheers! Hopefully I've provided enough info without being too verbose...
Update: I managed to work around the issue and achieve what I was after, after a good night's sleep:
testContext = {
currentTest: this.currentTest
}
Now a reference to currentTest is maintained even after beforeEach() loses it (still not sure why that happens).

Externalize a function in a Typescript method while maintaining closure

Reworded:
A common pattern is to pass callback functions, such as with Mongoose's save (just for example and simplified - no error handling):
someMethod(req:Request, res:Response){
document.save( function(err){ res.status(200).send({message: 'all good'})});
}
I'd like to externalize the callback. You can do this this way:
var respond = function(err:any, res:Response){
res.status(200).send({message: 'all good'});
}
someMethod(req:Request, res:Response){
document.save( function(err){ respond(err, res)});
}
...but ideally I'd like to do this by just passing a function like respond without having to create a call back function to enclose respond. I wanted to know if this is possible. Since the anonymous function has access to res, I thought there might be some way to gain access to res in a function defined externally. It appears there is not a way to do this so I'll live with wrapping it.
My original question was trying to isolate the specific issue I was interested in - which is to gain access to the caller's variables implicitly. Doesn't seem like that is possible. Fair enough.
Original Question:
I'd like to externalize a bit of code I use frequently and I'm having trouble understanding closure in the context of a Typescript method. Take a look:
var test = function(){
console.log("Testing external: "+JSON.stringify(this.req.body));
}
class Handler {
static post(req: Request, res: Response){
(function(){
console.log("TESTING anon: "+JSON.stringify(req.body));
}) ();
test();
}
}
Besides the fact that this does nothing useful, in this bit of code, the inline anonymous function has access to the req object, but the test() function does not. this in test is undefined. Removing this to match the inline function doesn't help.
I believe if I were to bind on this for the call I'd just end up with a reference to the Handler class when I really want to bind on the post method.
My motivation for doing this is that I want to make a function that can be passed as a callback to a bunch of different request handlers. When I write the functions inline it all works, but when I externalize it I can't get a closure over the variables in the enclosing method. I've read "You Don't Know JS: this & Object Prototypes", and in pure Javascript I can manage to make these sorts of things work but I'm obviously doing something wrong here (it may not be Typescript related, maybe I'm just messing it up).
So bottomline - is there a way I can externalize the handler and get access to the method variables as if I were writing it inline? I could just create an inline anonymous function as the callback that calls the external function with all the variables I need, but I want to really understand what is happening here.
This is not an answer, but will hopefully give me enough feedback to give you one because its not at all clear what you're actually trying to accomplish here and whether or not you actually understand what the terms mean is an open question since you use them correctly one minute and sketchily the next.
var test = function(){
console.log("Testing external: " + JSON.stringify(this.req.body));
}
In strict mode this will throw an error, in sloppy it will try to access the req property of the global object which is not likely what you want.
(function(){
console.log("TESTING anon: "+JSON.stringify(req.body));
}) ();
The IFFE wrapper is completely unnecessary, it literally adds nothing to the party. So why include it?
static post(req: Request, res: Response){
console.log("TESTING anon: "+JSON.stringify(req.body));
test(); // is this the spot where you are 'in-lining?'
}
What I think you want is this:
var test = function(reqBody) {
console.log("Testing external: " + JSON.stringify(reqBody));
};
class Handler {
static post(req: Request, res: Response) {
test(req.body);
}
}

Backbone: fetch and scope

Think I been staring at this to long but, I'm trying to bring an object outside the scope of the fetch success method when it completes the fetch.
cars.fetch().complete(function(){
newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
})
console.log(newSuggested) //undefined
How can I get the newSuggested outside the fetch scope after it successfully fetched?
Unless you have declared newSuggested somewhere above in the code, it is a global variable on the window (this is not the problem, just pointing it out).
The reason it is undefined where you are logging it, is because when that console.log statement is run, the fetch has not completed.
Whatever you are going to do with newSuggested, you need to do it from within the complete callback function.
// declare the variable using var, so it is not global
var newSuggested;
cars.fetch().complete(function(){
newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
console.log(newSuggested); // works!
// do something with newSuggested here, hard to tell what you are trying to do.
probablyUpdateViewInSomeWay(newSuggested);
});
// fetch not complete here!
// this is not a scope problem, but an async problem.
// complete callback has not been called yet.
console.log(newSuggested) //undefined, this is expected
Side note: complete is deprecated in jQuery 1.8, so you should use done instead.
Your script is correct, you can even explicitly use window.newSuggested to make the variable global (though is default like this).
You have to move the console.log after "complete" as call order in the execution flow
cars.fetch().complete(function(){
window.newSuggested = cars.models.filter(function (model) {
return _.contains(model.attributes.suggestedTo, storedVin)
});
global_log();
})
function global_log(){console.log(newSuggested);};

Calling a method within a Javascript Object

I'm trying to create a javascript object that can call other methods within itself. However, I'm running into a weird problem that I just can't seem to figure out.
I have the following code
myObjectDef = function() {
this.init = function() {
//do some stuff
this.doSecondInit();
}
this.doSecondInit = function() {
//do some more stuff
}
}
myObject = new myObjectDef();
myObject.init();
I am getting an error that states "Message: Object doesn't support this property or method". And it ends at this.doSecondInit();. I can't quite figure out why it's doing this. My code runs great up to the call to the second method. How do I make this work?
There's an extra set of parenthesis here:
this.doSecondInit() = function() {
You can't assign to the result of a function call, let alone to the result of a function that doesn't even exist.
After your edit, your thing seems to work fine:
http://jsfiddle.net/nabVN/
You sure you didn't have the same typo in your actual code? Better start getting used to not putting that () after every function call, which is probably a bad habit carried over from languages where functions aren't values.

JavaScript object returned from function available in Chrome Dev Tools, but not from script

EDIT
I was a bit quick there, the problem arises in the function and not where I first said. Here is the function:
function returnAnObject(url) {
var apiurl = 'http://url.com';
var info = {};
$.getJSON(apiurl, function(data) {
$.extend(info, {
a : data.x,
b : data.y,
c : data.z
});
});
console.log(info); // Shows object as usual
console.log(info.a); // Shows undefined
return info;
}
Does that make it clearer?
END EDIT
Ok so, I have a little problem.
I have a function that returns a fairly simple object, which looks something like this:
{
a: 'x',
b: 'y',
c: 'z'
}
I save it to a variable like this:
var something = functionThatReturnsObject(someargument);
console.log(something); // In chrome dev tools, I see the object and its values
console.log(something.a); // This, however, logs undefined
console.log(something['a']); // This also logs undefined
Why is this? I think I'm going crazy here, I must have overlooked something...
The really weird part happens if instead of
var something = functionThatReturnsObject(someargument);
I write
window.something = functionThatReturnsObject(someargument);
console.log(something); // Still works, showing the object and properties
console.log(something.a); // Still doesn't work
console.log(someting['a']); // Still doesn't work
If I now access the object directly from the dev tools, inputting
something; // returns object, I can see everything in it etc.
something.a // Now, for some mysterious (to me) reason, this works, returning the value of a
So, does anyone understand what is going on here?
As I suspected. You're assigning info in the success handler for an asynchronous function call. The success handler does not execute until AFTER the ajax call completes, but your function returns right after the ajax call starts (and long before it finishes and succeeds). You may not believe this, but this is the fourth time I've answered this exact same type of issue today. It's a very common mistake. Because of the inline success handler, it appears that it all happens inside of the main function, but in reality it happens long after that function finishes.
You can't use the return result from the ajax call until after the success handler is called. If you want to pass that result on to subsequent code, you will have to call that subsequent code from the success handler, not continue it after the returnAnObject function call.
It works in the dev tools because the ajax call completes by the time you type anything into the dev tools or look there. But, info.a is not available at the end of the returnAnObject function. It's only available when the success handler for the ajax function is called.

Categories