0

My api call returns data the following way:

{
  "items": [
    {
      "id": 1,
      "descp": "item 1",
    },
    {
      "id": 2,
      "descp": "item 2",
    },
    {
      "id": 3,
      "descp": "item 3",
    }
  ]
}

I am trying to get the array of items from this using rxjs but have not been able to so far. I keep running into core.js:4197 ERROR TypeError: You provided an invalid object where a stream was expected.

  getItems() {
    const iAmStuck$ = this.service.getAllItems();
    iAmStuck$
      .pipe(mergeMap((insideArray) => insideArray))
      .subscribe((res) => console.log(res));
  }

service call

  getItems(): Observable<Item[]> {
    const url = `${environment.url}/allItems`;
    return this.http.get<Item[]>(url);
  }

I created the following interfaces

export interface IInsideItems{
  id: number;
  descp: string;
}

export interface Item{
  items: IInsideItems[];
}

In the end of all this, I want to put id and descp into a new array of IDropdown

IDropdown{
 id: string;
 descp: string;
}
8
  • 1
    Please include the code for getAllItems function. Also, what is your desired outcome? Your comment in the code: <-- how to return a stream can be answered just by saying "Use return", but I guess that's not exactly what you're wondering. Be specific. Commented Oct 28, 2020 at 17:55
  • @DanielB, added more information. Thanks. Commented Oct 28, 2020 at 17:57
  • Is your content type defined? Commented Oct 28, 2020 at 18:01
  • What is the purpose of the mergeMap? That's whats causing you problems. Commented Oct 28, 2020 at 18:05
  • @DanielB, I was trying to use mergeMap and then map to get the data out but I guess that is not the right way Commented Oct 28, 2020 at 18:06

1 Answer 1

2

You don't need mergeMap, you only need map. You don't even need to subscribe anywhere. The error you get is from using mergeMap, since it expects a stream as argument, not an actual object. Your code could work with mergeMap if you would wrap insideArray.items in the of operator.

I assume you have a template in which you want to populate your dropdown with your values. The best way to get the values from your observable is to simply return the observable itself, and let Angulars async pipe handle the subscribe/unsubscribe for you.

Your getItems() would become

getItems() {
   return this.service.getAllItems(
        map(response => response.items) // We use map to return the items of the response
   );
}

and you could then use that in a template

<select name="" id="">
  <option *ngFor="let item of getAllItems() | async" [value]="item.id">{{item.descp}}</option>
</select>

I created a StackBlitz example for you to look at that demonstrates this with a mocked response. The example isn't with types, but I don't see why that wouldn't work.

As a rule of thumb, try to minimize your manual subscriptions as much as possible, and instead work with the observables. Mixing reactive and imperative programming, such as subscribing and then assigning the unwrapped observable value to a variable is bad practice. You should instead (as I suggested above) return or use the observable itself.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.