我有一组组件,服务应该更改这些组件的属性。 组件有一个非常小的模型,应该保持小。 它们使用 *ngFor 呈现。
服务有一个大对象,应该知道所有组件并能够操作其属性。
我已经查看并尝试了BehaviorSubject,但对我来说似乎有点不对劲的是,一个小组件会听一个大对象:
class Service{
_bigModel: BehaviorSubject<object>
bigObject: Observable<object>
}
class Component{
constructor(){
bigObject.subscribe((newBigObject)=>{
let partNeeded = newBigObject.key1.keya.blabla;
//...do sth with partNeeded;
});
}
}
这里的缺点:
组件将订阅不以组件为目标的事件。
可能将服务的模型加载到所有小组件中, 炸毁 DOM。 不知道行为主体是否正在处理 变量引用等
- 组件需要知道服务的模型,以便获得工作所需的内容。
我认为更有意义的是,如果服务可以直接操作组件:
class Service{
componentList: Component[]; //dunno the type of a Component
changeComponent(idOfComponent, propertyToChange, value);
changeAllComponents(propertyToChange){
for(c of componentList){
let val = computeSomeValue();
changeComponent(c, propertyToChange, val);
}
};
}
class Component {
someProperty: someType;
someOtherProperty: someType;
}
//Template:
<div [ngClass]="someOtherProperty">{{someProperty}}</div>
这种分配是否合理和可能?在这种情况下,如何操作服务中的特定组件? 如果没有,还有什么更好的选择?
使用第一种方法。它基本上是NGRX和任何基于redux的状态容器/存储所做的,所以它是一个经过实战验证的概念。
浏览您的要点:
组件将订阅不以组件为目标的事件。
这取决于您的实现。您可以通过使用正确的 rxjs 运算符来避免这种情况,例如:
Observable<string> stringProperty = this.service.dataChanges()
.pipe(
// extract the property that you need
map(data => ...),
// skip until a different value is emited
distinctUntilChanged()
);
可能将服务的模型加载到所有小组件中, 炸毁 DOM。 不知道行为主体是否正在处理 变量引用等
对于这一点的第一部分,我不知道你的意思。我的猜测是,您指的是将服务数据对象直接呈现到 DOM 中的情况,这是一个有效的用例,您将负责不炸毁事情。
对于第二部分,在理想的实现中,服务发出的每个数据对象都应该是唯一的不可变对象。否则,事情会很快变得丑陋,因为组件可能不会拾取在组件树的其他部分中产生的数据对象的突变。
组件需要知道服务的模型,以便 获得他们工作所需的东西。
这是怎么回事?
我不会过多考虑您的第二种方法,因为它会带来很多问题:
- 如果你没有定义一个通用的组件接口,你将被迫在没有类型检查的情况下工作(例如:以 object["someProperty"]] 等方式访问对象属性(
- 如果您定义了一个通用组件接口,为了获得最大的可用性,您很可能会被迫公开服务数据对象,这不会解决您的第三个要点。
- 跟踪组件会增加复杂性,因为您必须正确注册/注销实例。
您不应尝试直接从服务修改组件。服务用于不与特定视图/组件关联的数据或逻辑。
服务不应了解整个应用中使用的组件,而应仅提供对所需数据的访问(通常使用可观察量(。然后,组件应决定它们需要使用哪个服务,以及它们要订阅该服务中的哪些数据。
您可以让服务将大模型的不同部分作为单独的可观察量提供。然后,您的组件会选择所需的正确可观察对象。组件使用服务,而不是相反。
将您的大模型拆分为多个模型/主题。 是否所有数据真的必须在一个对象中,也许重新考虑您的设计。
class Service{
_model1: BehaviorSubject<object>
object1: Observable<object>
_model2: BehaviorSubject<object>
object2: Observable<object>
_model3: BehaviorSubject<object>
object3: Observable<object>
}
或者将大模型的某些部分作为自己的可观察部分提供:
class Service{
_bigModel: BehaviorSubject<object>
bigObject: Observable<object>
object2 = _bigModel.pipe(
map(newBigObject => newBigObject.key1.keya.blabla),
distinctUntilChanged()
)
object3 = _bigModel.pipe(
pluck('key2', 'keya', 'blabla'),
distinctUntilChanged()
)
}