DEV Community

Julian Dierkes
Julian Dierkes

Posted on • Edited on

Provider Scopes in Angular

Angular Level:

level


Angular's modular system provides you with the ability to create Components and Services.
While Components should be focused on the view, meaning the html template, a Service should be used for the application logic. While, from my experience, they are mostly used for HTTP requests, Services mayfulfill other purposes as logging or validation. Many Services will be reused throughout one application.

When reusing Services, it is important to think about the scope, in which you want to provide it.

Angular basically offers three scopes:

  1. Root Scope
  2. Module Scope
  3. Component Scope

The Root Scope

The root scope is the most commonly used scope for providing Services as it is also the default scope when creating a Service via Angular CLI.

@Injectable({
  providedIn: 'root'
})
export class ExampleService {}
Enter fullscreen mode Exit fullscreen mode

The default providedIn argument in the CLI generated Service above means that the Service will be provided in the application root, the AppModule. Therefore the Service will be a singleton, meaning that there will be only one instance of this Service even if it is injected in multiple Modules and used in multiple Components or Directives.

Hint: Using the providedIn argument in @Injectable can also optimize bundle sizes if a service isn't used, which is especially useful for writing libraries. More info in the Angular Docs

The Module Scope

The same way we can provide the Service in Module scope:

@Injectable({
  providedIn: 'ExampleModule'
})
export class ExampleService {}
Enter fullscreen mode Exit fullscreen mode

But what if we don't want to share the service between modules?
We can then (instead of provideIn) use the providers array in the corresponding @NgModule:

@NgModule({
  ...
  providers: [
    ExampleService
  ]
})
export class ExampleModule {}
Enter fullscreen mode Exit fullscreen mode

This way an instance of the service is created for the Module. If the service is added to the providers array of multiple Modules, each Module gets its own Service instance.

The Component Scope

We can also create an individual Service instance for a Component by using the Component scope:

@Component({
  ...
  providers: [ExampleService]
})
export class ExampleComponent{}
Enter fullscreen mode Exit fullscreen mode

Let's see this in action

Let's get our hands dirty and create an example application using Module Scope and Component Scope.

I've used the Angular CLI to create a project with the following structure:
Project structure

There is only one Service, which is located in the SharedModule:

@Injectable()
export class ExampleService {

  name = "Frank";

  constructor() {
  }
}
Enter fullscreen mode Exit fullscreen mode

This Module and therefore the Service is used two other modules which both are then imported in the AppModule.

  1. The ComponentScopeModule which uses the Component Scope and consists of two components

    1. ExampleComponent1 which provides an input for the service's property name

      @Component({
        selector: 'component-scope-example1',
        template: `<input [(ngModel)]="service.name">`,
        providers: [ExampleService]
      })
      export class Example1Component {
      
        constructor(readonly service: ExampleService) {
        }
      }
      
    2. ExampleComponent2 which just displays the service's property name

      @Component({
        selector: 'component-scope-example2',
        template: `<p>{{service.name}}</p>`,
        providers: [ExampleService]
      })
      export class Example2Component {
      
        constructor(readonly service: ExampleService) {
        }
      }
      
  2. The ModuleScopeModule which uses the Module Scope and consists of two similar components.
    The difference is, that the Components don't use the providers array. Instead the Service is provided
    in the Module:

    @NgModule({
      declarations: [Example1Component, Example2Component],
      imports: [
        CommonModule,
        SharedModule,
        FormsModule
      ],
      exports: [
        Example1Component,
        Example2Component
      ],
      providers: [
        ExampleService     <--- it's provided here instead
      ]
    })
    export class ModuleScopeModule {
    }
    

All four Components are then displayed using the AppComponent:

<div>
  <h1>Module Scoped</h1>
  <module-scope-example1></module-scope-example1>
  <module-scope-example2></module-scope-example2>
</div>
<div>
  <h1>Component Scoped</h1>
  <component-scope-example1></component-scope-example1>
  <component-scope-example2></component-scope-example2>
</div>
Enter fullscreen mode Exit fullscreen mode

And finally this is what we get:

Result in action

We can see that in the "Module Scoped" section both components use the same service and therefore the input of the first component changes the output of the second component.
In the "Component Scoped" section this does not work as there are two service instances created,
one for each component.


Thanks for reading!

Cheers Julian

Top comments (3)

Collapse
 
anhdnitt2008 profile image
anhdnitt2008

Hi Cheers Julian

Please send me the source code of this article!

Thank you!

Collapse
 
anhdnitt2008 profile image
anhdnitt2008
Collapse
 
jimyhdolores profile image
Jimy Dolores

Excelente!!