3

我认为这一定是一种常见的情况,我想知道在 Knockout 中是否有一个公认的如何处理这个问题的约定。您有一个“是-否”下拉菜单(或一对单选按钮),它的默认值是一个空白项(或两个都未选中,对于单选按钮)。用户必须做出选择才能继续。

这并不能完美地映射到模型中的布尔值,因为实际上存在三个可能的值。真假,没有用户选择。在 C# 中,您可能会考虑使用可为的布尔值,而在 Java 中,您可能会使用java.lang.Boolean。在这两种情况下,“null”都可能表示没有用户选择。

JavaScript 没有可空值,但由于它不强制使用变量类型,因此您可以采用特定变量可以为 null、true 或 false 的约定,并以与 C# 或 java 中的可空布尔值类似的方式使用它.lang.布尔值。

首先,存在绑定到布尔值的问题。默认情况下,Knockout 希望所有绑定值都是字符串类型。这在此处此处进行了讨论,RP Niemeyer 提供的解决方案是使用自定义绑定,如下所示:(此示例的链接到 JS Fiddle

ko.bindingHandlers.booleanValue = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function() {
                    return observable().toString();
                },
                write: function(newValue) {
                    observable(newValue === "true");
                }                   
            });

        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

所以我以此为出发点,我想出了这个自定义绑定。它似乎工作。我对社区对此方法的反馈感兴趣。签出jsfiddle 以自己进行试验。这有什么缺点和/或可扩展性问题吗?

ko.bindingHandlers.nullableBooleanValue = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function() {                                           
                    console.log(observable());
                    console.log(typeof(observable()));

                    var result = null;
                    if(observable() === true){
                        result = "true";
                    } else if(observable() === false){
                        result = "false";
                    } else { // Default is null, which represents no user selection
                        result = "null";
                    }

                    console.log("transforming on read:")
                    console.log(typeof(observable()));
                    console.log(observable());
                    console.log(typeof(result));
                    console.log(result);
                    return result;
                },
                write: function(newValue) {
                    var result = null;
                    if(newValue === "true"){
                        result = true;
                    } else if(newValue === "false"){
                        result = false;
                    } else { // Default is null, which represents no user selection
                        result = null;
                    }

                    console.log("transforming on write:")
                    console.log(typeof(newValue));
                    console.log(newValue);
                    console.log(typeof(result));
                    console.log(result);
                    observable(result);
                }                   
            });

        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

var model = {
    state: ko.observable(null)
};

ko.applyBindings(model);
4

1 回答 1

3

好的,扩展器方法并没有按照我想要的方式工作,所以我放弃了它(如果你好奇的话,仍然在编辑历史中)。我修改了您的绑定以使其放置选项,因此您无需在 HTML 中指定它们。您还可以选择指定“Null”选项文本(您可以扩展它以允许设置每个标签)。

此方法让您可以将 observable 视为标准的可空布尔值。这是 HTML(注意,这nullLabel是完全可选的):

<select data-bind="yesNoNull: answer, nullLabel: 'Null' "></select>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>​

这是绑定:

ko.bindingHandlers.yesNoNull = {    
    init: function(element, valueAccessor, allBindingsAccessor) {
        var target = valueAccessor();
        var nullLabel = allBindingsAccessor().nullLabel || "";
        var options = function() { return [ nullLabel, "Yes", "No"]; };
        ko.bindingHandlers.options.update(element, options, allBindingsAccessor);

        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function() {
                    var result = nullLabel;
                    if(observable() === true){
                        result = "Yes";
                    } else if(observable() === false){
                        result = "No";
                    }
                    return result;
                },
                write: function(newValue) {
                    var result = null;
                    if(newValue === "Yes"){
                        result = true;
                    } else if(newValue === "No"){
                        result = false;
                    }
                    observable(result);
                }                   
            });

        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

这是小提琴

于 2012-11-08T19:49:50.180 回答