RXJS Observable - How to call next from outside of the Observable's constructor
Asked Answered
P

5

39

I am building a service which exposes an Observable. In this service I receive external function calls which should trigger a next call on the Observable so that various consumers get the subscribe event. During Observer constructor I can call next and everything works great, but how can I access this outside of the constructor so that external triggers can fire next calls?

private myObservable$: Observable<any>;

During service init I do

this.myObservable$ = new Observable(observer => {
    observer.next("initial message");
}

Then in other methods of the same service I want to be able to execute something like

this.myObservable$.observer.next("next message");

The above obviously doesn't work, but how can I accomplish this goal?

I'm assuming I'm missing something basic since there must be a way to emit further messages outside of the Observable's initial constructor

Pilloff answered 25/1, 2017 at 18:42 Comment(0)
M
53

You should create a Subject for that

this.myObservable$ = new Subject();

And then you can call at any point:

this.myObservable$.next(...);

Or use subscribe:

this.myObservable$.subscribe(...)
Mannerly answered 25/1, 2017 at 18:42 Comment(4)
This is pure rxjs - it has nothing to do with angular.Mannerly
Note: Only subscriptions setup before calling next will receive new values. If you want to allow subscriptions created afterwards use BehaviorSubject or ReplaySubject.Tuinenga
that is the best way to trigger anything from any point through service in angular.Hixon
Relevant because what @olsn said imgflip.com/i/6i16c0Oscillograph
W
13

Actually Subject is used for both publisher and subscriber, and here I think you need only to publish your value, so simply use Observable.

By using observable, assign Subscriber to class level variable and then use it, like below code

subscriber: Subscriber<boolean>;

public observe(): Observable<boolean> {

    return new Observable<boolean>(subs => {
      this.subscriber = subs;
    });
  }

public callNext() {

    if (this.subscriber) {
      this.subscriber.next();
      this.subscriber.complete();
    }
  }
Wolford answered 20/4, 2018 at 12:29 Comment(1)
Duh. I was trying to go the other way by passing the Subscriber into the constructor. This should be the accepted answer.Mccall
D
6

Two ways:

  1. Make myObservable$ public:

    public myObservable$: Observable;
    
  2. Encapsulate the observable in a subject stream, and provide a helper to call next:

    export class TestService {
      public myObservable$: Observable;
      private _myObservableSubject: Subject;
    
      constructor() {
        this._myObservableSubject = new Subject();
        this.myObservable$ = this._myObservableSubject.asObservable();
      }
    
      public NextMessage(message?: string): void {
        this._myObservableSubject.next(message);  
      }
    }
    
Dulcie answered 25/1, 2017 at 18:48 Comment(4)
Thanks Boyan, the second looks like it'd work but I'm going with olsn's answer above, seems much more concise.Pilloff
Sure, we are talking about the same thing. I got my answer from reading the Angular 2 docs. Specifically this section, angular.io/docs/ts/latest/cookbook/…Dulcie
Subject extends Observable, so you don't need this wrapper. Just use Subject.Guidry
Unfortunately, the subject can only have one subscriber if i'm not wrong, not the same behaviourArola
L
3

Observable: You have to call the next() function from inside the constructor and only one time you can subscribe

message = new Observable((observer)=>{
   observer.next(9);
})
     
this.messsage.subscribe((res)=>{
   console.log(res)
})

output: 9

Subject: You have to call next() function from outside the constructor and multiple times you can subscribe. The subject does not store any initial value before subscribe.

messsage = new Subject()
this.messsage.next(3)
this.messsage.subscribe((res)=>{
   console.log(' A '+res)
})
this.messsage.next(4)
this.messsage.next(5)
this.messsage.subscribe((res)=>{
   console.log(' B '+res)
})
this.messsage.next(6)

output: 
A 4
A 5
A 6
B 6

BehaviorSubject: You have to call next() function from outside the constructor and multiple times you can subscribe. The BehaviorSubject does store only one initial value before subscribe.

messsage = new BehaviorSubject ()
this.messsage.next(3)
this.messsage.subscribe((res)=>{
   console.log(' A '+res)
})
this.messsage.next(4)
this.messsage.next(5)
this.messsage.subscribe((res)=>{
   console.log(' B '+res)
})
this.messsage.next(6)
        
output: 
A 3
A 4
A 5
B 5
A 6
B 6
Ludivinaludlew answered 20/11, 2019 at 19:59 Comment(1)
I liked it.. so subject advanced version of observable?Claiborn
O
2

I ended up combining a couple of things:

  • olsn's answer, which nicely demonstrates Subject's ease of use
  • Ravi's answer, which correctly points out that we only want an Observable exposed
  • a more functional approach, because this and Class give me the shivers
  • TypeScript typings for generic use
const createObservableWithNext = <T>(): {
  observable: Observable<T>;
  next: (value: T) => void;
} => {
  const subject = new Subject<T>();
  const observable = subject.asObservable();

  const next = (value: T) => subject.next(value);

  return {
    observable,
    next,
  };
};
Oscillograph answered 31/5, 2022 at 5:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.