alexk
alexk

Reputation: 1528

How do I inject a dependency in a class definition of a component?

I have this class-defined component (transpiled later by webpack and babel back into ES5). I need to use the $http service in one of its methods. How do I do that? Where do I inject $http as a dependency? If I do it in the constructor arguments, I get an error as if I hadn't injected it at all. Maybe classes is not the way to go here?

angular.module('myApp').component('home', {
    template: require('./home.component.html'),
    controller: class HomeCtrl {
        constructor() {
        }
        doMe() {
            $http.get('http://www.yahoo.com/');
        }
    }
});

Upvotes: 8

Views: 3760

Answers (3)

gkalpak
gkalpak

Reputation: 48212

ES2015 classes (or transpiled classes) are just syntactic sugar over prototypal inheritance. What this means is that the methods you define are put on the prototype of the "class". In order to be able to access the dependencies injected in the constructor, you need to somehow store them for later reference by the prototype method.

This is usually done by putting them on the instance:

function HomeController($http) {
  this.$http = $http;
}
HomeController.prototype.doMe = function() {
  this.$http.get('http://www.yahoo.com/');
};

In class-based syntax this translates to:

class HomeController {
  constructor($http) {
    this.$http = $http;
  }

  doMe() {
    this.$http.get('http://www.yahoo.com/');
  }
}

EDIT:
If you are using TypeScript, you can save some boilerplate by using access modifiers on the constructor arguments. E.g.:

class HomeController {
  constructor(private $http) {}
}

...which is shorthand for:

class HomeController {
  private $http;

  contructor($http) {
    this.$http = $http;
  }
}

EDIT 2:
If you want to make your controller minification-friendly, you can use one of the options described here (possibly along with a tool like ngAnnotate). For example, this is how you could use the "$inject Property Annotation" method:

ES5

HomeController.$inject = ['$http'];
function HomeController($http) {...}
HomeController.prototype.doMe = function() {...}

ES2015

class HomeController {
  constructor($http) {...}

  doMe() {...}
}
HomeController.$inject = ['$http'];

// OR

class HomeController {
  static get $inject() { return ['$http']; }
  constructor($http) {...}

  doMe() {...}
}

TypeScript

class HomeController {
  static $inject = ['$http'];
  constructor(private $http) {}

  doMe() {...}
}

Upvotes: 11

alexk
alexk

Reputation: 1528

In the end, I did:

controller: class {
    constructor($http, Restangular, $state) {
        Object.assign(this, {$http, Restangular, $state});
    }
    doMe() {
       // use this.$http, this.Restangular & this.$state freely here
    }
}

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222369

The class should have explicit $inject annotation in order to be properly minified:

class HomeCtrl {
    static get $inject() {
        return ['$http'];
    }
    // or unstandardized shortcut:
    // static $inject = ['$http'];

    constructor($http) {
        this.$http = $http;
    }

    doMe() {
         this.$http...
    }
}

Upvotes: 3

Related Questions