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

Converting Enum into an array in TypeScript to return the keys of the Enum

After defining the following enum: export enum Types { Type1 = 1, Type2 = 2, ... } We can create an array based on this enum with the function below: export function EnumKeys<T>(obj: object): string[] { return Object.keys(obj) ...

Adjust the height, width, and color of the "kendo-switch" widget

I am looking to customize the height, width, and color of the "kendo-switch" component. I attempted to modify the element's style directly, but it did not have the desired effect. What would be the most effective approach for achieving this customiza ...

Conditional formatting for form field styles

Is there a way in Angular to conditionally render a material style? For instance, I am looking to apply the following style only to my Password form field text, and only when both input boxes have content: .mat-form-field-appearance-outline .mat-form-fiel ...

React Formik - application triggers an undesired submission request when the Enter key is pressed in the input field

Whenever I enter a value in the "name" field and hit Enter, the app sends a request and the address bar in the web browser changes to http://localhost:3000/?name=something. However, if I simply add another input field to the form, the app's behavior c ...

Make sure to add the .npmrc file when setting up a fresh Angular project

Currently, I am in the process of developing a command line application with node.js. This specific application is designed to utilize the ng new command from angular CLI. During the creation of a new angular project, dependencies are automatically install ...

Display Google font as SVG path but encapsulate within a promise

I'm facing an issue with the following script, where it performs an async operation. My goal is to wrap it in a promise, but I'm unsure about the right approach. static convertGoogleFontToSVG(): Promise<string> { const url = 'htt ...

An issue with the "req" parameter in Middleware.ts: - No compatible overload found for this call

Currently, I am utilizing the following dependencies: "next": "14.1.0", "next-auth": "^5.0.0-beta.11", "next-themes": "^0.2.1", In my project directory's root, there exists a file named midd ...

What is the best way to access object IDs from Firebase and then save them individually?

I'm currently developing an Ionic application using the Angular framework. In order to remove objects from Firebase's real-time database, I need the ID of each object. Despite being a beginner in this field, I have not been able to find a solutio ...

Set the style of the mat-select element

I'm having an issue with my select option in Angular Material. The options look fine, but when I select one, the strong tag disappears. Can anyone help me style only that part? Thank you in advance. <mat-select formControlName="projectId" ...

Steps to trigger a modal using an effect and automatically close it upon receiving a specific dispatched action

Is there a better way to handle the closing of a dialog after a specific action is dispatched? Currently, I have a CalendarEventDeleteDialog with "yes" and "no" options. If the user selects "yes," a CalendarEventDeleteAction is dispatched followed by a Cal ...

How to access different state sections within a reducer using ngrx

If one state value changes, I want to remove another state value. How can I access other parts of the store from a reducer in NgRx? Here is my current store schema: a: {...} b: {...} I am trying to access the 'b' feature store from the ' ...

Currency formatting in ionic2 is not working properly when tested on a

Formatting currency in Ionic2 framework can be done like this: {{finalPremium | currency : 'eur' : true :'.2-2' }}. Interestingly, this functionality only appears to function properly in the browser. When tested on an iPhone device, no ...

ParcelJS takes a unique approach by not bundling imported JavaScript libraries

My NodeJS app, which is a Cloudflare Worker, seems to be having trouble with bundling the 'ping-monitor' dependency. In my main typescript file (index.ts), I import the handler module and the first line reads: const Monitor = import('ping-m ...

Create a variety of URL formats for various object cases

Can you guide me on how to verify and create a URL under different circumstances? I am dealing with 3 cases that involve different types of objects: "repositories": { "toto": { "tata": "https://google.com/", ...

Setting placeholders for mat-radio-button or mat-radio-group: A beginner's guide

I am working with a DOM element: <mat-radio-group [formControlName]="field.name" [disabled]="field?.disabled"> <div> <mat-label>{{ placeholder }}</mat-label> </div> <mat-radio-button *ngFor="let option of field.o ...

The given 'FC<ComponentType>' type argument cannot be assigned to the 'ForwardRefRenderFunction<unknown, ComponentType>' parameter type

Currently, I am using react in conjunction with typescript. Within my project, there are two components - one serving as the child and the other as the parent. I am passing a ref to my child component, and within that same child component, I am binding my ...

Distributing elements evenly across the parent width using Angular FlexLayout for a responsive design

I need to create 3 div squares: div-left, div-middle, and div-right, each with a width of 300px. These squares should be arranged horizontally within the parent div main-section, which will adjust its size based on the screen width, being 1300px if the scr ...

Exploring the power of combining React, styled-components, and TypeScript: Creating a functional component that encapsulates a styled component

Struggling to create a wrapper for my styled component with proper types. Imagine having a styled component defined like this: const Button = styled.button<ButtonProps>` background-color: ${(props) => props.color}; `; Now the goal is to have a ...

Setting up a passthrough for a directory of external resources in Angular

Issue: I am currently facing a challenge with a project that requires accessing photos from a system that has recently been updated from AngularJs to Angular 16. Initially, we were able to serve these photos from a virtual directory like https://myawesom ...

Exploring an array in Angular 2 using TypeScript

Just starting out with typescript and angular2 and working through some issues. I have a form that needs to display results from an array of changing items, so I don't know the exact index of each result. Here is my scenario: In my form.html file: ...