DEV Community

Nhan Nguyen
Nhan Nguyen

Posted on

Modern Angular State Management with Signals and Dependency Injection

In this post, we’ll explore how to combine Angular’s Signals with Dependency Injection to create predictable, reactive, and reusable component state.

A Signal is a reactive value that notifies dependents when it changes.

Example:

import { signal } from '@angular/core';

const count = signal(0);
count(); // 0
count.set(1);
Enter fullscreen mode Exit fullscreen mode

Managing State via Dependency Injection

  • Create a StateService that holds signals.

  • Provide it at the component or root level.

@Injectable({ providedIn: 'root' })
export class CounterState {
  count = signal(0);

  increment() {
    this.count.update(c => c + 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

Scoped State with Component Providers

@Component({
  selector: 'app-parrent',
  providers: [CounterState],
  templateUrl: './parent.html'
})
export class ParentComponent {
  state = inject(CounterState);
}
Enter fullscreen mode Exit fullscreen mode

Using the State in child Components

@Component({
  selector: 'app-child',
  template: `
    <button (click)="state.increment()">+</button>
    <p>Count: {{ state.count() }}</p>
  `
})
export class ChildComponent {
  state = inject(CounterState);
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Keep services pure and focused on state logic.
  • Avoid too much global state.
  • Use computed for derived state.
  • Prefer DI scoping over manual signal creation in many components.

A complete example is here

Conclusion

The main benefits:

  • Reactive updates with zero boilerplate
  • Clean state isolation using DI
  • Easier debugging and reasoning

Next steps:

  • Try integrating Signals in existing projects
  • Explore computed() and effect() for advanced patterns

Happy coding!

I hope you found it helpful. Thanks for reading!
Let's get connected! You can find me on:

Top comments (0)