Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Andy Matthews
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Andy Matthews@commadelimited )

Understanding The Role Of Static Methods In An Angular 2 Dependency-Injection Context

By Ben Nadel on

In the Angular 2 dependency-injection (DI) framework, Angular maps tokens onto singleton instances of JavaScript "classes." This allows us to swap in and out various implementations of a class so long as all of the other classes agree on which DI token is needed to reference the abstract implementation. Each concrete implementation of a the given class token then has to adhere to the interface inherent in the DI token contract. But, classes can have both static and instance methods; so, which of these need to be implemented by a concrete class in a dependency-injection context?

At first, you might think that both static and instance methods are part of the class "contract" in a dependency-injection context. After all, if you're trying to swap in and out various implementations, it's natural to think that the entire topology of the swappable classes needs to be interchangeable.

And, you'd be right - if we were actually dealing with "classes." But, in a dependency-injection framework, like Angular 2, we're not dealing with classes, we're dealing with instances of classes. This is a very important difference. In JavaScript (and therefore in Angular 2), class instances don't include static methods in their public API. As such, the set of static methods exposed on a constructor is not part of the "API contract" bound to each instance created by that constructor.

It might be easier to think about this divergence outside of a dependency-injection framework. So, let's look at the native Array class in JavaScript. While the Array class exposes static methods, like isArray(), those static methods cannot be accessed on each array instance:


 
 
 

 
 Static methods are not exposed on the instances of a class and are therefor enot part of the dependency-injection contract. 
 
 
 

What this means for consumers of the Angular 2 dependency-injection framework is that when you need to implement a given class interface, you only need to implement the instance methods of that class. Since the dependency-injection framework only deals in instances, no injected dependency will ever expose a static method (see epilogue). Therefore, static methods are not part of the contract inherent in a dependency-injection token.

Epilogue: The Angular 2 dependency-injection framework deals primarily with class instances, but not exclusively. As such, it is most certainly possible to make the DI framework provide a class constructor as opposed to a class instance. In that case, the injected value would expose static methods since the injected value is a class and not an instance of a class. But, that's not really relevant to this discussion.




Reader Comments

Hi Ben,

again nice deep overview about more, kind of complex, ng2 behind the scenes concepts.

Providing a class constructor for ng2 DI is a no brainer, because ng2 is so awesome :)

I've created a plunk to demonstrate

http://plnkr.co/edit/z0uYfrJw5yfuFMW7kgHY?p=preview

Reply to this Comment

@All,

On Twitter, Darien (@bhathos) pointed out the fact that you can technically gain access to the Constructor "static" methods using the the .constructor property on the class instance. Using the Array example, you could technically do something like:

var a = [];
a.constructor.isArray( "blam" );

.... but, in my opinion, the .constructor property should be treated more like meta-data and less like a property of the instance itself. For example, I think it's fine to assume that the .constructor property can power the "instanceof" operator (and to define the constructor property when outlining your prototype). But, beyond that, I wouldn't really depend on it too heavily.

But, that's just my opinion.

Reply to this Comment

@Martin,

Excellent example. For anyone who is not exactly sure what Martin is demonstrating, in his provider collection, he's using:

provide( "FooClass", { useValue: Foo } )

This is telling Angular 2 to use the *CLASS* Foo as the injectable, as opposed to *instantiating* the class Foo and using the *instance* as the injectable. This way, you could use either the static or the instance methods; but, they would be attached, so to speak, to two different injectables.

Reply to this Comment

God bless you for such a clear explanation.
Any thought on how to disable webstorm "method can be static" message?

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.