qzr
qzr

Reputation: 732

How to use typescript inheritance with angular dependency injection

I'm building an application with angular and recently switched to TypeScript. The core of my application consists of multiple actual classes that interact with eachother (so not just global services). The way I did this is as follows:

class Person {
    constructor(private $http, public name: string) {
        // ... use this.$http for some task
    }
}

class PersonFactory {
    static $inject = ['$http'];

    constructor(private $http) {}

    create(name: string) {
        return new Person(this.$http, name);
    }
}

angular.module('app').service('personFactory', PersonFactory);

So if some other class needs to create a Person object, it needs to get injected with PersonFactory and call its create method. This works ok, even though it requires some boilerplate.

My main problem arises when I want to subclass Person:

class Student extends Person {
    constructor(private $http) {
        super(this.$http, 'Some actual name')
    }
}

I could follow the same pattern as above, but then I need to, again, create a StudentFactory that passes the required services like $http to the class constructor, which in turn passes it to the constructor of the super method. If I now had multiple such extending classes and would change the base class Person, I had to change every extending class and their factories. Is there a better way to approach this problem? Might it be better to just remove all dependencies on angular services in my core application and just use angular for the UI code?

Solution

As suggested in the answers, I added the $injector service to the window, like this:

module('app').run(['$injector', function($injector) {
    window.$injector = $injector;
}]);

The service can now be used in any class to get other services like $http. This didn't work for unit tests though, so I had to put the injector on the global scope again:

// MyClass.test.js
var $injector;

describe('My class', function() {
    beforeEach(module('app'));

    beforeEach(inject(function(_$injector_) {
        $injector = _$injector_;
    }));

    ...
});

Upvotes: 3

Views: 2583

Answers (1)

basarat
basarat

Reputation: 276199

I had to change every extending class and their factories. Is there a better way to approach this problem

Just inject $injector and then the base class can get what it wants using $injector.get().

or even more crazy ... put $injector (its a singleton) on the global scope / or some module ... and just use that in your classes to get angular specific stuff.

Upvotes: 1

Related Questions