Lloyd Banks
Lloyd Banks

Reputation: 36659

Updating 'this' Context Property Inside of a $Promise In An Angular JS Service

I have a function being used in my service that is defined as:

var getData = function() {
        return anotherService.getData().$promise;
};

and a this property that I manipulate throughout the service.

this.someProperty = 'a string';

I call the above function inside the return section of my service:

return{
    updateProperty: function(){
        getData().then(function(data){
            this.someProperty = data;
        });
    }
}

In the above, I get an this is undefined related error in my browser console. I assume this is because the resolved $promise is an AJAX call and this is used out of context. What's the best way to manipulate a this property using the returned data from an AJAX call in this instance?

Upvotes: 15

Views: 2214

Answers (2)

Meir
Meir

Reputation: 14395

The simplest way would be to use the bind function. This function sets the 'this' context for the function call. So, in your case you'd have to use it twice so that the proper 'this' populates in.

return{
    updateProperty: function(){
        getData().then((function(data){
            this.someProperty = data;
        }).bind(this));
    }
}

This comes to ensure that the handler you passed to the promise is executed with the original 'this' (passed to updateProperty). Now, to pass the correct 'this' value to the updateProperty function, you should, in your controller do:

(myService.updateProperty.bind(this))();

There are numerous versions of binding, including binding the entire service. Also, have a look at lodash for function extensions.

I prepared a small pen to demonstrate this. It covers what I listed above, plus another important thing to note. When you use setTimeout, the handler is invoked with in the global context (in this case, 'window'), this is why I added a third bind, to make sure 'this' is relevant inside the timeout handler. I also added various count increment calls to demonstrate that 'this' is the same value along the way.

If this is a repeating scenario, you might want to pass either the target object (and then use the handler just to know it was updated), or a handler (which also needs binding). I added examples for these scenarios as well.

One last word, call, apply and bind are key to javascript and worth learning. Put some time into it and work your way out of context hell.

Upvotes: 4

Ben Lesh
Ben Lesh

Reputation: 108491

if you're manipulating this throughout your service, assign it to a variable like var self = this. The problem is that this is the context of a function and can be changed by other code using fn.call(context) or fn.apply(context, args). So it's liable to be different inside of the scope of any given function.

So just assign it to some variable and use it:

var self = this;

return {
    updateProperty: function(){
        getData().then(function(data){
            self.someProperty = data;
        });
    }
};

Upvotes: 13

Related Questions