RxJS:使用另一个可观察变量过滤一个可观察变量

时间:2018-08-16 15:08:01

标签: angular observable reactive-programming angular6 rxjs6

我有一个流承载来自API响应的数据,而另一个流则发出需要从源流中过滤掉的值。

@Injectable({
    providedIn: 'root'
})
export class SmpService {
    private _smp$ = new ReplaySubject(1);
    private _deleteSubject = new BehaviorSubject(null);

    constructor(private http: HttpClient) {
        const allSmp$ = this.loadSmp().pipe(map(list => list.map(item => item.id)));
        const delete$ = this._deleteSubject.pipe(map(value => this.filterSmp(value)));

        allSmp$
            .pipe(
                combineLatest(delete$, (notifications, xf) => {
                    return xf(notifications);
                }),
                tap(x => console.log('console ', x))
            )
            .subscribe(this._smp$);
    }

    loadSmp(): Observable<any> {
        const contextPath = 'some_url';
        const url = this.uriPrefix + contextPath;
        return this.http.get(url).pipe(map((response: any) => response.notifications || []));
    }

    filterSmp(value) {
        return notifications => notifications.filter(notification => value !== notification);
    }

    deleteSmp(subscribeItem) {
        this._deleteSubject.next(subscribeItem);
    }

    getSmp(): Observable<any> {
        return this._smp$.asObservable();
    }
}

过滤正常。但是在页面加载时,我无法在页面上呈现初始API响应。当我通过某些操作触发deleteStream时,才收到该消息。

即使未触发deleteStream,有什么方法可以获取初始数据?

3 个答案:

答案 0 :(得分:0)

使用Subject而不是BehaviorSubject并使用一些默认值。例如null

private _deleteSubject = new BehaviorSubject(null);

BehaviorSubject每次订阅都会发出默认值。

答案 1 :(得分:0)

您遇到的主要问题可能是$delete是一个"cold",直到_deleteSubject有了一个值。如果要在_smp$被赋予任何值之前填充_deleteSubject,则需要使用默认值对其进行初始化,如@ m1ch4ls所述。

@Injectable({
    providedIn: 'root'
})
export class SmpService {

    private _smp$ = new ReplaySubject(1);
    private _deleteSubject = new BehaviorSubject(null);

    constructor(private http: HttpClient) {
        const allSmp$ = this.loadSmp().pipe(map(list => list.map(item => item.id)));
        const delete$ = this._deleteSubject.pipe(map(value => value ? this.filterSmp(value) : (notifications => notifications)));

        delete$
            .pipe(
                withLatestFrom(allSmp$, (xf, notifications) => {
                    return xf(notifications);
                }),
                tap(x => console.log('console ', x))
            )
            .subscribe(this._smp$);
    }

    loadSmp(): Observable<any> {
        const contextPath = 'some_url';
        const url = this.uriPrefix + contextPath;
        return this.http.get(url).pipe(map((response: any) => response.notifications || []));
    }

    filterSmp(value) {
        return (notifications) => notifications.filter(notification => value !== notification);
    }

    deleteSmp(subscribeItem) {
        this._deleteSubject.next(subscribeItem);
    }
}

答案 2 :(得分:0)

可以使用另一种方法过滤一个Observable的另一种方法

    private _smp$ = new ReplaySubject(1);
    private _deleteSubject = new Subject();

    constructor(private http: HttpClient) {
        const allSmp$ = this.loadSmp().pipe(map(list => list.map(item => item.id)));
        const delete$ = this._deleteSubject.pipe(
            map(value => (value ? this.filterSmp(value) : notifications => notifications))
        );

        allSmp$.merge(delete$)
            .pipe(
                startWith([]),
                scan((acc, value) => {
                    if (typeof value === 'function') {
                        return [...value(acc)];
                    }
                    return [...acc, ...value];
                })
            )
            .subscribe(this._smp$);
    }    

在此,我们完成了以下操作:

  1. 替换了BehaviourSubject(null) -> Subject()
  2. 替换了combineLatest -> merge
  3. 添加了startWithscan以保持持久的内部状态

瞧,它起作用了。但仍在寻找其他更好的解决方案。