Sort the observable data by a value returned from an API request

I am completely new to using RxJS and any assistance offered would be greatly appreciated!

Within my component's HTML template, I am looking to create a radio button list.

The values for this list are fetched from an observable using the async pipe.

This snippet shows the template section for the radio button list:

<div *ngIf="entities$ | async as entities">
    <mat-radio-group
        aria-labelledby="entities-radio-group-label"
        class="entities-radio-group"
        <mat-radio-button *ngFor="let entity of entities" [value]="entity">
          {{entity.description}}          
        </mat-radio-button>
      </mat-radio-group>
</div>

In the component class, I have an observable that retrieves the entities' data via a service. I also apply a filter based on the entity type.

  entities$: Observable<Entity[]> = this.eventsService.entities$
  .pipe(
    map(items => items.filter(item => item.type.toLowerCase().indexOf("set") > -1)),
    )

The structure of a retrieved entities object is as follows:

[
{"id":"34534534643364",
 "type":"SET",
 "description":"some description",
 "link":"/conf/sets/34534534643364"},
{"id":"5474745744457547",
 "type":"SET",
 "description":"other description",
 "link":"/conf/sets/5474745744457547"}
 ]

While the current setup successfully filters and displays "SET" type entities, I need to implement additional filtering based on information obtained from an API call using the entity link.

A sample request to the API initiated by the service looks like this:

 restService.call<any>(entityUrl)
 .pipe(finalize(()=>this.loading=false))
 .subscribe(
    apidata => console.log(`data: ${JSON.stringify(apidata)}`),
    error => this.alert.error('Failed to retrieve entity: ' + error.message)
    );

The response received is an object structured as shown below:

{
    "id": "34534534643364",
    "name": "some name",
    "description": null,
    "type": {
        "value": "LOGICAL",
        "desc": "Logical"
    },
    "content": {
        "value": "IEP",
        "desc": "This it the value I need"
    },
    "status": {
        "value": "ACTIVE",
        "desc": "Active"
    }
}

To conduct additional filtering, specifically utilizing the value under "desc", I attempted adding a function to the source observable but encountered issues due to the asynchronous nature of the API request processing.

An alternative approach I considered was working only with the results from the API for populating the template's radio button group. I managed to create an array of observables representing each API result but am uncertain about incorporating this array into the template.

Any guidance or suggestions provided will be highly valued!

Answer №1

It seems that the initial scenario is as described below:

  1. You are retrieving multiple items from your API
  2. For each item, you need to fetch a filter object from the API to determine if the item should be filtered out

The primary issue with your code is: You cannot use the map operator to chain observables. Instead, I recommend using switchMap along with forkJoin.

I propose making the following modifications to your async pipe (refer to my comments in the code for better understanding):

entities$: Observable<Entity[]> = this.eventsService.entities$
    .pipe(
        tap((items) => console.log("started pipe", items)),
        map(items => items.filter(item => item.type.toLowerCase().indexOf("set") > -1)),
        tap((items) => console.log("after set filtered pipe", items)),
        switchMap(items => {

            // Create an array of filter observables that include an api-call
            // and return the item itself if the condition is met, or NULL otherwise
            const filterObs = items.map(item => this.descFilter(item));
            
            // Utilize forkJoin to execute the array with the http-requests
            return filterObs.length ? forkJoin(filterObs) : of([]);
        }),
        // Exclude the NULL values:
        map(items => items.filter(i => i !== null)),
        tap((items) => console.log("after descFilter: ", items)),
        tap(() => this.clear())
    );

You will also need to modify the descFilter() method to make it compatible with the async pipe:

/* Depending on whether the condition is satisfied, either the item itself or NULL will be returned */
descFilter(item: Entity): Observable<Entity | null> {
    return this.restService.call<any>(item.link).pipe(
        map(res => res.content.desc === "some string" ? item : null)
    );
}

Note: At some point, you may need to subscribe to entities$ to execute the code.

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

What's the issue with my ExpressJS req.query not functioning as expected?

My NodeJS repl setup includes an input, button, and h1 element. The goal is to update the HTML inside the h1 element with the value of the input upon button click. Here's a glimpse of my code: index.js: const Database = require("@replit/database ...

What is the best way to dynamically link an Angular Material table with information pulled from the backend server

I am attempting to connect a mat-table with data from the backend API following this Angular Material Table Dynamic Columns without model. Below is the relevant content from the .ts file: technologyList = []; listTechnology = function () { ...

Error encountered when attempting to close a MatDiaLog in Angular as the close button is unable to read the property 'close' of null

Looking to develop a component that incorporates multiple content projects in angular 6. Here's the code I used in app.component.html: <popup> <button buttonTrigger mat-button><span >Open the popup!</span></button> ...

Configuring a custom domain for a Rust service on Clever Cloud: A step-by-step guide

After successfully deploying my Rust service to the Clever Cloud platform and testing it on PROD and localhost, I am now facing the challenge of binding my custom domain with the deployed service. I would like to eliminate the need for using the lengthy C ...

Error message: NextJs throws aReferenceError when trying to access the document object on page refresh

encountered the error ReferenceError: document is not defined when attempting to refresh the page I am working on creating a component using react-quill and then calling that component within a page. This is my component : import React, { useState } from ...

How to compare and filter items from two arrays using TypeScript

I am looking to filter out certain elements from an array in TypeScript Original Array: [ {"Id":"3","DisplayName":"Fax"}, {"Id":"1","DisplayName":"Home"}, {"Id":&quo ...

"Customizing the template of the Angular Material 2 datepicker: A step-by-step

Looking to make changes to the templates of the angular 2 material date-picker? These templates are located within various internal components in @angular/material/esm5/datepicker.es5.js. One option is to directly modify the template in the node package, ...

Enhance the functionality of the custom transaction form in NetSuite by incorporating new actions

I'm currently working on incorporating a new menu option into the "Actions" menu within a custom transaction form in NetSuite. While I can successfully see my selection in the Actions Menu on the form, I'm running into an issue with triggering th ...

Exploring VueJS: Sorting objects within an array

Currently, I am tackling a challenge in vue-js. In my codebase, there exists a data object known as items. I am iterating through these items and aiming to present a dropdown menu containing the list of products. My goal now is to showcase only those pro ...

Hiding a div with Javascript when the Excel dialog box is loaded

I have a piece of JavaScript code that is activated when the user clicks on an excel image. $("#excel").on("click", function () { $('#revealSpinningWheel').reveal(); $(window).load(function () { $('#revealSpinningWheel').hide ...

Utilize Angular to simultaneously filter search results by URL and make selections in a dropdown menu

As a newcomer to the Angular JS framework, I have successfully created a client-company table where clients can be filtered by company name using a drop-down menu. However, I am now looking to implement a filtering mechanism based on the URL structure su ...

What are the consequences of relying too heavily on deep type inference in React and Typescript?

In the process of migrating my React + Javascript project to Typescript, I am faced with preserving a nice unidirectional flow in my existing code. The current flow is structured as follows: 1. Component: FoobarListComponent -> useQueryFetchFoobars() 2 ...

Using parameters and data type in Typescript

When I remove <IFirst extends {}, ISecond extends {}> from the declaration of this function, the compiler generates an error. Isn't the return value supposed to be the type after the double dot? What does <IFirst extends {}, ISecond extends { ...

Discover an alternative to Events by harnessing the power of Observables to effectively listen for dismiss events in Angular Ionic

Currently, I am utilizing Ionic's inline modal feature that is activated by a boolean value. However, after the modal is closed, the boolean does not automatically reset to zero. The Ionic documentation suggests that developers should monitor the ionM ...

Load Angular modules dynamically

Is there a way to dynamically load a module (js, css and html) using a single directive at any point during the app's lifecycle? <my-module id="contacts"></my-module> The template for this directive looks like this <my-module id="con ...

Having issues sorting the ranking table numerically in a Javascript/jQuery/JSON/localStorage game

I have successfully created a leaderboard for my game, but I am struggling to automatically sort the scores from high to low. I believe that placing the objects into an array and then sorting them may be the solution, however, I am unsure of how to do th ...

Displaying and Concealing Messages with VueJS

Currently, I have set up a basic CLI structure environment and created a component that displays messages/alerts such as "Login Failed." Since this component is intended to be reused throughout the entire app, I decided to import it into the root App.vue f ...

The parameter type '==="' cannot be assigned to the 'WhereFilterOp' type in this argument

I'm currently working on creating a where clause for a firebase collection reference: this.allItineraries = firebase .firestore() .collection(`itinerary`); Here is the issue with the where clause: return this.allItiner ...

Design an interactive listbox with both HTML and Angular that allows for multiple selections

I am trying to implement a multi select listbox with keyboard navigation capability. Currently, this is the HTML layout I have: <div> <div *ngFor="let tag of tags; let index=index" [class.selectedRow]="rowIsSelected(tag.id) ...

Tips for maintaining JSON data in CK Editor

I'm having an issue where my JSON data is not being displayed in CKEditor when using this code function retrieveRules(){ $.ajax({ url: "api", type: "POST", data: { version:'0.1' }, ...