0

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


问题
当我使用 jQuery 设置单选按钮的选中状态时……似乎我的 KnockoutJs 视图模型没有跟踪此更改!

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

唯一一次更新viewModel是如果我直接单击div 内的单选按钮。如果我只是单击 div 内的某个位置(它执行我的 jQuery),那么....即使收音机明显被选中.... knockoutjs 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>
4

5 回答 5

4

谢谢

谢谢大家的回答。特别是,杨先生帮助我意识到我可能从一开始就以错误的方式思考它。按照他列出的教程,我继续为 knockoutjs 实现了我的第一个自定义绑定。虽然 BenjaminPaul 发布的答案肯定会通过 jQuery 解决问题,但我相信自定义绑定方法确实是一个更干净的解决方案,它允许分离 Young 先生所说的关注点。

解决方案

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

注意事项:

  • 'fancyDivRadio' 必须绑定到包含单选按钮的 div。(不,我不想在 div 中自动生成单选按钮,这会限制我未来的样式选择或在 div 中包含其他项目的能力。)
  • 正如您通过单击下面的 jsFiddle 链接所看到的那样,无论您单击彩色 DIV 中的哪个位置,viewModel 都会更新。 奇迹般有效! :)
  • 缺点是我最终写了很多代码!!!(与 Benjamin Paul 建议的更直接的 jQuery 方法相反,它只有 3 行代码。)
  • 好处是我可以在其他带有单选按钮的页面上重用此代码,我希望单选按钮更容易让用户单击。
  • 我什至可以使用这种自定义绑定方法将一些 jQuery 动画添加到 DIV,这非常酷……它确实为您提供了很好的控制!
  • 我很可能会将这个自定义绑定剪切并粘贴到一个单独的 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>
于 2013-01-29T23:12:07.580 回答
2

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

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

Knockout 的重点是提供数据绑定处理程序和逻辑控制,因此您不必在其他方面浪费时间。

您可以通过 pre 标记查看正在执行的绑定。

<!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)。这种分离是关键,因为它需要删除通常必须编写的用于向服务器发送和接收数据、向用户显示该数据以及在整个操作重新开始之前验证任何用户输入的内容。

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

于 2013-01-29T17:45:40.893 回答
2

我会在视图模型上设置值并允许更改通知为您选中该框...

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

        });
于 2013-01-29T17:37:11.437 回答
1

选中的绑定在单击时触发,而不是值更改事件我为这种情况做了一个标签绑定

http://jsfiddle.net/CkEMA/7/

<input data-bind="checked: HourlyOrSalary, label: { caption: 'Hourly'}"
/></div>
于 2013-01-29T17:45:31.317 回答
0

不需要自定义活页夹,只需修复拼写错误(divKnockous 而不是 divKnockout)并使用您的视图模型更改值:

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

http://jsfiddle.net/CkEMA/8/

于 2013-01-29T17:54:42.143 回答