【问题标题】:Rxjs strange problem with BehaviorSubjectBehaviorSubject 的 Rxjs 奇怪问题
【发布时间】:2019-05-03 07:32:28
【问题描述】:

我在 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() 时订阅都会恢复。

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

感谢您的任何帮助。

【问题讨论】:

  • add 在哪里调用?请将此包含在您的 sn-ps 中
  • 在您的后一个 sn-p 中,似乎没有人订阅 BehaviorSubject 的事实似乎很可疑。
  • 从您的代码中可以看出您订阅了add 方法,而不是dataFromServer,因为它是私有的,您将无法从内部订阅它..
  • 确实如此,getAll 似乎返回了主题,但是该函数中的大括号必须放错了位置。
  • 没有看到,我猜add的结果没有被订阅。因此,tap 永远不会被调用。

标签: angular typescript rxjs


【解决方案1】:

问题是您在getAll 中调用this.http.get&lt;IDataFromServer[]&gt;('/api/rest'),但没有人订阅它。

您提到的第一个日志必须是null,因为您将BehaviorSubject 初始化为null。

您需要做的是简单地从getAll 返回http.get,这样任何人调用此方法并订阅它都会触发http 调用。此外,tap 将确保您在BehaviorSubject 中的数据将得到更新。

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

【讨论】:

  • 感谢您的回答。如果我添加返回并删除返回 this.dataFromServer.asObservable();正如您建议的那样,第一次确实有效,但以后的更新仍然无效。
【解决方案2】:

连同关于为什么您的解决方案不起作用的出色答案,我将添加我对您的方法有什么问题的看法。

据我所见,您正试图从您的服务中公开一个单独的可观察对象,该服务将在每个 REST 调用中使用,从而允许视图从数据更新登录中解耦和重用数据可视化逻辑(即,它们只需要显示data 每次更新数据时都会调用相同的逻辑)。

问题是httpClient Observables 是惰性的,实际上只有在有人订阅时才发送请求。

我也做过同样的事情,我通常做的是在我的服务中隐藏 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/ 的调用将返回一样。

【讨论】:

  • 这成功了。谢谢你,感谢@ggradnig 指出我思考错误的正确方式。
【解决方案3】:

虽然您没有包含调用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();

问题源于对 Observables 的常见误解。 Maybe this post helps.

【讨论】:

  • 如果我订阅 add(),我会收到不可观察的错误。
  • 您可以执行第一个或第二个解决方案。如果你想订阅它(解决方案 2),那么 add 需要返回你原来的 sn-p 中看到的 observable。
  • 您是对的,并且与@bracco23 一起回答向我澄清了我的错误。我唯一的遗憾是我无法将您的评论标记为答案中的解决方案
猜你喜欢
  • 2017-05-04
  • 1970-01-01
  • 2021-04-19
  • 2015-02-27
  • 2018-06-26
  • 2011-08-13
  • 2011-08-17
  • 2011-02-02
  • 2021-10-10
相关资源
最近更新 更多