12
votes

I'm currently following the Angular tutorial on their main website, and I'm having difficulty with one of their concepts. 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. The link takes you right to the problem part of the tutorial.

https://angular.io/tutorial/toh-pt4#injectable-services

So far as I can tell, a Service is used to serve data to Components. Components gain access to Services by having the Services Injected into them. However, the Injector is something that exists in its own right, and the Service needs to be made avilable to it, before it can inject it.

However, the guide that I'm reading feels loose in the way it refers to what is the Service and what is the Provider. At first I thought the entire Hero.Service.ts was the Service. Then it says that @Injectable marks the class as the Service. Okay fine, and yeah that's how Components work, the Decorator specifies what it is. But then the guide says,

"The HeroService class is going to provide an injectable service"

Wait, so HeroService isn't the service, it provides the service object? Remember the words "is going to provide an injectable service", as it comes up later.

The guide goes into a little detail about how the Service retrieves data for Components, and then comes to how Services need to be introduced to the dependency injector via Providers.

Here's the trouble. It says that the Provider creates or delivers the Service, in this case instantiating HeroService. Then it says that HeroService is going to be the Provider of HeroService. To repeat, here are two lines in which they refer to the HeroService as a Service

Implicitly: "You must make the HeroService available to the dependency injection system before Angular can inject it into the HeroesComponent"

Again implicitly "You do this by registering a provider. A provider is something that can create or deliver a service; in this case, it instantiates the HeroService class to provide the service."

Before the tutorial header Create HeroService " They should focus on presenting data and delegate data access to a service...In this tutorial, you'll create a HeroService that all application classes can use to get heroes." Note: heroes is the data

However, then they say that HeroService is registered as the Provider? "Now, you need to make sure that the HeroService is registered as the provider of this service" If HeroService is the Provider, what is "this service" referring to?

Now remember the line "HeroService class is going to provide an injectable service"

So I have two lines of a tutorial referring to HeroService as a Service, and another two lines saying that it's a Provider. Unless, in the latter case, they were using the word "provide" or "service" in a way that was strictly colloquial and wasn't actually referring to to Providers and Services.

3
essentially, it's both. The service is the class that does the work, but it also provides instructions on how it should be created, for the injection system to use.Claies
I think this maybe helpful: stackoverflow.com/a/41690604/796919towry

3 Answers

20
votes

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.

0
votes

In simple words:

Putting @Injectable annotation above your service it will be able to get discovered by dependency injection. The @Injectable annotation is basically a flag for a class that it will be able to be discovered elsewhere from other elements (classes).

In your module you have to add your service into "provides" array. When your module is being instantiated it creates a single instance of this service. So think of your module as the provider. Once that mechanism is up you can use the service everywhere in constuctors (components, other services etc.).

0
votes

A service is just a typescript class. It does not need any decorator unlike the components, directives etc

So how do you access an instance of service from a component?

We can just import the service class and instantiate it with new keyword. But instantiating the class manually is a bad practice.

So we now need to look into Angular dependency injection to understand how to access service instance from a component. Angular dependency injection is a hierarchical injection i.e. if an instance is already injected in higher level then all child components will automatically get access to that instance

Usually we import the service class, use the type in constructor argument with an access modifier. This is enough to inform Angular that the component needs access to an instance of a particular service. So this needs to be done in all components that needs to access an instance of a service

But sometimes we also need to mention the service name in providers field. When do we need to do this?. If Any one of the below scenarios is applicable:

  1. To explicitly tell Angular that the component needs a new instance of the service even if an instance is already available in higher level.
  2. Incase there is no instance of service already injected anywhere i.e in any of the parent component or root module etc then we need to mention the service class name in providers field so that Angular knows that a new instance need to be created and then injected into this component(which is then available to all its child components)