Angular Services and Dependency Injection Explained

Angular Tutorial: Beginner to Advanced · 7. Services and dependency injection · 4 min read · Updated

Components should focus on the screen. Logic that is not about drawing, such as loading data, calculating prices or remembering the signed-in user, belongs somewhere else. In Angular that place is a service, and the way components get hold of services is called dependency injection.

What you will learn

  • What a service is and why it helps
  • What dependency injection means in simple terms
  • How to create a service and use it with inject()

What is a service?

A service is a plain class with a job. It has no template. Components ask for it when they need it, and Angular supplies the same shared instance every time.

Good uses for services:

  • Talking to a server (you will do this in the HTTP module)
  • Holding data that several components share
  • Business rules that you want to test on their own

What is dependency injection?

Suppose a component needs a quote service. The component could create one itself with new QuoteService(), but then it is stuck with that exact class, and it is hard to replace it in a test. With dependency injection the component only says what it needs, and Angular provides it. This keeps components simple and easy to test.

Create a service

Generate one with ng generate service quote. The CLI creates a class marked with the @Service() decorator, which tells Angular to make it available everywhere in your app. Here it is with some content:

// src/app/quote.ts
import { Service } from '@angular/core';

@Service()
export class Quote {
  private readonly quotes = [
    'Make it work, then make it better.',
    'Small steps every day add up.',
    'Read the error message. It is usually right.',
  ];

  random(): string {
    const index = Math.floor(Math.random() * this.quotes.length);
    return this.quotes[index];
  }
}

By default there is one instance of the service for the whole application, shared by everyone who asks for it.

Use it with inject()

Inside a component, call inject() with the class you want:

// src/app/quote-box/quote-box.ts
import { Component, inject, signal } from '@angular/core';
import { Quote } from '../quote';

@Component({
  selector: 'app-quote-box',
  template: `
    <blockquote>{{ text() }}</blockquote>
    <button type="button" (click)="next()">Another quote</button>
  `,
})
export class QuoteBox {
  private readonly quote = inject(Quote);
  protected readonly text = signal(this.quote.random());

  protected next(): void {
    this.text.set(this.quote.random());
  }
}
// src/app/app.ts
import { Component } from '@angular/core';
import { QuoteBox } from './quote-box/quote-box';

@Component({
  imports: [QuoteBox],
  selector: 'app-root',
  templateUrl: './app.html',
  styleUrl: './app.css',
})
export class App {}
<!-- src/app/app.html -->
<main>
  <h1>Services</h1>
  <app-quote-box />
</main>

The component never writes new Quote(). It asks for it with inject(Quote), and Angular hands over the shared instance. Call inject in a field initializer or the constructor, which are the places where Angular can supply it.

The older way you will still see

Before @Service(), services were written with @Injectable and an option that makes them available everywhere. Both work, and you will see the older form in most existing projects and tutorials:

// src/app/legacy-greeting.ts
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class LegacyGreeting {
  hello(name: string): string {
    return `Hello, ${name}!`;
  }
}

Older code also injects through the constructor, such as constructor(private quote: Quote) {}. The inject() function does the same job with less code and is easier to use in functions.

Where do instances live?

  • App-wide (default): one instance for the whole app, which suits data and logic you share.
  • Per component: if you add the service to a component's providers list, each instance of that component gets its own copy. This is useful when every card needs separate state.

Try it yourself

  1. Add a count() method to the Quote service that returns how many quotes it knows, and show it.
  2. Create a second component that also uses Quote. Confirm that both work.
  3. Create a service Greeter with a method that returns a greeting for a name, and use it in a component.

Quick check

  1. What is a service?
  2. What does inject(Quote) do?
  3. How many instances of a default service does the application have?

Next, put a service to work by sharing state in Share state with a service. The dependency injection guide has the full details.

Frequently asked questions

What is the difference between @Service() and @Injectable()?

Both mark a class that Angular can inject. @Service() is the newer, shorter form for services that are available app-wide. @Injectable({ providedIn: 'root' }) does the same job and is common in existing code.

Can a service use another service?

Yes. Inside the service call inject() for whatever it needs. Angular builds the chain for you.