ways to coordinate two subscriptions so that one initiates only when the other one emits

Currently, I am developing an Angular application with a specific scenario. I have an observable signal named dataFetchedEvent$, which indicates that data has been fetched from a remote location. Additionally, there is a form that relies on this remote data and listens to user changes through the formChanges$.

   const dataFetchedEvent$ = this.event.asObservable()
      .pipe(
        tap((fetchedData) => {
          this.createFormControls(fetchedData); // builds reactive form controls based on the fetched data
        }),
        takeUntil(this.componentDestroyed) // triggered in ngOnDestroy of the component
      );

    const formChanges$ = this.form.valueChanges // listening to form changes
      .pipe(
        switchMap((formDataRaw) => this.formatData(formDataRaw)), // transforming data
        takeUntil(this.componentDestroyed)
      );

    dataFetchedEvent$.subscribe();

    formChanges$.subscribe(
      (formattedFormData) => {
        // perform actions...
      }
    );

All functions correctly; however, when dataFetchedEvent$ is activated, the formChanges$ subscription triggers every time a control is altered by

this.createFormControls(fetchedData)
. I aim to prevent this behavior and only monitor form changes after the controls are created or updated.

I've explored various solutions:

Using skipUntil:

   const formChanges$ = this.form.valueChanges // listening to form changes
      .pipe(
        skipUntil(dataFetchedEvent$),
        switchMap((formDataRaw) => this.formatData(formDataRaw)), // transforming data
        takeUntil(this.componentDestroyed)
      );

Although this approach stops multiple emissions of formChanges$ while creating controls and works during component destruction and recreation, it unsubscribes dataFetchedEvent$ after initial emission. This causes issues as dataFetchedEvent$ may need to emit again to update form controls.

Utilizing concat:

   const formChanges$ = this.form.valueChanges // listening to form changes
      .pipe(
        switchMap((formDataRaw) => this.formatData(formDataRaw)), // transforming data
        tap((formattedFormData) => {
          // carry out operations...
        }),
        takeUntil(this.componentDestroyed)
      );

    concat(dataFetchedEvent$, formChanges$).subscribe();

In this instance, the formChanges$ stream remains unresponsive because dataFetchedEvent$ doesn't complete.

Exploring combineLatest:

combineLatest(dataFetchedEvent$, formChanges$).subscribe([val1, val2] => { // perform tasks... });

This method isn't ideal as formChanges$ still emits while creating controls.

Experimenting with mergeMap or switchMap:

const dataFetchedEvent$ = this.event.asObservable()
  .pipe(
    tap((fetchedData) => {
      this.createFormControls(fetchedData); // creates reactive form control based on remotely fetched data
    }),
    mergeMap(() => formChanges$),
    takeUntil(this.componentDestroyed) // triggered in ngOnDestroy of the component
  );

  dataFetchedEvent$.subscribe();

The above solution works initially or during component destruction and recreation. However, if dataFetchedEvent$ emits again after the first call, formChanges$ emits whenever form controls are updated, even with switchMap.

What would be the most effective strategy to achieve my objective?

------ EDIT ------

In the previous example, the code was generic and simplified.

Here are the form controls:

form = this.fb.group({
    tourLength: this.fb.group({
      length: [false]
    }),
    types: this.fb.group({
      type: [false]
    }),
    categories: this.fb.group({}),
    languages: this.fb.group({
      lang: [false]
    }),
    prices: this.fb.group({
      opt: [false]
    })
  });

The only dynamically changing group is 'categories'. When dataFetchedEvent$ emits, it means new categories were fetched remotely, prompting me to update the controls using _createControlsCategories() (essentially the prior createFormControls function).

private _createControlsCategories(categories) {
   const categoriesGroup: FormGroup = this.filterForm.get('categories') as FormGroup;
   categoriesGroup.controls = {};
   for (const cat of categories) {         
      categoriesGroup.addControl(cat.id_remote, new FormControl(false));
   }

}

Each category is linked to a checkbox in the view.

Answer №1

Take a look at this amazing code snippet:

const dataRetrievedEvent$ = this.event.asObservable()
  .pipe(
    tap((retrievedData) => {
      this.generateFormControls(retrievedData); 
    }));

  dataRetrievedEvent$
    .pipe(
      switchMap(() => formChanges$.pipe(takeUntil(dataRetrievedEvent)),
      takeUntil(this.componentDestroyed)
  ).subscribe();

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

The type FormGroup<any> lacks the controls and registerControl properties compared to AbstractControl<any>

I've been developing a reactive nested form inspired by this YouTube tutorial - https://www.youtube.com/watch?v=DEuTcG8DxUI Overall, everything is working fine, except for the following error - Below are my files. home.component.ts import { Compone ...

The matToolTip will not appear regardless of whether filtering/ngFor is implemented or not

Take a look at I came across this post about ngFor, and also found some insights on GitHub Interested in finding solutions for both standalone use and when using ngFor. ...

Can you provide guidance on how to communicate an event between a service and a component in Angular 2?

I'm currently working with angular2-modal to create a modal alert in my application. I am specifically trying to capture the confirm event that occurs when the modal is triggered. Does anyone know how I can achieve this? ...

What is the best way to handle code versioning using Django, Angular2, and Webpack?

Currently, I am utilizing Django in conjunction with Angular 2 and Webpack. Within Django, I have set up a URL to display my application at http://example.com/user/beta. Initially, my index.html file is rendered, which contains my Angular 2 components. Wit ...

Creating a Connection between Angular 4 and MySQL Database using PHP for Data Posting

After spending a few weeks searching for the answer to my question, I still haven't found what I'm looking for. I understand that Angular is a front-end framework and that I have freedom to choose any database and backend for my project. Current ...

Transition your Sequelize migrations to TypeORM

I'm currently in the process of transitioning a Node.js application from vanilla JS to Nest.js. In our previous setup, we used Sequelize as our ORM, but now we've decided to switch to TypeORM for its improved type safety. While exploring the Type ...

Utilizing Google's Speech-To-Text for real-time data streaming in Angular

Utilizing the Google Speech-to-Text service through @google-cloud/speech library in my node.js Firebase functions has been helpful, but I am looking to implement it for streaming data which seems impossible with Firebase functions. As a result, I plan to ...

Incorporating a Custom CKEditor5 Build into an Angular Application

I am currently in the process of developing an article editor, utilizing the Angular Integration for CKEditor5. By following the provided documentation, I have successfully implemented the ClassicEditor build with the ckeditor component. Below are the ess ...

Using @Input to pass data from a parent component to a

Looking to modularize the form code into a separate component for reusability? Consider using @Input and referencing it in the HTML to pass values to the post method. Here's how you can achieve this: Previously, everything worked smoothly when all th ...

Ways to usually connect forms in angular

I created a template form based on various guides, but they are not working as expected. I have defined two models: export class User { constructor(public userId?: UserId, public firstName?: String, public lastName?: String, ...

ERROR: Unhandled promise rejection: Route cannot be found. URL Segment: 'details'

My current setup involves a router configuration in my Angular application. Below is the code snippet showcasing my router settings: import { Route, RouterModule } from '@angular/router'; import { ProjectDetailsComponent } from '../componen ...

Maintaining search filters across pages in Angular 2 using URL parameters

I am attempting to store certain filters in the URL for my application, so that they can be reapplied when the page is reloaded. I am encountering challenges with Dates (using moment), nullable properties, and special characters (/). When I pass values to ...

Best Practices for Showing JSON Data from MongoDB in an Angular Material Table

Desire I'm trying to extract data from MongoDB and exhibit it in an Angular Material Table. Problem Even though I can view the results of my MongoDB query in the console/terminal, the Chrome browser console indicates that my JSON data has been save ...

Rendertron always renders base routes as empty

I'm running into an issue while trying to use rendertron via an Apache proxy - all the base routes are showing up as "null." Could this be due to a configuration error on my part? Any help would be greatly appreciated. The Rendertron service is curre ...

Creating a Mobile-friendly Sidebar: A Guide to Angular 5

I've been seeing a lot of recommendations online about using Angular Material for creating a sidebar, but I'm hesitant to install an entire library just for one component. After following the instructions provided here, I managed to develop a si ...

Setting up the vscode launch configuration to enable debugging on the cloud-run emulator with TypeScript

I am currently facing an issue with debugging a Google Cloud Run application on the Cloud Run emulator. The application is built using TypeScript. While I can successfully run and debug the application locally, breakpoints are being ignored or grayed out w ...

"I am experiencing an issue with the PrimeNG year picker as it is unable

My goal was to set up a simple PrimeNG calendar with just a year picker. I followed the implementation instructions from the documentation: <p-calendar inputId="year" [(ngModel)]="date1" view="year" dateFormat=" ...

What is the method for transmitting a concealed attribute "dragable" to my component?

Currently, I have successfully integrated a here map into my project, but I am now tackling the challenge of adding draggable markers to this map. To achieve this, I am utilizing a custom package/module developed by my company. This package is designed to ...

Customize the columns of your Angular Material 2 table by defining them using TemplateRef and ngTemplateOutlet

Looking to create a flexible material table with the ability to utilize TemplateRef along with ngTemplateOutlet for generating columns. Check out this demo where I have employed the cards component within my custom material-table component. Inside the card ...

Why isn't useEffect recognizing the variable change?

Within my project, I am working with three key files: Date Component Preview Page (used to display the date component) useDateController (hook responsible for managing all things date related) In each of these files, I have included the following code sn ...