Rxjs BehaviorSubject的奇怪问题

时间:2019-05-03 07:32:29

标签: angular typescript rxjs

我在我的angular(7)应用程序中使用BehaviorSubject遇到了奇怪的行为。

我创建了一个使用一些剩余呼叫的服务。要显示代码中发生的事情,您在这里:

export class MyService {
    private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);

    constructor(private http: HttpClient) { }

    getAll(): Observable<IDataFromServer[]> {
        this.http.get<IDataFromServer[]>('/api/rest')
        .pipe(
            tap(data => this.dataFromServer.next(data))
        );
    }
    return this.dataFromServer.asObservable();
}

到目前为止,一切都很好。当我需要使用此服务时,我订阅了像

这样的getAll()方法
this.myService.getAll().subscribe(console.log);

并将数据打印到控制台。

现在我必须将前端的数据添加到其余的api

export class MyService {
    private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);

    // snip...

    add(item: IDataFromServer): Observable<any> {
        return this.http.post<IDataFromServer>('/api/manage', item).pipe(
            tap(data => {
                let internal = this.dataFromServer.getValue();
                if (!internal) {
                    internal = new Array<IDataFromServer>();
                }
                internal.push(material);
                this.dataFromServer.next(internal);
            })
        );
    }
}

这是麻烦开始的地方。上面的订阅是第一次打印,但没有收到next()触发的新数据。

让我感到困惑的是,如果我在浏览器上单击了刷新,而没有更改代码的任何行,则每次单击next()时,订阅都将重新生效。

我显然做错了事,但我不明白为什么在哪里。

感谢您的帮助。

3 个答案:

答案 0 :(得分:3)

问题是您在#year { width:150px; } 内致电#year option{ width:150px; } ,但没有人订阅。

您提到的第一个日志必须为this.http.get<IDataFromServer[]>('/api/rest'),因为您将getAll初始化为空。

您需要做的就是简单地从null返回BehaviorSubject,以便任何人调用此方法并订阅该方法都会触发http调用。另外,http.get将确保您在getAll中的数据将被更新。

tap

答案 1 :(得分:1)

尽管您没有包括调用add函数的部分,但我可以肯定您不订阅该函数的结果。

您可能会有一行类似这样的代码

myService.add(myItem);

现在,这将返回一个Observable。但是请注意,如果没有人订阅该Observable,则不会触发连接的管道。

因此,您有两种解决方案。

首先:不要使用tap,而是要订阅。我几乎可以确定这就是您想要的。它是这样的:

add(item: IDataFromServer): void {
    this.http.post<IDataFromServer>('/api/manage', item)
        .subscribe(data => {
            let internal = this.dataFromServer.getValue();
            if (!internal) {
                internal = new Array<IDataFromServer>();
            }
            internal.push(material);
            this.dataFromServer.next(internal);
        }
    );
}

第二个:订阅add的结果。像这样:

myService.add(myItem).subscribe();

该问题是由对Observable的常见误解引起的。 Maybe this post helps

答案 2 :(得分:1)

连同有关为什么您的解决方案不起作用的好答案,我将对您的方法有何不足之处加我的看法。

据我所知,您正在尝试从服务中公开一个可观察的对象,将其用于每个REST调用,从而允许视图与数据更新登录分离并重用数据可视化逻辑(即,它们仅需要显示每次更新数据时,都会调用相同的逻辑。)

问题在于httpClient的可观察对象是惰性的,实际上只有有人订阅时才发送请求。

我做过同样的事情,通常要做的是在服务中隐藏http调用并在内部订阅。借用您的课程,看起来像这样:

export class MyService {
    private dataFromServer = new BehaviorSubject<IDataFromServer[]>(null);

    constructor(private http: HttpClient) { }

    getAll(): Observable<IDataFromServer[]> {
        this.http.get<IDataFromServer[]>('/api/rest')
                 .subscribe(data => this.dataFromServer.next(data));
        return this.dataFromServer.asObservable();
    }

    add(item: IDataFromServer): Observable<any> {
        return this.http.post<IDataFromServer>('/api/manage', item)
                        .subscribe(data => this.dataFromServer.next(data));
    }
}

我还假设您的端点遵循REST原则,因此/api/manage路径将返回使用新添加的数据更新的完整列表作为响应,就像在POST之后返回对/api/rest/的调用一样打电话。