Services in Angular are the packages that hold the logic for a component. While components are responsible for the user interface, services handle everything else — data fetching, business rules, state management, and communication with APIs.
Separating logic into services keeps components clean and makes the application easier to test and maintain.
Why Use Services
Without services, every component would need to duplicate its own data-fetching and logic code. Services solve this by providing a single, shared location for logic that multiple components can use.
Without Services With Services
---------------- -------------
Component A Component A
- fetch data (duplicate) - uses service
- process data (duplicate) |
v
Component B +----------+
- fetch data (duplicate) | Service | <-- Single source
- process data (duplicate) +----------+
^
|
Component B
- uses service
Generating a Service
The Angular CLI creates a service with the correct structure:
ng generate service <service-name>
# or the shorthand:
ng g s <service-name>
This creates a file with the @Injectable decorator already applied:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CartService {
constructor() { }
}
Understanding `@Injectable`
The @Injectable decorator marks a class as available for Angular's dependency injection system. The providedIn: 'root' configuration registers the service at the application root level.
Services in Angular are **singletons**. There is only one instance of the service shared across the entire application.
This means all components that inject the same service share the same instance — making services ideal for shared state like a shopping cart, user session, or cached data.
Adding Logic to a Service
A service can hold data, methods, and state:
import { Injectable } from '@angular/core';
import { IProduct } from './product.model';
@Injectable({
providedIn: 'root'
})
export class CartService {
private cartItems: IProduct[] = [];
addToCart(product: IProduct): void {
this.cartItems.push(product);
}
removeFromCart(productId: number): void {
this.cartItems = this.cartItems.filter(item => item.id !== productId);
}
getCartItems(): IProduct[] {
return this.cartItems;
}
getCartTotal(): number {
return this.cartItems.reduce((total, item) => total + item.price, 0);
}
}
Injecting a Service into a Component
To use a service in a component, declare it in the constructor. Angular's dependency injection system automatically provides the singleton instance:
import { Component, OnInit } from '@angular/core';
import { CartService } from './cart.service';
import { IProduct } from './product.model';
export class CartComponent implements OnInit {
cartItems: IProduct[] = [];
cartTotal: number = 0;
constructor(private cartService: CartService) {}
ngOnInit(): void {
this.cartItems = this.cartService.getCartItems();
this.cartTotal = this.cartService.getCartTotal();
}
remove(productId: number): void {
this.cartService.removeFromCart(productId);
this.cartItems = this.cartService.getCartItems();
this.cartTotal = this.cartService.getCartTotal();
}
}
Service Architecture
A typical Angular feature module uses services as the data and logic layer:
+-------------------+
| Component | (UI only — display data, handle user events)
+---------+---------+
|
| injects
v
+-------------------+
| Service | (Logic — business rules, data fetching, state)
+---------+---------+
|
| calls
v
+-------------------+
| HTTP / API | (External data source)
+-------------------+
Summary
Services are the backbone of Angular application architecture. They centralize logic, enable code reuse, and keep components focused purely on the UI.
Key things to remember:
- Use
ng g sto generate a service - Services are singletons — one instance shared across the whole app
- Inject services via the constructor using Angular's dependency injection
- Keep components thin and move all business logic into services
Well-designed Angular apps have thin components and rich services.