Using RxJS with Angular to intercept the valueChanges of a FormControl prior to subscribing

I decided to create a new observable using the values emitted by the FormControls.valueChanges observable. This creation of the observable takes place within the ngOnInit method in the following manner:

  ngOnInit(): void {
    this.myObservable$ = combineLatest([
      this.formControl1.valueChanges, 
      this.formControl2.valueChanges
    ]).pipe(
      map(([v1, v2]) => parseInt(v1, 10) + parseInt(v2, 10))
    );

    this.isLoading = true;

    this.loadData().subscribe({
      next: result => {
        this.formControl1.setValue(result[0]);
        this.formControl2.setValue(result[1]);
        this.isLoading = false;
      },
    });
  }

The subscription to the observable occurs after setting isLoading to false:

<div *ngIf="!isLoading">{{ myObservable$ | async }}</div>

Therefore, in this scenario, myObservable$ will only start emitting values once formControl1 and formControl2 are manually changed, since the first emissions of valueChanges happen before subscribing to this observable.

A more elaborate solution could be crafted for this situation:

this.myObservable$ = defer(() => combineLatest([
   this.formControl1.valueChanges.pipe(startWith(this.formControl1.value)), 
   this.formControl2.valueChanges.pipe(startWith(this.formControl2.value))
])).pipe(
   map(([v1, v2]) => parseInt(v1, 10) + parseInt(v2, 10))
);

Is there perhaps a more elegant solution to tackle this?

( is the StackBlitz link for a basic example)

Answer №1

One approach to handle this situation within the template is as follows, although it may seem verbose. Alternatively, utilizing startWith provides a more concise solution:

<ng-container *ngIf="{ value: (myObservable$ | async) } as my">
  <div *ngIf="!isLoading">{{ my.value }}</div>
</ng-container>

Another method involves subscribing and using shareReplay:

isLoading: true;

readonly destroy$ = new Subject<void>();

ngOnInit(): void {
  this.myObservable$ = combineLatest([
    this.formControl1.valueChanges, 
    this.formControl2.valueChanges
  ]).pipe(
    map(([v1, v2]) => parseInt(v1, 10) + parseInt(v2, 10)),
    shareReplay(1),
    takeUntil(this.destroy$)
  );

  this.myObservable$.subscribe();

  this.loadData().subscribe({
    next: result => {
      this.formControl1.setValue(result[0]);
      this.formControl2.setValue(result[1]);
      this.isLoading = false;
    },
  });
}

ngOnDestroy(): void {
  this.destroy$.next();
  this.destroy$.complete();
}

Answer №2

To improve the code, one strategy could be to create some helper functions to reduce repetition.

// Helper Functions

const getControlValueStream = (({ control, valueChanges }): FormControl) => valueChanges.pipe(startWith(value));

const sumNumbers = (numbers: number[]) => numbers.reduce((a, b) => a + b, 0);
// Component Logic

const controls = [this.formControl1, this.formControl2];
const valueStreams = controls.map(getControlValueStream);

combineLatest(...valueStreams).pipe(
  map(values => values.map(value => parseInt(value, 10))),
  map(sumNumbers),
);

Some may argue that this approach makes the code less readable, as it prioritizes maintainability over aesthetics.

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

You are unable to insert a variable within the channels.get() method in discord.js

Attempting to troubleshoot this issue has been quite frustrating. Despite my efforts, it seems that something is not working as expected. I can't help but wonder if I am simply overlooking a simple mistake due to fatigue. Here's the scenario: It ...

Run the js.erb code only when a certain condition is met

I'm feeling a bit lost when it comes to CoffeeScript and JS. I have a quick editing feature that sends an AJAX request and updates the database. Currently, I manually change the edited content's place and display it, but it feels like a workaroun ...

Adding elements to an array in Node.js

In my 'both' object, I need to store 2 arrays: eng and fr. Each of these arrays contains multiple objects. How can I transfer all values from frDisplayData to fr:[] within the 'both' object: const displayData = []; var both = {eng:dis ...

Update the input value with the selected option from the dropdown menu in Angular

How can I dynamically set the value of an input field based on the selection from a dropdown menu in Angular using Reactive Forms? Below is my HTML code: <nb-card> <nb-card-header> Services </nb-card-header> <nb-card-body&g ...

Exploring the implementation of query parameters in Nest.js

I am currently a freshman in the world of Nest.js. Below is an excerpt from my code: @Get('findByFilter/:params') async findByFilter(@Query() query): Promise<Article[]> { } I have utilized Postman to test this specific router. ht ...

Optimal Placement for FB.Event.subscribe

Here is the code I have implemented on my website for the Facebook Like and Share buttons. The functionality works seamlessly. When I click the Like button, a notification is promptly displayed on my Facebook profile page. Additionally, Facebook automatic ...

Showing the AngularFireList data on the HTML page following successful retrieval

Does anyone know how to display an AngularFireList on an HTML page? import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import {AngularFireAuth} from 'angularfire2/auth'; import {AngularF ...

Switch between MMM dd yyy and dd/mm/yyyy date formats easily

I have implemented a native material-ui date picker which currently displays dates in the dd/mm/yyy format. However, I need to customize the display format to show dates like this: Jun 18 2012 12:00AM. This specific date format is essential for consistency ...

Encountered an issue during deployment with Vercel: The command "npm run build" terminated with exit code 1 while deploying a Next.js web application

I've been working on a local Next.js app, but encountered an error when deploying it. Vercel Deployment Error: Command "npm run build" exited with 1 Here is the log from the build process: [08:26:17.892] Cloning github.com/Bossman556/TechM ...

Tips on customizing regex to selectively identify specific strings without capturing unrelated ones

Currently, I am working with this regex: /(?<=^|\/)(?:(?!\/)(?!.*\/))(.*?)[:-]v([\d.-]+)(?=\.|$)/ The goal is to extract the captured group from the following strings: rhmtc/openshift-velero-plugin-rhel8:v1.7.9-4 oc-mirror-plug ...

Guide on showing the content of an uploaded file as an object in JavaScript using file reader

When using the file upload function to upload a json file and read its contents, I am encountering an issue where the result is in string format instead of object. How can I display it as an object? Here is my code: .html <div class="form-group"> ...

Changing the border color of a Material UI textbox is overriding the default style

Upon the initial page load, I expected the border color of the text box to be red. However, it appeared grey instead. I tried setting the border color to red for all classes but the issue persisted. Even after making changes, the border color remained unch ...

interactive vuetify navigation trail elements

Currently working on a vuetify project and I'm facing an issue with implementing breadcrumbs. The problem arises when clicking on a breadcrumb, as it deletes the ones that come after it in the list. I've tried some code snippets but could only ma ...

Having trouble integrating a Bootstrap-based ecommerce theme into an Angular8 project?

I have a bootstrap-based ecommerce theme with all the necessary files. The HTML theme is loading perfectly. Check out the official theme page I am integrating into my angular8 project: Theme Page Link I have created a repository for my angular8 project ...

How can Firebase and Ionic be used to customize the password reset template for sending verification emails and more?

I'm facing an issue with firebase's auth templates not supporting my native language. Is there a way to customize the password reset template to also handle verification and email address change emails? ...

What could be causing the media queries to not take effect at a max-width of 425px?

When I try to apply media queries for max-width 768px and max-width 425px, I noticed that the styles intended for max-width 768px are also being applied when the width is 425px. This results in the al-articles-tags-chip-list-container having a width of 600 ...

Adjust the size of a Div/Element in real-time using a randomly calculated number

Currently, I am working on a script that is designed to change the dimensions of a div element when a button on the page is clicked. The JavaScript function connected to this button should generate a random number between 1 and 1000, setting it as both the ...

The function called Nuxt: n2 is not defined

When using Nuxt 3, I encountered a TypeError that looks like the following: Uncaught TypeError: n2 is not a function My issue revolves around a button that triggers the function toggleSelectRow with a @click.prevent directive. The function in question is ...

There is no Api.js file in the Cordova iOS platform

I have been attempting to integrate the iOS platform into a new Cordova 7.0.1 project, but I continuously encounter the error mentioned in the title of this post. Despite following the steps outlined in this thread here, including removing and re-adding ...

Show or hide side menu depending on when a div reaches the top of the

I have a side menu that opens when a specific div reaches the top of the window. The menu includes a toggle button for opening and closing it. However, I am encountering an issue where the script keeps closing the menu on scroll even after manually openi ...