问题
I'm relatively new to reactive programming and I'm trying to create an Angular service which can display notifications to the user. So far this is what I have:
https://stackblitz.com/edit/angular-rxjs-notifications?file=app%2Fapp.component.html
But there is a major problem with my implementation that I don't know how to solve: How, in a reactive way, can I queue up notifications? I would like my notification div
to appear when the first notification is pushed and when the user clicks "Clear" I would like the div to disappear unless other notifications have been pushed since. That way clear would show the next notification and so on until all notifications have been cleared. Then as soon as a new notification is pushed the div should pop up again.
I chose a Subject
in my implementation over a ReplaySubject
because I don't want users to see notifications that were pushed while they were loading the next screen but I realized that if I'm using a router inside my app.component.html
then I'll run into that behavior anyways. Maybe I need to clear the notification queue when a routing occurs?
Thanks in advance!
回答1:
The example service can look like this
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { merge } from 'rxjs/observable/merge';
import { map, scan } from 'rxjs/operators';
enum ActionType {
push = 'push',
pop = 'pop'
}
interface Action {
type: ActionType;
}
interface PushAction extends Action {
payload: string;
}
interface PopAction extends Action { }
@Injectable()
export class NotificationService {
messages$: Observable<string[]>;
private pushSource = new Subject<string>();
private popSource = new Subject<void>();
constructor() {
const push$ = this.pushSource.asObservable()
.pipe(map((payload) => ({ type: ActionType.push, payload })));
const pop$ = this.popSource.asObservable()
.pipe(map((payload) => ({ type: ActionType.pop })));
this.messages$ = merge(push$, pop$)
.pipe(
scan((acc: any, { payload, type }) => {
if (type === ActionType.pop) {
acc = acc.slice(0, -1);
}
if (type === ActionType.push) {
acc = [...acc, payload]
}
return acc;
}, [])
)
}
pushMessage(msg: string) {
this.pushSource.next(msg)
}
popMessage() {
this.popSource.next()
}
}
Live demo
We have two streams, pop$
and push$
that we merge together. We than reduce it into a single array using the scan
operator.
So a stream of
push(hello) -> push(world) -> pop -> push(kitty)
would be reduced to [hello, kitty]
.
来源:https://stackoverflow.com/questions/49907996/user-notification-service-with-rxjs-in-angular