Is it a Service or a Provider? Confused by Angular tutorial
It feels loose in the way it refers to what is the Service and what is the Provider, I can't tell if its one or the other.
A Service is a JavaScript object that exists in your application, and a Provider is the way you gain access to that object. The two are not the same thing, but they work together to make your Service accessible from other places. You can't get your Service without a Provider, and a Provider needs something to provide.
A Service is just a term used to describe an instance of an object (which is often a TypeScript class). We call them Services because this term is often used in dependency injection to describe a shared object that performs a single purpose.
"The HeroService class is going to provide an injectable service"
Here the word provide refers to the verb in the grammar. It should not be confused with the TypeScript type Provider which is used by Angular to define an entry in the list of providers.
A better way of writing the above sentence:
"The HeroService class is going to declare an injectable service"
I agree with your claims that it's confusing. So I'll just explain how things work.
Providers
A single provider is a JavaScript object that implements the Provider interface (it's actually multiple interfaces), but my point is that it's an object that tells Angular how to associate a token with a value.
Only a NgModule
can define providers at compile time. We often do it like this:
@NgModule({
providers: [HeroService]
})
Now, remember above I said that a provider was a JavaScript object, but in the NgModule
example we don't define an object. We just use HeroService
which is technically a constructor function.
This is to make it easier for the programmer. At compile time Angular will look at HeroService
to see what the metadata says about it, and it will automatically generate the provider object.
That metadata is created by doing the following.
@Injectable()
export class HeroService {}
The @Injectable
decorator adds hidden metadata that describes how to create a HeroService
instance.
We can do the same thing by defining the provider object ourself, and if we do it using a provider object the service doesn't need a @Injectable
decorator, because we are creating it ourselves.
@NgModule({
providers: [{provide: HeroService, useValue: new HeroService()}]
})
So we can now explain why they said "The HeroService class is going to provide an injectable service". It's because HeroService uses the @Injectable
decorator to declare the provider for the service.
Injecting
Angular does a lot of work for you. When you are writing a component and you want to inject the HeroService
so that you can use it. All you have to do is the following.
@Component({....})
export class AppComponent {
constructor(heroService: HeroService) {
// ^^^ this is the provider token
}
}
The above works because Angular can infer which providers are needed by the constructor. It looks at the first argument and sees that the type is HeroService
. We call this the injectable token, and Angular can search through the providers looking for one where it has provide: HeroService
. It then uses that provider to get the value and pass it to the constructor.
We can break dependency injection by doing the following.
@Component({....})
export class AppComponent {
constructor(heroService: any) {
// ^^^ this is an unknown provider
}
}
Angular can no longer infer the token for the provider. It doesn't know which of the many providers that are declared is the one the programmer wanted to use.
We can declare which provider token we really want.
@Component({....})
export class AppComponent {
constructor(@Inject(HeroService) heroService: any) {
// ^^^ we declare the token manually
}
}
When Angular needs help we use the @Inject
decorator to manually declare which provider token should be used.
Singletons
In Angular 6 a new feature was introduced that allowed you to declare a Service as a provider in the root module. It's the "providedIn" option for injectables.
@Injectable({ providedIn: 'root' })
export class HeroService {}
Above I said that @Injectable
makes it easier to add classes to the providers array in a NgModule
. Well, the providerIn
takes it one step further and now adds it for you directly to the module.
These things can be confusing, because they hide the underlying work that the dependency injection system is doing. It makes more of the features look like magic instead of illustrating what they're doing.