Tips for managing your time while anticipating an observable that may or may not

I am facing a dilemma in my Angular app where I need to conditionally make an HTTP call to check for the existence of a user. Depending on the result of this call, I may need to either proceed with another API request or halt the processing altogether. Is there a way to achieve this without using .toPromise() and ensure that the execution waits for a call inside an if block?

  handleEvent() {
    const userData: IUser = {
      ...this.userForm.value
    };

    let stopProcessing = false;

    if (this.isNewUser()) {
      // *** This is optional
      this.userService.checkDuplicate(userData.email, userData.username)
        .subscribe(result => {
          if (result.response !== '') {
            stopProcessing = true;
          }
        });
    }

    // *** This condition gets executed before the observable completes
    if (stopProcessing) {
      return;
    }

    //  *** This part runs when it shouldn't due to premature execution
    this.userService.updateUserData(userData)
      .subscribe(response => {
        this.modal.close(response);
      });
  }

Answer №1

When dealing with If/Then logic using Observables, it can sometimes be challenging, as demonstrated in your question. For a detailed discussion on this topic, you can check out this article.

You've already received some answers that may work in specific scenarios, and I was initially going to leave the task of helping you to others. However, your query about adding more conditions intrigued me. This led me to search for an observable/functional pattern that is not only easy to read but also allows for effortless extension when introducing new conditions in the future. Your thought-provoking question prompted me to dive deeper into finding a suitable solution.

I decided to implement the pattern suggested in the aforementioned article to address your issue. Although I assumed in my code that you intended for the contact to be updated when the user is NOT new, please note that I have not extensively tested this solution through different scenarios. Ideally, I would prefer to validate my approach by creating test cases on Stackblitz, but unfortunately, time constraints prevent me from doing so at the moment. Nevertheless, the pattern I followed involves creating branches for all possible paths within the If/Then logic, making it easy to incorporate additional actions using `tap()` if required.

The essence of this pattern lies in the `merge()` function utilized at the end. By merging two observables into a single one, we ensure that upon completion of either observable, the `updateContact()` function will execute. In this context, due to the filter condition of `isNewUser()`, only one observable remains active, preventing both from completing simultaneously. However, remember to include a `take(1)` if you wish to prioritize the first asynchronous response in other implementations of this pattern.

In addition, I included subscribe and unsubscribe functions to adhere to best practices.

onSomeEventSub : Subscription; // variable at component scope for later unsubscribing

onSomeEvent() {
    const contact: IContact = {
      ...this.userForm.value
    };

    const duplicateUserSearch$: Observable<boolean> = 
        this.userService.duplicateUserSearch(contact.email, contact.userName).pipe(
            map(result => (result.result === '')));

    const source$ = of(0).pipe(
        share()
    );

    const isNewUserAndIsDupe$ = source$.pipe(
        filter(() => isNewUser()),
        mergeMap(() => duplicateUserSearch$),
        filter(res => res)
    );

    const isNotNewUser$ = source$.pipe(
        filter(() => !isNewUser())
    );

    this.onSomeEventSub = merge(isNewUserAndIsDupe$, isNotNewUser$).pipe(
        mergeMap(() => this.userService.updateContact(contact))
    ).subscribe(res => this.activeModal.close(res));

}

ngOnDestroy() {
    if (this.onSomeEventSub) this.onSomeEventSub.unsubscribe();
}

Answer №2

Elevating functionality in fashion.

handleEvent() {
  const data: IData = {
    ...this.userFormData
  };
const updateData=this.dataService.updateData(data)
  .pipe(tap(response => {
    this.modalHandler.close(response);
  }))

return of(this.isUserNew()).pipe(mergeMap(isNew=>{
   if(!isNewUser)
      return updateData
   return this.dataService.searchForDuplicateUser(data.email, data.username)
     .pipe(mergeMap(result=>result.result === ''?updateConcat:empty()))  
}))

}

// execute
this.handleEvent().subscribe()

Answer №3

One issue arises from the fact that onSomeEvent is executed synchronously while the assignment of cancelProcessing happens asynchronously at a later time. One possible solution involves modifying the code as shown below (using RxJS 5):

onSomeEvent() {
    const contact: IContact = {
        ...this.userForm.value
    };

    if (this.isNewUser()) {
        this.userService.duplicateUserSearch(contact.email, contact.userName)
            .filter((result) => result.result === '')
            .switchMap(() => this.userService.updateContact(contact))
            .subscribe((res) => this.activeModal.close(res));
    } else {
        this.userService.updateContact(contact)
            .subscribe((res) => this.activeModal.close(res));
    }
}

If clause

In this section, the observable stream is first filtered using the filter operator to only allow items from the duplicateUserSearch observable where result.result is an empty string.

Next, the passing values are ignored and replaced by observables returned from updateContact, which is invoked on demand once a value passes through the filter applied earlier.

The switchMap operator flattens the input values, ensuring that if they are observables, their values will be emitted through the observable produced by switchMap rather than the observable instances themselves.

Finally, we can subscribe to the stream generated by switchMap and observe the values emitted by updateContact directly.

Else clause

If the user is not new, updateContact is called without any prior validation with duplicateUserSearch.


For RxJS 6.x, you would use the pipe operator to specify the sequence of operators in one continuous flow:

onSomeEvent() {
    const contact: IContact = {
        ...this.userForm.value
    };

    if (this.isNewUser()) {
        this.userService.duplicateUserSearch(
            contact.email,
            contact.userName,
        ).pipe(
            filter((result) => result.result === ''),
            switchMap(() => this.userService.updateContact(contact)),
        ).subscribe((res) => this.activeModal.close(res));
    } else {
        this.userService.updateContact(contact)
            .subscribe((res) => this.activeModal.close(res));
    }
}

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

Using external URLs with added tracking parameters in Ionic 2

I am looking to create a unique http link to an external URL, extracted from my JSON data, within the detail pages of my app. Currently, I have the inappbrowser plugin installed that functions with a static URL directing to apple.com. However, I would lik ...

Is there a way to automatically validate v-forms inside a v-data-table when the page loads?

In my data entry form, I have utilized a v-data-table with each column containing a v-form and v-text-field for direct value updates. My goal is to validate all fields upon page load to identify any incorrect data inputs. However, I am facing challenges in ...

Encountering issues with Angular2 forms while working with JavaScriptServices/Universal

My Angular2 app was built using the JavaScriptServices starter from GitHub. The issue I'm encountering is a runtime error when I include a form in a component. Both FormsModule and ReactiveFormsModule are being imported. This is how my form is stru ...

Displaying data from multiple checkboxes in an Angular application using an array

I'm currently facing an issue with displaying checked data in an Array within my Angular application. Upon clicking the checkbox, I receive a true value, but unfortunately, the data does not display as it contains null values. For reference, here is ...

Manipulating a DOM element in Angular 2 to alter its class attribute

I am a beginner in angular2. Here is my code: import { Component, OnInit } from '@angular/core'; @Component({ selector: 'main', template: ` <div class="current"> </div> ` }) export class MainComponent impl ...

Resolving issues with Typescript declarations for React Component

Currently utilizing React 16.4.1 and Typescript 2.9.2, I am attempting to use the reaptcha library from here. The library is imported like so: import * as Reaptcha from 'reaptcha'; Since there are no type definitions provided, building results ...

Proper management of setTimeout in an Angular application

I am working on a one-page web application where the main component's ngOnInit() function triggers a recursive function called loopDoSomething() using setTimeout: ngOnInit(): void { // Perform some operations this.loopDoSomething(); } loopDoSome ...

When running `aws-cdk yarn synth -o /tmp/artifacts`, an error is thrown stating "ENOENT: no such file or directory, open '/tmp/artifacts/manifest.json'"

Starting a new aws-cdk project with the structure outlined below src └── cdk ├── config ├── index.ts ├── pipeline.ts └── stacks node_modules cdk.json package.json The package.json file looks like this: " ...

"The `ngClass` directive allows for applying classes as an `object` rather than just a simple `class` value

When applying the class name like this: <tr *ngIf="crud.isCreate" [ngClass]="{'create' : crud?.isCreate}"> The class name is not being added correctly. In HTML, it appears as: <tr _ngcontent-yql-c9="" ng-reflect-ng-class="[object Obje ...

Steps to resolve the issue with "Error: StaticInjectorError(AppModule)[NgbDropdown -> ChangeDetectorRef]"

My attempt at creating a web app using Angular resulted in successful compilation with no errors. However, upon execution, the browser displays a blank page accompanied by the following error message: ERROR Error: Uncaught(in promise): Error: St ...

Testing NextJS App Router API routes with Jest: A comprehensive guide

Looking to test a basic API route: File ./src/app/api/name import { NextResponse } from 'next/server'; export async function GET() { const name = process.env.NAME; return NextResponse.json({ name, }); } Attempting to test ...

Difficulty encountered when trying to apply a decorator within a permission guard

I'm a newcomer to Nestjs and I am currently working on implementing Authorization using Casl. To achieve this, I have created a custom decorator as shown below: import { SetMetadata } from '@nestjs/common'; export const Permission = (acti ...

Which is the best option: Service variable, Service Observable, or Service Subject?

Lately, I've been contemplating the idea of global variable declaration, and I'm struggling to see the advantage of using a Subject in a service instead of a simple variable or even an Observable. Could you help me understand why someone would ch ...

Communicating data between Angular components that have no direct binding or relationship

Struggling to transfer data between two unrelated components, anyone have advice on how to accomplish this? Here's an example: I have a page with 3 buttons that pass string values upon click to a variable named selectAgent. ~agents.html~ <div rou ...

Best practices for interacting with a REST API using Angular

I am currently working on a web Angular app where in globalservice.ts I have defined basepath:string = "https://myapi.com/api/v2/". I need to retrieve data from this API. To achieve this, I have included the following code snippet in server.js. Any recomme ...

Ways to address the Generic Object Injection Sink eslint error (security/detect-object-injection)

I am seeking a solution to resolve this issue without needing to deactivate eslint. Moreover, I am eager to comprehend the cause of the error. const getMappedCard = (cardName: CardName) => { const mappedCards = { Mastercard: <Mastercard /> ...

Leveraging Json data in Angular components through parsing

I am currently developing an angular application where I need to retrieve and process data from JSON in two different steps. To start, I have a JSON structure that is alphabetically sorted as follows: { "1": "Andy", "2": &qu ...

What is the process for configuring React on one server and springboot on a separate server?

Can you help me with the setup of the following: Web Server : I need to set up a react + typescript application using npm at Backend Server : I also need to configure a Springboot backend server at I am currently using webpack to build the react applica ...

Steps to eliminate the Bearer authorization header in an Angular 4 POST request to an API

Is it possible to exclude the Authorization Bearer in a POST request? The following code was not successful in removing the authorization bearer that is being added by the HTTP interceptors. Error: 403 - Unauthorized Request. The Authorization header is ...

Can Angular 4 experience race conditions?

Here is a snippet of my Angular 4 Service code: @Injectable() export class MyService { private myArray: string[] = []; constructor() { } private calculate(result): void { myArray.length = 0; // Perform calculations and add results to myAr ...