100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Creating and Injecting Services

Learn how to define an Angular service class, register it with Angular's dependency injection system, and inject it into components and other services.

Services & Dependency InjectionBeginner8 min readJul 9, 2026
Analogies

Creating and Injecting Services

Services are ordinary TypeScript classes used to encapsulate logic and state that shouldn't live directly inside a component: fetching data from a backend, sharing state across unrelated components, wrapping browser APIs, or holding business logic that needs to be tested independently of any UI. Angular's dependency injection (DI) system creates and hands out instances of these classes automatically, so consuming code never has to call new Service() manually — it simply declares what it needs and Angular supplies it.

🏏

Cricket analogy: A service is like a team's dedicated statistician who tracks every run and wicket independently of the players on the field, and Angular's DI is like the team manager assigning that statistician to whichever match needs the data, without players hiring one themselves.

Defining a service

A service is created like any class, but decorated with @Injectable() so Angular's compiler knows to generate the metadata needed to construct it and resolve its own dependencies. The providedIn: 'root' option registers the service with the application's root injector, making it a singleton available anywhere in the app without needing to list it in a component's providers array.

🏏

Cricket analogy: @Injectable() is like the BCCI certifying a coach so any franchise can legally hire them without re-vetting credentials, and providedIn: 'root' is like that coach being contracted centrally so every IPL team shares the same one instead of hiring separately.

typescript
import { Injectable, signal } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CartService {
  private readonly _items = signal<string[]>([]);
  readonly items = this._items.asReadonly();

  add(item: string): void {
    this._items.update(current => [...current, item]);
  }

  clear(): void {
    this._items.set([]);
  }
}

Injecting a service

There are two idiomatic ways to obtain a service instance in a component or another service: constructor injection, where the service type is declared as a constructor parameter, and the inject() function, which can be called at field-initializer time or inside a constructor without needing a parameter. Angular resolves the request by walking up the injector hierarchy starting from where the consumer lives until it finds a provider that matches.

🏏

Cricket analogy: Constructor injection is like a new batsman walking in and the umpire already handing them the guard-marking equipment as part of the ritual, while inject() is like a fielder grabbing sunglasses from the kit bag mid-over without needing that formal handoff.

typescript
import { Component, inject } from '@angular/core';
import { CartService } from './cart.service';

@Component({
  selector: 'app-cart-summary',
  standalone: true,
  template: `<p>{{ cart.items().length }} items</p>`,
})
export class CartSummaryComponent {
  // inject() style (common in standalone components)
  protected cart = inject(CartService);
}

// Equivalent constructor-injection style:
// constructor(protected cart: CartService) {}

A service registered with providedIn: 'root' is tree-shakable: if no code in the application ever injects it, Angular's build process can exclude it from the final bundle entirely — something that isn't possible with services registered only via an NgModule's providers array, which are always included once the module is loaded.

Forgetting @Injectable() on a service that itself has constructor dependencies is a common early mistake — TypeScript's decorator metadata (needed for Angular to know the constructor parameter types at runtime) is only emitted for classes decorated with @Injectable(), @Component(), or @Directive(). A service with no dependencies of its own may appear to work without the decorator, but it should still always be added for consistency and future-proofing.

Services can themselves depend on other services simply by injecting them the same way, and Angular resolves the entire dependency graph automatically and lazily — each service is instantiated only the first time it (or something depending on it) is actually requested, and providedIn: 'root' singletons are cached and reused for the lifetime of the application.

🏏

Cricket analogy: It's like a fast bowler needing a fitness trainer, who in turn needs a physiotherapist, and the franchise only actually books the physio the first time a bowler gets injured, then reuses that same physio for the rest of the season.

  • Services are plain classes decorated with @Injectable() to allow Angular's DI system to construct and supply them.
  • providedIn: 'root' registers a service as an application-wide singleton and makes it tree-shakable if unused.
  • Services can be injected via constructor parameters or the inject() function.
  • Services commonly hold shared state, data-fetching logic, or reusable business logic outside of components.
  • Angular instantiates services lazily, only when first requested, and caches root-provided singletons for reuse.
  • Services can depend on other services; Angular resolves the whole dependency graph automatically.

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#AngularStudyNotes#WebDevelopment#CreatingAndInjectingServices#Creating#Injecting#Services#Defining#StudyNotes#SkillVeris