What is the best way to trigger a code block once an observable in Angular has completed all of its tasks?

I have an observable made from an array of form controls in Angular. I am using dropdowns and inputs to calculate the sum of all currencies in relation to one currency, and it works. However, my issue is that when I want to update the field itself, the value of the sum is not changed immediately due to an HTTP request delay. I need to set the value of the field only when all the code has finished executing and the sum has its final form.

 let sum = 0;
 let arrayOfControls = [];

    for (let control of this.currencies.controls) {
      arrayOfControls.push(control);
    }

    const myObservable = from(arrayOfControls);

    const myObserver = {
      next: (control) => {
        let currency = control.get('dropdown').value;
        let amount = Number(control.get('amount').value);
        if (currency && amount) {
          this.updateRate(currency).subscribe((serverData) => {
            let rate = serverData['rates'][this.dropdown2.value];
            sum += amount * rate;
            // The sum changes here.
          });
        }
      },
      complete: () => {
         // When trying to set the value of the field, the sum may not have changed yet due to the delay.
        this.amount2.setValue(sum.toFixed(2));
      },
    };

    myObservable.subscribe(myObserver);

Answer №1

To optimize your code and avoid nested subscriptions, consider using higher order mapping operators such as switchMap. More information can be found here. Additionally, make use of the RxJS EMPTY constant to prevent emission when certain conditions are not met.

Give this approach a try:

import { from, EMPTY } from 'rxjs';
import { switchMap } from 'rxjs/operators';

let total = 0;
let controlArray = [];

for (let item of this.items.controls) {
  controlArray.push(item);
}

const customObservable = from(controlArray).pipe(
  switchMap(item => {
    let value = item.get('dropdown').value;
    let num = Number(item.get('amount').value);
    return (value && num) ? this.updateData(value) : EMPTY;
  })
);

const customObserver = {
  next: serverResponse => {
    let rateValue = serverResponse['rates'][this.dropdownSelection.value];
    total += num * rateValue; // update total
  },
  complete: () => {
    this.result.setValue(total.toFixed(2));
  },
};

customObservable.subscribe(customObserver);

Answer №2

My approach to solving this problem is as follows:

I will create an array of updateRate function calls and utilize forkJoin method to synchronize their execution. Once all the calls are complete, I will calculate the sum of the products of rates * amounts.

The code implementation can be summarized as below:

const currencyRates = this.currencies.controls
  // Extract currency and amount values
  .map(control => ({
    currency: control.get('dropdown').value,
    amount: Number(control.get('amount').value)
  }))
  // Filter out controls with missing currency or amount
  .filter(({currency, amount}) => currency && amount)
  // Define updateRate call and map it to returned rate and amount
  .map(({currency, amount}) => 
    this.updateRate(currency).pipe(
      map(serverData => ({
        rate: serverData['rates'][this.dropdown2.value],
        amount
      }))
    )
  );

let totalSum: number;
// Execute updateRate calls in parallel and collect results in an array
forkJoin(currencyRates).subscribe(results => 
  // Calculate the sum by multiplying each amount with its corresponding rate
  totalSum = results.reduce(
    (acc, curr) => acc + (curr.amount * curr.rate), 
    0
  )
);

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

Determine characteristics of object given to class constructor (typescript)

My current scenario involves the following code: abstract class A { obj; constructor(obj:{[index:string]:number}) { this.obj = obj; } } class B extends A { constructor() { super({i:0}) } method() { //I wan ...

Guide on switching locales from US to Japan in react-big-calendar

Currently, I am trying to customize a calendar component using the react-big-calendar library. My goal is to localize it for Japan, but I'm facing some challenges. Error Message: Unexpected require(). 'ja' is defined but never used. Code S ...

What is the correct way to utilize the mapState function within a TypeScript environment while implementing Vuex?

In my Vue.js project integrated with Vuex, I am using Typescript syntax. While trying to use the mapState method as computed in my .ts file, I encountered a syntax error. Currently, I am following the suggested syntax for computed function in the documenta ...

Issue when transmitting information from Angular to Express

I'm attempting to send data from Angular to Express.js Below is my TypeScript function connected to the button: upload(): void { const nameFromId = document.getElementById('taskName') as HTMLInputElement; this.taskName = nameFromI ...

Modify the state of a Babylon JS instance that was set up within an Angular 2 component

Hi there, I'm currently experimenting with injecting variables using Angular 2's Dependency Injection to alter the state of an object initialized within BabylonJS. I've tried using [(ngModel)]="Service.var" to access the variable and (ngMod ...

Stop useEffect from triggering during the first render

I'm working on implementing a debounce functionality for a custom input, but I'm facing an issue where the useEffect hook is triggered during the initial render. import { useDebouncedCallback } from "use-debounce"; interface myInputProps { ge ...

Guide on how to add a generic return type to a function in typescript

Is there a way to annotate a function that returns a factory in TypeScript to ensure it contains correct type definitions? Consider the following code: class item<T> { constructor(a: T) { this.a = a; } a: T } function generate(c) { ret ...

An easy guide on incorporating external npm modules into Angular 2, such as an encryption library

Within my Angular 2 application (utilizing the SystemJS module manager and Typescript as the scripting language), I am in need of importing an npm module for encryption purposes. This could be either Crypto-JS, Forge-JS, or any alternative that serves the ...

Jest encounters an issue while attempting to import Primeng CSS files

I am currently utilizing Jest version 26.6.3 for testing Angular components. Unfortunately, the unit tests for components that utilize Primeng's checkbox component are failing during the compileComponents step with the error message "Failed to load ch ...

Issue with Formik compatibility in Next JS 14 Application Structure

I attempted to create a basic validation form using Formik. I meticulously followed their tutorial and example, but unfortunately, the form is not functioning correctly. Despite my efforts, I have been unable to identify a solution (Please correct me if I& ...

Encountered a unique error code "TS1219" in Visual Studio

Recently, I made some changes to the architecture of my UI project and encountered a slew of errors (TS1219 and TS2304). Could the culprint be a poorly configured tsconfig.json file, or is it something else entirely? Despite encountering no issues when dec ...

Discover the wonders of utilizing @blur events on your custom Vue components!

Trying to create a customized component that mimics an input field with validation, I'm encountering issues with getting @Change, @blur, and other events to function properly as they would on a standard input field. This is the structure of my custom ...

Access to Angular 7 granted through Gmail authentication

I am currently attempting to authorize Gmail in Angular 7 using Angular5-social-login. While it is working for me, I need the function to run on page load. I have tried calling it in ngOnInit and ngAfterViewInit but encountered the following error: Un ...

How can I enable editing for specific cells in Angular ag-grid?

How can I make certain cells in a column editable in angular ag-grid? I have a grid with a column named "status" which is a dropdown field and should only be editable for specific initial values. The dropdown options for the Status column are A, B, C. When ...

How can I dynamically reference two template HTML files (one for mobile and one for desktop) within a single component in Angular 6?

Here is the approach I have taken. Organizational structure mobile-view.component.html <p> This content is for mobile view </p> desktop-view.component.html <p> This content is for desktop view </p> mobile.component.ts import ...

I am encountering a problem with HttpClient Angular POST when trying to communicate with Google FCM Server. How can I

I have encountered an issue while trying to send FCM messages using Angular HttpRequest, even though I am able to do so successfully via a POST and HTTP v1 Firebase API through Postman: Error Below are the imports I am using: import { Injectable } from & ...

Angular 2 Directive for Ensuring Required Conditions

Is there a way to make form fields required or not based on the value of other fields? The standard RequiredValidator directive doesn't seem to support this, so I've created my own directive: @Directive({ selector: '[myRequired][ngControl ...

What is the process for attaching a function to an object?

Here is the complete code: export interface IButton { click: Function; settings?: IButtonSettings; } abstract class Button implements IButton { click() {} } class ButtonReset extends Button { super() } The component looks like this: expor ...

What is the process of playing blob videos (avi, mov) in Angular14?

I've been struggling with this issue for quite some time without knowing how to resolve it. After some research, I came across a similar question: how to play blob video in angular. However, the problem is that the demo provided in the answer does no ...

Efficiently transforming a nested object array into a single dynamic array

// I have a collection of various objects _id: "5e5d00337c5e6a0444d00304" orderID: 10355 orderDate: "2020-03-02" user: _id: "5e2e9699a648c53154f41025" name: "xyz1" email: "<a href="/cdn-cgi/l/email-protection" class="_ ...