Multiple service instances in Angular

October 28th 2022 Angular Dependency Injection

By default, services in Angular are treated as singletons – the same instance is used throughout the application. This behavior is specified by the providedIn property of the Injectable decorator:

@Injectable({
  providedIn: "root",
})
export class CounterService {
  public value = 0;

  public increment() {
    this.value++;
  }
}

Any state in such a service is also shared. In the CounterService above, all components with this service injected will see the same value:

<p>First counter: {{ counterService.value }}</p>
<button (click)="counterService.increment()">Increment</button>

If one component increments the value, it affects all components:

@Component({
  selector: "app-first",
  templateUrl: "./first.component.html",
  styleUrls: ["./first.component.scss"],
})
export class FirstComponent {
  constructor(public counterService: CounterService) {}

  public onClick() {
    this.counterService.increment();
  }
}

If this is not the desired behavior, you can change it by removing the providedIn property from the Injectable decorator for the service:

@Injectable()
export class CounterService {
  public value = 0;

  public increment() {
    this.value++;
  }
}

And then add the service as a provider to each component:

@Component({
  selector: "app-first",
  templateUrl: "./first.component.html",
  styleUrls: ["./first.component.scss"],
  providers: [CounterService],
})
export class FirstComponent {
  constructor(public counterService: CounterService) {}

  public onClick() {
    this.counterService.increment();
  }
}

Now each component gets its own instance of the service with its own state. Any increments to its value will be visible only in that component (and its child components, if it has any). When a new instance of the same component is created, it will also get a new instance of the service.

You can see the difference between the two behaviors in action in the sample project from my GitHub repository. The last commit provides different service instances for each component. The previous commit provides the same service instance to all components.

In its newer versions, Angular hides the details of registering your injectable services with the dependency injector and ensures that they are treated as singletons at the application level by default. However, you can still take control over this if needed. In this post, I showed how to provide each component with its own service instance so that they do not share their state.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License