问题
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