0

In many RxJs service store tutorials (all of them probably) - BehaviouralSubjects are used to store data in store services. For example here: https://dev.to/avatsaev/simple-state-management-in-angular-with-only-services-and-rxjs-41p8

I think I would understand the concept, if somewhere in my app I need to suscribe to my array data. But than again, as in tutorials, the BehaviouralSubject is private and I need to use service method to get it's value so I can't just subscribe to it.

What real benefit do I get from using BehaviouralSubject? Using arrays is way easier..

What is the best way to store array data, which comes from api and is expanded whenever api is called?

Aleks Grunwald
  • 315
  • 3
  • 12

2 Answers2

1

It is to handle your data through a stream. In your link, you can see they are using the async pipe in the html template to loop over todoStore.todo$ which actually result in a subscription to your observable.

Without the stream (so if it's a normal array), even if your todoStoreService update your store, your html template wouldn't be updated because it would not be notified with the changed data.

This is how i understand it, not sure if it's entirely true but I think this is pretty much what happen in this example.

Quentin Grisel
  • 4,794
  • 1
  • 10
  • 15
  • Yes. That's true. I undestand that. But If I'm not using 'todos$' in a template or not subscribing to it anywhere else - is there a point in using Subject over array? – Aleks Grunwald Jun 22 '20 at 21:22
  • 1
    In this case, there is no point in using a store. Think about how you will use your data. If you need to store data in a service (so it is available wherever you want), then there is no way you don't need to retrieve it, and the best way to do it is through the store approach I think. Maybe you could explain in your question your context and why you need to store data so answers will be more accurate. – Quentin Grisel Jun 22 '20 at 21:35
1

I'm not an expert, but this is how I understand Observables, and their benefits over simple arrays.


Ultimately we want a value to passively update itself in our template whenever it changes, without checking it manually.

Something like this:
<div>{{ observableData$ | async }}</div>

The observableData$ variable is an Observable (indicated by the convention of an ending $), which sends asynchronous messages, able to be unwrapped in your template with the async pipe.

Your template can consume this just as easily as a simple array.
eg. <div>{{ [1, 2, 3] }}</div>

But the Observable is just a stream of data.
Think of a river, it has a directional stream / flow of water molecules. But the stream is more of a verb than a noun.

What every river needs is a source. Where are the water molecules actually originating from?
In rxjs, the "source" of an Observable stream is called a Subject.

The Observable doesn't "do" anything but provide a conduit from your "source" / Subject to wherever you're subscribing to the data. Just like a garden hose doesn't "do" anything but direct the water flowing through it.

Now, think of a BehaviorSubject as simply a Subject with a specified initial value.


What real benefit do I get from using BehaviouralSubject? Using arrays is way easier..

To use a BehaviorSubject is simply to include one piece of a few pieces needed for an Observable stream.

Observable streams are asynchronous and update themselves, like a radio station, to whoever is "tuned in" or subscribed.

A simple array obviously has a simpler syntax for storing and extracting data, but an array can't update itself across multiple components without manual intervention. This is the value of an Observable stream.

Additionally, the BehaviorSubject is kept private because nothing external should need direct access to it in the first place. An external component would only need to access an Observable stream, not the BehaviorSubject itself. The source is irrelevant to the component if the component has access to the stream. Better to keep it private, and have a function within the same service to update the BehaviorSubject's value if needed.


For example..

In a service:

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

@Injectable({ providedIn: 'root'})
export class ExampleService {
  
  private exampleSource = new BehaviorSubject<any>(null);
  example$: Observable<any> = this.exampleSource.asObservable();
  
  updateSource(value: any) {
    this.selectedDateSource.next(value);
  }

}

We're defining a BehaviorSubject with the initial value of null.
Then we're defining example$ as the Observable (garden hose) to contain the stream which the BehaviorSubject emits.

A function can update the .next() value for the BehaviorSubject, at which point example$ will be transporting the new value through the stream, and each subscription will passively see its new value.

Then, in a component:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  template: `
    <div>{{ exampleData$ | async }}</div>
  `,
})
export class ExampleComponent implements OnInit {

  exampleData$: Observable<any>;

  constructor(private exampleService: ExampleService) {}

  ngOnInit() {
    this.exampleData$ = this.exampleService.example$;
  }

}

Additionally, you can manually subscribe to the observable in your component, however there are a few more steps to follow, and you'll have to manually unsubscribe to the observable as well.
This functionality is effectively contained in Angular's built in async pipe used in the previous example.

This component example yields the same result as the previous example:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';

@Component({
  template: `
    <div>{{ exampleData }}</div>
  `,
})
export class ExampleComponent implements OnInit, OnDestroy {

  exampleData: any;
  exampleData$: Observable<any>;
  exampleDataSubscription: Subscription;

  constructor(private exampleService: ExampleService) {}

  ngOnInit() {
    this.exampleData$ = this.exampleService.example$;
    this.exampleDataSubscription = this.exampleData$.subscribe(data => {

      this.exampleData = data;  // <-- your data from the BehaviorSubject

    });
  }

  ngOnDestroy() {
    this.exampleDataSubscription.unsubscribe();
  }

}

Hope this helps.

donmckenna
  • 164
  • 6
  • OK. Thank you. I do get that, it makes sense. I'll probably change few of my variables connected to templates to Observables now. But basically - if my, let's say, array is not in the template and I am not subscribing to it any other way - it makes sense for it to stay just an array? – Aleks Grunwald Jun 22 '20 at 23:15
  • 1
    It would depend how you would like to consume it later. If it matters to you that other components should be able to "listen to" changes in your array, I'd use an observable. And if you don't care if the changes you make to your array get propagated to other components, yeah it could probably stay just an array. – donmckenna Jun 23 '20 at 04:57