skip to content
BKB
Table of Contents

Page components should focus on the interface. Shared data and business operations belong in a service.

What is dependency injection?

Dependency injection means a class asks Angular for something it needs instead of constructing it itself.

private readonly topicService = inject(Topic);

Angular creates and supplies the service. With providedIn: 'root', the application shares one instance.

1. Generate the service

Terminal window
ng generate service services/topic
import { Injectable, signal } from '@angular/core';
export interface TopicItem {
id: number;
name: string;
completed: boolean;
}
@Injectable({ providedIn: 'root' })
export class Topic {
readonly topics = signal<TopicItem[]>([
{ id: 1, name: 'Components', completed: true },
{ id: 2, name: 'Signals', completed: true },
{ id: 3, name: 'Routing', completed: false }
]);
findById(id: number): TopicItem | undefined {
return this.topics().find(topic => topic.id === id);
}
}

2. Use it from a page

private readonly topicService = inject(Topic);
protected readonly topic = computed(() =>
this.topicService.findById(this.topicId())
);

computed() derives a value from signals. When the route ID or topic list changes, Angular recalculates the selected topic.

3. Add an update operation

toggleCompleted(id: number): void {
this.topics.update(topics =>
topics.map(topic =>
topic.id === id
? { ...topic, completed: !topic.completed }
: topic
)
);
}

We create a new object and array instead of mutating the existing topic. This keeps state changes predictable.

Will the detail page and dashboard get separate services?

No. A root-provided service is shared, so both pages observe the same state.

Checkpoint

Change a topic on its detail page, return to the dashboard, and confirm the dashboard shows the updated status.

Small challenge

Add setCompleted(id, completed) and make it do nothing when the requested status is already set.

Navigation: Previous: Routing · Series overview · Next: Signal state