0

更新:コードソリューションを以下の回答として投稿しました。これは、KnockoutJsカスタムバインディングの完全な(そしてかなり単純な)例を見たい場合に役立ちます。


問題
jQueryを使用してラジオボタンのチェック済みステータスを設定すると、KnockoutJsビューモデルがこの変更を追跡していないように見えます!

シナリオ
複数の大きなDIVがあり、各DIVが1つのラジオボタンをラップしています。(これにより、クリックする領域が大きくなるため、ユーザーはラジオボタンをクリックしやすくなります。)ユーザーがdivのどこかをクリックしたときに、ラジオボタンをチェックしたいと思います。これは問題なく機能します。ただし、このラジオボタンにバインドされているviewModelプロパティから値を読み取ろうとすると、更新されていません。:-(

viewModelが更新されるのは、div内のラジオボタンを直接クリックした場合のみです。(jQueryを実行する)div内のどこかをクリックすると、ラジオが目に見えてチェックされたとしても....knockoutjsのviewModelプロパティは新しい値で更新されていません。

質問: jQueryを使用してラジオボタンのチェック状態を変更し、KnockoutJを適切に再生して更新する方法を教えてもらえますか?

コードは以下にあり、ここに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が更新されます。 チャームのように機能します! :)
  • 欠点は、私が非常に多くのコードを書くことになったということです!!! (BenjaminPaulによって提案されたより直接的なjQueryメソッドとは対照的に、3行のコードしかありませんでした。)
  • 利点は、このコードをラジオボタンのある他のページで再利用できることです。この場合、ユーザーがラジオボタンをクリックしやすくなります。
  • このカスタムバインディングアプローチを使用して、いくつかのjQueryアニメーションをDIVに追加することもできます。これは非常に優れています...これにより、高度な制御が可能になります。
  • ほとんどの場合、このカスタムバインディングを切り取って別のJSファイルに貼り付け、将来のカスタムバインディングの蓄積を開始できるようにします。

ここで試してみてください!http ://jsfiddle.net/CkEMa/66/(注:jsFiddleリンクを修正しました。GithubKOの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

ビューモデルに値を設定し、変更通知でチェックボックスをオンにできるようにします...

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

        });
于 2013-01-29T17:37:11.437 に答える
2

これは、組み込みのKnockOut CheckedBindingHandlerを使用して処理するのがおそらく最適です。または、カスタムバインディングハンドラーを使用して、はるかに柔軟なハンドラーを構築することもできます。 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>

ここに追加したかったのですが...ノックアウトのポイントは、DRYプリンシパル(Do n't Repeat Yourself)に従い、モデル(純粋なJSON)、ViewModel(監視可能なモデル+計算されたロジック+検証)を適切に分離することです。 )、および表示(HTML)。この分離は、サーバーとの間でデータを送受信するために通常書き込む必要のあるものを切り取り、そのデータをユーザーに表示し、操作全体を最初からやり直す前にユーザー入力を検証する場合に重要です。

全部をハード/ハンドコーディングするだけですそれはあなた自身の時間(または足)なので、これらのようなパターンは、機能を複製することなく大規模なソリューションを作成するときに時間を節約することを目的としています機能は異なるコードパスでも同じです)。

于 2013-01-29T17:45:40.893 に答える
1

チェックされたバインディングは、値の変更ではなくクリックイベントでトリガーされますこのシナリオだけでラベルバインディングを作成しました

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

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

カスタムバインダーは必要ありません。スペルミスを修正し(divKnockoutではなくdivKnockous)、ビューモデルを使用して値を変更します。

    // 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 に答える