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();
}
}
CodePudding user response:
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));
CodePudding user response:
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>