Checkbox Knockout.js可写计算布尔可观察绑定到复选框-不可靠属性设置

Checkbox Knockout.js可写计算布尔可观察绑定到复选框-不可靠属性设置,checkbox,knockout.js,computed-observable,Checkbox,Knockout.js,Computed Observable,我有一个复选框,它以前直接绑定到视图模型上的可观察属性。我在所有视图模型上使用一个通用脏标志,它监视视图模型上所有可观察的属性的变化 现在,对于一个属性,如果用户在取消设置基础可观察对象之前尝试取消选中复选框,我希望提供一个确认警报。我尝试将其实现为一个可写的计算可观察对象,它将可观察对象包装为: function VM() { var self = this; self.SelectedInternal = ko.observable(false); self.se

我有一个复选框,它以前直接绑定到视图模型上的可观察属性。我在所有视图模型上使用一个通用脏标志,它监视视图模型上所有可观察的属性的变化

现在,对于一个属性,如果用户在取消设置基础可观察对象之前尝试取消选中复选框,我希望提供一个确认警报。我尝试将其实现为一个可写的计算可观察对象,它将可观察对象包装为:

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中)看到的是,当我如上所述将SelectedInternalObservable值默认为false时,“selected”write函数仅在我选中复选框时触发,而不是在我取消选中它时触发。如果我将SelectedInternalObservable值默认为true,那么在我第一次取消选中它时,write setter将执行,但不会在随后取消选中时执行

这里有一把小提琴来演示:

这里发生了什么?有没有更好的方法来实现这一点


更新:这种方法可能无论如何都不会起作用,因为我无法将钩子插入原始的单击以返回false,如果用户在确认框中选择了Cancel,则将observable重置为true似乎不会生效。但我仍然想知道为什么计算setter本身的行为不符合预期

当read函数的值更改时,将调用write函数。参见文档中的示例#1

我在write函数中做的一件事是设置其他可观察的值。例如,选中一个复选框可清除组中的所有其他复选框。我通过更新write函数中的观察值来实现这一点


编辑

我把一把小提琴放在一起,展示如何做上一段描述的事情-。使其工作的计算函数是

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 若要回答有关希望手动确认从false到true的已检查状态问题的评论,请执行以下操作

处理此问题的最干净方法是使用自定义活页夹。密钥部分正在注册一个自定义的已更改事件处理程序。在该函数内部,您可以询问用户是否真的希望将复选框设置为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);
    }
};

更新的fiddle位于

我迄今为止已经实现了这一点(为简单起见重命名了属性):


当您选择“确定”取消选中时,复选框(已被单击清除)会短暂地再次显示为选中,然后最终取消选中,这有点小瑕疵。但是在确认正确之前,防止可观察到的变化的行为。

我遇到了同样的问题,尽管在不同的情况下。(我试图使用复选框对列表进行大规模更新)在查看了这里的答案后,我决定放弃计算的选中绑定,而是使用可观察绑定和订阅来进行更新:

$(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);
});
$(文档).ready(函数(){
函数VM(){
var self=这个;
self.items=ko.array([
{名称:“Foo”,活动:ko.observable(true)},
{name:“Bar”,活动:ko.observable(true)},
{名称:“Bas”,活动:ko.observable(true)}
]);
self.allActive=ko.可观察(真);
self.allActive.subscribe(函数(值){
if(自激活取消){
self.allactiveconceled=false;
返回;
}
如果(!确认('真的?')){
setTimeout(函数(){
self.allactiveconceled=true;
自激活(!值);
}, 1);
返回;
}
var items=self.items();
对于(变量i=0,l=items.length;i
下面是对相关标记的修改:


setTimeout和cancelled标志感觉有点像黑客,但它完成了任务。

我还建议使用自定义的敲除绑定,但请确保重新使用/继承完整的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);
    }
};

谢谢,但是你能用那把小提琴来演示一下你是如何用这种技术实现确认警报的吗?我一直在尝试一个单独的点击绑定和选中绑定,但我还没能把它做好。谢谢,但这并不能解决我的情况-我只有一个复选框,当该复选框未选中时,我想在更改基础的可观察对象之前寻求确认。请参阅我的答案。我愿意接受任何更好的方法:-)这是一种方法。在这种情况下,自定义绑定将更好地工作。我至少有一个部分有效的答案。我需要多花点时间,我得去上班了。今天晚些时候我会给你一个工作样品。
$(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);
});
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);
    }
};