How to correctly return a void Observable?

AngularRxjs

Angular Problem Overview


In my Angular 4 application, I have a method that have to return an Observable. This method has 3 conditions. First and second conditions make a get call, but the third condition does nothing and in this case I have to return an Observable as well, because this method is the first part of a .flatmap operator. So to chain the second part of the .flatmap operator, I need an Observable from the first part.

I've tried return new Observable<void>();, but I've got an error: > ERROR TypeError: Cannot read property 'subscribe' of undefined

This is the initial method that calls a service to load the data.

loadModelData() {
      this.customerService.getModelDetail(this.code)
        .subscribe(response => {
          this.selected = response.body as Customer;
        });
    }
  }

This is the service method that have to chain 2 calls.

getModelDetail(code) {

    const params = new HttpParams().set('projection', 'withBalance');

    return this.endPointUrlService.loadMaps(this.entityLink)

      .flatMap((res) => {
        return this.http.get(this.endPointUrlService.cutLinks(
          this.endPointUrlService.map.get('customers')) + '/' + code, {observe: 'response', params: params})
          .map((response) => <any>response);
      })
  }

And this is the methods from a support service. checkIfMapIsReady() is a method that returns a void Observable in the third case:

  loadMaps(entityLink: EntityLink[]): Observable<void> {
    console.log(entityLink);

    for (const i in entityLink) {
      if (entityLink.hasOwnProperty(i)) {
      return this.checkIfMapIsReady(entityLink[i].name, entityLink[i].searchMap, entityLink[i].endPoints[0])
      }
    }
  }


  public checkIfMapIsReady(modelName: string, mapName: string, endPoints: string) {

    if (this.map.get(modelName) === undefined) {
      console.log('endpoint url service All maps undefined ' + modelName);
      return this.getLinks(modelName, this.mapNames.get(mapName), false)
    } else {
      console.log('endpoint url service Populate only ' + mapName);
      if (this.mapNames.get(mapName).get(endPoints) === undefined) {
      return  this.getLinks(modelName, this.mapNames.get(mapName), true)
      } else {
        return new Observable<void>();
      }
    }
  }

Angular Solutions


Solution 1 - Angular

return of(); doesn't create a stream.

Whereas

return of(void 0);

does.

if you are mocking and need to return a stream of void. Like my life. [Sad Panda]

Solution 2 - Angular

An observable that matches Observable<void> type is

Observable.of();

It results in complete observable with no values and is similar to Observable.empty().

While

new Observable<void>();

is incorrect because it lacks subscribe function argument. It could be instead:

new Observable<void>(observer => observer.complete());

Solution 3 - Angular

You can also try:

return of(null);

Solution 4 - Angular

Perhaps Observable.empty() is a better options, since it does not emit any values, the downstream operators can't have a problem with what it returns!

Note, downstream flatMap(res => ...) and subscribe() will not trigger.
Since you're not using res, I presume this is the desired effect?

For good measure, make the return type Observable<any>.

I think this below is logically equivalent to the posted checkIfMapIsReady(),

public checkIfMapIsReady(modelName: string, mapName: string, endPoints: string) {

  const map = this.map.get(modelName);
  const name = this.mapNames.get(mapName);
  const endPointCheck = name ? name.get(endPoints) : null;

  const links = !endPointCheck 
    ? this.getLinks(modelName, name, !!map)     // !! gives boolean true or false
    : Observable.empty();

  const msg = !map 
    ? 'endpoint url service All maps undefined ' + modelName
    : 'endpoint url service Populate only ' + mapName
  console.log(msg);    
       
  return links;
);

Solution 5 - Angular

For RxJS 6.x, you need to import it like this:

import { of } from 'rxjs';

And then just use

return of();

As you can see: without any parameter means an observable that matches Observable<void>.

Solution 6 - Angular

you can use bahaviour observables. they have an initial value, and when you subscribe to them, the subscription works on that initial value. so let's say you wanna operate on a new instance of Customer if the third condition was true, so you do this:

`

let customer = new Customer();
let subj = new BehaviorSubject<Customer>(customer);
....
if(thirdcondition){
  return subj;
}

`

so now when you call subscribe() on this method, and that last block is executed, you have what you wanted. you can change new customer() to anything you want by default.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlessandro CeleghinView Question on Stackoverflow
Solution 1 - Angularjenson-button-eventView Answer on Stackoverflow
Solution 2 - AngularEstus FlaskView Answer on Stackoverflow
Solution 3 - AngulartheKRAYView Answer on Stackoverflow
Solution 4 - AngularRichard MatsenView Answer on Stackoverflow
Solution 5 - AngularA. MassonView Answer on Stackoverflow
Solution 6 - AngularFatemeh MajdView Answer on Stackoverflow