Angular: writing configurable modules
Hi folks! It’s been some time since my last article, I promise I’ll be more active! :)
Actually I’ve been pretty busy reviewing projects and helping devs structuring their apps, and what I saw most of the times is this: Angular gives you lots of features out of the box, which is why you probably chose it, but it appears that most devs are not taking full advantage of them. The features I’m talking about are Dependency Injection and Observables, but in this article I’ll be focusing only on Dependency Injection.
DI is not just an ‘add-on’ of the framework, it’s a built-in mechanism which powers Angular’s most useful features and there’s nothing that prevents you from exploring every single possibility it offers!
If we had to give DI a description, it is a pattern which lets you inject services (classes) into components without creating them manually and passing them through a constructor. Who provides the right services? Angular of course! And the mechanism is dictated by the way you compose your modules, I wrote an article about that.
That’s cool! But take a breath and think: what is a dependency?
A dependency is something needed by something else in order to work properly. Most of the times a dependency is a service, but it could be something else. In fact, a component can have a dependency on Classes, Values or Objects. Let’s see the tools we have and how useful they are in our apps!
Remember that when you use the class name to specify a provider, Angular takes it and interprets it this way:
What this means is that we can take a class, even a blank one, and change it with another one:
This is pretty useful for mocking services during development, and it can be used to introduce a new service keeping the old class-name as well for compatibility reasons.
useValue is useful when you don’t want to provide a class, but a value (or an object!). Remember that a dependency is something that’s needed by something else, and a component could require an object! This is an awesome feature and we’ll discuss this later, for now just notice this: the base class we provide is overwritten by the object, therefore it’s useless.
It would be better to provide a string name, instead of an useless class, this way:
But again, this approach can lead to name collisions, and as always, there’s a better way that we’ll cover in a while! ;)
useFactory does what it says: it uses a factory (a simple function) and Angular provides the returned value of that function. This is useful when you have to do some checks or some operations before knowing what to provide.
Again, we’re using a string to identify the provider, it’s time to solve this.
Now we’re facing the issue of providing string names AND providing classes that will be overwritten (and are, therefore, useless). What can we do?
Angular gives us InjectionToken (previously OpaqueToken, with some differences) which does exactly what it says: it creates a token (a name) to use in our provide field, instead of using strings or classes. Since every instance of InjectionToken is different, we don’t have to worry about naming collisions, even if we specify the same value at initialization phase.
Remember that those values are simple descriptions, they do nothing to our logic. So now we can use that token both in the module and the component, awesome!
5) Configuring a module from outside
This is where I wanted to end. You can use Dependency Injection to inject a configuration object into your modules, which you can use to configure your services! Sounds confusing? Let’s make an example.
Imagine we have a service which talks to an API, let’s say… Contentful, for example! It’s the most famous Headless CMS which lets you build your own custom models (city, person, employee, etc…), a sort of WordPress without templates. Since your data will only be accessible through the API, the team released some SDKs to facilitate the process of making the calls. Let’s make a service which uses this SDK (a really simple library) and let’s put this service into its own module, ContentfulModule.
Beautiful! Now, the SDK needs to know our Space ID (our work area) and our Access Token in order to work. Let’s save them somewhere, shall we? Why don’t we create a file with an object in our module to st —
This is what you should NOT do if you want to create a reusable module. You’re making a useful module which can be reused across other apps, and if your module grows enough you could even think about publishing it to npm. It wouldn’t be nice to write on the GitHub page:
Sorry, in order to configure the module you must edit the source files, and be careful with updating the package…
Ouch. What a bad idea. Your module needs to be configured from the outside! And if you read my previous article, you might have an idea on how to do that.
Yes! You probably already know that RouterModule uses a forRoot() custom method in order to provide our configuration to the router: our Routes! They are in fact configuration objects! :) Let’s copy the approach.
So, here’s what to do:
- Let’s write a forRoot() static method in our module which takes the object and provides it.
- Let’s also create an interface for our configuration object, so that out module’s users know how to create that object.
- Let’s provide that object with an InjectionToken (which prevents collisions) and useValue.
That’s awesome! Now let’s dig into our ContentfulService and ask for that object into the constructor, as we always do with services, but with a different syntax:
Voilà! Now you have a [not really] awesome reusable module which can be configured from the outside!
I made for you a working example, you just need to replace the Space ID and the Access Token with your own (in AppModule) and ask for one of your content types in AppComponent (in my case it was ‘article’):
Thank you for reading, see you soon! 😃