34
votes

In my Angular 4 application I have a method that have to return an observable, this method have 3 conditions, first and second conditions make a get call, but the third conditions have to do nothing and in this case I have to return an observable too because this method is the first part of a .flatmap operator, so to chain the second part of flatmap I need an observable from the first part.

I try with: return new Observable<void>(); but I have this error:

ERROR TypeError: Cannot read property 'subscribe' of undefined

This is the initial method that have to call 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 called endPointUrlService, checkIfMapIsReady() is the method that have to return 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>();
      }
    }
  }
6

6 Answers

36
votes

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]

18
votes

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());
6
votes

You can also try:

return of(null);
3
votes

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;
);
3
votes

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>.

0
votes

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.