使用 rxjs ReplaySubject 在两个组件之间共享数据的正确方法是什么?
What is the proper way to share data between two components using rxjs ReplaySubject?
我开发了一个有两个视图的组件。组件 A 有联系表格,组件 B 是 "Thank you" 页面。
组件 A:
您填写表格并提交。
一旦响应到达,就会创建一个新的 ReplaySubject 值。
用户将被路由到组件 B。
组件 B:
组件已初始化。
组件从主题中获取值。
视图已呈现并显示感谢消息。
HTTP调用响应(表单数据post请求成功后返回):
{
"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;
从主题中提取值的正确方法? (我将 'message' 传递给视图。)
有没有更好的方法可以达到同样的效果?
提前致谢。
这是使用主题的正确方法吗?
ReplaySubect
unconstrained 将重播它之前向新订阅者发出的所有值。这可能会导致用户可能会收到之前发出的消息,直到他们最终收到当前消息。因此,要么限制主题,要么考虑使用 BehaviorSubject
。
从主题中提取值
A Subject
及其所有导数都是 Observable
s 和 Observer
s。向消费者提供主题时,您不想公开 Observer
接口,即消费者永远不能调用 next
、error
或 complete
。因此,正如评论中所建议的那样,您应该确保只通过首先调用 asObservable
方法向消费者公开 Observable
接口。
readonly message: Observable<ItemCreated> = this.results.asObservable();
后续步骤
如果您想继续在组件之间使用基于服务的通信,那么我认为您有机会根据问题评论中链接的文档 clean/refine 您的代码。
如果您的应用程序变得越来越复杂,我会引导您使用 Redux 风格的架构并研究 NgRx,特别是使用 effects 来管理副作用。
Effects 可以通过简单、谨慎的可观察结构满足您的所有要求,即处理表单提交、接收响应、甚至导航到成功页面的效果。 More information about effects can be found here.
redux 架构对于简单的任务来说可能有点矫枉过正,但如果你正在开发一个管理大型状态树的大型应用程序,我更喜欢这种方法而不是基于服务的集成。
我开发了一个有两个视图的组件。组件 A 有联系表格,组件 B 是 "Thank you" 页面。
组件 A: 您填写表格并提交。 一旦响应到达,就会创建一个新的 ReplaySubject 值。 用户将被路由到组件 B。
组件 B: 组件已初始化。 组件从主题中获取值。 视图已呈现并显示感谢消息。
HTTP调用响应(表单数据post请求成功后返回):
{
"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;
从主题中提取值的正确方法? (我将 'message' 传递给视图。)
有没有更好的方法可以达到同样的效果? 提前致谢。
这是使用主题的正确方法吗?
ReplaySubect
unconstrained 将重播它之前向新订阅者发出的所有值。这可能会导致用户可能会收到之前发出的消息,直到他们最终收到当前消息。因此,要么限制主题,要么考虑使用 BehaviorSubject
。
从主题中提取值
A Subject
及其所有导数都是 Observable
s 和 Observer
s。向消费者提供主题时,您不想公开 Observer
接口,即消费者永远不能调用 next
、error
或 complete
。因此,正如评论中所建议的那样,您应该确保只通过首先调用 asObservable
方法向消费者公开 Observable
接口。
readonly message: Observable<ItemCreated> = this.results.asObservable();
后续步骤
如果您想继续在组件之间使用基于服务的通信,那么我认为您有机会根据问题评论中链接的文档 clean/refine 您的代码。
如果您的应用程序变得越来越复杂,我会引导您使用 Redux 风格的架构并研究 NgRx,特别是使用 effects 来管理副作用。
Effects 可以通过简单、谨慎的可观察结构满足您的所有要求,即处理表单提交、接收响应、甚至导航到成功页面的效果。 More information about effects can be found here.
redux 架构对于简单的任务来说可能有点矫枉过正,但如果你正在开发一个管理大型状态树的大型应用程序,我更喜欢这种方法而不是基于服务的集成。