Stefan Zvonar
Stefan Zvonar

Reputation: 4319

What is the purpose of providedIn with the Injectable decorator when generating Services in Angular 6?

When generating services in the Angular CLI, it is adding extra metadata with a 'provided in' property with a default of 'root' for the Injectable decorator.

@Injectable({
  providedIn: 'root',
})

What exactly does providedIn do? I am assuming this is making the service available like a 'global' type singleton service for the whole application, however, wouldn't be cleaner to declare such services in the provider array of the AppModule?

Upvotes: 297

Views: 158624

Answers (8)

JoannaFalkowska
JoannaFalkowska

Reputation: 3687

This is a fresh reply that's up to date with Angular 9+ (post-Ivy) and should be correct in 2022.


TL;DR: It's all about controlling how many instances of your service will be created, and where should they be available after being created.


Terminology:

  • Injectable - any class decorated with @Injectable, for example a service.

  • injector - an Angular class that is capable of providing Injectables to classes below it. (This includes all components and modules.)

  • injector scope/level - the scope of all class instances that live "below" a specific injector.

  • injector hierarchy - a proritized tree of injector scopes, organized in platform -> root -> module -> component order.

  • Injectable is provided - an instance of the Injectable will be given to classes below this specific injector level, whenever they request it.

  • Injectable is injected - a class constructor has requested to be given some instance of the service, so Angular will try to give it the nearest instance that can be found in the injector hierarchy".

  • tree-shaking - an optimization that happens automatically thanks to the Angular compiler. When it detects that some code is not being used, that code is removed from the final compilation of the app (or compilation of a given lazy-loaded module).

Other terms that you should already know: class, instance, module, component, lazy/eagerly loaded modules.


Q: What exactly does providedIn do?

It's a setting that determines which injectors should provide your Injectable.

Let's assume we create an Injectable called MyService, and go through what all the options do.

providedIn: Type<any> | 'root' | 'platform' | 'any' | null

providedIn: 'platform'

Angular will create and provide a single shared instance of MyService to all Angular applications on the page. (This is only relevant in advanced use cases, if you use a micro-frontends architecture.)

providedIn: 'root'

Angular will create a single shared instance of MyService and provide it to all classes in the application.

providedIn: 'any' // DEPRECATED since Angular v15

Angular will create a single shared instance of MyService and provide it to all classes in eagerly-loaded modules.

However, each lazy-loaded module will provide its own, new, separate instance of MyService (that will then be only available in classes inside that module).

providedIn: MyModule // DEPRECATED since Angular v15

Angular will only create an instance of MyService if MyModule is loaded.

If MyModule is eagerly loaded, that instance will be available to all other eagerly loaded modules from now on. (Note that this is effectively identical to providedIn: 'root'.)

However, if MyModule is lazy loaded, then this instance will be provided only for classes inside MyModule, whenever it happens to be loaded.

providedIn: MyComponent // DEPRECATED since Angular v15

Angular will create a new, fresh instance of MyService whenever MyComponent is instantiated.

This MyService instance will only be provided for descendants of that specific MyComponent instance, and will be destroyed as soon as the component instance is destroyed. (Note that means that a new MyService will be created for each time this component is rendered.)

providedIn: null

MyService can only ever be instantiated by being added to providers array in a specific module or component.

Whenever that module/component is instantiated, it will create a new instance of MyService, and provide it only in its specific scope. (See full description of providers array below.)


Q: What does providers array do?

Any injector can be set up with a providers array:

@NgModule({
  providers: [MyService],
})

@Component({
  providers: [MyService],
})

All Injectables can be added to a providers array, regardless of their providedIn setting.

Adding MyService to providers array will cause the injector to create and provide an entirely separate instance of it to classes in its scope. (The scope is exactly the same as described in providedIn: MyModule and providedIn: MyComponent examples above.)

This method of providing does not support tree-shaking. The service will always be included in the compilation, even if noone uses it. (See tree-shaking notes below.)

Q: Why would I use providers array and providedIn simultaneously?

An example use case might be if MyService is providedIn: 'root' and already has a shared instance, but you want your module/component to have its own, separate instance.


Additional notes:

Q: How do providedIn/providers settings affect tree-shaking?

An Injectable configured with providedIn will be tree-shaken if it is not injected by any (eagerly or lazy loaded) class in its assigned injector scope.

However, an Injectable assigned to a providers array in some module/component will never be tree-shaken, even if it is not injected anywhere.

To make tree-shaking most effective, you should aim to always use providedIn over providers array.

Q: Why would I use providedIn: 'root' if I think using providers array in AppModule looks cleaner?

As explained above, the main difference is that between the two methods, providedIn supports tree-shaking, and providers array does not.

Other than that, it's an architectural decision: if we set providedIn directly in the Injectable file, the Injectable owns the decision of how it should be provided. Distinguishing who owns the contract has significant implications for large apps and teams that have to cooperate between hundreds of modules.

Q: Is there a difference between setting providers: [MyService] array in AppComponent or AppModule?

Yes. MyService will be provided in lazy-loaded modules only if you do it in AppModule, not AppComponent.

(That's because lazy-loaded modules rely on Router, which gets imported in AppModule, one injector scope higher than AppComponent.)

Upvotes: 82

Ali
Ali

Reputation: 1759

Simply..

providedIn :'root' creates one instance to the whole application without the need to provide it from any NgModule. Just declaring it in the service through the @Injectable decorator.

if you want to have one new instance of this service for any component, then declare it through component's provider. this will create another new instance for this component and its child components if any. thus, you can have one instance with global scope and another instance for one component.

Upvotes: 2

Chanel
Chanel

Reputation: 1019

see Excellent explanation by @Nipuna,

I'd like to extend it by adding examples.

if you just use Injectable decorator without providedin property, like,

@Injectable()

then you would have to write service's name in respective Module's providers Array.

like this;

data.service.ts ↴

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

@Injectable()
export class DataService {
    constructor() {}

    // Code . . .
}

app.module.ts ↴

import { AppComponent } from './app.component';
import { DataService } from './core/data.service';

@NgModule({
    declarations: [AppComponent],
    providers: [DataService],    // ⟵ LOOK HERE WE PROVIDED IT
    imports: [...],
    bootstrap: [AppComponent],
})
export class AppModule {}

But, If you use providedIn: 'root', like this:

data.service.ts ↴

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

@Injectable({
    providedIn: 'root',
})
export class DataService {
    constructor() {}

    // Code . . .
}

Then our module would look like this:

app.module.ts ↴

import { AppComponent } from './app.component';
import { DataService } from './core/data.service';

@NgModule({
    declarations: [AppComponent],
    providers: [],
    imports: [...],
    bootstrap: [AppComponent],
})
export class AppModule {}

see I didn't add DataService in providers array this time, because it's not needed.

Good Practices

This might come in handy, from Angular Guides

Do provide a service with the app root injector in the @Injectable decorator of the service.

Why? The Angular injector is hierarchical.

Why? When you provide the service to a root injector, that instance of the service is shared and available in every class that needs the service. This is ideal when a service is sharing methods or state.

Why? When you register a service in the @Injectable decorator of the service, optimization tools such as those used by the Angular CLI's production builds can perform tree shaking and remove services that aren't used by your app.

Why? This is not ideal when two different components need different instances of a service. In this scenario it would be better to provide the service at the component level that needs the new and separate instance.

Upvotes: 42

Mick
Mick

Reputation: 8922

providedIn: 'root' is the easiest and most efficient way to provide services since Angular 6:

  1. The service will be available application wide as a singleton with no need to add it to a module's providers array (like Angular <= 5).
  2. If the service is only used within a lazy loaded module it will be lazy loaded with that module
  3. If it is never used it will not be contained in the build (tree shaked).

For further informations consider reading the documentation and NgModule FAQs

Btw:

  1. If you don't want a application-wide singleton use the provider's array of a component instead.
  2. If you want to limit the scope so no other developer will ever use your service outside of a particular module, use the providers array of NgModule instead.

Upvotes: 178

Nipuna
Nipuna

Reputation: 7026

From Docs

What is Injectable decorator?

Marks a class as available to Injector for creation.

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

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

The service itself is a class that the CLI generated and that's decorated with @Injectable().

What exactly does providedIn do?

Determines which injectors will provide the injectable, by either associating it with an @NgModule or other InjectorType, or by specifying that this injectable should be provided in the 'root' injector, which will be the application-level injector in most apps.

providedIn: Type<any> | 'root' | null

providedIn: 'root'

When you provide the service at the root level, Angular creates a single, shared instance of service and injects it into any class that asks for it. Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used.

providedIn: Module

It's also possible to specify that a service should be provided in a particular @NgModule. For example, if you don't want a service to be available to applications unless they import a module you've created, you can specify that the service should be provided in the module

import { Injectable } from '@angular/core';
import { UserModule } from './user.module';

@Injectable({
  providedIn: UserModule,
})
export class UserService {
}

This method is preferred because it enables Tree-shaking (Tree shaking is a step in a build process that removes unused code from a code base) of the service if nothing injects it.

If it's not possible to specify in the service which module should provide it, you can also declare a provider for the service within the module:

import { NgModule } from '@angular/core';
import { UserService } from './user.service';

@NgModule({
  providers: [UserService],
})
export class UserModule {
}

Upvotes: 125

Maarti
Maarti

Reputation: 3719

According to the Documentation:

Registering the provider in the @Injectable() metadata also allows Angular to optimize an app by removing the service from the compiled app if it isn't used.

Upvotes: 5

Jawad Farooqi
Jawad Farooqi

Reputation: 345

providedIn tells Angular that the root injector is responsible for creating an instance of the your Service. Services that are provided this way are automatically made available to the entire application and don't need to be listed in any module.

Service classes can act as their own providers which is why defining them in the @Injectable decorator is all the registration you need.

Upvotes: 19

Sajeetharan
Sajeetharan

Reputation: 222700

if you use providedIn, the injectable is registered as a provider of the Module without adding it to the providers of the module.

From Docs

The service itself is a class that the CLI generated and that's decorated with @Injectable. By default, this decorator is configured with a providedIn property, which creates a provider for the service. In this case, providedIn: 'root' specifies that the service should be provided in the root injector.

Upvotes: 75

Related Questions