Contradicting and incorrect Injectable docs
See original GitHub issue📚 Docs or angular.io bug report
Description
I wanted to know how Injectable worked and whether or not we are actually required to use it (not that I want to omit it, just as a matter of understanding how things work).
At first, I thought Injectable was always required for any service that’s used (either by adding it to the providers or using providedIn, which requires Injectable anyway) in Angular.
However, we noticed that Injectable is only required to be used as a decorator on a service that has dependencies which are being injected into its constructor.
This was easily verified using a Stackblitz: https://stackblitz.com/edit/angular-pgkrb5
- If you would omit the @Injectable from
FooService
, it’ll show an error:Can't resolve all parameters for FooService: (?).
. - If you would inject BarService (which has no @Injectable) into the AppComponent, everything works fine as well.
However, doing some more testing it turns out we can replace the Injectable decorator with any other decorator, even the HostBinding works: https://stackblitz.com/edit/angular-qkngb5
Doing some googling on this quickly showed me I wasn’t the first one noticing this, so this is not really something new I guess.
However, in order to fully understand how it worked, I needed to fiddle with Stackblitz, the docs didn’t really give me a proper answer. I found some things I wanted to update to be more accurate if anyone else ever would have the same question as I had.
As I don’t think the above findings are things that necessarily need to end up in the docs, I thought I would be better of creating an issue first to discuss this further before creating a PR to update the docs.
A few remarks on snippets from the docs
https://angular.io/api/core/Injectable
Injector throws an error if it tries to instantiate a class that is not decorated with @Injectable, as shown in the following example.
I think this isn’t entirely correct, it’s only the case when that service has dependencies which are being injected in its constructor.
https://angular.io/guide/dependency-injection#services-that-need-other-services
Notice that the Logger service also has the @Injectable() decorator, even though it might not need its own dependencies. In fact, the @Injectable() decorator is required for all services.
When Angular creates a class whose constructor has parameters, it looks for type and injection metadata about those parameters so that it can inject the correct service. If Angular can’t find that parameter information, it throws an error. Angular can only find the parameter information if the class has a decorator of some kind. The @Injectable() decorator is the standard decorator for service classes.
Both snippets above might be a bit contradicting, the first one mentions it’s required for all services. While the second snippet is mentioning the fact that the class’ constructor needs to have parameters.
What I’d propose to change:
https://angular.io/api/core/Injectable:
Injector throws an error if it tries to instantiate a class that is not decorated with @Injectable and is making use of Dependency Injection (add link to https://angular.io/guide/dependency-injection or https://angular.io/guide/dependency-injection#services-that-need-other-services here), as shown in the following example.
Maybe we can also include an example that shows it doesn’t throw in case we omit Injectable and have no Dependency Injection.
https://angular.io/guide/dependency-injection#services-that-need-other-services
Notice that the Logger service also has the @Injectable() decorator, even though it might not need its own dependencies. Even though it’s not required to add Injectable when you’re not making use of Dependency Injection, we do encourage adding it as this will make it easier to add dependencies later on.
When Angular creates a class whose constructor has parameters, it looks for type and injection metadata about those parameters so that it can inject the correct service. If Angular can’t find that parameter information, it throws an error. Angular can only find the parameter information if the class has a decorator of some kind. The @Injectable() decorator is the standard decorator for service classes.
Even tho this still isn’t 100% correct (as Injectable isn’t required, any decorator should do), I think this could be an improvement.
I’m also not aware if Ivy would change anything with regards to this, if this is the case these changes might be obsolete.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:2
- Comments:5 (3 by maintainers)
With Angular 9 and Ivy any decorator not longer works (it needs to be Injectable), but the rest of the post is true and should be fixed.
Relates to https://github.com/angular/angular/pull/27480