Service is a broad category encompassing any value, function, or feature that an application needs. A service is typically a class with a narrow, well-defined purpose. A component is one type of class that can use DI.
Angular distinguishes components from services to increase modularity and reusability. By separating a component's view-related features from other kinds of processing, you can make your component classes lean and efficient.
Ideally, a component's job is to enable the user experience and nothing more. A component should present properties and methods for data binding, to mediate between the view (rendered by the template) and the application logic (which often includes some notion of a model).
A component can delegate certain tasks to services, such as fetching data from the server, validating user input, or logging directly to the console. By defining such processing tasks in an injectable service class, you make those tasks available to any component. You can also make your application more adaptable by injecting different providers of the same kind of service, as appropriate in different circumstances.
Angular does not enforce these principles. Angular helps you follow these principles by making it easy to factor your application logic into services and make those services available to components through DI.
Here's an example of a service class that logs to the browser console.
export class Logger { log(msg: any) { console.log(msg); } error(msg: any) { console.error(msg); } warn(msg: any) { console.warn(msg); } }
Services can depend on other services. For example, here's a HeroService
that depends on the Logger
service, and also uses BackendService
to get heroes. That service in turn might depend on the HttpClient
service to fetch heroes asynchronously from a server.
export class HeroService { private heroes: Hero[] = []; constructor( private backend: BackendService, private logger: Logger) { } getHeroes() { this.backend.getAll(Hero).then( (heroes: Hero[]) => { this.logger.log(`Fetched ${heroes.length} heroes.`); this.heroes.push(...heroes); // fill cache }); return this.heroes; } }
Angular CLI provides a command to create a new service. In the following example, you add a new service to your application, which was created earlier with the ng new
command.
To generate a new HeroService
class in the src/app/heroes
folder, follow these steps:
ng generate service heroes/hero
This command creates the following default HeroService
.
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class HeroService { }
The @Injectable()
decorator specifies that Angular can use this class in the DI system. The metadata, providedIn: 'root'
, means that the HeroService
is visible throughout the application.
getHeroes()
method that returns the heroes from mock.heroes.ts
to get the hero mock data:import { Injectable } from '@angular/core'; import { HEROES } from './mock-heroes'; @Injectable({ // declares that this service should be created // by the root application injector. providedIn: 'root', }) export class HeroService { getHeroes() { return HEROES; } }
For clarity and maintainability, it is recommended that you define components and services in separate files.
To inject a service as a dependency into a component, you can use component's constructor()
and supply a constructor argument with the dependency type. The following example specifies the HeroService
in the HeroListComponent
constructor. The type of the heroService
is HeroService
. Angular recognizes the HeroService
as a dependency, since that class was previously annotated with the @Injectable
decorator.
constructor(heroService: HeroService)
When a service depends on another service, follow the same pattern as injecting into a component. In the following example HeroService
depends on a Logger
service to report its activities.
First, import the Logger
service. Next, inject the Logger
service in the HeroService
constructor()
by specifying private logger: Logger
.
Here, the constructor()
specifies a type of Logger
and stores the instance of Logger
in a private field called logger
.
The following code tabs feature the Logger
service and two versions of HeroService
. The first version of HeroService
does not depend on the Logger
service. The revised second version does depend on Logger
service.
import { Injectable } from '@angular/core'; import { HEROES } from './mock-heroes'; import { Logger } from '../logger.service'; @Injectable({ providedIn: 'root', }) export class HeroService { constructor(private logger: Logger) { } getHeroes() { this.logger.log('Getting heroes ...'); return HEROES; } }
import { Injectable } from '@angular/core'; import { HEROES } from './mock-heroes'; @Injectable({ providedIn: 'root', }) export class HeroService { getHeroes() { return HEROES; } }
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class Logger { logs: string[] = []; // capture logs for testing log(message: string) { this.logs.push(message); console.log(message); } }
In this example, the getHeroes()
method uses the Logger
service by logging a message when fetching heroes.
InjectionTokens
to provide and inject values other than services/classes
© 2010–2023 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://angular.io/guide/creating-injectable-service