Is it possible to get rid of the BehaviourSubject to have an Observable with an initial value? Another thing I don't like is that I'm using getValue() and then modify this value and push it with next back to the Observable. Maybe there is a more elegant way to get the same thing?
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class UserProvider {
users$: Observable<{ id: string; name: string }[]>;
private usersSource: BehaviorSubject<{ id: string; name: string }[]>;
constructor() {
this.usersSource = new BehaviorSubject<any>([
{ id: '111', name: 'Jack' },
{ id: '222', name: 'John' },
{ id: '333', name: 'Foo' },
]);
this.users$ = this.usersSource.asObservable();
}
addUser(user: { id: string; name: string }) {
const currentUsers = this.usersSource.getValue();
currentUsers.push(user);
this.usersSource.next(currentUsers);
}
removeUser(userId: string) {
const currentUsers = this.usersSource.getValue();
const index = currentUsers.findIndex(user => user.id === userId);
if (index !== -1) {
if (this.selectedUserIdSource.getValue() === currentUsers[index].id) {
this.selectedUserIdSource.next(null);
}
currentUsers.splice(index, 1);
this.usersSource.next(currentUsers);
}
}
}
2 Answers 2
This is a really good question -- starred it! I am not very experienced with RxJs, but I believe your code around BehaviorSubject
and Observable
is as concise as possible.
BehaviourSubject
itself is "subscribable" (just like an Observable
) but it's a really bad practice to expose it directly to the consumer, i.e. your separation of users$
and usersSource
is a right thing to do. Exposing Observable
is okay because it's designed to be read-only.
usersSource.getValue()
is inevitable in your code, unless you decide to keep the usersSource state as a local field, but IMO it does not do any good in this scenario. Having state to be stored as a part of the BehaviorSubject
is handy.
There are minor style things that could be improved in the code, but that's unrelated to RxJs, so I'll not comment on that.
Update 1
Just a few months ago I was not aware of .startsWith()
which seems to be an answer to a part of your question:
const neverButStartsWithZero = Rx.Observable
.never()
.startWith(0);
// Emits 0
const subscription1 = neverButStartsWithZero
.subscribe(value => console.log('subscription1:', value));
const threeEventsWithExtraZero = Rx.Observable
.of(1, 2, 3)
.startWith(0);
// Emits 0, 1, 2, 3
const subscription2 = threeEventsWithExtraZero
.subscribe(value => console.log('subscription2:', value));
I know its an old post but you could also do a getter for your example
get users$() {
return this.usersSource$.asObservable();
}
-
3\$\begingroup\$ Welcome to Code Review! This may be the start of an answer, but it's not much of a review yet. Please see our help page on How to write a good answer. \$\endgroup\$2020年10月23日 11:05:46 +00:00Commented Oct 23, 2020 at 11:05
Subject
instead which do not require initial value \$\endgroup\$