Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR ANGULARJS GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Angular Services & Dependency Injection


Services organize reusable logic and state outside of components.


Services & DI Essentials

  • What: A service holds reusable logic/state. DI (dependency injection) supplies instances where needed.
  • Scope: Provide in root for a shared singleton, or provide in a component for isolated instances.
  • Use cases: Data fetching, caching, business rules, cross-component state.
  • Mental model: DI is like a power outlet: you plug in and Angular gives you a ready instance.
  • Decorator: Use @Injectable() on classes that inject other services.
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService { value = 0; inc() { this.value++; } }

// Inject in a component
// constructor(public counter: CounterService) {}

Notes:

  • Related: See Components to consume services, Router for guards using services, and HTTP for services that fetch data.
  • Use @Injectable({ providedIn: 'root' }) for app-wide singletons.
  • Provide at a component when you need isolated instances.
  • Use inject() in functions like route guards to retrieve dependencies outside constructors.

Service Basics

  • Decorate classes with @Injectable() (required if they inject other services).
  • Inject services into constructors to use them in components.
  • Use providedIn: 'root' for a shared singleton.
@Injectable({ providedIn: 'root' })
export class DataService {}

// class App { constructor(private data: DataService) {} }

Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService {
  value = 0;
  inc() { this.value++; }
  dec() { this.value--; }
  reset() { this.value = 0; }
}

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h3>Services</h3>
    <p>Counter: {{ counter.value }}</p>
    <button (click)="counter.inc()">+</button>
    <button (click)="counter.dec()">-</button>
    <button (click)="counter.reset()">Reset</button>
  `
})
export class App {
  constructor(public counter: CounterService) {}
}

bootstrapApplication(App);
<app-root></app-root>

Run Example »

Example explained

  • @Injectable({ providedIn: 'root' }): Registers a singleton service available app‑wide.
  • Inject in constructor: constructor(public counter: CounterService) exposes the service to the template.
  • Methods: Call inc() / dec() / reset() to update shared state.

Notes:

  • Metadata required when injecting: If a service injects other services, add @Injectable() so DI can generate metadata.
  • Keep state minimal: Avoid large shared mutable state in services; Use explicit methods and return values.
  • Standalone bootstrapping: Ensure providers are registered where needed and use inject() in functional constructs (e.g., guards).


Shared Service Across Components

  • providedIn: 'root' shares one instance across the app.
  • Updating in one component reflects in others using the same service.
<counter-a></counter-a>
<counter-b></counter-b>
<!-- Both use the same service instance -->

Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class CounterService {
  value = 0;
  inc() { this.value++; }
  dec() { this.value--; }
}

@Component({
  selector: 'counter-a',
  standalone: true,
  template: `
    <h4>Counter A</h4>
    <p>Value: {{ counter.value }}</p>
    <button (click)="counter.inc()">+1</button>
    <button (click)="counter.dec()">-1</button>
  `
})
export class CounterA {
  constructor(public counter: CounterService) {}
}

@Component({
  selector: 'counter-b',
  standalone: true,
  template: `
    <h4>Counter B</h4>
    <p>Value: {{ counter.value }}</p>
    <button (click)="counter.inc()">+1</button>
    <button (click)="counter.dec()">-1</button>
  `
})
export class CounterB {
  constructor(public counter: CounterService) {}
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CounterA, CounterB],
  template: `
    <h3>Shared Service Across Components</h3>
    <counter-a></counter-a>
    <counter-b></counter-b>
    <p><em>Both components use the same CounterService instance.</em></p>
  `
})
export class App {}

bootstrapApplication(App);
<app-root></app-root>

Run Example »

Example explained

  • providedIn: 'root' shares one instance across the app.
  • Shared state: Clicking +/− in one component updates the value shown in the other.
  • Template access: Injected as public so the template can read counter.value and call methods.

Notes:

  • Singleton vs local: providedIn: 'root' yields a single shared instance; providing in a component creates separate instances per subtree.
  • Be deliberate about scope: Provide at the level that matches your sharing needs to avoid surprises.

Component-Provided Service (Hierarchical DI)

  • Provide a service in a component's providers to create a local instance for its subtree.
  • Sibling subtrees receive separate instances.
@Component({ providers: [LocalCounterService] })
export class Panel {}

Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Injectable } from '@angular/core';
import { CommonModule } from '@angular/common';

@Injectable()
export class LocalCounterService {
  id = Math.floor(Math.random() * 10000);
  value = 0;
  inc() { this.value++; }
}

@Component({
  selector: 'counter-view',
  standalone: true,
  template: `
    <p>Service #{{ svc.id }} value: {{ svc.value }}</p>
    <button (click)="svc.inc()">+1</button>
  `
})
export class CounterView {
  constructor(public svc: LocalCounterService) {}
}

@Component({
  selector: 'panel-a',
  standalone: true,
  imports: [CommonModule, CounterView],
  providers: [LocalCounterService],
  template: `
    <h4>Panel A (own provider)</h4>
    <counter-view></counter-view>
    <counter-view></counter-view>
  `
})
export class PanelA {}

@Component({
  selector: 'panel-b',
  standalone: true,
  imports: [CommonModule, CounterView],
  providers: [LocalCounterService],
  template: `
    <h4>Panel B (own provider)</h4>
    <counter-view></counter-view>
    <counter-view></counter-view>
  `
})
export class PanelB {}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, PanelA, PanelB],
  styles: [`
    .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
    h4 { margin: 0 0 8px; }
    button { margin-top: 6px; }
  `],
  template: `
    <h3>Component-Provided Service (Hierarchical DI)</h3>
    <p>Each panel provides its own service instance.</p>
    <p>Counters inside the same panel share the instance.</p>
    <p>Different panels do not.</p>
    <div class="grid">
      <panel-a></panel-a>
      <panel-b></panel-b>
    </div>
  `
})
export class App {}

bootstrapApplication(App);
<app-root></app-root>

Run Example »

Example explained

  • providers: [LocalCounterService]: Each panel component gets its own service instance for its subtree.
  • Within a panel: Multiple CounterView children share the same local instance.
  • Between panels: Panel A and Panel B have different instances (see the different id values).

Notes:

  • Multiple instances expected: Each provided component subtree gets its own instance; siblings do not share it.
  • Avoid circular dependencies: Two services injecting each other will fail; extract common logic into a third service or redesign.

Service Design Tips

Keep services focused and easy to test:

  • Avoid component coupling: Do not inject components into services; keep services UI-agnostic.
  • Expose clear APIs: Use small methods returning plain values or Observables; keep internal state private.
  • Configuration via tokens: Use injection tokens (a unique lookup key, often a class or an InjectionToken object) for configurable values to simplify testing and reuse.
  • Scope deliberately: Use providedIn: 'root' for app-wide singletons; provide at a component for isolated instances.


×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.