为什么Knockout Js不知道JQuery制作的单选按钮的变化?

时间:2013-01-29 17:33:43

标签: jquery knockout.js

更新:我发布了我的代码解决方案作为下面的答案,如果有人想看到一个完整的(并且相当简单)KnockoutJs自定义绑定的示例,这可能会有所帮助。


问题
当我使用jQuery设置单选按钮的已检查状态时...似乎我的KnockoutJs视图模​​型不会跟踪此更改!

方案
我有多个大型DIV,每个DIV包含一个单选按钮。 (这样,用户可以通过单击更大的区域来更轻松地单击单选按钮。)当用户单击div中的某个位置时,我想检查它们的单选按钮....这很好用。但是,当尝试从绑定到此单选按钮的viewModel属性中读取值时....它尚未更新。 : - (

更新viewModel的唯一时间是我在div中的单选按钮上单击 DIRECTLY 。如果我只是点击div内部的某个地方(执行我的jQuery),那么....即使收音机明显被检查......但是敲门js viewModel属性还没有用新值更新。

问题: 有人可以告诉我如何使用jQuery更改单选按钮的检查状态,并让KnockoutJs很好地播放并更新吗?

代码如下,这里是jsFiddle:http://jsfiddle.net/CkEMa/67/

<script>
    $(document).ready(function ()
    {
        var self = this;

        function ViewModel()
        {
            this.HourlyOrSalary = ko.observable("");
        }
        viewModel = new ViewModel();
        ko.applyBindings(viewModel, document.getElementById('divKnockout'));

        // Click event for DIV around radio button
        $('.divRadioWrapper').click(function ()
        {
            var radio = $(this).find('input[type="radio"]');
            radio.prop('checked', true);

        });

        // Just for testing...
        $('#testButton').click(function ()
        {
            var viewModelVal = viewModel.HourlyOrSalary();
            alert('Value --> ' + viewModelVal);
        });

    });
</script>

<style>
    .divRadioWrapper {
        background-color: #dde9f5;   /*#d8f5f0;*/  /* #dcfbff; */
        width: 75px; 
        padding: 5px 10px; 
        border: 1px solid lightgray;
        cursor: pointer;
        margin-bottom: 10px;
    }
</style>

<div id="divKnockout">
    <div class="divRadioWrapper">
        <input type="radio" name="formType" value="hourly" data-bind="checked: HourlyOrSalary"
        />Hourly</div>
    <div class="divRadioWrapper">
        <input type="radio" name="formType" value="salary" data-bind="checked: HourlyOrSalary"
        />Salary</div>
    <br />

    <input type="button" id="testButton" value="Display viewModel data" />    
</div>

5 个答案:

答案 0 :(得分:4)

<强>感谢

感谢大家的回答。特别是,杨先生帮我意识到我可能从一开始就错误地思考它。我按照他列出的教程继续实施我的第一个针对knockoutjs的Custom Binding。虽然BenjaminPaul发布的答案肯定会通过jQuery来解决问题,但我相信Custom Binding方法确实是一个更加干净的解决方案,可以分离Young先生谈到的问题。

<强>解决方案

我想分享我在下面创建的解决方案,这是一个名为 fancyDivRadio 的knockoutjs自定义绑定。 :-)

注意事项:

  • 'fancyDivRadio'必须绑定到包含单选按钮的div。 (不,我不想在div中自动生成单选按钮,这会限制我未来的样式选项或者在div中包含其他项目的能力。)
  • 通过点击 jsFiddle链接可以看到,无论您点击的彩色DIV中的哪个位置,都会更新viewModel。 像魅力一样工作!:)
  • 缺点是我最终编写了更多代码! (与BenjaminPaul建议的更直接的jQuery方法相反,后者只有3行代码。)
  • 好处是我可以在带有单选按钮的其他页面上重复使用此代码,我希望单选按钮更容易让用户点击。
  • 我甚至可以使用这种自定义绑定方法为DIV添加一些jQuery动画,这非常酷......它真的给你一个很好的控制力!
  • 我很可能将这个自定义绑定剪切并粘贴到一个单独的JS文件中,我可以开始累积未来的自定义绑定。

在这里试试吧!http://jsfiddle.net/CkEMa/66/ (注意:修正了jsFiddle链接.Github KO url被破坏了。)

代码:

<script>
    $(document).ready(function ()
    {
        ko.bindingHandlers.fancyDivRadio = {
            init: function (element, valueAccessor)
            {
                // Get radio button located inside this div
                var radio = $(element).find('input[type="radio"]');

                // When div is clicked, check the radio and trigger radio change event
                $(element).click(function ()
                {   
                    radio.prop('checked', true);
                    radio.change();
                });

                // When radio button is checked, update the viewModel property!!
                $(radio).change(function ()
                {
                    if (radio.prop('checked'))  // only if it was changed to checked
                    {
                        // Set viewModel property to value of the radio button that was clicked
                        var value = valueAccessor();
                        value(radio.val());
                    }
                });
            },
            update: function (element, valueAccessor, allBindingsAccessor)
            {
                var value = valueAccessor();
                var valueUnwrapped = ko.utils.unwrapObservable(value);

                // Get radio button located inside this div
                var radio = $(element).find('input[type="radio"]');

                // Set radio to be checked or unchecked
                var shouldBeChecked = valueUnwrapped == radio.val();
                radio.prop('checked', shouldBeChecked);
            }
        };

        function ViewModel()
        {
            this.HourlyOrSalary = ko.observable("");
        }
        viewModel = new ViewModel();
        ko.applyBindings(viewModel);        

    });
</script>

<style>
    .divRadioWrapper {
        background-color: #dde9f5;
        width: 75px; 
        padding: 5px 10px; 
        border: 1px solid lightgray;
        cursor: pointer;
        margin-bottom: 10px;
    }
</style>

<div>
    <div class="divRadioWrapper" data-bind="fancyDivRadio: HourlyOrSalary">
        <input type="radio" name="formType" value="hourly" />
        Hourly
    </div>
    <div class="divRadioWrapper" data-bind="fancyDivRadio: HourlyOrSalary">
        <input type="radio" name="formType" value="salary" />
        Salary
    </div>
    <br />

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

答案 1 :(得分:2)

我会在viewmodel上设置值,并允许更改通知为您复选框...

// Click event for DIV around radio button
        $('.divRadioWrapper').click(function ()
        {
            var newValue = !viewModel.HourlyOrSalary();
            viewModel.HourlyOrSalary(newValue);

        });

答案 2 :(得分:2)

使用内置的KnockOut Checked Binding Handler可能是最好的方法。或者,您可以使用自定义绑定处理程序构建更灵活的处理程序。 http://knockoutjs.com/documentation/custom-bindings.html

还有一个很好的教程。 http://learn.knockoutjs.com/#/?tutorial=custombindings

Knockout的目的是提供数据绑定处理程序和逻辑控制,这样你就不必浪费时间去做。

您可以通过预标签查看绑定。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Test</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
</head>
<body>


            <input type="radio" name="formType" value="hourly" data-bind="checked: HourlyOrSalary" />Hourly

            <input type="radio" name="formType" value="salary" data-bind="checked: HourlyOrSalary" />Salary

        <br />

        <input type="button" id="testButton" value="Display viewModel data" />


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


    <script>


        $(document).ready(function()
        {
            var viewModel = {
                HourlyOrSalary: ko.observable("")

            };


            ko.applyBindings(viewModel);
        });


    </script>
</body>
</html>

我只想在这里添加...... Knockout的观点是遵循DRY原则(不要重复自己),并实践模型的良好分离(纯JSON),ViewModel(可观察模型+计算逻辑+验证)和视图(HTML)。这种分离是切断您通常必须编写的用于向服务器发送和接收数据,向用户显示该数据以及在整个操作重新开始之前验证任何用户输入的内容的关键。

可以只是硬/手工编码整个事情。这是你自己的时间(或脚)所以这样的模式旨在节省你的时间(现在,可能更晚),当你编写一个大的解决方案,而不必重复功能(阅读我没有在这里说代码,因为不同代码路径的功能可以相同。)

答案 3 :(得分:1)

选中的绑定会触发click not value chamge事件 我为这个场景制作了一个标签绑定

http://jsfiddle.net/CkEMa/7/

<input data-bind="checked: HourlyOrSalary, label: { caption: 'Hourly'}"
/></div>

答案 4 :(得分:0)

不需要自定义绑定器,只需修复拼写错误(divKnockous而不是divKnockout)并使用viewmodel更改值:

    // Click event for DIV around radio button
    $('div.divRadioWrapper').on("click", function () {
        viewModel.HourlyOrSalary($(this).find(":radio").val());
    });

http://jsfiddle.net/CkEMa/8/