1. How do you share data between components using a service?
What is sharing data using a service?
In Angular, a service can act as a shared data store or communication layer between components.
Instead of passing data through multiple levels using input() and output(), multiple components can inject the same service instance and read or update shared state.
A common modern approach is:
Component A
│
│ updates
▼
Shared Service
│
│ exposes state
▼
Component B
For modern Angular, Signals are a simple option for shared component state. RxJS BehaviorSubject is also widely used, especially when the data is naturally stream-based.
Why do we need it?
Suppose we have two components:
Header
Product List
When Product List changes the cart count, Header needs to display the new count.
These components may not have a convenient direct parent-child relationship.
Instead of:
Product → Parent → Layout → Header
we can use:
Product ──► CartService ◄── Header
Both components communicate through the same service.
Full Simple Sample Program
Let's share a username between two components.
user.service.ts
import { Injectable, signal } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class UserService {
userName = signal('Guest');
changeUserName(name: string) {
this.userName.set(name);
}
}
sender.ts
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-sender',
standalone: true,
template: `
<h3>Sender Component</h3>
<button (click)="changeName()">
Change Name
</button>
`
})
export class Sender {
private userService = inject(UserService);
changeName() {
this.userService.changeUserName('Syed Mohamed');
}
}
receiver.ts
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-receiver',
standalone: true,
template: `
<h3>Receiver Component</h3>
<p>User Name: {{ userService.userName() }}</p>
`
})
export class Receiver {
userService = inject(UserService);
}
app.ts
import { Component } from '@angular/core';
import { Sender } from './sender';
import { Receiver } from './receiver';
@Component({
selector: 'app-root',
standalone: true,
imports: [Sender, Receiver],
template: `
<app-sender></app-sender>
<hr>
<app-receiver></app-receiver>
`
})
export class App {
}
How does it work?
The important part is:
@Injectable({
providedIn: 'root'
})
Angular creates a shared UserService instance in the root injector.
Both components inject that service:
inject(UserService);
Therefore:
Root Injector
│
▼
UserService
userName = Guest
▲ ▲
│ │
Sender Receiver
When Sender executes:
this.userService.changeUserName('Syed Mohamed');
the service updates:
this.userName.set(name);
Because userName is a signal, Angular updates the Receiver's template:
{{ userService.userName() }}
So:
Before:
User Name: Guest
Click button
After:
User Name: Syed Mohamed
Advantages
- Easy communication between unrelated components.
- Avoids long chains of
input()andoutput(). - Centralizes shared state and related logic.
- Multiple components can use the same data.
- Services are reusable and easy to test.
- Signals provide reactive updates with relatively little code.
Disadvantages
- Too much shared state can make an application harder to understand.
- Components can become tightly dependent on shared services.
- A root service lives for the application lifetime, so state can remain longer than expected.
- Large applications may require more structured state-management patterns.
2. What happens when a service is provided at the component level?
What is component-level service provision?
Normally we might define a service globally:
@Injectable({
providedIn: 'root'
})
export class UserService {
}
This usually gives us one root-scoped service instance.
But we can instead provide a service from a component:
@Component({
providers: [UserService]
})
export class Parent {
}
Angular then creates a service instance associated with that component's injector.
Why do we need component-level services?
Sometimes we do not want application-wide shared state.
For example, imagine two independent editor components:
Editor A
Editor B
Each editor should have its own temporary state.
If both use a root service, they would share one service instance.
Component-level providers allow each editor instance to have its own service instance.
Full Simple Sample Program
counter.service.ts
import { Injectable, signal } from '@angular/core';
@Injectable()
export class CounterService {
count = signal(0);
increment() {
this.count.update(value => value + 1);
}
}
counter.ts
import { Component, inject } from '@angular/core';
import { CounterService } from './counter.service';
@Component({
selector: 'app-counter',
standalone: true,
providers: [CounterService],
template: `
<h3>Count: {{ counterService.count() }}</h3>
<button (click)="increment()">
Increment
</button>
`
})
export class Counter {
counterService = inject(CounterService);
increment() {
this.counterService.increment();
}
}
app.ts
import { Component } from '@angular/core';
import { Counter } from './counter';
@Component({
selector: 'app-root',
standalone: true,
imports: [Counter],
template: `
<h2>Counter 1</h2>
<app-counter></app-counter>
<hr>
<h2>Counter 2</h2>
<app-counter></app-counter>
`
})
export class App {
}
Explanation
Notice this line:
providers: [CounterService]
The service is provided by the Counter component.
We create the component twice:
<app-counter></app-counter>
<app-counter></app-counter>
Each Counter instance gets its own CounterService instance.
App
│
├── Counter 1
│ │
│ └── CounterService Instance #1
│ count = 0
│
└── Counter 2
│
└── CounterService Instance #2
count = 0
If we increment Counter 1:
Counter 1 → 1
Counter 2 → 0
Counter 2 is unaffected because the service instances are separate.
Important: What about child components?
A component-level service is not necessarily available only to that exact component.
Its descendants can also resolve that service through Angular's hierarchical DI system.
For example:
Parent
providers: [UserService]
│
├── Child A
│
└── Child B
The Parent and its descendants can resolve the same component-provided service instance, unless a descendant provides another instance.
Parent Injector
│
└── UserService Instance
▲
│
┌──────┴──────┐
│ │
Child A Child B
This is an important Angular interview concept.
Root-level vs Component-level Service
| Feature | Root Level | Component Level |
|---|---|---|
| Configuration | providedIn: 'root' |
providers: [Service] |
| Typical instance scope | Application-wide | Component subtree |
| Shared globally | Yes | No |
| Children can access it | Yes | Yes, through hierarchy |
| New component instance can get new service | No, normally same root instance | Yes |
| Good for | Global/shared functionality | Local component state |
| Lifetime | Usually application lifetime | Tied to component/provider lifetime |
Root level
@Injectable({
providedIn: 'root'
})
export class UserService {
}
Think:
One shared service for the application.
Component level
@Component({
providers: [UserService]
})
Think:
A local service instance for this component subtree.
Key Points
- Services are commonly used to share state and functionality between components.
providedIn: 'root'makes the service available through the root injector.- Components injecting a root-provided service normally receive the same instance.
- Signals can be used to hold reactive shared state:
userName = signal('Guest');
- A component can provide its own service:
providers: [UserService]
- Each instance of that component can receive a separate service instance.
- Child components can inherit a service from a parent component's injector.
- If a child provides the same service again, Angular creates another instance for that child's subtree.
- This behavior is part of Angular's hierarchical dependency injection.
Interview Questions and Answers
1. How can two unrelated components share data?
Use a shared service. Provide it at an appropriate injector level, inject it into both components, and expose shared state using mechanisms such as Signals or RxJS.
2. Why use a service instead of input() and output()?
input() and output() are ideal for direct component relationships.
A service is useful when components are unrelated or when the same state/functionality must be shared across several components.
3. What does providedIn: 'root' mean?
@Injectable({
providedIn: 'root'
})
It registers the service with the application's root environment injector, making it broadly available and normally giving consumers the same root-scoped instance.
4. What happens if I put a service in a component's providers?
@Component({
providers: [UserService]
})
Angular creates an instance associated with that component's injector. Its descendant components can also resolve that instance through hierarchical DI.
5. If I create the same component twice, will they share a component-level service?
Normally no.
<app-counter></app-counter>
<app-counter></app-counter>
With:
providers: [CounterService]
each component instance gets its own CounterService.
6. Root service vs component service — when should I use each?
Use root-level services for application-wide functionality or shared state, such as authentication, API services, user information, or global configuration.
Use component-level services when state or functionality should be isolated to a particular component subtree.
Easy interview memory
providedIn: 'root'
↓
Shared application-level instance
providers: [Service]
↓
Local component/subtree instance
The key concept behind both questions is service scope + Angular's hierarchical dependency injection.