nickponline
nickponline

Reputation: 25914

How do I access outer scope variables from a callback in Angular?

I have a loop over some webpages and I want to check if they are available:

var urls = ['http://www.google.com', 'http://www.facebook.com'];
for(i in urls) {
    $http({method: 'GET', url: urls[i] })
            .success(function(data, status, headers, config) {
                console.log(urls[i] + ' is available.');
            })
            .error(function(data, status, headers, config) {
                console.log(urls[i] + ' is NOT available.');
            });
}

but urls[i] is not available inside the callback functions how do I make is accessible?

Upvotes: 2

Views: 2984

Answers (2)

Ethan Brown
Ethan Brown

Reputation: 27282

Just based on that snippet, the urls variable should be available from within the functions. But there are some other things that are going on...that are probably causing the problem.

Since those methods are asynchronous, they will execute at some later point. So what could be happening is this:

var urls = ['http://www.google.com', 'http://www.facebook.com'];
for(i in urls) {
    $http({method: 'GET', url: urls[i] })
            .success(function(data, status, headers, config) {
                console.log(urls[i] + ' is available.');
            })
            .error(function(data, status, headers, config) {
                console.log(urls[i] + ' is NOT available.');
            });
}
// some time later...
urls = null;

Even worse, i inside your callbacks is not going to be what you expect it to be. The loop will execute first, and i will end up being (for both URLs) the last URL called. This is a classic case of scoping issues with asynchronous functions.

By the time the functions get around to executing, urls could be null, or used for something else. And, as already mentioned, i will not be what you expect it to be. Try wrapping the $http call in an immediately-invoked function expression (IIFE):

var urls = ['http://www.google.com', 'http://www.facebook.com'];
for(i in urls) {
    (function(urls, i){
        // urls now "captured" in function-scope variable
        $http({method: 'GET', url: urls[i] })
            .success(function(data, status, headers, config) {
                console.log(urls[i] + ' is available.');
            })
            .error(function(data, status, headers, config) {
                console.log(urls[i] + ' is NOT available.');
            });
    })(urls, i);
}

I would also like to point out that it's considered a bad idea to use for/in on arrays: you should change that to using a regular for loop with an index variable, or use .forEach. Here's how I would do it (which also, handily, eliminates the IIFE):

var urls = ['http://www.google.com', 'http://www.facebook.com'];
urls.forEach(function(url){
    $http({method: 'GET', url: url })
        .success(function(data, status, headers, config) {
            console.log(url + ' is available.');
        })
        .error(function(data, status, headers, config) {
            console.log(url + ' is NOT available.');
        });
});

Upvotes: 3

Mardie
Mardie

Reputation: 1672

You could get the url from "config.url" inside the callback.

Upvotes: 1

Related Questions