0

We're using angular constants to define classes. How I can I make it so that a Class constant can have a parameter in constructor when instantiating but also have dependency injection? I'm thinking along the lines like this:

ClassA.js

class ClassA { ... }

angular.module("myApp").constant("ClassA", ClassA);

ClassB.js

class ClassB {
  constructor(constrParams, ClassA) { // mix of constructor parameter and DI
    this.constrParams = constrParams;
    this.ClassA = ClassA;
  }
}

ClassB.$inject = ["ClassA"]; // ClassB needs reference to ClassA
angular.module("myApp").constant("ClassB", ClassB);

Would I still be able to do this when importing ClassB in another constant

ClassC.js

class ClassC {
  constructor(ClassB) {
    this.classBinst = new ClassB("myparams"); // instantiate new ClassB instance
  }
}

ClassC.$inject = ["ClassB"];
angular.module("myApp").constant("ClassC", ClassC);
geofrey
  • 466
  • 3
  • 12

2 Answers2

1

If you want to pass in any constructor arguments manually, I think you'll have to pass them all in manually.

So ClassC would have to take in ClassA as a parameter as well, to pass it to ClassB:

class ClassC {
  constructor(ClassB, ClassA) {
    this.classBinst = new ClassB("myparams", ClassA); // instantiate new ClassB instance
  }
}

But then if you create ClassD which requires ClassC, you're stuck with the same problem - needing to pass in ClassC along with all of its dependencies (ClassB and ClassA).

But if ClassC is the final level and can be instantiated and injected as a singleton, you can then create a service which instantiates ClassC without you having to worry about its dependencies:

angular.module("myApp").service('instanceC', ClassC);

angular.module("myApp").controller('myController', function (instanceC) {
    // Everything has been resolved
});

Maybe you'd be better off creating a service which creates instances of these classes for you. That service could resolve the dependencies the classes need and pass them in manually, while the controller can pass in any custom parameters:

class factory {
    constructor(ClassA, ClassB, ClassC) {
        this.ClassA = ClassA;
        this.ClassB = ClassB;
        this.ClassC = ClassC;
    }

    createB(customParamForClassB) {
        return new this.ClassB(customParamForClassB, this.ClassA)
    }
}

angular.module("myApp").service('factory', factory);

angular.module("myApp").controller('myController', function (factory) {
    let instanceB = factory.createB('myparams');
});
Frank Modica
  • 10,238
  • 3
  • 23
  • 39
  • I extended your idea and used a Factory instead. This provides the actual class definition to instantiate instead of explicit create functions while having dependency injection – geofrey Jun 12 '17 at 16:19
0

I extended Frank's idea of providing classes as service, but used a factory construct instead. The factory provides the class definition that can be instantiated directly, instead of an explicit create function in the service. The pattern is as follows:

ClassA.js

class ClassA { ... }

angular.module("myApp").factory("ClassA", () => ClassA);

ClassB.js

class ClassB {
  constructor(constrParams) {
    this.constrParams = constrParams;
    // this.ClassA reference is already injected through factory
  }
}

angular.module("myApp")
.factory("ClassB", ["ClassA", (ClassA) => {
  // this is where dependency injection happens
  ClassB.prototype.ClassA = ClassA;
  return ClassB; // class definition
}]);

ClassC.js (end goal - same as before)

class ClassC {
  constructor() {
    // instantiate new ClassB instance
    this.classBinst = new this.ClassB("myparams");
  }
}

angular.module("myApp").factory("ClassC", ["ClassB", (ClassB) => {
  ClassC.prototype.ClassB = ClassB;
  return ClassC;
}]);

Related pattern: Correct way of wrapping javascript class in AngularJS module and inject angular services

geofrey
  • 466
  • 3
  • 12