Angular/RxJS update piped subject manually (even if no data changed), “unit conversion in rxjs pipe”

ε祈祈猫儿з 提交于 2021-01-29 14:30:55

问题


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.


回答1:


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;
        })
    );
}


来源:https://stackoverflow.com/questions/61727665/angular-rxjs-update-piped-subject-manually-even-if-no-data-changed-unit-conv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!