首页 > 解决方案 > 如何订阅和更新深层嵌套在 BehaviorSubject 中的单个属性?

问题描述

我需要创建一种方法来动态构建可单独订阅的可观察数据的数据存储。我正在使用BehaviorSubject,因为生成的数据的值可能会发生变化并且需要反映。

我正在创建一个模块,用于SVG从形状如下面的界面的数据对象生成代码。

export interface GraphicElement{
    element: string;
    elementId: string;
    selfClosing?: boolean;
    bind?: GraphicBinding; //this property is most relevant to this question
    value?: string;
    properties: GraphicProperties;
    responsiveProperties?: ResponsiveProperties;
}

export interface GraphicBinding{
    library: string;
    property: string;
}

在我的模块中,我有一个svg-shell-component封装所有代码的模块,一个将对象svg-group-component迭代到一个生成 svg 元素并将其注入模板的模块中。GraphicElementsvg-element-component

可选bind属性将让组件知道需要将值绑定到该元素。为了给出一个真实世界的例子来更好地说明我在做什么,我有一个product-spec-component产品图表,其中包含不同尺寸、零件尺寸、开/闭跨度等的测量值,具体取决于产品。这product-data是一个独立的存储,包含不同维度的所有测量值,这些测量值将用于生成在显示测量值graphic-data的实例中应该绑定到的数据。svg-element-component用户还可以在英寸和厘米之间切换,这将更新本示例中绑定的属性的值。

我处理这种情况的方法是使用服务文件以以下界面的形式动态生成和设置这些属性。

export interface GraphicBindingData{ [library: string] : GraphicBindingProperty; }
export interface GraphicBindingProperty{ [property:string] : string; }

所以在这个例子中,服务文件中会生成一个像这样的对象

BindingData: GraphicBindingData = {
     specMeasurements:{
        width: '2in',
        height: '5in',
        thickness: '.125in',
        depth: '9in'
    },
    someOtherLibrary:{
        somePropA: 'some value',
        somePropB: 'some value'
    }
}

回到GraphicBinding接口中bind属性的GraphicElement接口,该library属性将被设置为specMeasurements,并且该property属性将被设置为它需要绑定到的任何维度的名称。

因此binding-data-service,我们将调用它的文件将被导入product-spec-component以设置此数据,而graphic-element-component将使用bind属性中的值来订阅它需要的任何属性。我添加了另一个虚拟数据对象,因为我想展示如何可能存在其他属性对象,以进一步强调是什么让我很难弄清楚如何去应用BehaviorSubject这种情况,所以个人图书馆或可能仅在一个图书馆中的个别财产可能会发生变化,而其他一切都保持不变。在这个例子中,product-spec-component这不是什么大问题,但是在如何使用这个模块的其他方面,可能会有更多的对象具有更深层次的数据。

我正在考虑做下面的事情。

export interface GraphicBindingData{ [library:string] : BehaviorSubject<GraphicBindingProperty>; }
export interface GraphicBindingProperty{ [property:string] : BehaviorSubject<string>; }

在考虑了 aBehaviorSubject通常如何被另一个变量观察到然后订阅之后,我不确定如何为每个库中的每个属性创建一个观察者。我也不确定.next()在这种情况下必须如何使用来更新数据。我一直在尝试查找有关在 BehaviorSubjects 中使用 BehaviorSubjects 的更多信息,如果这对于我想要实现的目标来说是必要的,但找不到任何东西。有人可以帮助我了解如何塑造它,以便我可以订阅和更新各个属性而不影响其他可观察对象吗?

标签: angulartypescriptrxjs

解决方案


我不推荐嵌套行为主题,它会使您的流消耗更加复杂,使用单个状态对象并应用一些过滤逻辑可能会更好

假设你有一个像

const myObj=new BehaviorSubject({orange:{apple:{cherry:'my cherry'},banana:'my banana'})

并且您想更新cherry(在这里重建不可变对象可能会很痛苦)

myObj.next({orange:{apple:{cherry:'my cherry updated'},banana:'my banana'})

并注意樱桃变化

myObj.pipe(pluck('orange','apple','cherry'), distinctUntilChanged())

因为每当其中一个值发生更改时,您都必须 .next() 整个对象,所以要做到这一点,您必须破坏并重新构造状态对象

const obj=myObj.value
//add a grape
myObj.next({...obj ,orange:{...obj.orange, grape:'my grape'}})

// or json stringify then json parse
const obj=JSON.parse(JSON.stringify(myObj.value))
obj.orange.grape='my grape'
myObj.next(obj)

或者,如果您不想要不变性,您可以直接设置属性

let obj=myObj.value
obj.orange.grape='my grape'
myObj.next(obj)

推荐阅读