2

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

现在对于一个属性,如果用户在取消设置底层可观察对象之前尝试取消选中复选框,我希望显示确认警报。我试图将其实现为一个可写的计算 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 可观察值默认为 false 时,“selected”写入函数仅在我每次选中复选框时触发,而不是在我取消选中它时触发。如果我将 SelectedInternal 可观察值默认为 true,那么我第一次取消选中它时 write setter 会执行,但不会在后续取消选中时执行。

这是一个要演示的小提琴:

http://jsfiddle.net/xHqsZ/18/

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

更新:这种方法可能无论如何都行不通,因为我无法钩入原始点击以返回 false,并且如果用户在确认框中选择取消,则将 observable 重置为 true 似乎没有采取影响。但我仍然想知道为什么计算的 setter 本身没有按预期运行。

4

4 回答 4

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);
    }    
};

当您选择“确定”取消选择时,有一个轻微的故障,复选框(已被单击清除)在最终取消选中之前再次短暂地显示为选中状态。但是在确认正确之前防止可观察到的变化的行为。

于 2012-12-12T02:41:56.003 回答
1

查看http://knockoutjs.com/documentation/computedObservables.html上的文档。

当读取函数的值发生更改时,将调用写入函数。请参阅文档中的示例 #1。

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


编辑

我整理了一个小提琴,展示了如何做上一段中描述的事情 - 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 回答关于想要手动确认 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);
    }
};

更新的小提琴位于http://jsfiddle.net/photo_tom/xHqsZ/32/

于 2012-12-10T01:00:40.850 回答
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);
    }
};
于 2014-06-21T18:50:52.207 回答
0

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

$(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 和 cancelled 标志感觉有点像 hack,但它完成了工作。

于 2013-04-11T19:52:16.083 回答