37

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

panepeter
  • 3,224
  • 1
  • 29
  • 41
Joshua Ohana
  • 5,613
  • 12
  • 56
  • 112

5 Answers5

52

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(...)
olsn
  • 16,644
  • 6
  • 59
  • 65
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();
    }
  }
Ravi Sevta
  • 2,817
  • 21
  • 37
  • Duh. I was trying to go the other way by passing the Subscriber into the constructor. This should be the accepted answer. – Brian T. May 12 '21 at 06:13
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);  
      }
    }
    
Pang
  • 9,564
  • 146
  • 81
  • 122
Ben Richards
  • 3,437
  • 1
  • 14
  • 18
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
Soura Ghosh
  • 879
  • 1
  • 9
  • 16
1

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,
  };
};
panepeter
  • 3,224
  • 1
  • 29
  • 41