1

I have this ES6 class, which fails when this.categories = data.data is executed with the error TypeError: Cannot set property 'categories' of undefined.

I think it is due to this refering to the context inside the promise.

How do I set the this.categories from inside the promise?

Thanks.

class NavbarController {
    constructor(bnCategoryService) {
        this.categories = [];    // This is what should be set with data.data.
        this.bnCategoryService = bnCategoryService;
        this.getCategories();
    }

    getCategories() {
        this.bnCategoryService.getCategories()  // Returns a promise
            .success(function(data, status, headers, config) {
                console.log(data.data);
                this.categories = data.data;    // This fails!
            })
            .error(function(data, status, headers, config) {
                console.log('Category service: An error occured getting tags from the server.');
            });
    }
}
Amit
  • 45,440
  • 9
  • 78
  • 110
skovmand
  • 4,372
  • 5
  • 25
  • 30
  • What is `.success`? what API is that? and why did you tag ES6? – Amit Nov 26 '15 at 20:47
  • (1) .success is a promise from a $http angular object, (2) the API is my websites api where data.data is an array of music tracks, (3) It's a class, therefore it uses ES6-syntax – skovmand Nov 26 '15 at 20:49
  • `success` is just a sugar for `then` with some http-response pre-parsing – vp_arth Nov 26 '15 at 20:53
  • @skovmand, you just don't understand how `this` works. You should cache it `var that = this;` or use `arrow functions`: `(data) => {this.categories = data.data;}` – vp_arth Nov 26 '15 at 20:55

2 Answers2

7

Simply use an arrow function in your success handler to get lexical this binding:

.success((data, status, headers, config) => {
  console.log(data.data);
  this.categories = data.data;    // This won't fail!
})
Amit
  • 45,440
  • 9
  • 78
  • 110
2

Even when your code implements an ECMA-Script 6 class, it doesn't mean that this keyword still has different meanings depending on scope.

Inside the success handler, this won't be your current class instance.

Refactor the whole code to declare a variable before calling the asynchronous operation which will hold the this value, and later use in the success handler the whole variable:

// This variable holds the "this" value from the class function scope.
var that = this;
this.bnCategoryService.getCategories()  // Returns a promise
            .success(function(data, status, headers, config) {
                console.log(data.data);
                that.categories = data.data;    // This fails!
            })
            .error(function(data, status, headers, config) {
                console.log('Category service: An error occured getting tags from the server.');
            });
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206