36

There is existing subject that is in use:

const fooSubject = new BehaviorSubject(null);

And there is another observable (another subject in this example):

const barSubject = new Subject();
barSubject.subscribe(
  value => fooSubject.next(),
  err => fooSubject.error(err),
  () => fooSubject.complete()
);

barSubject.next('bar');

The code works but looks clumsy.

Is there a better way to pipe (in broad sense, not necessarily using pipe operator) barSubject observable to fooSubject? It looks like an operation that could be handled by the library itself.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565

2 Answers2

47

Since Subject is already an observer with methods next(), error() and complete() you can just subscribe it to any Observable:

const fooSubject = new BehaviorSubject(null);

const barSubject = new Subject();
barSubject.subscribe(fooSubject);

barSubject.next('bar');
martin
  • 93,354
  • 25
  • 191
  • 226
  • Thanks, that's what I meant. Totally forgot that subscribe accepts observers too. – Estus Flask Feb 20 '18 at 08:28
  • 4
    It may not be clear for some, that to subscribe a Subject to an Observable it's Observable.subscribe(Subject), not Subject.subscribe(Observable) – chrismarx Apr 11 '19 at 17:49
  • 2
    I guess generally you should unsubscribe somehow from `fooSubject` when `barSubject`goes out of scope. Right? But do you have to unsubscribe from `fooSubject` if `fooSubject` does complete? – jbandi Sep 01 '19 at 10:05
1

Regarding unsubscribing after the source Observable completes, I've been using this code. It functions as expected, but I don't know if it's an "anti-pattern" or not...?

const subscription = this.http.get(url)
  .pipe(finalize(() => subscription.unsubscribe()))
  .subscribe(this.mySubject$);

EDIT: You don't need to unsubscribe from http.get(..) because it's done automatically. Therefore, with respect to the code above, the correct form would be:

this.http.get(url).subscribe(mySubject$)

EDIT 2: A gotcha with the above code is that when http.get completes, then mySubject$ will also complete. Now if you .subscribe(mySubject$) or mySubject$.next(..) it will not emit values. To avoid this and keep mySubject$ hot, use this code:

this.http.get(url).subscribe(r => this.mySubject$.next(r))
Adam
  • 2,616
  • 33
  • 29
  • 1
    Thanks, it's possible to do this but it's an antipattern indeed. There are operators to subscribe for 1 value, and they are not needed if an observable is completed after 1 value - http likely will. Notice that the question was about subjects. – Estus Flask Nov 03 '21 at 13:43
  • Apols for topic pollution (Google kept bringing me back here). Would this be better code? `this.http.get(url).pipe(take(1)).subscribe(mySubject$)` Do I even need to bother with unsubscriptions when dealing with HttpClient? – Adam Nov 03 '21 at 14:44
  • You don't need to unsubscribe from e.g. `http.get(..).subscribe()` because internally, upon receipt of the HTTP response, the code calls `complete()` which automatically unsubscribes all subscribers. Source https://lukaonik.medium.com/do-we-need-to-unsubscribe-http-client-in-angular-86d781522b99 – Adam Nov 03 '21 at 14:48
  • 1
    "Would this be better code?" - yes, it's better if it's not a one-off observable (i.e. emit 1 value and complete, similarly to a promise). In case http is Angular's `Http`, it was made one-off as the article says, because it can't emit multiple values – Estus Flask Nov 03 '21 at 17:03