Angular RxJS: The never-ending reduction

I have developed a component that contains two buttons (searchButton, lazyButton). The ngOnDestroy method is defined as follows:

public ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
}

I have created two observables from the click events of these two buttons:

this.eager$ = Rx.Observable.fromEvent(this.searchButton, 'click')
    .takeUntil(this.$unsubscribe);

this.lazy$ = Rx.Observable.fromEvent(this.lazyButton, 'click')
    .takeUntil(this.$unsubscribe);

Thus, when the component is destroyed, both observables are automatically unsubscribed.

However, every time a button is clicked, I need to make an HTTP request:

Observable
    .merge(this.eager$, this.lazy$)
    .switchMap(() => this.service.getItemsFromStorage())

Now, I receive an Observable<Response> which requires me to handle the response:

Observable
    .merge(this.eager$, this.lazy$)
    .switchMap(() => this.service.getItemsFromStorage())
    .map(response => response.json())                       
    .map(page => <Array<AdministrationUser>>page.included)   

After these transformations, I obtain an

Observable<Array<AdministrationUser>>
.

Next, I need to apply certain modifications on each AdministrationUser. Therefore, I create an

Observable<AdministrationUser>
:

Observable
    .merge(this.eager$, this.lazy$)
    .switchMap(() => this.service.getItemsFromStorage())
    .map(response => response.json())
    .map(page => <Array<AdministrationUser>>page.included)
    .switchMap(page => Observable.from(page.users))  

Once the users are obtained, I can modify each user individually:

Observable
    .merge(this.eager$, this.lazy$)
    .switchMap(() => this.service.getItemsFromStorage())
    .map(response => response.json())
    .map(page => <Array<AdministrationUser>>page.included)
    .switchMap(page => Observable.from(page.users))
    .do(user => /*modify user*/)  

Finally, all modified users need to be collected together again:

Observable
    .merge(this.eager$, this.lazy$)
    .switchMap(() => this.service.getItemsFromStorage())
    .map(response => response.json())
    .map(page => <Array<AdministrationUser>>page.included)
    .switchMap(page => Observable.from(page.users))
    .do(user => /*modify user*/)
    .reduce((acc, value) => [...acc, value], [])   
    .do(users => this.rowsToShow = [...users])    
    .takeUntil(this.unsubscribe$)
    .subscribe();

Despite this approach, the code at (&&$$&&) does not seem to execute.

This could possibly be due to the behavior of the reduce method, which waits for the observable to end. However, in this case, the observable switches to an array of elements once the HTTP response is received through

.switchMap(page => Observable.from(page.users))
. This should lead to the observable ending when all elements of the array are processed.

Do you have any suggestions or ideas on how to resolve this issue?

Answer №1

The original observable you are working with is .merge(this.eager$, this.lazy$), a hot observable that does not complete, resulting in your stream not completing as well. To solve this issue, consider adding first() after merge to only take one emission from the source and complete it.

Alternatively, you can incorporate reduce inside the inner observable like this:

.switchMap(page => Observable.from(page.users).reduce(....))

Answer №2

The explanation lies in the fact that reduce waits for the final emission from the parent observable before outputting the ultimate reduced value. As the parent observables being merged are click events, they continuously emit values until a certain threshold is met.

.switchMap => This function simply resolves the Observable it returns and provides the value. Utilizing switchMap does not signify the end of emissions. Whenever a new value is emitted, switchMap will process Observable.from(page.users) and return the value.

Instead of using the code snippet below, it's recommended to unsubscribe the Observables during component destruction. You can also modify the observable like so:

.reduce((acc, value) => {this.rowsToShow = [...acc, value];
    return this.rowsToShow }, [])
    //.do(users => this.rowsToShow = [...users])    
    .takeUntil(this.unsubscribe$)
    .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 typescript error "Cannot read properties of undefined" is encountered while trying to access the 'map' function

I was attempting to follow a guide on creating an app using typescript and react, but I'm encountering an error that says "Cannot read properties of undefined (reading 'map')". I'm not sure why this is happening, can someone please offe ...

What could be the reason behind Angular2 TestBed's compileComponents failing to locate my templates?

My current project includes a component that I'll refer to as MyComponent. This particular component utilizes my.component.html as its templateUrl. @Component({ selector: "my-component", templateUrl: "./my.component.html", styleUrls: ["./my.com ...

Guide to transforming API Response into Custom type in Angular 5

Describing my method to structure the API Response - interface MyTest { property2: string } Incorporating Angular 5 Service Code - getAPI(searchKey: string) { this.productsAPIUrl = https://localhost:44331/api/SampleData/WeatherFore ...

Should the method of creating a Dropdown with Angular be considered a poor practice?

I've recently dived into Angular and successfully created my first dropdown using it, which is working great. However, I'm a bit concerned about the number of comparisons being made and wondering if this approach is considered bad practice. The ...

When using EmotionJS with TypeScript, the theme type is not properly passed to props when using styled components

CustomEmotions.d.ts import '@emotion/react'; declare module '@emotion/react' { export interface Theme { colors: { primaryColor: string; accentColor: string; }; } } MainApp.tsx import { ...

Detecting the State of the Keyboard in Ionic 2

Seeking an easy way to determine if the mobile device keyboard has been opened or closed using Ionic2 and Angular2. Is there a 'keyboard-open' or 'keyboard-close' class that Ionic sends to the body/html? ...

JSON.stringify omits any properties in a custom class that have not been explicitly declared in the constructor

I recently defined a new class in my code: export class SavedData{ public isDone : boolean; } After trying to stringify an instance of this class, I noticed that the isDone property was not included: console.log(new SavedData()); This resulted in a ...

Using CamanJs in conjunction with Angular 6

Struggling to integrate camanjs with Angular 6? Wondering how to add the JavaScript library and use it within an Angular project when there are no types available on npm? Here are the steps I followed: First, install Caman using npm. Next, add it to ...

Ways to verify if the current date exists within a TypeScript date array

I am trying to find a way in typescript to check if the current date is included in a given array of dates. However, even after using the code below, it still returns false even when the current date should be present within the array. Can anyone please pr ...

Evaluating string combinations in JavaScript using valid comparisons

After choosing values on the screen, two variables store their value. var uval = '100'; var eval = '5'; There are 2 combinations with values: let combination1= 'u:100;e:1,4,5,10' let combination2 = 'u:1000;e:120,400,500, ...

GitHub Actions causing build failure in Angular project exclusively

I've encountered an issue where the GitHub Action workflow fails to compile an Angular project, even though it works fine on my local machine and that of my colleagues. It's worth noting that I'm using npm ci instead of npm install. The err ...

Learn how to bring a component into another component within Angular

I have developed a component named CopySchedulefromSiteComponent and now I am looking to import it into another component called SiteScheduleComponent. However, I am unsure of the correct way to do this. The CopySchedulefromSiteComponent contains one fiel ...

In the else-branch, a type guard of "not null" results in resolving to "never."

After creating a type guard that checks for strict equality with null: function isNotNull<T> (arg: T): arg is Exclude<T, null> { return arg !== null } Testing it showed that the then-branch successfully removes null from the type. const va ...

How can I show only a portion of the text from a chosen option in an ng-select input field?

Utilizing ng select to showcase the options text, comprised of both first and last names. My aim is to only show the first name in the input field upon selecting an option. I attempted to set the value as the first name. Consequently, I receive the first ...

Error: Unable to iterate through posts due to a TypeError in next.js

Hi there, this is my first time asking for help here. I'm working on a simple app using Next.js and ran into an issue while following a tutorial: Unhandled Runtime Error TypeError: posts.map is not a function Source pages\posts\index.tsx (1 ...

Six Material-UI TextFields sharing a single label

Is there a way to create 6 MUI TextField components for entering 6 numbers separated by dots, all enclosed within one common label 'Code Number' inside a single FormControl? The issue here is that the label currently appears only in the first tex ...

Is there a way to retrieve the Angular-Redux store in a child module?

Within my Angular application, I utilize angular-redux for managing the application state. In my main module, I have defined the redux store in the following manner: export class MainModule { constructor(private ngRedux: NgRedux<MainAppState>, ...

Efficiently transferring input to a Typescript file

Is there a better way to capture user input in Angular and pass it to TypeScript? <form > <input #input type="text" [(ngModel)]="inputColor" (input)="sendInput(input.value)" /> </form> The current method involves creating a ...

Angular 7 fails to send XHR request

This is my initial question, so I'll try to keep it concise. Here is the Angular method that I am using: delete(id: number): Observable<User[]> { console.log(id); return this.http.delete(`${this.baseUrl}/deleteUser`) .pipe(map(re ...

What is the recommended TypeScript type for the NextJS _app.tsx Component and pageProps?

Take a look at the default _app.tsx code snippet from NextJS: function MyApp({ Component, pageProps }) { return ( <Component {...pageProps} /> ) } The issue arises when transitioning to TypeScript, as ES6Lint generates a warning indicating t ...