将真/假绑定到 Knockout JS 中的单选按钮

Posted

技术标签:

【中文标题】将真/假绑定到 Knockout JS 中的单选按钮【英文标题】:Binding true / false to radio buttons in Knockout JS 【发布时间】:2012-04-25 00:01:53 【问题描述】:

在我的视图模型中,我有一个值为真或假的 IsMale 值。

在我的 UI 中,我希望将其绑定到以下单选按钮:

<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-way 绑定?

【问题讨论】:

对于 Knockout 版本 >= 3.0,请参阅 Natan's answer 以获得比已接受答案建议的更简单的解决方案。 【参考方案1】:

我知道这是一个旧线程,但我遇到了同样的问题,并发现了一个更好的解决方案,可能在这个问题得到正式回答后被添加到淘汰赛中,所以我会把它留给有同样问题的人问题。

目前不需要扩展器、自定义绑定处理程序或计算。 只需提供一个“checkedValue”选项,它将使用该选项而不是 html 的“值”属性,这样您就可以传递任何 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"/>

【讨论】:

看源码,好像决定使用checked值here。 'useCheckedValue' 设置为 true if the input is a radio or a checkbox with the value as an array。另外,我正在使用 Knockout 3.0。看看有没有帮助。 是的,谢谢,我已经升级到 3.0.0,现在它就在那里。仍然需要将我的布尔值包装在一个字符串中,因为服务器代码期待这些 ;-) 好消息。另外两点:(1)我发现在 v3.0 之前的淘汰赛中,我能够使用 value 绑定,然后是 checked 绑定(按此顺序),但在 3.0 中,我必须使用 @987654328 @ 绑定而不是 value 绑定。 (2) pre-v3.0 对要求value 绑定在checked 绑定之前很挑剔才能正常工作,所以我觉得如果你把checkedValue 绑定在 checked 绑定之前,就像它们在 docs 中显示的那样。 @ZiglioNZ 这可能失败的一种方法是,如果您的 checked 设置器(或者可能依赖它的东西)引发错误 - 那么正确的复选框未被选中 我正在使用 3.4 版本,发现我仍然需要输入 value="true" 和上面的内容才能让它工作。【参考方案2】:

一种选择是使用writeable computed 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 吗? 这是一个将计算出的 observable 创建推入自定义绑定的替代方案,这样您就不必担心在映射插件中处理它:jsfiddle.net/rniemeyer/utsvJ +1 使用绑定而不是在每个对象上创建可观察对象 @RPNiemeyer 绝对是正确的选择。 @RPNiemeyer 谢谢! cmets中的那个小提琴对我有用。【参考方案3】:

这对我有用:

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>

【讨论】:

我看到的唯一问题是,当序列化视图模型以传递给服务器时,您将获得可观察的整数(而不是布尔值)。在序列化 json 发送到服务器之前,您需要调用 vm.IsMale(!!vm.+IsMale());(以防服务器端无法正确处理) 我认为你是对的,但我的 Javascript 知识欠缺。你能向我解释一下 !!+ 是如何工作的吗?我不熟悉这种语法。 @C.J.这会将字符串或数字转换为布尔值 - 检查这个jsfiddle.net/nickolsky/6ydLZ,如果字符串已经是布尔值,它将保持为布尔值 非常感谢。我认为这也是一个很好的解决方案。 它不能双向工作。加载时未选中单选按钮。【参考方案4】:
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);
        
    
;

使用这个 binder 而不是创建愚蠢的 ko 计算 observables。

例子:

<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>

【讨论】:

【参考方案5】:

一旦您发现单选按钮的初始匹配只想匹配一个字符串并且想要将值设置为一个字符串,只需将您的初始匹配值到字符串。我不得不用 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",

在我的项目中,我不需要转换布尔值,因为我可以使用没有同样挫折感的复选框。

【讨论】:

【参考方案6】:

为什么不是简单的真假而不是 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>

【讨论】:

如果您发送的是 json 对象,它会有所不同。【参考方案7】:

您也可以使用extender,以便将它们重用于更多可观察对象:

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/

【讨论】:

【参考方案8】:

在对 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/

【讨论】:

以上是关于将真/假绑定到 Knockout JS 中的单选按钮的主要内容,如果未能解决你的问题,请参考以下文章

Python Tkinter 中的单选按钮值

如何正确地将数据绑定到 Angular2 中的单选按钮?

我需要弄清楚如何将我的类库中的枚举绑定到我的应用程序中的单选按钮

如何有条件地绑定到 knockout.js 中的“valueUpdate”?

Knockout + Radiobuttons - 在取消选中时将值切换回“false”

将多个输入绑定到可观察数组中的同一变量(Knockout.JS)