Stop the observable based on the data rather than the cause

In my service, I am making http requests to a backend that is beyond my control in order to retrieve marketing page content. There are instances when I need to load multiple pieces of marketing content simultaneously. I have implemented an effect that calls this service.

@Effect()
marketingContent$ = this.actions$
  .ofType(LOAD_MARKETING_CONTENT)
  .switchMap(({ payload }) => this.marketingService.getContent(payload)
    .map(content => Action.LoadMarketingContentComplete(content))
  )

Everything was running smoothly until I realized that if I trigger the loading of more than one piece of marketing content at the same time, the .switchMap cancels the previous request.

store.dispatch(Action.LoadMarketingContent('A'));
store.dispatch(Action.LoadMarketingContent('B'));
// Only 'B' gets loaded as 'A' is canceled before completion

I tried using .mergeMap instead of .switchMap, but then duplicate requests were not being canceled.

Another approach could be creating separate actions for each piece of marketing content, but that would involve setting up new actions and effects for each piece.

Is there a way to modify .switchMap so that it only cancels requests for the same content (when the payload matches), or perhaps another method to execute simultaneous requests for different content while canceling duplicates within the same stream?

Answer №1

If you implement the action CANCEL_MARKETING_CONTENT, you can use mergeMap in this manner:

@Effect()
marketingContent$ = this.actions$
  .ofType(LOAD_MARKETING_CONTENT)
  .mergeMap(({ payload }) => this.marketingService
    .getContent(payload)
    .map(content => Action.LoadMarketingContentComplete(content))
    .takeUntil(this.actions$.ofType(CANCEL_MARKETING_CONTENT))
  );

This approach allows you to load multiple pieces of marketing content, with the ability to cancel any pending loads by dispatching a CANCEL_MARKETING_CONTENT action before dispatching the LOAD_MARKETING_CONTENT action(s).

For instance, to load piece A only, the process would be:

store.dispatch(Action.CancelMarketingContent());
store.dispatch(Action.LoadMarketingContent('A'));

And for loading both pieces A and B:

store.dispatch(Action.CancelMarketingContent());
store.dispatch(Action.LoadMarketingContent('A'));
store.dispatch(Action.LoadMarketingContent('B'));

Alternatively, there is a cleaner method that avoids introducing another action.

You can utilize the same action with the same payload as the cancellation trigger. For example:

@Effect()
marketingContent$ = this.actions$
  .ofType(LOAD_MARKETING_CONTENT)
  .mergeMap(({ payload }) => this.marketingService
    .getContent(payload)
    .map(content => Action.LoadMarketingContentComplete(content))
    .takeUntil(this.actions$
      .ofType(LOAD_MARKETING_CONTENT)
      .skip(1)
      .filter(({ payload: next }) => next === payload)
    )
  );

In this scenario, skip is used to ignore the currently processed action within the effect. It's important to note that the assumption here is that the payload is either "A" or "B", etc.

Answer №2

Instead of directly canceling the effect, you have the option to modify the effect to fetch marketing content in batches while still utilizing switchMap for terminating subsequent requests.

@Effect()
marketingContent$ = this.actions$
  .ofType(LOAD_MARKETING_CONTENT)
  .switchMap(({ payload }) => forkJoin(
    payload.map(name => this.marketingService.getContent(payload))
  ).map(content => Action.LoadMarketingContentComplete(content))

In this scenario, the payload consists of an array of content names for retrieval instead of a single name, enabling you to load content in groups.

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

Retrieving an array of objects from a JSON file using Angular 2

I am encountering an issue where the class is not filled properly in an object obtained from a JSON array, resulting in an 'undefined' error. Here is the code snippet for retrieving the object: getFeatures() { return this.http.get('h ...

Issue with Angular Reactive form: Checkbox checked property not binding correctly when the page initially loads

Looking to achieve Two-way data binding of Checkbox in Angular Reactive forms. After checking the checkbox, I am updating the 'isdateChkd' variable and storing it in the state. Despite the variable being set to TRUE, the checkbox does not get aut ...

Adding an object dynamically to a JSON array: Step-by-step guide

I am facing a challenge trying to dynamically add an attribute inside each object within my JSON array. Unfortunately, I have not been successful in achieving this as my new object is being appended at the end of the JSON which is not the desired outcome. ...

The process of extracting all arrays from a JSON object

Within my JSON object, there is a list of countries each with multiple regions stored in an array. My goal is to extract and combine all the regions into one single list. However, when I attempt to map the data, it does not consolidate all the regions as e ...

"Learn how to trigger an event from a component loop up to the main parent in Angular 5

I have created the following code to loop through components and display their children: parent.component.ts tree = [ { id: 1, name: 'test 1' }, { id: 2, name: 'test 2', children: [ { ...

Exploring Angular 2's nested navigation using the latest router technology

Is there a way to implement nested navigation in Angular? I had this functionality with the previous router setup. { path: '/admin/...', component: AdminLayoutComponent } It seems that since rc1 of angular2, this feature is no longer supported. ...

Develop a flexible axios client

I have a basic axios client setup like this: import axios from "axios"; const httpClient = axios.create({ baseURL: "https://localhost:7254/test", }); httpClient.interceptors.request.use( (config) => config, (error) => Prom ...

Having trouble retrieving features from a QR code generated with Angularx-qrcode?

I utilized angularx-qrcode in order to create a QR code that displays data from qrInfo. However, I'm encountering the following error: ERROR TypeError: Cannot read properties of undefined (reading 'qrcElement') The code within qr-code-gener ...

No TypeScript error in Angular app when assigning a string to a number data type

Today, I encountered some confusion when my app started acting strangely. It turns out that I mistakenly assigned a string to a number without receiving any error alerts. Any thoughts on why this happened? id:number; Later on: this.id = ActiveRoute.params ...

Translating Bootstrap 5 into Angular components for version 13 and above

In Angular, the lack of support for optional host-elements and containerless components means that each component comes with its own div wrapper. However, when designing Bootstrap components, a host-less component (without an extra div) is needed to mainta ...

Ionic 4: Facing issues with setting complete background image on ion-card

https://i.stack.imgur.com/3lH6h.png I'm currently attempting to set a background image for an Ion-card in order to completely cover the card's area. However, I'm facing an issue where the background image does not cover the entire area and ...

Generate a fresh class instance in Typescript by using an existing class instance as the base

If I have these two classes: class MyTypeOne { constructor( public one = '', public two = '') {} } class MyTypeTwo extends MyTypeOne { constructor( one = '', two = '', public three ...

Converting European date format to a standard format

THE ISSUE: The dates retrieved from the API are in European format: 01-06-2018 This means day - month - year (1 June 2018). I want to display these dates in a more visually appealing way - the 'd MMM y' format seems perfect for this purpose ...

Ways to ascertain if a TypeScript type is a specific value

Can a utility type be created to identify whether a type is a literal value? For example: type IsLiteral<T> ... type IsStringALiteral = IsLiteral<string> // false type IsStringLiteralALiteral = IsLiteral<'abc'> // true type IsS ...

Guide on incorporating a YouTube iframe in React with Typescript

It appears that Typescript is posing some challenges for me in this scenario. Here's the code snippet I am trying to include: <iframe width="560" height="315" src="https://www.youtube.com/embed/BLAH?showinfo=0" frameBorder="0" ...

Creating versatile list components that can accommodate various types of list items

As part of my project using Next.js, typescript, and type-graphql, I found myself creating Table components. These components are meant to display custom object types as rows within a table. While each piece of data has its own unique structure, they all ...

Developing web applications with Angular 6, C#, and MVC involves dynamically generating and returning JsonResults from the controller in the form of

I have been working on exporting datasets to Excel within an Angular 6 application. To achieve this, I have been utilizing XLSX and File-save functionalities as outlined in the following example: https://medium.com/@madhavmahesh/exporting-an-excel-file-in- ...

Exploring the benefits of leveraging TypeScript with AWS NodeJS for improved stacktrace visibility over traditional JavaScript

I'm contemplating the idea of transitioning my existing JavaScript codebase to incorporate TypeScript in NodeJS. One aspect that I am concerned about is being able to view the stack trace in AWS CloudWatch (request log) in case an error occurs during ...

Oh no! It seems like the build script is missing in the NPM

https://i.stack.imgur.com/el7zM.jpg npm ERR! missing script: build; I find it strange, what could be causing this issue? Any suggestions? I have included the fullstack error with the package.json. Please also review the build.sh code below. Fullstack err ...

What is the best way to showcase a file edited in Emacs within Atom?

The coding project I'm working on is built with Typescript, but I don't believe that's relevant. I've noticed that Emacs has a unique approach to indentation. According to the documentation, in Text mode and similar major modes, the TAB ...