Search code examples
angularangular-services

How to share data between components using service in Angular 17


I want to share a value between all my components (which are not necessarily related) using a service.

There are a number of examples online but it doesn't work for me, I don't know what I did wrong.

Also, many examples are based on old versions of Angular whereas I would like to use modern methods to do it.

For now I have this 3 files, and I want to share a "Theme" value :

theme.service.ts :

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

@Injectable({
  providedIn: 'root'
})
export class ThemeService {
  private _theme: 'light'|'dark' = 'light';

  public get Theme() {
    return this._theme;
  }
  public set Theme(theme: 'light'|'dark') {
    this._theme = theme;
  }

  constructor() { }
}

app.component.ts :

import { Component, AfterViewInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { ThemeService } from './services/theme.service';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  providers: [ThemeService],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements AfterViewInit {

  constructor(private themeService: ThemeService) { }

  ngAfterViewInit() {
    console.log("app.component:", this.themeService.Theme);
    this.themeService.Theme = "dark";
    console.log("app.component:", this.themeService.Theme);
  }
}

mycomponent.component.ts :

import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
import { ThemeService } from '../../services/theme.service';

@Component({
  selector: 'app-mycomponent',
  standalone: true,
  imports: [],
  providers: [ThemeService],
  templateUrl: './mycomponent.component.html',
  styleUrl: './mycomponent.component.scss'
})
export class MyComponent implements AfterViewInit {

  constructor(private themeService: ThemeService) { }

  ngAfterViewInit() {
    console.log("mycomponent.component:", this.themeService.Theme);
  }
}

I would like to have this result:

app.component: light
app.component: dark
mycomponent.component: dark

But I have this:

app.component: light
app.component: dark
mycomponent.component: light

I imagine that the two components are using different instances of the service, how do I make it so that the service is shared between the two?


Solution

  • Don't add ThemeService to your components' providers arrays. The service is already providedIn: 'root' which means you already have a singleton for the application. If you add the service to the providers array for the component, you'll create a second copy that only that component knows about.

    See the Dependency injection and injectors hierarchy section of the Getting started with standalone components guide (emphasis mine):

    Angular applications can configure dependency injection by specifying a set of available providers. In a typical application, there are two different injector types:

    • module injector with providers configured in @NgModule.providers or @Injectable({providedIn: "..."}). Those application-wide providers are visible to all components in as well as to other services configured in a module injector.
    • node injectors configured in @Directive.providers / @Component.providers or @Component.viewProviders. Those providers are visible to a given component and all its children only.