DazDylz
DazDylz

Reputation: 1096

RxJS delay in updating the next value when using filter()

I have this piece of code:

app.component.ts:

import {Component, AfterViewInit, ChangeDetectorRef} from '@angular/core';
import {Subject} from 'rxjs/Subject';

import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/filter';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
    changes$ = new Subject<number>();
    current$ = this.changes$
        .scan((x, y) => x + y, 0)
        .filter((x) => x >= 0);
    constructor(private cdRef: ChangeDetectorRef) {}

    ngAfterViewInit(): void {
        this.changes$.next(0);
        this.cdRef.detectChanges();
    }
}

app.component.html:

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
    <button (click)="changes$.next(-1)">-1</button>
    <button (click)="changes$.next(1)">+1</button>

    <div>
        <span>{{ current$ | async }}</span>
    </div>
    <div>
        <span>{{ current$ | async }}</span>
    </div>
    <div>
        <span>{{ current$ | async }}</span>
    </div>
</div>

My problem was that I didn't want to go below 0 so I added the filter operator. This solved my problem but when I get to 0 and press like 10 times the minus button and then press the add button again, it doesn't get updated unless I press the add button a lot of times.

Why is this? How can I solve that?

Example:

The problem in GIF

Upvotes: 2

Views: 534

Answers (2)

Deepak Sharma
Deepak Sharma

Reputation: 1901

Try this in the scan. The reason is that every time a value is emitted the scan will be processed no matter whether it is filtered or not.

.scan((x, y) => (x + y) >= 0 ? x + y : 0, 0)

Upvotes: 3

Alexander Leonov
Alexander Leonov

Reputation: 4794

Well, first of all - as Deepak said you need to move it to the scan(). The reason being is that when changes$ subject emits new value scan() will still continue to count whatever it emits including negative numbers, but you just won't see it from behind the filter(). You need precisely to stop counting if you don't want your counter to go below 0, not masking its output by filter().

Also, you don't need to manually emit the first value, you don't need constructor and ngAfterViewInit calling ChangeDetectorRef. This all is totally unnecessary.

Below is the fixed code:

import {Component} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/startWith';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    changes$ = new Subject<number>();
    current$ = this.changes$
        .startWith(0)
        .scan((x, y) => (x + y) >= 0 ? x + y : 0, 0);
}

Upvotes: 1

Related Questions