The risk of a race condition could arise when working with nested switchMaps in ngr

I am currently working on an Angular 9 application that heavily relies on observables. In a specific component, I have the following requirements:

  1. Retrieve all companies to access certain information.
  2. Fetch all responses and link additional company details (such as the company name) to each response.
  3. Return this compiled list as an observable and subscribe to it in the template using the async pipe.

To achieve this, I initially wrote the following code snippet (which seems to be functioning correctly):

getPopulatedResponses(): Observable<ResponseModel[]> {
    return this.companyFilesStore
      .select(companyFilesSelector)
      .pipe(
        switchMap(companies => {
          return this.getAccessibleResponses(companies);
        })
      )
  }

getAccessibleResponses(accessibleCompanies: CompanyFilesModel[]): Observable<ResponseModel[]> {
    return this.responsesStore
      .select(responsesSelector)
      .pipe(
        map((responses) => {
          return responses?.map((response) => {
            const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
            response.companyName = company?.companyName;
            return response;
          }).sort((a, b) => {
            return a.completedDateTime < b.completedDateTime ? 1 : -1;
          })
        })
      )

Initially, I was uncertain if switchMap was the correct operator since I was unsure if updating the companyFilesSelector would terminate the previous subscription when responsesSelector started executing.

Now, I also need to include a subscription to a filter service. I made some modifications to the getAccessibleResponses method which seem to be functioning, but I feel like it might be more of a coincidence than deliberate design:

getAccessibleResponses(
    accessibleCompanies: CompanyFilesModel[],
  ): Observable<ResponseModel[]> {
    return this.searchAndFilterService.getFilter()
      .pipe(
        switchMap(filter => {
          return this.responsesStore
            .select(responsesSelector)
            .pipe(
              map((responses) => {
                return responses?.map((response) => {
                  const company = accessibleCompanies?.find(c => c.companyGuid === response.companyId);
                  response.companyName = company?.companyName;
                  return response;
                })
                  ?.filter(r => !r.isArchived)
                  ?.filter(r => !filter?.selectedCompany || r.companyId === filter.selectedCompany.companyId)
                  ?.filter(r => !filter.selectedAssessmentTemplate ||
                    r.assessmentTemplateId === filter.selectedAssessmentTemplate.assessmentTemplateId)
                  .sort((a, b) => {
                    return a.completedDateTime < b.completedDateTime ? 1 : -1;
                  })
              })
            )
        })
      )
  }

I am aware that my current approach may not be optimal or accurate. I am finding it difficult to grasp how to accomplish the following objectives:

  1. Retrieve all companies initially.
  2. Combine data from the companies observable with the responses.
  3. Apply filters when the filter service observable is updated to refine the combined results of steps 1 and 2.

I would greatly appreciate any assistance or advice on how to improve this process.

Answer №1

It appears that you have three key components(companies, responses, and filter) to manage, and whenever any of these components change (e.g., emit a new value), you want to update the data accordingly.

If this is the case and assuming your application state is maintained in a single source of truth, here is how I would approach it:

const companies$ = this.store.select(companyFilesSelector);

const accessibleResponses$ = this.store.select(responsesSelector);

const activeFilter$ = this.store.select(activeFilterSelector);

const data$ = combineLatest(
  companies$,
  accessibleResponses$,
  activeFilter$,
).pipe(
  map(([companies, responses, filter]) => {
    // Your logic goes here...
  })
)

combineLatest is used because you need to update the displayed data whenever any of the 3 components change.

Therefore, if your initial state looks like this:

{
 companies: [],
 responses: [],
 activeFitler: null,
}

Whenever you add new companies or responses, the function inside map will be executed to reflect these changes in the displayed information for the user. The same applies when adjusting the filter.


Edit

Could I utilize switchMap to avoid multiple subscriptions to the store and potentially conserve memory?

In my opinion, implementing switchMap may not significantly reduce memory usage, as its primary function is to unsubscribe from the current observable and create a new one based on the latest value and function provided.

Furthermore, since you are subscribing to the store, the initial data retrieval should occur synchronously, especially if the store behaves like a BehaviorSubject (e.g., NgRx).

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

Issues with the Angular Global Messaging service and how to tackle them

I am currently developing a web application using Angular 7 and I have implemented an API interceptor to show alerts when errors occur. Everything was working fine until I added a messaging service and messaging component. When I tried pushing the error me ...

Encountering the ExpressionChangedAfterItHasBeenCheckedError message despite updating the property via the @Output event

Attempting to update a property on the parent component through an event in the child component has proved challenging. Research suggests that this can be achieved using @Output as it is the recommended method to transmit data from child component to pare ...

Transmitting intricate Objects to Angular Directive

I am facing an issue where I need to pass a complex object to my directive. The object is a different formControl so I cannot use [formControl], formControlName, etc. <input matInput appDateInputEvents [control]=”fieldGroup.get(‘date’)”> Thi ...

What is the best way to trigger a 'Save As' dialog box on a browser when a button is activated within an Angular application, guaranteeing cross-browser compatibility?

The solution needs to be compatible with both Windows and Mac operating systems. Additionally, we should be able to specify a default file name and type. ...

While attempting to send a GET Request in Angular, access to XMLHttpRequest has been denied due to CORS policy restrictions

I am attempting to establish a GET method for my PHP API. Here is the code snippet I am using: export class PerfilComponent { perfil: any; constructor(private http: HttpClient) { } ngOnInit() { const token:string | null = localStorage.getItem(&ap ...

The operation of multiplying values is not functioning properly in the output field

I'm currently working on a functionality where an output field needs to multiply its value based on the input entered into another field. For example, if the input field is set to 2, then the output field should display the result of multiplying that ...

When utilizing a personalized Typescript Declaration File, encountering the error message 'Unable to resolve symbol (...)'

I'm having trouble creating a custom TypeScript declaration file for my JavaScript library. Here is a simplified version of the code: App.ts: /// <reference path="types.d.ts" /> MyMethods.doSomething() // error: Cannot resolve symbol "MyMetho ...

Is it possible to combine various SVG icons into a single component?

I am currently able to code SVGs in React-Native using typescript. This allows me to call them as individual react native components. Below is an example of my current capability: <View> <BackArrow color ="red" wid ...

Unable to provide any input while utilizing npm prompts

After installing npm prompts, I encountered a strange issue. When trying to run the example code for npm prompts, I found that I couldn't enter any input at all. The underscore in the input field would blink for a few seconds, then the cursor would ju ...

TypeScript introduces a new `prop` method that handles missing keys in objects

Is there a way to create a prop function that can return a default type if the specified key is not found in object o? type Prop = <K, O extends {}>(k: K, o: O) => K extends keyof O ? O[K] : 'Nah'; /* Argument of type 'K ...

Angular and Bootstrap 5 combine to create a dynamic multi-item carousel featuring animated slide transitions and embedded YouTube videos

I'm trying to create a multi-item carousel using YouTube videos, and although I have managed to get it working with Bootstrap 5 carousel and cards, the animation when the carousel slides is not as smooth as I would like. The issue seems to be that the ...

How to transfer data between components in Angular 6 using a service

I'm facing an issue with passing data between the course-detail component and the course-play component. I tried using a shared service and BehaviorSubject, but it didn't work as expected. Strangely, there are no errors thrown, and the data remai ...

What steps need to be taken in VSCode to import React using IntelliSense?

When I press Enter in that image, nothing seems to occur. I believed IntelliSense would automatically insert import React from 'react'; at the beginning of the file. https://i.stack.imgur.com/7HxAf.png ...

Angular does not seem to be able to detect the Google gtag within the index.html file

I tried following the instructions from Google and added the gtag in the head section of index.html. However, I encountered an error. <!doctype html> <html lang="en"> <head> <base href="/"> <meta charset= ...

What is the best way to add a value to a nested JSON array in Angular 5?

Need help transferring nested JSON data format from Web API to Angular5 {"contractId":1, "contractName":"Temp", "contractServiceList":[ {"id":1, "serviceId":{"serviceId":1,"serviceName":"Emergency Room"}, "providerTier":"Tier 1", "coi ...

Having trouble modifying the fields in the formArray

https://i.sstatic.net/B4uTq.pngWorking with reactive forms, I have a UI feature that displays radioButton options which, when selected, reveals details about the chosen value within the form. Once a button is selected, the form fetches data from the backen ...

Assign an appropriate label to this sonarqube input field

Sonarqube flagged an issue with the following line of code: <div class="dropdown-language"> <label>{{'GENERALE.LINGUA' | translate }}</label> <select #langSelect (change)="translate.use(langSe ...

Sending an Angular signal value from a component input to a service

During some experimentation with Angular 17 and signals, I encountered a scenario that I'm unsure how to tackle without resorting to ngOnChanges and @Input handling. Imagine you have a component with input signals, and you want to replicate or set th ...

The property functions normally outside the promise, but is undefined when within the promise context

I am currently working on filtering an array based on another array of different objects but with the same key field. Although I have made some progress, I keep encountering errors that I am unable to resolve. @Component({ selector: 'equipment&ap ...

Improving the process of class initialization in Angular 4 using TypeScript

Is there a more efficient method to initialize an inner class within an outer class in Angular 4? Suppose we have an outer class named ProductsModel that includes ProductsListModel. We need to send the ProductId string array as part of a server-side reque ...