⚠️ This lesson is retired and might contain outdated information.

Understand Angular @Injectable

Pascal Precht
InstructorPascal Precht
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated a month ago

In order to resolve a dependency, Angular’s DI uses type annotations. To make sure these types are preserved when transpiled to ES5, TypeScript emits metadata. In this lesson we’ll explore how the @Injectable decorator ensures metadata generation for services that have their own dependencies.

[00:01] Angular uses type annotation to resolve service dependencies. This means when we're injecting a service of type dataservices it's the type annotation that gives Angular all the information that's needed to create a dependency for that type. That's why the same type is used in the list of providers, to tell Angular what to inject and how that thing we want to inject is created.

[00:24] In this case we tell Angular to inject an instance of class dataservice, with a dependency of type dataservices requested. The interesting question though is how is that metadata preserved into transpiled ES5 code so that TypeScript works in the browser when we run our application.

[00:41] We can easily find that out by taking a look at the transpiled file in the distapplist component.js. This is ES5 code that is later executed in the browser. One of the interesting parts here is that the component class translates into a plain old constructor function. We can see very nicely how it asks for a parameter encoded dataservice.

[01:05] Another interesting observation is that class methods end up in the constructor functions prototype. Let's take a look at the constructor again. Type annotations don't exist in ES5, and that's why we can't see them here. In order to preserve that information, types could generate two functions, decorate and metadata.

[01:26] Decorate attaches metadata generated by decorators to objects. Here we can see how it attaches the addComponent metadata to the list component. In fact, this is all the magic that makes decorates in TypeScript work. The metadata function, however, takes care of attaching type annotations of construction parameters as part of the decoration process to objects.

[01:51] So this, for example, can be the dataservice type that we use to annotate our dataservice dependency. Now, down here we can see how the metadata function attaches that type information to our list component. This says there can be multiple constructor parameters, this function takes a list of types whereas each type maps the constructor parameter in the order they are defined.

[02:17] That's how Angular knows that the first constructor parameter is a dependency for the type dataservice. But let's say we want to refactor our code a little bit, and call logDebugger.debug inside the getItems method, so we don't have to do it manually every time we want to debug that function call. So we go ahead and remove the logDebugger dependency and the debug call, and ask for a logDebugger dependency in our dataservice, and call logDebugger.debug whenever getItems is called.

[02:58] If we save the file and reload the browser, we'll see that there's an error that Angular can't resolve all parameters for dataservice. In fact, it seems it doesn't even know what dependency type we're asking for, hence the question mark. If we take a look at the generated ES5 code, we can see that dataservice does ask for a logDebugger dependency. However, there's no metadata attached to the service, which is why Angular can't resolve the dependency.

[03:27] The reason why there's no metadata emitted by TypeScript, is because TypeScript only does that if there is at least one decorator on a class. That's why we didn't run into this problem when we injected dependencies into our list component, it already comes with a decorator.

[03:42] Going back to our dataservice class, there's clearly no decorator. This can be easily fixed by attaching some decorator to our dataservice. It really doesn't matter if we're using a custom decorator, or a decorator provided by Angular. Angular, however, has a decorator called @Injectible that we can use exactly for this scenario. So we can go ahead and import injectable from @AngularCore, and attach it to our class.

[04:10] If we now take a look at the transpiled ES5 code we can see that the metadata is now properly emitted, and our application runs again.

Michael Sevestre
Michael Sevestre
~ 8 years ago

The end result is a bit weird in my opinion. The ListComponent is the component configuring the LogService and the ConsoleService but the DataService is the one using them! This breaks all encapsulation don't you think?

Michael Sevestre
Michael Sevestre
~ 8 years ago

Unless you consider the ListComponent the main file of the application where all configurations for injectable components is made. (such as the the FactoryProvider for LogDebugger). Does this mean then that this configuration is GLOBAL for all components? What happens if another component defines another FactoryProvider for LogDebugger?

Markdown supported.
Become a member to join the discussionEnroll Today