我有一个带选项和默认文本的选择器元素:
self._selected = ko.observable();
self.option = ko.computed({
read:function(){
return self._selected;
},
write: function(data){
if(data){
if(confirm('are you sure?')){
self._selected(data);
}else{
//reset
}
}
}
});
<select data-bind="options: options, value:option, optionsCaption: 'choose ...'"></select>
这个问题:
它应该是“选择......”
jsbin here,仅在镀铬上进行了测试
这里存在不对称性:
当您更改选择框的值时,DOM会立即更新并随后进行淘汰(当然,淘汰赛取决于DOM更改事件)。因此,当您的代码询问“您确定吗?”时,DOM已经具有新值。
现在,当您不将该值写入绑定到value:
的observable时,viewmodel的状态不会更改。并且当可观察到的更改时,knockout仅更新DOM。因此DOM保持在选定的值,并且viewmodel中的绑定值是不同的。
最简单的方法是将旧值保存在变量中,始终将新值写入observable,如果用户单击“否”,则只需恢复旧值。这样就破坏了不对称性,DOM和视图模型保持同步。
var AppData = function(params) {
var self = {};
var selected = ko.observable();
self.options = ko.observableArray(params.options);
self.option = ko.computed({
read: selected,
write: function(value) {
var oldValue = selected();
selected(value);
if (value !== oldValue && !confirm('are you sure?')) {
selected(oldValue);
}
}
});
return self;
};
// ----------------------------------------------------------------------
ko.applyBindings(new AppData({
options: ['one','two','three']
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="options: options, value: option, optionsCaption: 'Select...'"></select>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
这是一个淘汰扩展器的完美候选者,要求确认价值变化。这样我们就可以将它重用于不同的observable并保持viewmodel的清洁。
ko.extenders.confirmChange = function (target, message) {
return ko.pureComputed({
read: target,
write: function(newValue) {
var oldValue = target();
target(newValue);
if (newValue !== oldValue && !confirm(message)){
target(oldValue);
}
}
});
};
// ----------------------------------------------------------------------
var AppData = function(params) {
var self = this;
self.options = ko.observableArray(params.options);
self.option = ko.observable().extend({confirmChange: 'are you sure?'});
};
// ----------------------------------------------------------------------
ko.applyBindings(new AppData({
options: ['one','two','three']
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="options: options, value: option, optionsCaption: 'Select...'"></select>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
问题是底层变量的值没有变化,因此没有事件告诉Knockout它的value
与viewmodel不同步。
使用正常的可观察量,你可以调用valueHasMutated
来表示发生了一些隐匿性变化,但计算结果似乎没有。但他们确实有notifySubscribers
。事实上,你的例子非常像this example in the docs。
这是一个有效的例子:
function vm() {
const self = {};
self.options = ko.observableArray(['one', 'two', 'three']);
self._selected = ko.observable();
self.option = ko.pureComputed({
read: self._selected,
write: function(data) {
if (data) {
if (confirm('are you sure?')) {
self._selected(data);
} else {
self.option.notifySubscribers(self._selected());
}
}
}
});
return self;
}
ko.applyBindings(vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="options: options, value:option, optionsCaption: 'choose ...'"></select>
<div data-bind="text:_selected"></div>
<div data-bind="text:option"></div>