简单,干净的方式同步来自不同视图模型观测(Simple, clean way to sync obs

2019-06-24 22:46发布

说我有两个视图模型,每有一个观察的属性,代表着不同的,但类似的数据。

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
    ...
}

这里是我想出来的一些选项。

  1. 使用计算观察到的,上面写着一个和写入两个:

     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 } 

    这将保持值保持同步,只要改变总是通过计算。 但是,如果潜在的可观察到的直接改变,也不会这么做。

  2. 使用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); 这也有可观测量必须是简单的一个问题(它不会正确的,如果工作site1site2本身是观测)。

  3. 使用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); 

    这种格式可以让我们有其他的依赖关系。 例如,我们可以使site1site2观测,然后用this.site1().username(this.site2().username()); 这种方法也需要平等的,以避免无限循环的检查。 如果我们不能依赖于观察的做到这一点,我们可以内计算出的检查,但将再增加依赖于观察到的我们正在更新(到类似observable.peek可用)。 这种方法也有运行更新的代码,一旦最初设置的依赖关系(因为这是怎样的负面computed作品)。

因为我觉得所有的这些方法都有一个缺点,有另一种方式来做到这一点,这将是简单的(少于10行代码),高效(不运行不必要的代码或更新),灵活(处理可观的多层次)?

Answer 1:

这不是代码完全10行(虽然你可以剥离下来根据自己的喜好),但我用这种状况视图模型之间的pub / sub消息。

下面是我写的是一个小型图书馆: https://github.com/rniemeyer/knockout-postbox

其基本思想就是创建一个ko.subscribable和使用基于主题的订阅。 库扩展subscribables添加subscribeTopublishOnsyncWith (发布和订阅一个主题)。 这些方法将建立正确的预订可观察到自动参加此消息,并保持与主题同步。

现在,您的视图模型并不需要有相互的直接引用,并可以通过PubSub的系统进行通信。 你可以重构你的视图模型不破坏任何东西。

就像我说的,你可以剥夺它下降到不到10行的代码。 图书馆只是增加了一些额外的喜欢能够退订,能够有发布时实际发生的事情(equalityComparer)在控制,并且可以指定一个变换对传入值运行。

随意张贴任何反馈。

这是一个基本的例子: http://jsfiddle.net/rniemeyer/mg3hj/



Answer 2:

瑞恩和约翰,感谢你们为您解答。 不幸的是,我真的不希望引入的发布/订阅系统需要一个全球性的命名系统。

瑞安,我同意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/



Answer 3:

另一种选择是创建一个孤立的datacontext维持可观的车型。 在所有的ViewModels看到DataContext为他们的数据和引用相同的对象,所以当一个更新,他们都做。 虚拟机的依赖是DataContext的,但不能在其他虚拟机。 我最近一直在做这一点,它一直运作良好。 虽然,它是比使用的pub / sub更加复杂。

如果你想简单的pub / sub,你可以使用他提到瑞安Niemyer的图书馆,或者使用内置的amplify.js已发布/订阅消息(基本上是一个信使或事件聚合器)。两者都是轻量级和分离。



Answer 4:

如果有人需要。 另一个选择是创建一个参考对象/可观察的。 这也处理一个包含多个观察的对象。

(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);
    ....
}


文章来源: Simple, clean way to sync observables from different view models