在淘汰赛中很多次我遇到以下情况:
我有一个observable,我想在observables 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());
您会注意到,当您在第一个文本框中键入内容时,会触发一个循环:
结果是,如果您在第一个输入框中键入一个大写字母,它将立即转换为小写字母,由第二个订阅,反之亦然。
虽然在这个例子中看起来很不错,但它可能导致很难找到错误。解决问题的一个简单方法是,在绑定中有一个标志,当我们进入另一方的更新时,这将避免更新:
....
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;
});
....
现在当你改变第二个输入时,它不会&#34;反击&#34;但只能向一个方向射击。
现在终于我的问题了:
适配器是否是一个无效的用例,是否暗示了代码的概念性问题?
你会如何防止这种情况发生?有一个像我的例子中的旗帜?也许使用throtteling?
答案 0 :(得分:1)
我在类似的情况下使用了以下方法。我认为它很干净,您不必担心使用标志来管理变量的状态。
基本上,使用可写的observable,并为每个相互依赖的项创建一个后备observable来存储它的状态,然后使用writeable observable来处理逻辑,当该值发生变化时,对于其他observable会发生什么
所以,你的viewmodel看起来像这样:
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以绑定到计算的observable,如下所示:
<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>
我希望有所帮助! : - )
答案 1 :(得分: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>