0

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'.
jugglervr
  • 315
  • 1
  • 14
  • could it be the if-else block in the switchMap, in the if block you are returning the map which returns and observable, while in the else you are returning the function selectEntity, of which I'm not sure of the return type. – arbghl Sep 29 '20 at 23:44
  • 1
    Use `mergeMap` instead of `map` at `return this.rosterService.loadRoster(slug).pipe(map(res => {`. After changing to `mergeMap`, it should work as expected. – user2216584 Sep 30 '20 at 01:01
  • you need, in map return the object registry at the end of map: `.pipe(map(roster => {..let registry={...};return registry}))` – Eliseo Oct 01 '20 at 06:35

2 Answers2

0

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

jugglervr
  • 315
  • 1
  • 14
0

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

Mrk Sef
  • 7,557
  • 1
  • 9
  • 21