Reputation: 331
This is my code
//Service method
private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject(null);
getCartItems(): Observable<CartItem[]> {
return this.cartObs$.asObservable();
}
//method in component
sum$: Observable<number>;
this.sum$ = this.cartService
.getCartItems()
.pipe(map((cart) =>
cart.reduce((total, item) => total + item.qty, 0)));
i need to calculate sum of qty in rxjs. am getting error if obeservable is empty how to calculate sum in rxjs with empty list
I am following similar to this arrayof obervable sum
Thanks in advance
ERROR
ERROR TypeError: Cannot read property 'reduce' of null
at MapSubscriber.eval [as project] (https://cddm0ddd.ddd.app/src/app/components/header/header.component.ts:17:50)
at MapSubscriber._next (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/operators/map.js:47:29)
at MapSubscriber.Subscriber.next (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/Subscriber.js:73:12)
at BehaviorSubject._subscribe (https://cugm0.csb.app/node_modules/rxjs/_esm5/internal/BehaviorSubject.js:34:18)
at BehaviorSubje
Upvotes: 0
Views: 524
Reputation: 31125
Although the solution @VovaBilyachat solves the issue, there are some edge cases where it might exhibit erroneous behavior.
Consider the following
1. Default value empty array
In this case the BehaviorSubject
is initialized with an empty array as default value.
private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject([]);
In this case you'd get the same error as the question if some component would push null
or undefined
to it like cartObs$.next(null)
.
2. Checking if the value is defined inside map
While this would the issue from solution 1, it still returns the sum as 0
while the input array is either null
or undefined
. This might not be expected from the usage PoV of the sum$
observable.
To tackle these you could use the filter
operator in the sum$
observable to use the cartObs$
emissions only when they are defined.
this.sum$ = this.cartService.getCartItems().pipe(
filter(cart => (!!cart && !!(cart.length))),
map((cart: any) => cart.reduce((total, item) => total + item.qty, 0))
);
That said, if the BehaviorSubject
's default value isn't used anywhere, I'd recommend you to use ReplaySubject
with buffer 1 instead. It'll also "hold" the current value and emit it to future subscribers without the need for a default value.
private cartObs$: ReplaySubject<CartItem[]> = new ReplaySubject(1);
Upvotes: 1
Reputation: 19474
I would assume that your this.cartObs$
is of type BehaviorSubject and by default it require to set default value. So I think you are setting default value to null instead of an empty array or you are emmiting null to your subject.
So I see two options here:
Set to empty array private cartObs$: BehaviorSubject<CartItem[]> = new BehaviorSubject([]);
Second fix reducer so it can handle null values ( I think this is safest way since then you dont care if someone will push null into your subject)
this.sum$ = this.cartService
.getCartItems()
.pipe(map((cart) => cart ?
cart.reduce((total, item) => total + item.qty, 0)) : 0);
cart ? cart.reduce((total, item) => total + item.qty, 0)) : 0
this means if cart is not null run reduce of its null return 0
Upvotes: 1