I have become slightly confused over Observables, Observers, Subjects, BehaviorSubjects, Services, injections, etc.
I am trying to make a simple finance app. I have a transactions service that grabs the data from a REST server and then provides it to other services that can update it if they need to. I can get the list transactions component (which gets its data from getTransactions()
)to update with the service, but I don't know how to get the edit component (which gets its data from "getTransaction(id)
" to work.
What is the best way to let components play with data from the entire array (read/write access) as well as those that just want to play with a single element of the array?
Here's what I have right now: (I've changed it a ton so it's currently broken, but you can see what I was doing)
export class DataBrokerService {
public transactions:Transaction[];
constructor() {}
ngOnInit(){
}
getTransactions(): Observable<Transaction[]>{
if (needUpdate){
// Download from REST API, subscribe result to transactions
this.updateTransactions();
}
return of(this.transactions);
}
getTransaction(id:number): Observable <Transaction>{
let transaction:Transaction;
this.getTransactions().subscribe(txns => transaction = txns.find(txn => txn.id === id))
});
return transaction$;
}
I also made this demo showcasing what I am going for:
https://stackblitz.com/edit/angular-stackblitz-demo-dftmn3
This is a followup question from: Get one value from observable array
It's a good question. I know it's kind of confusing to work with RxJs at first but when you get the hang of it, it's quite powerful.
What you want can be achieved with BehaviorSubject
.
Let's find out what Subject
is and move onto BehaviorSubject
Subject: is the equivalent to an EventEmitter, and the only way of multicasting a value or event to multiple Observers
For more info, take a look at here
So, we know that Subject
is like an EventEmitter
. When someone subscribes to it, whenever you call subject.next(value)
, every subscriber will get the new value. However, late subscribers (meaning subscribing to the Subject
after next
method is called) won't get the previous values. This is where BehaviorSubject
will come into the scene.
It is just like Subject
, but when a new subscriber subscribes to it, it'll emit the previous value. Let's use it in your code.
export class DataBrokerService {
// do not expose your subject to other classes
private _transactions$: BehaviorSubject<Transaction[]> = new BehaviorSubject([]);
// instead provide it as an observable
public readonly transactions: Observable<Transaction[]> = this._transactions$.asObservable();
needUpdate = true;
constructor(private http: HttpClient) { }
ngOnInit() {
}
// just return the observable
getTransactions(): Observable<Transaction[]> {
if (needUpdate) {
this.updateTransactions();
}
return this.transactions;
}
// you can iterate over your transactions and filter them,
// but DO NOT ever subscribe to it within a service.
// Otherwise, other classes won't be able to retrieve the value.
getTransaction(id: number): Observable<Transaction> {
return this.getTransactions().pipe(
map(txns => txns.find(txn => txn.id === id))
);
}
// when you need to update the transactions,
// simply call this method
updateTransactions() {
this.http.get('transactions').subscribe(txns => {
this.needUpdate = false;
this._transactions$.next(txns);
});
}
}