javascriptangularrxjsobservablesubject

Observable from Subject emits first value as null


I'm trying to have an observable that is based on a Subject, so that it gets updated every time the SUbject emits.

For some reason, when trying to fetch the value of the observable, first value is always null, and rest contain values. I shall expect to retrieve the latest emitted value of the observable. I've tried with all kind of SUbjects. What is what I'm missing?

...
  activeBookings$: Subject<Booking[]> = new BehaviorSubject<Booking[]>(null);
  activeBooking$: Observable<Booking> = this.activeBookings$.pipe(
    filter((bookings) => bookings?.length > 0),
    mergeMap((bookings) => this.getBookingById(bookings?.[0]?.id)),
    share()
  );
  i = 0;
  constructor(
    public http: HttpClient,
    public store: Store,
    public asyncPipe: AsyncPipe,
    public bookingStoreService: BookingStoreService
  ) {
    this.getBookings().subscribe((bookings) => {
      this.bookings$.next(bookings);
    });
    this.getActiveBookings().subscribe((bookings) => {
      this.activeBookings$.next(bookings);
    });
    this.getPreviousBookings().subscribe((bookings) => {
      this.previousBookings$.next(bookings);
    });
  }
...

Here is where I try to obtain the latest value:

...
    const booking: Booking = this.asyncPipe.transform(
      this.bookingService.activeBooking$
    );

    booking.status = status; //error: booking is null
...



Solution

  • You are using a BehaviorSubject here with an initial value of null:

    activeBookings$: Subject<Booking[]> = new BehaviorSubject<Booking[]>(null);
    

    You could initialise it with an empty array instead for exemple:

    activeBookings$: Subject<Booking[]> = new BehaviorSubject<Booking[]>([]);
    

    It would be more consistent type wise else your real type is:

    activeBookings$: Subject<Booking[] | null> = new BehaviorSubject<Booking[] | null>(null);
    

    Why are you getting null ? A BehaviorSubject needs to be initialised with a value and that is probably why the first value you get is null. A plain Subject doesn't need to contain an initial value and you would probably not get null

    activeBookings$: Subject<Booking[]> = new Subject<Booking[]>()
    

    A cleaner way to obtain the latest value without subscribing in your other service would be to leverage the features of the BehaviorSubject and the fact that calling activeBooking$.value makes you retrieve the latest value of the BehaviorSubject. Transform your activeBooking$ field so that it is a BehaviorSubject and when subscribing to the result of getActiveBookings(), you could do the computing that is currently in the pipe of the activeBooking$ definition there and call next on activeBooking$ with the result and expose a getter for your other service:

    get activeBooking(): Booking {
       return this.activeBooking$.value
    }