假设我有两个视图模型,每个模型都有一个可观察的属性,表示不同但相似的数据。
function site1Model(username) {
this.username = ko.observable(username);
....
}
function site2Model(username) = {
this.username = ko.observable(username);
....
}
这些视图模型是独立的,不一定相互关联,但在某些情况下,第三个视图模型会在它们之间创建链接。
function site3Model(username) = {
this.site1 = new site1Model(username);
this.site2 = new site2Model(username);
// we now need to ensure that the usernames are kept the same between site1/2
...
}
以下是我提出的一些选项。
使用读取一个并写入两者的计算observable:
site3Model.username = ko.computed({
read: function() {
return this.site1.username(); // assume they are always the same
},
write: function(value) {
this.site1.username(value);
this.site2.username(value);
},
owner: site3Model
}
只要更改始终通过计算,这将使值保持同步。但是如果直接改变了潜在的可观察性,它就不会这样做。
使用subscribe
方法更新彼此:
site3Model.site1.username.subscribe(function(value) {
this.site2.username(value);
}, site3Model);
site3Model.site2.username.subscribe(function(value) {
this.site1.username(value);
}, site3Model);
只要observable在值相同时抑制通知,这就有效;否则你最终会得到一个无限循环。您也可以在之前进行检查:if (this.site1.username() !== value) this.site1.username(value);
这也存在一个问题,即观察者必须简单(如果site1
和site2
本身是可观察的,它将无法正常工作)。 / p>
使用computed
进行订阅和更新:
site3Model.username1Updater = ko.computed(function() {
this.site1.username(this.site2.username());
}, site3Model);
site3Model.username2Updater = ko.computed(function() {
this.site2.username(this.site1.username());
}, site3Model);
此格式允许我们拥有其他依赖项。例如,我们可以创建site1
和site2
observable,然后使用this.site1().username(this.site2().username());
此方法还需要检查相等性以避免无限循环。如果我们不能依赖于observable这样做,我们可以在计算中检查,但会在我们正在更新的observable上添加另一个依赖项(直到observable.peek
之类的东西可用)。
这个方法在初始运行更新代码时也有一个缺点,就是设置依赖项(因为这就是computed
的工作方式)。
因为我觉得所有这些方法都有缺点,是否有另一种方法可以做到这一点很简单(少于10行代码),高效(不运行不必要的代码或更新),以及灵活(处理多个方法)可观察的水平)?
答案 0 :(得分:7)
这不完全是10行代码(尽管你可以根据自己的喜好去掉它),但是在这种情况下我在视图模型之间使用了pub / sub消息。
这是我为它写的一个小型图书馆:https://github.com/rniemeyer/knockout-postbox
基本思路是创建ko.subscribable
并使用基于主题的订阅。该库扩展了可订阅的内容,以添加subscribeTo
,publishOn
和syncWith
(对主题进行发布和订阅)。这些方法将为observable设置正确的订阅,以自动参与此消息传递并与主题保持同步。
现在,您的视图模型不需要彼此直接引用,并且可以通过pubsub系统进行通信。您可以在不破坏任何内容的情况下重构视图模型。
就像我说的那样你可以把它剥离到少于10行的代码。该库只增加了一些额外功能,例如能够取消订阅,能够控制发布实际发生的时间(equalityComparer),并且您可以指定转换以在传入值上运行。
随时发布任何反馈。
答案 1 :(得分:5)
subscribe
方法可能是最好的方法。我已经整理了一组函数来处理订阅。我没有使用扩展,因为我还想处理observable本身可能是动态的情况。这些函数接受可观察对象或返回可观察对象的函数。如果source observable是动态的,我将accessor函数调用包装在一个计算的observable中,以便有一个固定的observable来订阅。
function subscribeObservables(source, target, dontSetInitially) {
var sourceObservable = ko.isObservable(source)
? source
: ko.computed(function(){ return source()(); }),
isTargetObservable = ko.isObservable(target),
callback = function(value) {
var targetObservable = isTargetObservable ? target : target();
if (targetObservable() !== value)
targetObservable(value);
};
if (!dontSetInitially)
callback(sourceObservable());
return sourceObservable.subscribe(callback);
}
function syncObservables(primary, secondary) {
subscribeObservables(primary, secondary);
subscribeObservables(secondary, primary, true);
}
这是大约20行,所以也许我的目标不到10行有点不合理。 : - )
我修改了Ryan的邮箱示例以演示上述功能:http://jsfiddle.net/mbest/vcLFt/
答案 2 :(得分:1)
另一种选择是创建一个隔离的datacontext来维护可观察的模型。 viewmodels都会查看datacontext的数据并引用相同的对象,所以当一个更新时,它们都会这样做。 VM的依赖关系在datacontext上,但不在其他VM上。我最近一直这样做,效果很好。虽然,它比使用pub / sub要复杂得多。
如果你想要简单的pub / sub,你可以使用他提到的Ryan Niemyer的库,或者使用内置了pub / sub消息(基本上是信使或事件聚合器)的amplify.js。两者都是轻量级的并且是解耦的。
答案 3 :(得分:0)
如果有人需要。 另一种选择是创建一个引用对象/ observable。 这也处理包含多个observable的对象。
(function(){
var subscriptions = [];
ko.helper = {
syncObject: function (topic, obj) {
if(subscriptions[topic]){
return subscriptions[topic];
} else {
return subscriptions[topic] = obj;
}
}
};
})();
在您的视图模型中。
function site1Model(username) {
this.username = syncObject('username', ko.observable());
this.username(username);
....
}
function site2Model(username) = {
this.username = syncObject('username', ko.observable());
this.username(username);
....
}