web-dev-qa-db-ja.com

ノックアウト+ Bootstrap 3ラジオボタン

関連: Bootstrap Radio Button Group

HTML:

<div class="btn-group" data-toggle="buttons">
    <label class="btn btn-primary">
        <input type="radio" name="options" id="option1" value="1" data-bind="checked: optionsValue"> Option 1
    </label>
    <label class="btn btn-primary">
        <input type="radio" name="options" id="option2" value="2" data-bind="checked: optionsValue"> Option 2
    </label>
    <label class="btn btn-primary">
        <input type="radio" name="options" id="option3" value="3" data-bind="checked: optionsValue"> Option 3
    </label>
</div>
<br />
<span data-bind="text: optionsValue"></span>

Javascript:

var ViewModel = function() {
    this.optionsValue = ko.observable()
};

ko.applyBindings(new ViewModel());

JsFiddle:


上記のコードを期待どおりに動作させようとしています。問題は、data-toggle="buttons"がbtn-group divに追加されます( Bootstrap 3の例 のように)ノックアウトバインディングが機能しなくなります。ボタングループのデータ切り替えをオフのままにすると、バインディングは期待どおりに機能しますが、ボタングループはひどいように見えます。 Bootstrap 2では実際には無線のスタイル設定に無線入力を使用していなかったため、これは機能しませんでした。動作しているにもかかわらず、なぜ動作しないのですか?

32
Kittoes0124

ブートストラップボタンknockout checked バインディングはまだうまくいっていません。

  • ノックアウトはclickバインディング内のcheckedイベントを使用して、変更する基礎となるオブザーバブルをタイガーします
  • ブートストラップはクリックイベントをサブスクライブして切り替えを行いますが、e.preventDefault()を呼び出すので、KOはクリックについて通知されません。

可能な解決策の1つは、changeイベント(bootstrap on toogleによって起動される)でサブスクライブするカスタムバインディングハンドラーを作成し、そこにオブザーバブル値を設定することです。

ko.bindingHandlers.bsChecked = {
    init: function (element, valueAccessor, allBindingsAccessor,
    viewModel, bindingContext) {
        var value = valueAccessor();
        var newValueAccessor = function () {
            return {
                change: function () {
                    value(element.value);
                }
            }
        };
        ko.bindingHandlers.event.init(element, newValueAccessor,
        allBindingsAccessor, viewModel, bindingContext);
    },
    update: function (element, valueAccessor, allBindingsAccessor,
    viewModel, bindingContext) {
        if ($(element).val() == ko.unwrap(valueAccessor())) {
             setTimeout(function () {
                $(element).closest('.btn').button('toggle');
             }, 1); 
        }
    }
}

そしてあなたの見解でそれを使ってください:

<label class="btn btn-primary">
    <input type="radio" name="options" id="option1" value="1" 
           data-bind="bsChecked: optionsValue"> Option 1
</label>

Bootstrap 3.0.2JSFiddle を使用したオリジナルのデモ。
Bootstrap 3.2.0JSFiddle を使用して更新されたデモ。

50
nemesv

同僚がそれを思いついたので、私はこれを信用できませんが、それは本当にうまくいきます。

<div class="btn-group" data-toggle="buttons">
    <label data-bind="css: { active: !HideInLeaderboards() }, 
                      click: function () { HideInLeaderboards(false); },
                      clickBubble: false" 
                      class="btn btn-default">
        Show Name
    </label>
    <label data-bind="css: { active: HideInLeaderboards() },
                      click: function () { HideInLeaderboards(true); },
                      clickBubble: false" class="btn btn-default">
        Hide Name
    </label>
</div>
15
Matthew Will

@nemesvがこれを提案するハンドラーを変更します。これは私のアプリで問題なく動作しました。

ko.bindingHandlers.bsChecked = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var value = valueAccessor();
        var newValueAccessor = function () {
            return {
                change: function () {
                    value(element.value);
                }
            }
        };
        if ($(element).val() == ko.unwrap(valueAccessor())) {
            $(element).closest('.btn').button('toggle');
        }
        ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, viewModel, bindingContext);
    }
}
1
danatcofo

Knockstrap はBootstrapとKnockoutの間のバインディングであるように見え、最新の状態に維持されているようです。ラジオボタンに関しては、具体的には次のコードを使用しています。

// Knockout checked binding doesn't work with Bootstrap radio-buttons
ko.bindingHandlers.radio = {
    init: function (element, valueAccessor) {

        if (!ko.isObservable(valueAccessor())) {
            throw new Error('radio binding should be used only with observable values');
        }

        $(element).on('change', 'input:radio', function (e) {
            // we need to handle change event after bootsrap will handle its event
            // to prevent incorrect changing of radio button styles
            setTimeout(function() {
                var radio = $(e.target),
                    value = valueAccessor(),
                    newValue = radio.val();

                value(newValue);
            }, 0);
        });
    },

    update: function (element, valueAccessor) {
        var $radioButton = $(element).find('input[value="' + ko.unwrap(valueAccessor()) + '"]'),
            $radioButtonWrapper;

        if ($radioButton.length) {
            $radioButtonWrapper = $radioButton.parent();

            $radioButtonWrapper.siblings().removeClass('active');
            $radioButtonWrapper.addClass('active');

            $radioButton.prop('checked', true);
        } else {
            $radioButtonWrapper = $(element).find('.active');
            $radioButtonWrapper.removeClass('active');
            $radioButtonWrapper.find('input').prop('checked', false);
        }
    }
};
1
CC Inc

より簡単な方法があります here

data-toggle属性を使用して、ノックアウトのデータバインディングを使用して同じ動作を実現しようとします。

ネイティブのHTMLラジオとほとんど同じくらい簡単です。

var ViewModel = function() {
  this.optionsValue = ko.observable("1");
};

ko.applyBindings(new ViewModel());
input[type="radio"] {
  /* Make native radio act the same as bootstrap's radio. */
  display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="btn-group">
  <label class="btn btn-primary" data-bind="css: {active: optionsValue() == '1'}">
    <input type="radio" name="options" id="option1" value="1" data-bind="checked: optionsValue">Option 1
  </label>
  <label class="btn btn-primary" data-bind="css: {active: optionsValue() == '2'}">
    <input type="radio" name="options" id="option2" value="2" data-bind="checked: optionsValue">Option 2
  </label>
  <label class="btn btn-primary" data-bind="css: {active: optionsValue() == '3'}">
    <input type="radio" name="options" id="option3" value="3" data-bind="checked: optionsValue">Option 3
  </label>
</div>
<br/>
<span data-bind="text: optionsValue"></span>
1
J3soon

ここでは、まだ言及されていないことに驚いた簡単な解決策があります。

1)btn-groupからdata-toggle="buttons"を削除します

ノックアウトが機能しているはずです

2)Bootstrapこれらの入力にカスタムcssを適用して非表示にします。データ切り替えを削除することで失われたため、次のcssを無線に適用しますinputs

position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;

3)最後に必要なことは、選択したオプションのボタンにアクティブなクラスを持たせることです。以下をlabel buttonsに追加します。

data-bind="css: {active: optionsValue() == valueOfThisInput}"
0
izzy