Using RXJS with Typescript to wait for an observable to emit until another observable of type boolean evaluates to false

In my current setup, when a user navigates in a view, an API call is made to fetch the data for that specific view. This data is then used later on to decide whether a dialog should pop up when a user performs an action.


While the solution is functional at the moment, I foresee a potential issue if a user with high latency triggers the above action before the API request completes. I don't want to display a loading indicator every time a user navigates as it can be quite annoying to deal with. Instead, I prefer to have the user wait until they actually intend to perform the action.


Furthermore, I have an observable indicating whether the API request is currently loading or not, which I plan to utilize somehow.


actionPerformed$ = createEffect(() => 
      this.actions$.pipe(
        ofType(performAction),
        withLatestFrom(entities$),
        mergeMap(data => {
          let payload = data[0];
          let dataFromApi = data[1];
          ...
          ...
        })

One idea that came to mind, although it may not be efficient, was to have an operator that checks the condition and throws an error if it's still loading, then retrying after a short delay.


Do you have any better suggestions? I've explored using `delayWhen`, but it seems inefficient to introduce a delay like that. I'd prefer something more like a "gate" that only opens the stream when `loading$` is set to false and emits the value immediately once that condition is met.


Thank you in advance!

Answer №1

This demonstration illustrates how the dependent$ observable will produce a value once the source$ observable emits false. To view the functional code, visit: https://stackblitz.com/edit/rxjs-r3yma1?devtoolsheight=33&file=index.ts

import { of, timer } from "rxjs";
import { filter, map, switchMap, take, tap } from "rxjs/operators";

const array = [true, true, true, true, true, true, false];
const source$ = timer(0, 1000).pipe(
  map(index => array[index]),
  tap(console.log)
);

const dependent$ = of("the other person mentioned false");

source$
  .pipe(
    filter(value => value === false),
    switchMap(() => dependent$),
    take(1)
  )
  .subscribe(console.log);

Answer №2

Here, the dependent observable will immediately emit since the service is not yet fetching data from the API. Once the service begins fetching data, the dependent observable will emit after the process is completed. To view the functioning code, visit https://stackblitz.com/edit/rxjs-3ecq5d?devtoolsheight=33&file=index.ts

import { BehaviorSubject, of } from "rxjs";
import { filter, switchMap, take } from "rxjs/operators";

class Service {
  loading$ = new BehaviorSubject<boolean>(false); // Initially set to false, indicating no ongoing data retrieval from the API

  fetchData() {
    console.log("Fetching data...");
    this.loading$.next(true);
    setTimeout(() => {
      console.log("Finished fetching data");
      this.loading$.next(false);
    }, 5000);
  }
}

class Component {
  service: Service;

  constructor(service: Service) {
    this.service = service;
  }

  emitWhenReady() {
    const dependent$ = of("I'll emit when not fetching data");
    this.service.loading$
      .pipe(
        filter(loading => loading === false),
        switchMap(() => dependent$),
        take(1)
      )
      .subscribe(console.log);
  }
}

const service = new Service();
const component = new Component(service);
component.emitWhenReady(); // The emission happens instantly as the service hasn't begun fetching data yet;
service.fetchData();
component.emitWhenReady(); // This will wait until the service finishes retrieving data.

Answer №3

It seems like utilizing combineLatest instead of withLatestFrom could be beneficial in this scenario:

combineLatest([
  this.actions$.pipe(ofType(performAction)),
  entities$
]).pipe(
  filter(([action, entities]) => entities != null), // Check depends on entities structure
  map(([action, entities]) => {
    // Perform desired actions here
  })
);

This approach ensures that both observables have emitted before proceeding, signifying that "entities" are loaded. Additionally, the Filter step serves as a precautionary measure. Furthermore, it considers the last user action if any occurred prior to the completion of the entitites$ call.

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

Steps for setting up a Node.js Express application that serves a Vue.js single page application

Currently, I am in the process of setting up a Node.js project that incorporates Express to create backend APIs and deliver a Single Page Application (SPA) designed with Vue.js. Upon initializing a project using the Vue cli, components such as the main fi ...

Facilitating the integration of both Typescript and JavaScript within a Node application

We are currently integrating Typescript into an existing node project written in JS to facilitate ongoing refactoring efforts. To enable Typescript, I have included a tsConfig with the following configuration: { "compilerOptions": { "target": "es6", ...

Router failure resulted in an internal server error

When navigating to a page in my router, I make a REST API request to retrieve data from the server in the beforeEnter clause as shown below: beforeEnter: (to, form, next) => { getData().then( (response) => { ...

displaying post data in the URL address bar

During the development of my portal using angular 5, everything was running smoothly in the testing environment. However, due to production requirements, I made some changes to access modifiers from private to public. This modification has caused issues in ...

Using Ionic to send email verification via Firebase

I have encountered an issue while attempting to send an email verification to users upon signing up. Even though the user is successfully added to Firebase, the email verification is not being sent out. Upon checking the console for errors, I found the f ...

The specified main access point, "@angular/cdk/platform", is lacking in required dependencies

I recently updated my Angular app from version 8 to version 9. After resolving all compilation and linter errors, I encountered one specific issue that is causing me trouble: ERROR in The target entry-point "@angular/cdk/platform" has missing dep ...

Check for the data attributes of MenuItem in the TextField's onChange event listener

Currently, I am facing a situation where I have a TextField in select mode with several MenuItems. My goal is to pass additional data while handling the TextField's onChange event. I had the idea of using data attributes on the MenuItems for this pur ...

Middleware in Redux Toolkit is ineffective in managing successful asynchronous actions

After integrating my own middleware into the Redux-Toolkit store using configureStore in my Next.js app, I noticed that the middleware functions appear to be greyed out. I added them via: getDefaultMiddleware({ thunk: { extraArgument: updateNavTabMid ...

@The value of @Input is consistently undefined

I'm using @Input to pass IDs into a child component, where they are used to filter the data set within that component. <div *ngIf="exerciseLength > 0"> <exercise [course]="Course" [filter-by-exercise-id]=""></exercise> </di ...

Angular2: The NgFor directive is designed to work with Iterables like Arrays for data binding

I'm currently working on a university project to develop a web application consisting of a Web API and a Frontend that interacts with the API. The specific focus of this project is a recipe website. Although I have limited experience with technologies ...

Struggling to destructure props when using getStaticProps in NextJS?

I have been working on an app using Next JS and typescript. My goal is to fetch data from an api using getStaticProps, and then destructure the returned props. Unfortunately, I am facing some issues with de-structuring the props. Below is my getStaticProp ...

The value of type 'number' cannot be assigned to type 'string | undefined'

Having an issue with the src attribute. I am trying to display an image on my website using the id number from an API, but when I attempt to do so, it gives me an error in the terminal saying 'Type 'number' is not assignable to type 'st ...

Every time I try to run my Angular app, it crashes. I've already tried reinstalling Node and rebooting

After attempting to relocate an angular project into a different folder yesterday, I encountered issues when trying to start the app using ng serve. Interestingly, creating a new project and running ng serve resulted in the same error. Both projects were ...

The Angular Node server is responding with the error message "You have entered 'undefined' instead of a stream."

Using angular 9 + universal has been smooth sailing until I encountered an issue after building the app with npm run build:ssr and attempting to run it using node: node dist/app/server/main.js. The error message that popped up in the terminal was: Node ...

The error at events.js:154 was not properly handled and caused an 'error' event to be thrown

Encountered an error while creating an Angular 2 application. I followed the instructions from this link to create a sample application. Upon running npm start Received the following error, events.js:154 throw er; // Unhandled 'error' even ...

Combining Vue with Typescript and rollup for a powerful development stack

Currently, I am in the process of bundling a Vue component library using TypeScript and vue-property-decorator. The library consists of multiple Vue components and a plugin class imported from a separate file: import FormularioForm from '@/FormularioF ...

If the input is unmounted in react-hook-form, the values from the first form may disappear

My form is divided into two parts: the first part collects firstName, lastName, and profilePhoto, while the second part collects email, password, confirmPassword, etc. However, when the user fills out the first part of the form and clicks "next", the val ...

Issues with Angular routing in Fuse administrator and user interfaces

I am encountering an issue with navigating routes for admin and user roles, where the user role has limitations compared to the admin role. <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.1/angular.min.js"></script> const ...

How to manually set the value of a select object in Angular 5

I am facing an issue with setting an object value manually for a select element. I have attempted to use setValue or patchValue with Object, but unfortunately, it did not yield the desired result. <mat-select placeholder="Selecione a resposta" (change) ...

How to send multiple queries in one request with graphql-request while using getStaticProps?

I am currently utilizing graphCMS in combination with NextJS and have successfully implemented fetching data. However, I am facing an issue where I need to execute 2 queries on the homepage of my website - one for all posts and another for recent posts. q ...