2017-09-02 56 views
0

在淘汰赛很多时候我都在下面的情况:KnockoutJS - 双向绑定适配器 - 避免循环

我有一个观察的,我想创造可观A和B之间的双向绑定适配器,也就是如果A的变化,改变B和当B的变化,然后改变A.

 
     +-------------+   +-------------+   +-------------+ 
     |  A  | -----> | Adapter | -----> |  B  | 
     | Observable | <----- |    | <----- | Observable | 
     +-------------+   +-------------+   +-------------+ 

起初,这似乎是一个不这样做,因为这将创建一个循环依赖,但最终这究竟会发生什么时您将GUI元素绑定到observable。想象一下,你有一个现有的绑定,但你想改变它的绑定结果,而不必触摸绑定本身。

让我们看一个例子(jsfiddle here):

HTML:

<body> 
    <p data-bind="text: 'Value:' + val()"></p> 
    <input type="text" data-bind="textInput: val"></input> 
    <p data-bind="text: 'Value 2:' + val2()"></p> 
    <input type="text" data-bind="textInput: val2"></input> 
</body> 

的Javascript:

function ViewModel() { 
    var self = this; 

    this.val = ko.observable(""); 
    this.val2 = ko.observable(""); 

    this.val.subscribe(function() { 
    console.log("VAL Changed!"); 
    self.val2(self.val().toUpperCase()); 
    }); 

    this.val2.subscribe(function() { 
    console.log("VAL2 Changed!"); 
    self.val(self.val2().toLowerCase()); 
    }); 
} 

ko.applyBindings(new ViewModel()); 

你会发现,当你在输入的东西第一个文本框一个周期被触发:

  • 的结合变化VAL
  • 的订阅VAL火灾和变化VAL2
  • 的订阅VAL2火灾和改变VAL
  • 敲除surpresses运行认购VAL再次(周期检测)

结果是,如果您在第一个输入框中键入一个大写字母,它将立即转换为小写字母,由第二个订阅,反之亦然。

虽然这个例子看起来不错,但它可能会导致很难找到错误。要解决现在的问题是一个简单的方法,有一个标志里面的绑定,这将避免升级时,我们是对方的更新里面:

jsfiddle here

.... 
var flag = false; 
    this.val.subscribe(function() { 
    if (flag) return; 
    flag = true; 
    self.val2(self.val().toUpperCase()); 
    flag = false; 
    }); 

    this.val2.subscribe(function() { 
    if (flag) return; 
    flag = true; 
    self.val(self.val2().toLowerCase()); 
    flag = false; 
    }); 
    .... 

现在当你改变第二个输入时,它不会“回火”,而只会朝一个方向发射。

现在终于我的问题:

  • 是适配器无效用例和它暗示与代码的概念问题?

  • 你会如何去防止气旋?有一个像我的例子中的国旗?也许使用throtteling?

回答

1

你有什么是一个实际的数据项目和两个计算器,以呈现为大写或小写。诀窍是计算结果需要可写。他们的写函数可以直接写入底层数据项,因为它的情况并不重要。

function ViewModel() { 
 
    var self = this; 
 

 
    this.val = ko.observable(""); 
 

 
    this.val1 = ko.pureComputed({ 
 
    read: function() { 
 
     return self.val().toLowerCase(); 
 
    }, 
 
    write: function(newVal) { 
 
     self.val(newVal); 
 
    } 
 
    }); 
 
    this.val2 = ko.pureComputed({ 
 
    read: function() { 
 
     return self.val().toUpperCase(); 
 
    }, 
 
    write: function(newVal) { 
 
     self.val(newVal); 
 
    } 
 
    }); 
 

 
    this.val1.subscribe(function() { 
 
    console.log("VAL Changed!"); 
 
    }); 
 

 
    this.val2.subscribe(function() { 
 
    console.log("VAL2 Changed!"); 
 
    }); 
 
} 
 

 
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> 
 

 
<body> 
 
    <p data-bind="text: 'Value:' + val1()"></p> 
 
    <input type="text" data-bind="textInput: val1" /> 
 
    <p data-bind="text: 'Value 2:' + val2()"></p> 
 
    <input type="text" data-bind="textInput: val2" /> 
 
</body>

+0

这不能解决循环依赖的影响。如果你运行你的代码片段,你会意识到,当输入一个小写字母到第二个输入时,它将立即被第一次订阅反射转换为大写字母。 –

+0

订阅不做任何转换。没有周期。当底层数据项发生变化时,两个计算的“读取”值都会改变,就这些了。 –

+0

对不起 - 我没有花时间理解你的解决方案。所以你说有些情况下,“适配器”的两端只代表“一个”值。上述解决方案有所不同,它允许适配器的两端具有不同的状态,而在您的情况下,适配器的两端都只依赖一个状态“val”。这似乎比我更清洁,比马修斯的解决方案。你认为有可能出现两种状态的解决方案更适用的情况吗?我的大脑无法完全围绕它。 –

1

我在类似的情况下使用了下面的方法。我认为它很干净,你不必担心用标志管理变量的状态。

基本上,使用可写的observables,并为每个相关项目创建一个支持observable来存储它的状态,然后使用可写的observable来处理逻辑关于当其他可观察值应该发生什么变化。

所以,你的视图模型应该是这样的:

function ViewModel() { 
    var self = this; 

    self.val = ko.observable(""); 
    self.val2 = ko.observable(""); 

    self.valComputed = ko.pureComputed({ 
    read: function() { return self.val(); }, 
    write: function (value) { 
     self.val(value); 
     self.val2(value.toUpperCase()); 
    } 
    }); 

    self.val2Computed = ko.pureComputed({ 
    read: function() { return self.val2(); }, 
    write: function (value) { 
     self.val2(value); 
     self.val(value.toLowerCase()); 
    } 
    }); 
} 

ko.applyBindings(new ViewModel()); 

而且你会改变你的HTML绑定到计算的观测,如:

<body> 
    <p data-bind="text: 'Value:' + valComputed()"></p> 
    <input type="text" data-bind="textInput: valComputed"></input> 
    <p data-bind="text: 'Value 2:' + val2Computed()"></p> 
    <input type="text" data-bind="textInput: val2Computed"></input> 
</body> 

我希望帮助! :-)

+0

啊哈我的脑袋疼... :-)行为实际观测的差异将是写val2Computed不会在其自身价值得到体现。你认为这将是保存添加'self.val2(价值);'到val2Computed的写处理函数,也是'self.val(value);'写入val处理器的写处理程序,以确保计算出的结果与真实可观察结果相似?看[这个小提琴](https://jsfiddle.net/domoran/L28p2ssd) –

+0

啊,当然。接得好!你一定要存储正在设置的observable的值。我编辑它,添加在这些行中。 –