Angular data store

user.service.ts:

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

/**
 * Service to store and access various data data across the website
 * BehaviorSubject was used to have ability to access value synchronously via getValue()
 * https://angular.io/guide/component-interaction#parent-and-children-communicate-using-a-service
 */
@Injectable({ providedIn: 'root' })
export class UserService {

  public userIdSource = new BehaviorSubject(0);
  public userId$ = this.userIdSubj.asObservable();

  constructor() {
  }

  setUserId(userId: number) {
    this.userIdSource.next(userId);
  }

}

user-control.component.ts:

import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-user-control',
  template: `
    <button (click)="userService.setUserId(5)">Set user</button>

    {{ userService.userId$ | json }}
  `,
  // providers: [UserService]
})
export class UserControlComponent {

  constructor(public userService: UserService) {
  }

}

Don't expose subjects directly

In this example we don't expose the subject directly to store clients, instead, we expose an observable.

This is to prevent the service clients from themselves emitting store values directly instead of calling action methods and therefore bypassing the store.

Avoiding event soup

Exposing the subject directly could lead to event soup applications, where events get chained together in a hard to reason way.

Direct access to an internal implementation detail of the subject is like returning internal references to internal data structures of an object: exposing the internal means yielding the control of the subject and allowing for third parties to emit values.

There might be valid use cases for this, but this is most likely almost never what is intended.

Leave a Comment