What are Angular services and how are they provided?
Angular services are single-instance classes designed to encapsulate reusable logic, data sharing, or external API interactions across different components. They promote modularity and testability by leveraging Angular's dependency injection system.
What are Angular Services?
In Angular, a service is typically a plain TypeScript class annotated with @Injectable(). It provides specific functionality or data management that can be injected into any component, directive, pipe, or other service that needs it. Services help keep components lean by offloading business logic and data manipulation, making applications more maintainable and testable.
How are Angular Services Provided?
For Angular to know how to create and deliver a service instance, it needs a "provider." A provider is an instruction to the dependency injection system on how to get an instance of a dependency. Services can be provided at various levels, influencing their scope and lifetime.
Root-level Provisioning (`providedIn: 'root'`)
This is the most common and recommended way to provide services since Angular 6. When providedIn: 'root' is specified in the @Injectable() decorator, Angular creates a single, shared instance of the service and makes it available throughout the entire application. This means there's only one instance of the service, ensuring true singleton behavior.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' // Service provided as a singleton throughout the app
})
export class MyDataService {
private data: string[] = ['Initial Data'];
getData(): string[] {
return this.data;
}
addData(item: string): void {
this.data.push(item);
}
}
Module-level Provisioning (`providers` array in `@NgModule`)
Providing a service in a module's providers array makes that service available to all components, directives, and services declared within that specific module. If the module is eagerly loaded, the service is instantiated once. If the module is lazy-loaded, a new instance of the service is created for each lazy-loaded module that provides it. This method is less common for root-level singletons now due to providedIn: 'root'.
import { NgModule } from '@angular/core';
import { MyDataService } from './my-data.service'; // Assume MyDataService is defined
@NgModule({
providers: [
MyDataService // Service provided to all components/services in this module
],
// ...
})
export class AppModule { }
Component-level Provisioning (`providers` array in `@Component`)
When a service is provided in a component's providers array, Angular creates a new instance of that service *for each new instance of the component*. This is useful when you need a separate, isolated instance of a service for a specific component and its children, ensuring that changes to the service's state within one component do not affect other components.
import { Component } from '@angular/core';
import { MyDataService } from './my-data.service'; // Assume MyDataService is defined
@Component({
selector: 'app-item-detail',
template: '<h2>Item Detail</h2><p>{{dataService.getData()}}</p>',
providers: [MyDataService] // Each ItemDetailComponent instance gets its own MyDataService
})
export class ItemDetailComponent {
constructor(public dataService: MyDataService) {
this.dataService.addData('Component Specific Data');
}
}
Advanced Provisioning Options
Angular's DI system offers more advanced ways to configure providers using a Provider object literal, which allows for greater flexibility, such as aliasing services or providing values that are not instances of classes.
{ provide: SomeToken, useClass: MyAlternateClass }: Use a different class for a given token.{ provide: CONFIG_TOKEN, useValue: { api: '/api' } }: Provide a static value or object.{ provide: Logger, useFactory: loggerFactory, deps: [AnalyticsService] }: Provide a factory function to create the dependency.{ provide: NewLogger, useExisting: OldLogger }: Alias an existing service.