将正确/错误绑定到Knockout JS中的单选按钮


84

在我的视图模型中,我有一个IsMale值,该值的值为true或false。

在我的用户界面中,我希望将其绑定到以下单选按钮:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
</label>

我认为的问题是checked期望一个字符串“ true” /“ false”。所以我的问题是,如何获得带有此UI和模型的2向绑定?


10
对于Knockout版本> = 3.0,请参阅Natan的答案,以得到比接受的答案建议的更简单的姿势。
Markus Pscheidt 2014年

Answers:


81

一种选择是使用可写的可计算的observable

在这种情况下,我认为一个不错的选择是使可写的可计算的可观察对象成为可观察对象的“子可观察对象” IsMale。您的视图模型如下所示:

var ViewModel = function() {
   this.IsMale = ko.observable(true);

   this.IsMale.ForEditing = ko.computed({
        read: function() {
            return this.IsMale().toString();  
        },
        write: function(newValue) {
             this.IsMale(newValue === "true");
        },
        owner: this        
    });          
};

您可以将其绑定在您的UI中,如下所示:

<label>Male
   <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale.ForEditing"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale.ForEditing"/>
</label>

范例:http//jsfiddle.net/rniemeyer/Pjdse/


似乎是一个很好的解决方案。我使用映射插件刷新虚拟机。您知道它会消灭IsMale上的ForEditing吗?
CJ 2012年

26
这是将计算出的可观察创建项推送到自定义绑定中的一种替代方法,这样可以使您不必担心在映射插件中处理它:jsfiddle.net/rniemeyer/utsvJ
RP Niemeyer

2
+1以使用绑定而不是在每个对象上创建可观察对象
Greg Ennis

1
@RPNiemeyer绝对是必经之路。
安德鲁

1
@RPNiemeyer谢谢!评论中的小提琴是对我有用的。
SFlagg

128

我知道这是一个旧线程,但是我遇到了同样的问题,并且找到了一个更好的解决方案,该问题可能在正式回答此问题后被添加到淘汰赛中,所以我将其留给同样问题的人使用。

当前,不需要扩展程序,自定义绑定处理程序或计算机。只需提供“ checkedValue”选项,它将使用该选项而不是html的“ value”属性,并且您可以通过该属性传递任何javascript值。

<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/>
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>

要么:

<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 1"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 2"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 3"/>

2
从源头看,似乎决定在此处使用检查的值。如果输入是单选或复选框(其值为数组),则将'useCheckedValue'设置为true 。另外,我正在使用Knockout 3.0。看看是否有帮助。
纳丹2014年

1
是的,谢谢,我已经升级到3.0.0,现在就可以了。仍然需要将我的布尔值包装在String中,因为服务器代码需要这些值;-)
ZiglioUK 2014年

好信息。还有两点:(1)我发现在v3.0之前的版本中,我能够使用value绑定,然后使用checked绑定(按该顺序),但是对于3.0,我不得不使用checkedValue绑定而不是value绑定。(2)预V3.0是挑剔的要求value结合先于checked正确结合的功能,所以我有一种感觉,事情也可能在所有情况下更好地工作,在V3.0如果你把checkedValue结合之前的checked结合,像它们显示在文档中
杰森·弗兰克

@ZiglioNZ失败的一种方法是,如果您的checked设置者(或可能依赖于它的东西)引发错误-然后未选中正确的复选框
Simon_Weaver 2014年

我正在使用3.4版,发现我仍然必须输入value =“ true”和上面的内容才能使其正常工作。
wirble

11

这对我有用:

http://jsfiddle.net/zrBuL/291/

<label>Male
   <input type="radio" name="IsMale" value="1" data-bind="checked:IsMale"/>
</label> 
<label>Female
   <input type="radio" name="IsMale" value="0" data-bind="checked:IsMale"/>
</label>

我看到的唯一问题是,在序列化视图模型以传递给服务器时,您将获得可观察的整数(而不是布尔值)。您将需要vm.IsMale(!!vm.+IsMale());在序列化json之前先调用以发送到服务器(以防服务器端无法正确处理)
Artem 2012年

我认为您是对的,但我的Java语言知识不足。能否请您向我解释!! +的工作原理?我不熟悉这种语法。
CJ 2012年

1
@CJ会将字符串或数字转换为布尔值-选中jsfiddle.net/nickolsky/6ydLZ,如果字符串已经是布尔值,则将其保留为布尔值
Artem

非常感谢。我认为这也是一个很好的解决方案。
2012年

1
但是,这两种方法都无法正常工作。加载时未选中单选按钮。
MikaelÖstberg'13

1
ko.bindingHandlers['radiobuttonyesno'] = {
    'init': function (element, valueAccessor, allBindingsAccessor) {
        var stateHandler = function (property, allBindingsAccessor, key, value, checkIfDifferent) {
            if (!property || !ko.isObservable(property)) {
                var propWriters = allBindingsAccessor()['_ko_property_writers'];
                if (propWriters && propWriters[key])
                    propWriters[key](value);
            } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
                property(value);
            }
        };

        var updateHandler = function () {
            var valueToWrite;

            if ((element.type == "radio") && (element.checked)) {
                valueToWrite = element.value;
            } else {
                return; // "radiobuttonyesno" binding only responds to selected radio buttons
            }

            valueToWrite = (valueToWrite === "True") ? true : false;

            var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue); //can be true of false

            stateHandler(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
        };
        ko.utils.registerEventHandler(element, "click", updateHandler);

        // IE 6 won't allow radio buttons to be selected unless they have a name
        if ((element.type == "radio") && !element.name)
            ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
    },
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());

        value = value ? "True" : "False";

        if (element.type == "radio") {
            element.checked = (element.value == value);
        }
    }
};

使用此绑定器代替创建愚蠢的ko可计算观察值。

例:

<label>Male
        <input type="radio" name="IsMale" value="True" data-bind="radiobuttonyesno:IsMale"/>
     </label> 
     <label>Female
        <input type="radio" name="IsMale" value="False" data-bind="radiobuttonyesno:IsMale"/>
     </label>

1

一旦您确定单选按钮的初始匹配只想匹配一个字符串并想将值设置为一个字符串,只需将您的初始值转换为字符串即可。我不得不用Int值来解决这个问题。

设置可观察值之后,将值转换为字符串,KO将从此开始。如果要映射单独的行,请在这些行中进行转换。

在示例代码中,我使用Json在单个命令中映射整个模型。然后让Razor在转换的引号之间插入值。

script type="text/javascript">
    KoSetup.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
    KoSetup.ViewModel.ManifestEntered("@Model.ManifestEntered");       //Bool
    KoSetup.ViewModel.OrderStatusID("@Model.OrderStatusID");           //Int
</script>

在开发过程中,我使用网页底部的“将其全部转储到屏幕上”。

<h4>Debug</h4>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

这是数据值,之前

"OrderStatusID": 6,
"ManifestEntered": true,

并且,

"OrderStatusID": "6",
"ManifestEntered": "True",

在我的项目中,我不需要转换Bools,因为我可以使用没有相同挫败感的复选框。


0

为什么不简单地用true和false代替1和0?

 <label>Male
    <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/>
 </label> 
 <label>Female
    <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/>
 </label>

2
如果您要发送json对象,则会有所不同。
医生

0

您还可以使用扩展器,因此可以很容易地将它们重用于更多可观察的对象:

ko.extenders.boolForEditing = function (target, allowNull) {
    var result = ko.computed({
        read: function () {
            var current = target();
            var newValue = null;
            if (current === undefined || current === null || current === '') {
                if (!allowNull) {
                    newValue = 'false';
                }
            } else {
                newValue = current ? 'true' : 'false';
            }
            return newValue;
        },
        write: function (newValue) {
            var current = target();
            var valueToWrite = null;
            if (newValue === undefined || newValue === null || newValue === '') {
                if (!allowNull) {
                    valueToWrite = false;
                }
            } else {
                valueToWrite = newValue === 'true';
            }
            // only write if it changed
            if (valueToWrite !== current) {
                target(valueToWrite);
            } else {
                if (newValue !== current) {
                    target.notifySubscribers(valueToWrite);
                }
            }
        }
    }).extend({
        notify: 'always'
    });

    result(target());

    return result;
};

然后像这样使用它:

this.IsMale.forEditing = this.IsMale.extend({boolForEditing: true});

提供给的参数boolForEditing指示该值是否可以为null。

参见http://jsfiddle.net/G8qs9/1/


0

在对3.0之前的较旧版本的淘汰赛进行了大量研究之后,可能有两个最佳选择

创建类似的敲除扩展器

ko.extenders["booleanValue"] = function (target) {
    target.formattedValue = ko.computed({
        read: function () {
            if (target() === true) return "True";
            else if (target() === false) return "False";
        },
        write: function (newValue) {
            if (newValue) {
                if (newValue === "False") target(false);
                else if (newValue === "True") target(true);
            }
        }
    });

    target.formattedValue(target());
    return target;
};

要在模型上使用扩展器,请执行以下操作:

function Order() {
  this.wantsFries= ko.observable(false).extend({ booleanValue: null });
}

<span>Do you want fries with that?</span>
<label>
  <input type="radio" name="question" value="True"
             data-bind="value: wantsFries.formattedValue" /> Yes
</label>
<label>
  <input type="radio" name="question" value="False"
             data-bind="value: wantsFries.formattedValue" /> No
</label>

来源:http//www.timlabonne.com/2013/02/building-a-knockout-js-extender-for-boolean-values/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.