从不同的视图模型同步可观察的简单、干净的方法 [英] Simple, clean way to sync observables from different view models

查看:13
本文介绍了从不同的视图模型同步可观察的简单、干净的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个视图模型,每个视图模型都有一个 observable 属性,表示不同但相似的数据.

Say I have two view models that each have an observable property that represents different, but similar data.

function site1Model(username) {
    this.username = ko.observable(username);
    ....
}

function site2Model(username) = {
    this.username = ko.observable(username);
    ....
}

这些视图模型是独立的,不一定相互链接,但在某些情况下,第三个视图模型会在它们之间创建链接.

These view models are independent and not necessarily linked to each other, but in some cases, a third view model creates a link between them.

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

以下是我想出的一些选项.

Here are some options that I've come up with.

  1. 使用计算出的 observable 读取一个并写入两个:

  1. Use a computed observable that reads one and writes to both:

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
}

只要更改始终通过计算,这将保持值同步.但如果直接更改底层 observable,则不会这样做.

This will keep the values in sync as long as changes always come through the computed. But if an underlying observable is changed directly, it won't do so.

使用 subscribe 方法相互更新:

Use the subscribe method to update each from the other:

site3Model.site1.username.subscribe(function(value) {
    this.site2.username(value);
}, site3Model);
site3Model.site2.username.subscribe(function(value) {
    this.site1.username(value);
}, site3Model);

只要 observables 在值相同时抑制通知,这就会起作用;否则你最终会陷入无限循环.你也可以提前做检查:if (this.site1.username() !== value) this.site1.username(value); 这也有一个问题,observables 必须简单(如果 site1site2 本身是可观察的,它就不会正常工作.

This works as long as the observables suppress notifications when the values are the same; otherwise you'd end up with an infinite loop. You could also do the check earlier: if (this.site1.username() !== value) this.site1.username(value); This also has a problem that the observables have to be simple (it won't work right if site1 and site2 themselves are observables).

使用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 observables,然后使用 this.site1().username(this.site2().username()); 此方法还需要检查相等性以避免无限循环.如果我们不能依赖 observable 来做这件事,我们可以在计算中检查,但会添加另一个对我们正在更新的 observable 的依赖(直到像 observable.peek 可用).这种方法也有一个缺点,即最初运行一次更新代码来设置依赖项(因为这是计算的工作方式).

This format allows us to have other dependencies. For example, we could make site1 and site2 observables and then use this.site1().username(this.site2().username()); This method also requires a check for equality to avoid an infinite loop. If we can't depend on the observable to do it, we could check within the computed, but would add another dependency on the observable we're updating (until something like observable.peek is available). This method also has the downside of running the update code once initially to set up the dependencies (since that's how computed works).

既然我觉得所有这些方法都有缺点,那么有没有另一种方法可以做到这一点,它简单(少于 10 行代码)、高效(不运行不必要的代码或更新)和灵活(处理多个可观察的水平)?

Since I feel that all of these methods have a downside, is there another way to do this that would be simple (less than 10 lines of code), efficient (not run unnecessary code or updates), and flexible (handle multiple levels of observables)?

推荐答案

Ryan 和 John,感谢你们的回答.不幸的是,我真的不想引入发布/订阅系统所需的全局命名系统.

Ryan and John, Thank you both for your answers. Unfortunately, I really don't want to introduce a global naming system that the pub/sub systems require.

Ryan,我同意 subscribe 方法可能是最好的.我已经整理了一组函数来处理订阅.我没有使用扩展,因为我还想处理可观察对象本身可能是动态的情况.这些函数接受 observables 或返回 observables 的函数.如果源 observable 是动态的,我会将访问器函数调用包装在一个计算过的 observable 中,以便订阅一个固定的 observable.

Ryan, I agree that the subscribe method is probably the best. I've put together a set of functions to handle the subscription. I'm not using an extension because I also want to handle the case where the observables themselves might be dynamic. These functions accept either observables or functions that return observables. If the source observable is dynamic, I wrap the accessor function call in a computed observable to have a fixed observable to subscribe to.

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 行有点不合理.:-)

This is about 20 lines, so maybe my target of less than 10 lines was a bit unreasonable. :-)

我修改了 Ryan 的邮箱示例来演示上述功能:http://jsfiddle.net/mbest/vcLFt/

I modified Ryan's postbox example to demonstrate the above functions: http://jsfiddle.net/mbest/vcLFt/

这篇关于从不同的视图模型同步可观察的简单、干净的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆