Search code examples
angulartypescriptrxjsnativescriptbehaviorsubject

Getting data from JSON using RxJS trying to return array and pass values to different behavior subjects


First, I am open to other things besides behavior subjects? but this is what is working for me so far. I have a Nativescript app using the Latest Angular7 build and Nativescript 5.1 I am trying to populate a ListView in NS with a json file and its all working good so far. But I would like to do more with the config file than just the initial listview.

import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable, of } from "rxjs";
import { catchError, map, tap } from "rxjs/operators";
import { IMenuItem } from "~/app/menuItem";

@Injectable({
  providedIn: "root"
})
export class DataItemService {

  items$ = new BehaviorSubject<Array<IMenuItem>>([]);

  constructor(private http: HttpClient) { }

  clear(): void {
    this.items$.next([]);
  }

  fetch(): Observable<any> {

    this.clear();

    return this.http.get("https://example.com/config.json")
      .pipe(
        map((data) => {
          return (data["result"]) ? data["result"] : false;
          }
        ),
        tap((items) => { if (items) { this.items$.next(items); }}),
        catchError((err) => {
          return of(false);
        })
      );
  }
}

My question goes like this. Using the code above I am getting a portion of my array in the "result" section. But I would also like to get config as well and save it. Is there a way for me to do the exact same thing I am doing with data["results"] and save it to this.config$ intstead of this.items$

I'd like both objects from the same json but not have to do 2 calls to set it all up. Is there a way I can do 1 call to the JSON and then save off the different objects as behavior subjects ?

{
 "config": [
     {
     "debug": true
     }
 ],
 "result": [
   {
     "id": 0,
     "title": "Home",
     "titleFontSize": 16,
     "iconFontSize": 30,
     "subtitle": "This is the subtitle for messages",
     "image": "",
     "icon": "fa-home",
     "url": "/home",
     "sidebar": true,
     "mainmenu": false,
     "bg": "#ffdc00",
     "titleColor": "white",
     "subtitleColor": "white",
     "squarebg": "rgba(0,0,0,.8)"
  }
 ]
}

--- EDIT ---

using the answer as inspiration from below.. i have this

fetch(): Observable<any> {

this.clear();

return this.http.get("https://example.com/config.json")
  .pipe(
    tap(({start, finish}) => {
  if (start) {
    this.items$.next(start);
    console.log("ITEMS ARE   " + JSON.stringify(this.items$.value));
  }
  if (finish) {
    this.config$.next(finish);
    console.log("CONFIG IS   " + JSON.stringify(this.config$.value));

  }
}
));

Now I modified my JSON to have 2 keys. start and finish (because config was complaining that it was already used in some other dependency). so with start and finish in my JSON I do console.log and I get both the keys.. start and finish.

the new issue is that my original call had a MAP which removed all the other crap and just gave me the array with no observable code. the new stuff gives me a bunch of observable content with it.. my current ListView doesn't populate now with the new code even though I know console log is doing what we want (mostly).

<RadListView [items]="items$ | async"

    ngOnInit() {
       this._dataItemService.fetch().subscribe();

       this.items$  = this._dataItemService.items$;
    }

Solution

  • You don't really need the map in between, you can extract data with ES6 syntax.

    this.http.get("https://example.com/config.json")
          .pipe(
             tap(({result,config})) => { 
    if (result) this.items$.next(items); 
    if (config) this.config$.next(config); 
    }),
            catchError((err) => {
              return of(false);
            })
          );