简体   繁体   中英

Rxjs not counting records in Angular service

I have a simple service that I want to count the records that are read from the backend service. While my data records are loaded correctly, I cannot get the .pipe(count()) to return a record count.

I am expecting that the data is filtered in the component, but I do want to show a complete record count. I can loop through the results in the service to set the count, which works, but I thought the pipe/count method was to do the same thing.

export class AlertsService implements OnDestroy {
    private ApiEndPoint: string = '/alerts';
    private Data$_: BehaviorSubject<Array<LIB_Alert_ResponseDTO>> = new BehaviorSubject<Array<LIB_Alert_ResponseDTO>>(new Array<LIB_Alert_ResponseDTO>());
    private LastUpdated_: Date = new Date();
    private Source$: Subscription = new Subscription();

    constructor(
        private Http_: HttpClient,
    ) {}

    get Alerts$(): Observable<Array<LIB_Alert_ResponseDTO>> {
        return this.Data$_.asObservable();
    }

    // This always leaves me with 0
    get Count$(): Observable<number> {
        return this.Alerts$.pipe(count()); 
    }

    // This method works
    get Count2$(): Observable<number> {
        let Count: number = 0;
        this.Alerts$.forEach( (Alerts: Array<LIB_Alert_ResponseDTO>) => {
            Count = Alerts.length;
        });
        return of<number>(Count);
    }

    get LastUpdated$(): Observable<string> {
        return of<string>(`${this.LastUpdated_.toLocaleDateString()} @ ${this.LastUpdated_.toLocaleTimeString()}`);
    }

    public Initialize(BaseURL: string): void {
        this.Source$ = this.Http_.get<Array<LIB_Alert_ResponseDTO>>(`${BaseURL}/alerts`, { observe: 'response' }).subscribe( data => {
            this.Data$_.next(data.body as Array<LIB_Alert_ResponseDTO>);
        });
    }

    public ngOnDestroy() {
        this.Source$.unsubscribe();
    }
}

The count function, per docs :

Counts the number of emissions on the source and emits that number when the source completes

This means it counts the number of times the observable emits and not the number of records emitted...

I would assume that the count would be 1 at the beginning (and more as it goes on), when there are 10 alerts, and this is correct.

To resolve this you need to use map instead. Something like this should work:

  return this.Alerts$.pipe(map(alerts => alerts.length)); 

Count will not emit until a steam completes, the operator you are looking for is scan, it is like a reduced that emits the accumulator each time the source stream emits a value.

this.Alerts$.pipe(scan((total, _) => total + 1, 0);

Each time Alerts$ emits a value the accumulator is incremented and then it emits the accumulator.

If you are having trouble understanding what is going on read up on how a reduce works on arrays to get your head around the concept.

 const numberOfItems = [1,2,3,4,5,6,7,8].reduce((count, _) => { console.log(`Accumulator is: ${count + 1}`); return count + 1; }, 0); console.log('Final result: ' + numberOfItems);

Then with a behaviour subject

 const { BehaviorSubject, scan } = rxjs; const bs$ = new BehaviorSubject(); bs$.pipe(scan((total, _) => total + 1, 0)).subscribe(count => { console.log(count); }); for (let i = 0; i <= 10; i++) { bs$.next(); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js" integrity="sha512-v0/YVjBcbjLN6scjmmJN+h86koeB7JhY4/2YeyA5l+rTdtKLv0VbDBNJ32rxJpsaW1QGMd1Z16lsLOSGI38Rbg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM