2

Sort of fighting with this at the moment as I don't understand why its happening. I'll show you the controller.

class ExampleClassController {
    constructor($scope, serviceItem, PrepService, consts) {
        // Services
        this.serviceItem = serviceItem;

        // Init Values
        this.grid = PrepService;
        this.dates = [
            moment().format('x'),
            moment().add(14, 'days').format('x')
        ];
        // Works fine
        console.log(this._init());

        // Events
        $scope.$on(consts.events.change, this._init);
    }

    _init() {
        // this.grid = {};
        // Returns null
        console.log(this, "in the init");
        return this.serviceItem.get().then((res) => this.grid = res);
    }
}

Here is the weird part, when I call this._init inside the constructor, its fine. Returns the promise as suggested. But when I call it in the $scope.$on event it just falls apart and says that this is null. I can't seem to figure out why its happening as it doesn't seem to happen to anyone else's example. Anything would be helpful, just insight as to why would be awesome.

Thanks!

Mike Huebner
  • 669
  • 1
  • 9
  • 23
  • 2
    You are losing the reference to `this` when using a callback. You can do `this._init.bind(this)` in `$scope.$on` – Pierrickouw Dec 02 '15 at 10:59
  • How did you know to use the arrow function in `_init`? `.then((res) => this.grid = res);` - apply the same logic to your `constructor` – CodingIntrigue Dec 02 '15 at 11:51

1 Answers1

3

As @Pierrickouw pointed in the comments from your question, you are losing the reference to this. You have multiple options to overcome this though:

  • Use Function.prototype.bind method

    // Events
    $scope.$on(consts.events.change, this._init.bind(this));
    
  • Use arrow functions (which have the property of holding the right context)

    // Events
    $scope.$on(consts.events.change, () => this._init());
    
  • Use angular bind function (this is just a variation of the first option)

    // Events
    $scope.$on(consts.events.change, angular.bind(this, this._init));
    

As a best practice, you should use either bind or the arrow function depending on your use case scenario.

  1. If you want to just delegate your handler with the right context, use .bind

    // .bind(theRightContext), e.g. this or $scope
    $scope.on('someEvent', this._init.bind(this));
    
  2. If you want to do some precomputations you should use the arrow function.

    $scope.on('someEvent', () => {
            // do some computations here
            console.log('will now call someMethod on', this);
            this.someMethod();
    });
    

Note that the arrow function from ES6 is equivalent to using .bind in ES5:

$scope.on('someEvent', function() {
        // do some computations here
        console.log('will now call someMethod on', this);
        this.someMethod();
}.bind(this));
eAbi
  • 3,220
  • 4
  • 25
  • 39
  • Which do you feel is best practice? I'm liking the .bind(this) method, but that is just because it is more visually appealing. I feel like the .bind is the way to go, but just not sure. – Mike Huebner Dec 02 '15 at 20:55