【问题标题】:Reactive Backend Calls - How to get IDs / Names /响应式后端调用 - 如何获取 ID / 名称 /
【发布时间】:2020-07-08 04:13:05
【问题描述】:

要跟进的最小 Stackblitz 回购:https://stackblitz.com/edit/angulare-backend-calls-so

假设我有一个为我提供数据的后端,在本例中是一个带有 ID 的简单计数器。

{id: number, counter: number}

我想在async 管道的帮助下在我的app.component.html 中显示收到的值,这样我就不会创建任何不必要的订阅。

一段时间后,用户决定增加计数器,现在我需要一个 ID,以便后端知道要增加哪个计数器对象。但由于我不能直接访问它,这并不容易。我不想.pipe(first())订阅只是为了打电话。

解决此问题的一种方法是将app.component 中的方法签名更改为也获取计数器 ID 并从模板内部传递它。但是如果我不使用*ngIf="(obs$ | async) as obs",我看不到这种方法有效,因为我必须使用(click)(或其他)处理程序内的另一个async管道再次订阅可观察的计数器对象,或者是这样预期的行为/最佳实践?如果我依赖多个流(click)=copyTimer((counter1|async).id, (counter2|async).id) 肯定不会是最佳解决方案会发生什么

第一个问题因此:当使用流作为主要数据源时,处理依赖 ID 的后端调用的最佳方法是什么?

让我们先跳过一些时间,假设服务器正确获取了 ID 并相应地增加了计数器。我将如何处理更新视图? 我所做的只是从后端返回完整的计数器对象,这可能适用于这个特定的最小示例,但如果使用更复杂的对象(我假设)会失控。

我显然可以只使用this.countingObj = this.appService.increaseCounter(id),但第二个组件会错过这些更新,因此更新需要在服务内部进行。

第二个问题:我是否应该在服务中保留一个 BehaviorSubject,它跟踪变化并作为 get 调用的代理,或者如何注意到应用程序范围内的对象变化它在所有组件中都是一致的吗?

【问题讨论】:

    标签: angular rest rxjs


    【解决方案1】:

    我会使用一个行为主体来处理我的增量函数的动作流。

    首先,当然您需要先加载数据,然后才能决定增加对象的计数,这没有意义,您的异步管道订阅将处理对流的侦听以及每个新的发射,将更新 UI。

    其次,您合并两个流(数据流 - 负责流式传输对象列表的流)和动作流(增量动作流),我将 combineLatest 用于此特定场景,因为它符合要求(需要在我们发出之前的两个值),不用担心,因为动作流是一个行为主体,你不需要一个动作来让 observable 发出值,我们有 null 作为初始值,然后我们在映射过程中忽略 null id功能。 您的多个组件将订阅此合并流(使用异步管道),然后您可以循环访问您的对象(原始的 stackblitz 是对象,我将其列出),每次用户单击增量时,您将 id 传递给操作流,它将发出一个要递增的新 id 值,使用您的 map 函数,我们增加计数,然后将一个新值发送到我们的 combineObservable (dataWithAction$),然后您的视图将检测到新发出的值,并将自动更新,这将适用于正在侦听您的 (dataWithAction$) 的任何组件。

    简单的代码(再次,我修改了 observable 对象以流式传输列表而不是单个对象,以使其更清晰的示例)

    服务

    incrementSubject = new BehaviorSubject<number>(null);
     incrementAction$ = this.incrementSubject.asObservable();
     data$ = of([{id: 0,counter: 0},{id: 1,counter: 0}]);
     dataWithAction$ = combineLatest([this.incrementAction$,this.data$]).pipe(
       map(([id,data])=> {
         if(id !== null){
         data.find(t => t.id == id).counter++
         }
         return data;
       }));
      constructor() { }
      increaseCounter(id: number) {
        this.incrementSubject.next(id);
      }
    

    你的组件 .ts

       countingObj: Observable<{ id: number; counter: number }[]>;
    
       constructor(private appService: AppService) {
        this.countingObj = this.appService.dataWithAction$;
       }
    
       increaseCounter(id) {
        this.appService.increaseCounter(id);
       }
    

    查看

    <div *ngIf="countingObj | async as obj">
        <div *ngFor='let x of obj'>
            <div>{{x.counter}}</div>
            <div>{{x.id}}</div>
            <button (click)=increaseCounter(x.id)> Increment  </button>
        </div>
    </div>
    

    RxJS 高级模式——操作大量动态 UI 的 |强烈推荐 Michael Hladky 演讲

    【讨论】:

    • 感谢您的回答。我部分满意。我如何从后端获取数据?我是否应该有一种方法来请求数据,然后将其附加到数据主体?这意味着我必须在我认为不可以的服务中订阅。
    • 用http get替换你的observable的data$,就是这样,不需要订阅你的view已经订阅了
    • 但是这样如果一些例如在我必须重做 http 调用之后,组件会被初始化,尽管我之前已经这样做了。
    • 您可以使用share operator 来避免这种多次调用。
    • 但是,如果它们彼此分离并且都访问 Service 中的 getData() 方法,该方法只返回一个共享的 observable,那么这将不起作用。我必须将请求存储在某个地方,我不确定 tk 如何实现这一点。
    猜你喜欢
    • 2017-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 2013-05-30
    • 2021-09-14
    • 2021-03-10
    相关资源
    最近更新 更多