w3resource

Angular Singleton Service

A singleton is a class that allows only a single instance of itself to be created and gives access to that created instance. It contains static variables that can accommodate unique and private instances of itself. It is used in scenarios when a user wants to restrict instantiation of a class to only one object.

A singleton service is a service instance that is shared across components.

Providing a singleton service

There are two ways to make a service a singleton in Angular:

  • Declare root for the value of the @Injectable() providedIn property
  • Include the service in the AppModule or in a module that is only imported by the AppModule

Using providedIn

Beginning with Angular 6.0, the preferred way to create a singleton service is to set providedIn to root on the service's @Injectable() decorator. This tells Angular to provide the service in the application root.

import { Injectable } from '@angular/core';
@Injectable({
  providedIn: 'root',
})
export class UserService {
}

NgModule providers array

In apps built with Angular versions prior to 6.0, services are registered NgModule providers arrays as follows:

@NgModule({
  ...
  providers: [UserService],
  ...
})

If this NgModule were the root AppModule, the UserService would be a singleton and available throughout the app. Though you may see it coded this way, using the providedIn property of the @Injectable() decorator on the service itself is preferable as of Angular 6.0 as it makes your services tree-shakable.

The forRoot() pattern

Generally, you'll only need providedIn for providing services and forRoot()/forChild() for routing. However, understanding how forRoot() works to make sure a service is a singleton will inform your development at a deeper level.

If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton.

There are multiple ways to prevent this:

  • Use the providedIn syntax instead of registering the service in the module.
  • Separate your services into their own module.
  • Define forRoot() and forChild() methods in the module.

Use forRoot() to separate providers from a module so you can import that module into the root module with providers and child modules without providers.

  1. Create a static method forRoot() on the module.
  2. Place the providers into the forRoot() method.

Greeting.module.ts

static forRoot(config: UserServiceConfig): ModuleWithProviders {
  return {
    ngModule: GreetingModule,
    providers: [
      {provide: UserServiceConfig, useValue: config }
    ]
  };
}

forRoot() and the Router

RouterModule provides the Router service, as well as router directives, such as RouterOutlet and routerLink. The root application module imports RouterModule so that the application has a Router and the root application components can access the router directives. Any feature modules must also import RouterModule so that their components can place router directives into their templates.

If the RouterModule didn’t have forRoot() then each feature module would instantiate a new Router instance, which would break the application as there can only be one Router. By using the forRoot() method, the root application module imports RouterModule.forRoot(...) and gets a Router, and all feature modules import RouterModule.forChild(...) which does not instantiate another Router.

Previous: Types of Feature Modules
Next: Providers



Follow us on Facebook and Twitter for latest update.