使用rxjs ReplaySubject在两个组件之间共享数据的正确方法是什么?

时间:2019-01-23 21:45:35

标签: javascript angular rxjs angular-services angular-components

我已经开发了具有两个视图的组件。组件A具有联系表单,组件B是“谢谢”页面。

组件A: 您填写表格并提交。 响应到达后,将立即创建一个新的ReplaySubject值。 用户将被路由到组件B。

组件B: 组件已初始化。 该组件从主题中获取价值。 视图将被渲染并显示“谢谢”消息。

HTTP调用响应(在成功发送Form数据后请求后返回):

{
   "status": 200,
   "body": {
              "message": "We have received your request with the card information below and it will take 48h to be processed. Thank you!",
              "card": {
                    "name": "John Doe",
                    "email": "john.doe@gmail.com",
                    "accountNumber": "12345-67890"
              }
            },
   "type": "ItemCreated"
}

A组件(表单)代码:

import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { RequestCardWidgetService } from './request-card-widget.service';
import { RouterService } from '@framework/foundation/core';
import { Item } from '@pt/request-card-data'

@Component({
  selector: 'pt-request-card-form',
  templateUrl: './request-card-form.template.html',
  providers: [RouterService]
})
export class RequestCardFormComponent {
  constructor(private fb: FormBuilder, private data: RequestCardWidgetService, private router: RouterService){}

  item: Item = {
    name: '',
    email: '',
    accountNumber: ''
  };

  requestCardForm = this.fb.group({
    name: ['', Validators.required],
    email: ['', Validators.email],
    accountNumber: ['', Validators.required]
  })

  onSubmit() {
    this.item = this.requestCardForm.value;
    this.data.requestCard(this.item)
      .subscribe(data => {
        this.data.processResult(data);
        this.router.navigate(['/success']);
      });
  }

}

组件B(谢谢页面)代码:

import { Component } from '@angular/core';
import { RequestCardWidgetService } from './request-card-widget.service';

@Component({
  selector: 'pt-request-card-success',
  templateUrl: './request-card-success.template.html'
})
export class RequestCardSuccessComponent {
  messages: any; // TODO: To use the proper type...

  constructor( private requestCardService: RequestCardWidgetService) {
    this.messages = this.requestCardService.message;
  }
}

组件B模板(谢谢页面):

<div *ngIf='(messages | async) as msg'>
  {{ msg.message}}
</div>

组件服务代码:

import { Injectable } from '@angular/core';
import { HttpResponse } from '@angular/common/http';

import { Observable, ReplaySubject } from 'rxjs';
import { map, take } from 'rxjs/operators';

import {
  RequestCardDataService,
  Item,
  ItemCreated
} from '@example/request-card-data';

@Injectable()
export class RequestCardWidgetService {

  constructor(private dataService: RequestCardDataService) { }

  private readonly results = new ReplaySubject<ItemCreated>();

  readonly message: Observable<ItemCreated> = this.results; // Message Line. This is the variable that I'm rendering in the template. Is this the correct way of extracting subject values?

  requestCard (card: Item): Observable<ItemCreated> {
    return this.dataService.postCardRecord(card).pipe(
      take(1),
      map((response: HttpResponse<ItemCreated>): ItemCreated | {} => {
        return response.body
          ? response.body
          : {};
      })
    );
  }

  processResult(data: ItemCreated) {
    this.results.next(data);
  }

}

回顾: 组件A具有一种形式。提交表单后,结果将作为新值存储在主题中。用户被路由到“谢谢”页面。 谢谢页面组件呈现该元素并从主题中获取最新值。然后呈现内容。

此代码有效,但我确实有一些疑问。

问题: 这是使用主题的正确方法吗?

这是:

readonly message: Observable<ItemCreated> = this.results;

从主题中提取值的正确方法是什么? (我正在向视图传递“消息”。)

是否有更好的方法可以达到相同的结果? 提前谢谢。

1 个答案:

答案 0 :(得分:2)

这是使用主题的正确方法吗?

不受限制的

ReplaySubect将重播它先前发送给新订户的所有值。这可能导致用户可能会收到以前发出的消息,直到他们最终收到当前消息为止。因此,要么约束主题,要么考虑使用BehaviorSubject

从主题中提取值

一个Subject及其所有派生词都是ObservableObserver。当向消费者提供主题时,您不想公开Observer界面,即,消费者永远不能调用nexterrorcomplete。因此,如评论中所建议,应确保首先通过调用Observable方法将asObservable接口暴露给使用者。

readonly message: Observable<ItemCreated> = this.results.asObservable();

后续步骤

如果您想继续使用组件之间的基于服务的通信,那么我认为您有机会根据问题注释中链接的文档来清理/优化代码。

如果您的应用程序变得越来越复杂,我将指导您采用Redux风格的体系结构,并研究NgRx,尤其是使用效果来管理副作用。

效果可以通过简单,谨慎的可观察结构满足您的所有要求,即,一种用于处理表单提交,接收响应甚至导航到成功页面的效果。 More information about effects can be found here

对于一个简单的任务,redux架构可能会显得过分杀伤,但是如果您正在处理管理大型状态树的大型应用程序,那么与基于服务的集成相比,我更喜欢这种方法。