【发布时间】:2018-02-11 07:42:46
【问题描述】:
很多时候我在淘汰赛中遇到以下情况:
我有一个可观察对象,我想在可观察对象 A 和 B 之间创建一个双向绑定适配器,即如果 A 更改,则更改 B,如果 B 更改,则更改 A。
+-------------+ +-------------+ +--------------+ |一个 | -----> |适配器 | -----> |乙| |可观察 |起初这似乎是一个不应该做的事情,因为这会创建一个循环依赖,但最终这正是当您将 GUI 元素绑定到可观察对象时发生的情况。想象一下,您有一个现有的绑定,但您想更改其绑定结果,而不触及绑定本身。
让我们看一个例子(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
- knockout 抑制再次运行 subscribe for val(循环检测)
这里的结果是,如果你在第一个输入框中输入一个大写字母,它会立即被第二个订阅转换为小写字母,反之亦然。
虽然这在本示例中看起来不错,但它可能会导致很难找到错误。现在解决问题的一个简单方法是,在绑定中设置一个标志,当我们在另一方的更新中时,这将避免更新:
....
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;
});
....
现在,当您更改第二个输入时,它不会“回火”,而只会朝一个方向开火。
最后是我的问题:
适配器是否是无效用例,是否暗示代码存在概念问题?
您将如何防止循环?用我的例子中的标志?也许使用节流?
【问题讨论】:
标签: javascript knockout.js data-binding