Question

I have a promise chain that looks like this ...

this.getFile(fileId).then(updateFile).then(displayAllDoneMessage);

where getFile() and updateFile() each use ngResource to construct the appropriate JSON call, and then return the $resource.$promise.

The chain fires OK, but I'm having a problem accessing $scope from within updateFile

So in getFile() I have

// this works
this.status.text = "fetching file"

But in updateFile() I have

// this fails at runtime because "this" is not $scope
this.status.text = "updating file"

What am I doing wrong, or what extra do I need to do to make $scope available within updateFile()?

I should possibly add that I'm using TypeScript in case that is significant.

Was it helpful?

Solution 2

When you call this.getFile, the context is this, in your case, I guess this is the $scope object so this inside getFile is the $scope.

But updateFile and displayAllDoneMessage are invoked by the framework as a callback and this no longer refers to the $scope.

Try .bind to bind the $scope as the context:

this.getFile(fileId).then(updateFile.bind(this)).then(displayAllDoneMessage.bind(this));

For older browsers, you could include this script as a polyfill (quoted from the docs):

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

OTHER TIPS

If you are using TypeScript and you want to maintain your this pointer, then you need to ensure you are using the lambda syntax.

Assuming you have saved off your $scope as a private/public variable in your constructor like this:

constructor(private $scope){ /* snip */ }

Then you can simply do this to ensure you are accessing your $scope like so:

this.getFile(fileid)
.then((result) => {
   return this.$scope.updateFile(result);
})
.then((result) => {
   return this.$scope.displayAllDoneMessage(result);
});

Under the hood this gets compiled down to something like the following:

//Save 'this' off to a closure
var _this = this;

_this.getFile(fileid)
.then(function(result){
   return _this.$scope.updateFile(result);
})
.then(function(result){
   return _this.$scope.displayAllDoneMessage(result);
});

TypeScipt uses a closure to maintain the appropriate references to this

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top