angularrxjs6

Angular6: RxJS switchMap operator is not working as expected


In my angular 6 application, I want to filter before it switchMap to inner Observable as below:

Basically, What I want to do is if the store selector gives me pertaining data for a given string I will use the response in switchMap to fire the inner observable and will do a subscription.

The idea here is that if filtration succeeds do find and data for the filters response using switchMap

 this._store.pipe(select(getDetails)).pipe(map((details: detail[]) => {
    return of(details.filter(({ tagName }) => tagName ===this.videoTagName))})
   switchMap((data:detail)=>this._store.pipe(select(getVideosByID(data.contentIds[0]))))).subscribe(data=>{
      console.log("Data",data)
   })

The above code gives the error below:

Type 'detail' is missing the following properties from type 'Observable<detail[]>': _isScalar, source, operator, lift, and 10 more.ts(2345)

will combineLatest help here? or something I need to do with switchMap only.. ?


Solution

  • A few things:

    1. You should probably define types or classes/interfaces in a way that they won't conflict with variables. Usually people will capitalize types, for instance you'd have type Detail = ... so that you can then define const detail: Detail = ....

    2. Assuming that a detail is not an Observable, you should not use of in the "project" argument of RxJs's map. The "project" argument of map is a function that takes as its argument the value emitted by an observable, and returns a new value that will be emitted instead. Here if you return of, your Observable will emit an Observable (which is usually not what you want). This is why you get your error: switchMap right below expects to be piped to an Observable that emits a detail, but instead it gets an Observable<detail[]>. If you fix that, you'll emit details instead of detail, which isn't perfect yet but we're getting closer.

    3. If you want to filter a unique detail (and not a list of details), you should use .find() instead of .filter(). Filtering returns an array of all elements that match the predicate (maybe it's an array of 1 element only, but it's still an array). Find returns the first element that matches. Now we're emitting a detail, so the typing inside switchMap will be ok and the error will go away.

    4. You don't need to chain .pipe() calls, you can just chain the arguments inside of a single pipe. This applies to the first two pipes, but obviously not to the one inside the switchMap, since if you move it inside of the "main" pipe, detail won't be in the scope anymore.

    With all of this, you get the following code, which should work:

    this._store.pipe(
      select(getDetails),
      map((details: Detail[]) => details.find(
        ({ tagName }) => tagName === this.videoTagName
      )),
      switchMap((detail: Detail) => this._store.pipe(
        select(getVideosByID(detail.contentIds[0])),
      )),
    ).subscribe(data => console.log("Data", data));