我有一个复选框,以前直接绑定到我的视图模型上的一个observable属性。我在所有视图模型上使用通用脏标志,该模型监视视图模型上的所有可观察属性以进行更改。
现在,对于一个属性,如果用户在取消设置基础observable之前尝试取消选中复选框,我希望提供确认提醒。我试图将它实现为一个可写的计算observable,它包含了observable,如下所示:
function VM() {
var self = this;
self.SelectedInternal = ko.observable(false);
self.selected= ko.computed({
read: function () {
return self.SelectedInternal();
},
write: function (value) {
alert('Clicked'); // To demonstrate problem
if (value === false) {
if (confirm('Are you sure you wish to deselect this option?')) {
self.SelectedInternal(value);
}
}
}
});
}
var vm = new VM();
ko.applyBindings(vm);
我所看到的(在Firefox和IE中)是当我将SelectedInternal可观察值默认为如上所述时,“选中”写入功能仅在我每次选中复选框时触发,而不是在我取消选中时触发它。如果我将SelectedInternal observable值默认为true,那么第一次取消选中它时,写入setter会执行,但不会在后续的unchecks中执行。
这是一个小提琴演示:
这里发生了什么?有没有更好的方法来实现这一目标?
更新:这种方法可能无论如何都不会起作用,因为如果用户在确认框中选择取消,我无法获取原始点击的钩子以返回false,并将observable重置为true似乎生效了。但是我仍然想知道为什么计算的setter本身没有按预期运行。
答案 0 :(得分:2)
到目前为止我已经实现了这一点(为简单起见,属性重命名):
<input type="checkbox" data-bind="checked: vm.optionComputed, click: vm.toggleOption" />
在视图模型中:
self.optionComputed = ko.computed({
read: function () {
return self.Option();
},
write: function (value) {
}
});
self.toggleOption = function (vm, event) {
var checkBox = $(event.target);
var isChecked = checkBox.is(':checked');
if (!(!isChecked && !confirm('Are you sure?'))) {
self.Option(isChecked);
}
};
当您选择“确定”取消选中时,会出现轻微的故障,在最终取消选中之前,复选框(已经被点击清空)会再次短暂显示。但是在防止可观察的改变直到确认之前的行为是正确的。
答案 1 :(得分:1)
查看http://knockoutjs.com/documentation/computedObservables.html处的文档。
当读取函数的值更改时,将调用write函数。请参阅文档中的示例#1。
我在write函数中做的一件事是设置其他可观察值。例如,一个复选框可清除组中的所有其他组。我这样做只是更新write函数中的那些observable。
修改强> 的
我已经把一个小提琴放在一起,展示了如何做到最后一段所描述的内容 - http://jsfiddle.net/photo_tom/xHqsZ/22/。使其工作的计算函数是
self.clearAll = ko.computed({
read: function() {
return !(self.option1() || self.option2() || self.option3());
},
write: function(value) {
alert('Clicked');
self.option1(false);
self.option2(false);
self.option3(false);
}
});
编辑#2 回答有关想要手动确认错误到真实检查状态问题的评论。
处理此问题最干净的方法是使用自定义活页夹。关键部分是注册自定义更改的事件处理程序。在该函数内部,您可以询问用户是否想要将复选框设置为true。
ko.bindingHandlers.checkConfirm = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
//handle the field changing
ko.utils.registerEventHandler(element, "change", function() {
// get the current observable value.
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
if (!valueUnwrapped && element.checked) {
var r = confirm("Confirm Setting value to true");
if (r) {
value(true);
return;
} else {
// not okayed, so clear cb.
element.checked = false;
}
}
value(false);
});
},
update: function(element, valueAccessor) {
// use default ko code to update checkbox
ko.bindingHandlers.checked.update(element, valueAccessor);
}
};
答案 2 :(得分:0)
我遇到了同样的问题,尽管情况不同。 (我试图使用复选框对列表进行批量更新。)在查看答案后,我决定放弃计算的检查绑定,而是使用了一个observable进行绑定,并订阅了我的更新:
$(document).ready(function() {
function VM() {
var self = this;
self.items = ko.observableArray([
{ name: "Foo", active: ko.observable(true) },
{ name: "Bar", active: ko.observable(true) },
{ name: "Bas", active: ko.observable(true) }
]);
self.allActive = ko.observable(true);
self.allActive.subscribe(function(value) {
if(self.allActiveCanceled) {
self.allActiveCanceled = false;
return;
}
if(!confirm('Really?')) {
window.setTimeout(function() {
self.allActiveCanceled = true;
self.allActive(!value);
}, 1);
return;
}
var items = self.items();
for(var i = 0, l = items.length; i < l; i++) {
items[i].active(value);
}
});
self.allActiveCanceled = false;
}
var vm = new VM();
ko.applyBindings(vm);
});
以下是相关标记的小提琴:http://jsfiddle.net/makeblake/dWNLA/
setTimeout和cancel标志感觉有点像黑客,但它完成了工作。
答案 3 :(得分:0)
我还建议使用自定义的敲除绑定,但请确保重新使用/继承完整的ko.bindingHandlers.checked绑定功能,以便从其旧版浏览器的兼容性处理中获益:
ko.bindingHandlers.confirmedChecked = {
'after': ['value', 'attr'],
'init': function (element, valueAccessor, allBindings)
{
ko.utils.registerEventHandler(
element,
'click',
function(event)
{
if (
element.checked &&
!confirm('Are you sure you want to enable this setting?')
)
{
if (event.stopImmediatePropagation)
{
event.stopImmediatePropagation();
}
element.checked = false;
}
}
);
ko.bindingHandlers.checked.init(element, valueAccessor, allBindings);
}
};