The result of my mapped observable is... an observable? (after running through a switchMap)

329 Views Asked by At

I was expecting the result of a mapped observable to be the entity that I selected from a prior chained observable, but instead it appears to be the observable itself. However, I can't subscribe to it or map it because it says it's of (custom) type Roster.

Maybe I'm constructing my switchmap in the wrong way? I'm trying to query my Akita store for an entity and populate it with an API call if the requested entity isn't present.

The basic flow is: Get a roster from the store or API > process the roster into a registry by adding some elements to it > ingest it via the template.

slightly redacted query service:

import { Injectable } from '@angular/core';
import { QueryEntity } from "@datorama/akita";
import { Observable } from 'rxjs';
import { map, filter, switchMap } from 'rxjs/operators';

import { Manifest } from 'src/app/models/manifest.model';
import { Roster, Registry } from 'src/app/models/roster.model'
import { Asset } from 'src/app/models/objects.model'

import { RosterState, RosterStore } from './roster.store';
import { RosterService } from 'src/app/state/roster.service';

@Injectable({providedIn: 'root'})
export class RosterQuery extends QueryEntity<RosterState> {
  constructor(private rosterStore: RosterStore,
              private rosterService: RosterService){
    super(rosterStore)
  }

  selectRoster(slug:string){
    debugger;
    const roster$ = this.selectEntity((e:Roster) => e.slug === slug).pipe(
      switchMap(res => {
        console.log('Response log:',res)
        if(!res){
          return this.rosterService.loadRoster(slug).pipe(map(res => {
            this.selectEntity((e:Roster) => e.slug === slug).subscribe(res => console.log('Entity log:',res))
            return this.selectEntity((e:Roster) => e.slug === slug) as Observable<Roster>;
          }));
        }else{
          return this.selectEntity((e:Roster) => e.slug === slug) as Observable<Roster>;
        }
      })
    );
    return roster$ as Observable<Roster>;
  }

  composeRegistry(slug:string):Observable<Registry>{
    return this.selectRoster(slug).pipe(map(roster => {
      console.log('Roster log:',roster)
      let manifest: Manifest = (manifestData  as  any).default;
      let registry: Registry = {
        ...roster,
        hash: manifest.hash,
        game: manifest.game,
      console.log('Registry log:',registry);
      registry.assets = [this.assetIterate(manifest,roster.assets[0])];
      return registry;
    }));
  }

The results of my console logging:

roster.query.ts:33 Entity log: {id: "756d65egh9h76e567g87ut567g7", slug: "Testing", manifest: "4e5y85"}
roster.query.ts:46 Roster log:  Observable {_isScalar: false, source: Observable, operator: DistinctUntilChangedOperator}
roster.query.ts:54 Registry log: {_isScalar: false, source: Observable, operator: DistinctUntilChangedOperator, hash: "6s43qhuy53as980u08647ugp864q867-08d4svbn9uh54xc8vu", game: "TBD", …}

As you can see, when I log my subscription to entityState, it provides a normal object but when I log the mapped result, it says it's an observable instead. However, when I subscribe to it or map it, it complains at me, telling me it's a Roster, not an observable:

Property 'subscribe' does not exist on type 'Roster'.
Property 'pipe' does not exist on type 'Roster'.
2

There are 2 best solutions below

0
On

chaining a mergeAll() at the end solved it (but I went a different direction anyway)

0
On

I know you don't need an answer anymore, but I'm going to try explain why mergeAll() worked for you in case it helps somebody else in the future.

const smallNumbers$ = from([1,2,3]);
const earlyAlpha = from(['a','b','c']);

// Stream 1
// This prints: 1a 1b 1c 2a 2b 2c 3a 3b 3c
// We've logged the values from all three streams
smallNumbers$.pipe(
  switchMap(smallNumber => 
    earlyAlpha$.pipe(
      map(earlyAlpha => smallNumber + earlyAlpha)
    )
  )
).subscribe(console.log);

// Stream 2
// This prints: 1a 1b 1c 2a 2b 2c 3a 3b 3c as well
smallNumbers$.pipe(
  switchMap(smallNumber => 
    earlyAlpha$.pipe(
      map(earlyAlpha => of(smallNumber + earlyAlpha))
    )
  ),
  mergeAll()
).subscribe(console.log);

// Stream 3
// This prints: 1a 1b 1c 2a 2b 2c 3a 3b 3c as well
smallNumbers$.pipe(
  switchMap(smallNumber => 
    earlyAlpha$.pipe(
      map(earlyAlpha => of(smallNumber + earlyAlpha)),
      mergeAll() 
    )
  ),
).subscribe(console.log);

// Stream 4
// This prints: 1a 1b 1c 2a 2b 2c 3a 3b 3c yet again!
smallNumbers$.pipe(
  switchMap(smallNumber => 
    earlyAlpha$.pipe(
      mergeMap(earlyAlpha => of(smallNumber + earlyAlpha))
    )
  )
).subscribe(console.log);

Stream 1 doesn't need to merge after the switchMap because it's mapping number onto Observable

Stream 2,3,4 are all mapping number onto Observable<Observable> and each represent a slightly different way of flattening the final result to Observable