说我有两个视图模型,每有一个观察的属性,代表着不同的,但类似的数据。
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
...
}
这里是我想出来的一些选项。
使用计算观察到的,上面写着一个和写入两个:
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);
作为观测抑制通知当值相同此作品为长; 否则你会最终获得一个无限循环。 你也可以做检查早: if (this.site1.username() !== value) this.site1.username(value);
这也有可观测量必须是简单的一个问题(它不会正确的,如果工作site1
和site2
本身是观测)。
使用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
观测,然后用this.site1().username(this.site2().username());
这种方法也需要平等的,以避免无限循环的检查。 如果我们不能依赖于观察的做到这一点,我们可以内计算出的检查,但将再增加依赖于观察到的我们正在更新(到类似observable.peek
可用)。 这种方法也有运行更新的代码,一旦最初设置的依赖关系(因为这是怎样的负面computed
作品)。
因为我觉得所有的这些方法都有一个缺点,有另一种方式来做到这一点,这将是简单的(少于10行代码),高效(不运行不必要的代码或更新),灵活(处理可观的多层次)?
这不是代码完全10行(虽然你可以剥离下来根据自己的喜好),但我用这种状况视图模型之间的pub / sub消息。
下面是我写的是一个小型图书馆: https://github.com/rniemeyer/knockout-postbox
其基本思想就是创建一个ko.subscribable
和使用基于主题的订阅。 库扩展subscribables添加subscribeTo
, publishOn
和syncWith
(发布和订阅一个主题)。 这些方法将建立正确的预订可观察到自动参加此消息,并保持与主题同步。
现在,您的视图模型并不需要有相互的直接引用,并可以通过PubSub的系统进行通信。 你可以重构你的视图模型不破坏任何东西。
就像我说的,你可以剥夺它下降到不到10行的代码。 图书馆只是增加了一些额外的喜欢能够退订,能够有发布时实际发生的事情(equalityComparer)在控制,并且可以指定一个变换对传入值运行。
随意张贴任何反馈。
这是一个基本的例子: http://jsfiddle.net/rniemeyer/mg3hj/
瑞恩和约翰,感谢你们为您解答。 不幸的是,我真的不希望引入的发布/订阅系统需要一个全球性的命名系统。
瑞安,我同意subscribe
方法可能是最好的。 我已经把一组函数来处理订阅。 我没有使用延长 ,因为我也想处理这种情况的观本身可能是动态的情况。 这些函数接受返回观测无论是可观测或功能。 如果源可观察到的是动态的,我包存取器函数调用在计算可观察到有一个固定的可观察到的订阅。
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/
另一种选择是创建一个孤立的datacontext维持可观的车型。 在所有的ViewModels看到DataContext为他们的数据和引用相同的对象,所以当一个更新,他们都做。 虚拟机的依赖是DataContext的,但不能在其他虚拟机。 我最近一直在做这一点,它一直运作良好。 虽然,它是比使用的pub / sub更加复杂。
如果你想简单的pub / sub,你可以使用他提到瑞安Niemyer的图书馆,或者使用内置的amplify.js已发布/订阅消息(基本上是一个信使或事件聚合器)。两者都是轻量级和分离。
如果有人需要。 另一个选择是创建一个参考对象/可观察的。 这也处理一个包含多个观察的对象。
(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);
....
}