Using pipes() and map() to extract an array from a nested hash with Angular's HttpClient Observable

Struggling with understanding RXJS Observables and pipes.

The API response structure is as follows:

    {
      "_embedded": {
        "users": [
          {
             "id": 1,
             "username": "steve"
          }
        ]
      }
    }

Here are some simplified functions related to this scenario:

  listUsers() {
    return this.http.get<Users>(API_BASE_URL + '/v1/users').pipe(map(u => u._embedded.users))
  }

  getFirstUser() {
    return this.listUsers().pipe(first())
  }

  listUsersById() {
    return this.listUsers().pipe(reduce((acc, user) => { 
      acc[user.id] = user
      return acc
    }, {}))
  }

Testing the functions outputs the following:

this.listUsers().subscribe(data => {
  console.log("FUNC: listUsers()")
  console.log(data)
  console.log(data[0])
})

this.listUsersById().subscribe(data => {
  console.log("FUNC: listUsersById()")
  console.log(data)
  console.log(data[0])
})

this.getFirstUser().subscribe(data => {
  console.log("FUNC: getFirstUser()")
  console.log(data)
})   

listUsers() works correctly by displaying an array of 11 user objects followed by the first user object in the array.

listUsersById() displays { undefined: [array of 11 user objects] } instead of a hash where keys represent ids and values represent user objects.

getFirstUser() incorrectly logs out an array of 11 user objects instead of just one.

The issue seems to be that RXJS doesn't internally treat the return value from listUsers() as an array, rendering the pipe functions ineffective.

I aim to create a seamless data pipeline without executing the function(s) until required. How can I achieve this when using Angular's HTTPClient that returns a promise?

If I manually resolve the http request and have the data accessible in an array, everything works smoothly:

from([
      {
        id: 1,
        username: "steve"
      },
      {
        id: 2,
        username: "jane"
      }
    ]).pipe(reduce((acc, user) => {
      acc[user.id] = user
      return acc
    }, {}))

Answer №1

As @jb-nizet pointed out in the comments, the event will only be emitted once.

Given this information, a straightforward solution to implement listUsersById() would look like this:

  listUsersById() {
    return this.listUsers().pipe(map(users => users.reduce((acc, user) => { 
      acc[user.id] = user
      return acc
    }, {})))
  }

Alternatively, you could use flatMap() to emit each item in the array individually, like so:

  listUsers() {
    return this.http.get<Users>(API_BASE_URL + '/v1/users').pipe(map(response => response._embedded.users), flatMap(x => x))
  }

While this approach preserves the functionality of getUsersById() as intended, it may not be the most optimal choice for my specific scenario.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Apply a CSS class once a TypeScript function evaluates to true

Is it possible to automatically apply a specific CSS class to my Div based on the return value of a function? <div class="{{doubleClick === true ? 'cell-select' : 'cell-deselect'}}"></div> The doubleClick function will ret ...

Tips for concealing routes in the address bar on Angular versions 5 and above

Is it possible to conceal routes from the browser's address bar? Example: http://localhost:4200/user/profile to http://localhost:4200 Is there a way to hide routes? ...

Having trouble opening a JPEG file that was generated using the Writefile Api in Ionic-Cordova

Currently, I am using the writeFile API to create a JPEG image. The process is successful and the image is stored in the directory as expected. However, when I try to open the file manually from the directory, I encounter an error message saying "Oops! Cou ...

choosing between different options within Angular reactive forms

I am attempting to create a select element with one option for each item in my classes array. Here is the TypeScript file: @Component({ selector: 'app-create-deck', templateUrl: './create-deck.component.html', styleUrls: [' ...

find all the possible combinations of elements from multiple arrays

I have a set of N arrays that contain objects with the same keys. arr[ {values:val1,names:someName},   {values:val2,names:otherName}, ] arr2[   {values:valx,names:someNamex}, {values:valy,names:otherNamey}, ] My goal is to combine all possible c ...

Troubleshooting a deletion request in Angular Http that is returning undefined within the MEAN stack

I need to remove the refresh token from the server when the user logs out. auth.service.ts deleteToken(refreshToken:any){ return this.http.delete(`${environment.baseUrl}/logout`, refreshToken).toPromise() } header.component.ts refreshToken = localS ...

How can you implement multiple validators on a single control in Angular 2?

I've been working on a form using Angular2 and I've implemented two custom validators for the email address field. The initial validator checks for valid email format, while the second (which is asynchronous) checks if the email address already ...

Tips for providing a base URL in Angular to fetch images from an API

How do I access the API URL to retrieve images from a database? <table class="table"> <tr> <th>Id</th> <th>Image</th> </tr> ...

How to easily scroll to the top of the previous page in Angular 8

In my Angular 8 application, there are buttons that are meant to take the user back to the previous page when clicked. While the functionality works as expected, I am facing an issue where the page does not automatically scroll to the top upon navigating ...

Ensure the cursor is continually grabbing while moving items within a list using the @angular/cdk/drag-drop functionality

I have an example on stackblitz where I am using @angular/cdk/drag-drop in my project. I am attempting to change the cursor to cursor:grabb and cursor:grabbing when the cursor is over an element and when I drag a picked element. Here is the CSS line I am ...

Creating a fresh ngx-translate pipeline (comparing pure and impure methods)

Edit: I am looking to enhance the functionality of ngx-translate's pipe by extending it. Here is an example of how I achieved this: import { Pipe, PipeTransform } from '@angular/core'; import { TranslatePipe } from "@ngx-translate/core"; @ ...

retrieving and presenting information stored in a Firebase repository

I am currently working on retrieving data from a firebase database and storing it in an array called courses that I have declared within my class. Here's the code I have so far: import { AngularFireDatabase, AngularFireList } from 'angularfire2 ...

Tips for altering the hue of an inline svg within Angular?

I'm not very familiar with SVGs. Currently, I am using ng-inline-svg package to display inline SVG images. Is there a way to change the color of the SVG image? I've looked on stack overflow for solutions but haven't found anything that wor ...

Distinguish among various mat accordion types

I created a custom wrapper for the mat accordion component in Angular Material to handle multiple accordions with different behaviors based on user interaction. Here is my implementation: Wrapper Mat Accordion HTML <mat-accordion> <mat-expa ...

What are the steps to set up tsline in settings.json file?

Currently utilizing visual studio code Upon searching for the settings.json file, the contents appear as follows: { "liveServer.settings.donotVerifyTags": true, "liveServer.settings.donotShowInfoMsg": true, "javascript ...

Submit file in Cypress while hiding input[type="file"] from DOM

Currently, I am conducting end-to-end testing on an Angular project that utilizes Ant Design (NG-ZORRO). In this project, there is a button with the class nz-button that, when clicked, opens the file explorer just like an input type="file" element would. W ...

Top method for continuously updating the position of DOM elements in Angular 2

Currently, I am in the process of developing a game using Angular (version 4). I understand that direct manipulation of the DOM is typically not recommended when working with Angular. However, for the particular functionality I'm trying to achieve, I& ...

Unable to find any routes that match child routes using the new Angular 2 RC1 router

ApplicationComponent import { Component } from '@angular/core'; import {Router, ROUTER_DIRECTIVES, Routes, ROUTER_PROVIDERS} from '@angular/router'; import {SchoolyearsComponent} from "./schoolyear/schoolyears.component"; @Component({ ...

Retrieve indexedDb quota storage data

I attempted the code below to retrieve indexedDb quota storage information navigator.webkitTemporaryStorage.queryUsageAndQuota ( function(usedBytes, grantedBytes) { console.log('we are using ', usedBytes, ' of ', grantedBytes, & ...

Angular 2 - Can a Content Management System Automate Single Page Application Routing?

I am interested in creating a single-page application with an integrated content management system that allows users to edit all aspects of the site and add new pages. However, I have found it challenging to configure the SPA to automatically route to a n ...