Angular/RxJS 手动更新管道主题(即使没有数据更改),“rxjs 管道中的单位转换" [英] Angular/RxJS update piped subject manually (even if no data changed), "unit conversion in rxjs pipe"

查看:25
本文介绍了Angular/RxJS 手动更新管道主题(即使没有数据更改),“rxjs 管道中的单位转换"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个使用 Angular 9 和 RxJS 6.5 的应用程序.我有问题的功能使用 Angular 服务从服务器获取一些数据并将这些数据提供给视图(使用 RxJS 行为主题).不过,在将该数据传递给视图之前,我想做一些单位转换,以便用户可以使用切换/开关在整个应用程序的两个单位之间切换,特别是公吨"和短吨".

虽然在专用服务中提供了实际的转换功能,但我需要控制应该转换数据的哪些属性(由服务器获取),所以我管道"主题,调用内部的转换方法管道.

//这个CommoditiesService"从服务器获取数据//并且应该返回所有获取的商品,所以 "Commodity[]"出口类 CommoditiesService {//在这个主题里面,存储了所有的 Commodities(没有单位转换)private _commodities: BehaviorSubject= 新行为主题(空);公共只读商品:Observable= this._commodities.pipe(//这个管道是所有商品单位转换发生的地方,//所以对于每个商品,函数convertCommodityUnits"都会被调用.//那里的参数是基本单位和目标单位.基数始终为千克,因为这是服务器返回的值.//目标单位是用户可以全局设置的单位.这个preferredWeightUnit"是在服务内部订阅的,所以一旦用户改变了单位,它就会更新.地图((商品:商品[])=> {//对于每个商品,将价格和库存转换为首选的显示单位返回商品?.map(commodity => this.convertCommodityUnits(commodity, "kg", this.preferedWeightUnit)) ||空值;}));}

到目前为止,这就像一个魅力:单位被转换,视图可以订阅可观察的商品.现在的问题是,当用户更新preferredWeightUnit"时,commodities" observable 不会被重新评估,因此this.preferredWeightUnit"在 CommoditiesService 内部被更新,但不会再次进行单位转换.

我想我可以用相同的数据更新主题,所以调用 this._commodities.next(this._commodities.value),但这在我看来是错误的.

一旦首选单位更改,我如何再次触发单位转换(因此是 RxJS 管道)?此外,这种设计选择是否是使单位以反应方式可变的好主意?我认为最好是更改视图中可能出现的任何地方的单位.

解决方案

您可以使用 combineLatest 运算符.

导出类 CommoditiesService {private _commodities: BehaviorSubject= 新行为主题(空);公共只读商品:Observable= combineLatest([this._commodities, this.preferredWeightUnit$]).pipe(地图(([商品:商品[],重量单位])=> {返回商品?.map(commodity => this.convertCommodityUnits(commodity, "kg", weightUnit)) ||空值;}));}

I am working on an app using Angular 9 and RxJS 6.5. The functionality I have problems with uses Angular services to fetch some data from a server and provides this data to views (using RxJS behaviorSubjects). Before passing that data to the views though, I want to do some unit conversion, so that the user can use a toggle/switch to change between two units for the entire app, specifically "metric tons" and "short tons".

While the actual conversion functions are provided within a dedicated service, I need to control which properties of the data (that was fetched by the server) should be converted, so I am "piping" the subjects, calling the conversion methods inside the pipe.

// This "CommoditiesService" fetches data from the server
// and should return all fetched commodities, so "Commodity[]"
export class CommoditiesService {
    // Inside this subject, all Commodities are stored (without unit conversion)
    private _commodities: BehaviorSubject<Commodity[]> = new BehaviorSubject(null);

   public readonly commodities: Observable<Commodity[]> = this._commodities.pipe(
    // This pipe is where all unit conversions for commodities is happening,
    // so for every commodity, the function "convertCommodityUnits" is called.
    // the arguments there are the base and the target unit. Base is always kilogram, as this is what the server returns.
    // The target unit is what the user can set globally. This "preferredWeightUnit" is subscribed to inside the service, so it is updated once the user changes the unit.

    map((commodities: Commodity[]) => {
      // For every commodity, convert Price and inventory into the prefered display unit
      return commodities?.map(commodity => this.convertCommodityUnits(commodity, "kg", this.preferedWeightUnit)) || null;
    })
  );
}

So far this works like a charm: The units are converted and views can subscribe to the commodities observable. The problem is now, when the user updates the "preferredWeightUnit", the "commodities" observable is not re-evaluated, so "this.preferredWeightUnit" is updated inside CommoditiesService, but the unit conversion is not done again.

I suppose I could update the subject with the same data, so calling this._commodities.next(this._commodities.value), but this just looks wrong to me.

How could I trigger the unit conversion (so the RxJS pipe) again once the prefered unit changed? Also, is this design choice even a good idea to make the units changable in a reactive way? I thought it was better then changing the units inside views everywhere they might appear.

解决方案

You can incorporate changes from multiple observable sources using the combineLatest operator.

export class CommoditiesService {
    private _commodities: BehaviorSubject<Commodity[]> = new BehaviorSubject(null);

    public readonly commodities: Observable<Commodity[]> = combineLatest([this._commodities, this.preferredWeightUnit$]).pipe(
        map(([commodities: Commodity[], weightUnit]) => {
           return commodities?.map(commodity => this.convertCommodityUnits(commodity, "kg", weightUnit)) || null;
        })
    );
}

这篇关于Angular/RxJS 手动更新管道主题(即使没有数据更改),“rxjs 管道中的单位转换"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆