Extending injector support to custom entities

Directives and components require dependencies to be introspected, resolved, and injected. Other entities such as service classes often require such functionality too. In our example, our Playlist class might rely on a dependency on a HTTP client to communicate with a third party to fetch the songs. The action of injecting such dependency should be as easy as declaring the annotated dependencies in the class constructor and have an injector ready to fetch the object instance by inspecting the class provider or any other provider available somewhere.

It is only when we think hard about the latter that we realize there is a gap in this idea: custom classes and services do not belong to the component tree. Hence, they do not benefit from anything such as a built-in injector or a parent injector. We cannot even declare a providers property, since we do not decorate these types of class with a @Component or @Directive decorator. Let's take a look at an example:

class Playlist {
songs: Song[];
constructor(songsService: SongsService) {
this.songs = songsService.fetch();
}
}

We might try this in the hope of having Angular's DI mechanism introspecting the songsService parameter of the Playlist class constructor when instantiating this class in order to inject it into MusicPlayerComponent. Unfortunately, the only thing we will eventually get is an exception like this:

It cannot resolve all parameters for Playlist (?). Make sure they all have valid type or annotations.

This is kind of misleading, since all constructor parameters in Playlist have been properly annotated, right? As we said before, the Angular DI machinery resolves dependencies by introspecting the types of the constructor parameters. To do so, it needs some metadata to be created beforehand. Each and every Angular entity class decorated with a decorator features this metadata as a by-product of the way TypeScript compiles the decorator configuration details. However, dependencies that also require other dependencies have no decorator whatsoever and no metadata is then created for them. This can be easily fixed thanks to the @Injectable() decorator, which will give visibility to these service classes for the DI mechanism:

import { Injectable } from '@angular/core';

@Injectable()
class Playlist {
songs: string[];

constructor(private songsService: SongsService) {
this.songs = this.songsService.fetch();
}
}

You will get used to introducing that decorator in your service classes, since they will quite often rely on other dependencies not related to the component tree in order to deliver the functionality.

It is actually a good practice to decorate all your service classes with the @Injectable() decorator, irrespective of whether its constructor functions have dependencies or not. This way, we prevent errors and exceptions because of skipping this requirement once the service class grows, and it requires more dependencies in the future.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset