【问题标题】:Knockout + Bootstrap 3 Radio Buttons淘汰赛 + Bootstrap 3 单选按钮
【发布时间】:2013-12-03 08:32:10
【问题描述】:

相关: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 example)时,淘汰赛绑定停止工作。如果我离开按钮组的数据切换,则绑定按预期工作,但按钮组看起来很糟糕。我知道这在 Bootstrap 2 中不起作用,因为他们实际上并没有将收音机输入用于收音机样式。为什么现在即使他们可以工作,它也拒绝工作?

【问题讨论】:

  • Bootstrap 工具按钮仍然不能很好地与 KO 配合使用,因此您需要一个自定义绑定处理程序,如下所示:jsfiddle.net/hd89y
  • 请注意,使用此代码的单选按钮组确实在 Bootstrap 2.x 中正常工作(尽管它们也在“按钮”内显示单选按钮本身,除非您自己隐藏它)。这是因为 Bootstrap 2.x 代码没有尝试拦截点击 - 它更喜欢 use actual button elements 来模拟同样的行为。

标签: javascript html twitter-bootstrap knockout.js twitter-bootstrap-3


【解决方案1】:

bootstrap buttonsknockout checked 绑定仍然不能很好地发挥作用:

  • knockout 使用 checked 绑定中的 click 事件触发底层 observable 进行更改
  • bootstrap 订阅 click 事件以进行切换,但调用 e.preventDefault(),因此不会通知 KO 关于点击。

一种可能的解决方案是创建一个自定义绑定处理程序,您可以在其中订阅 change 事件(这由工具上的引导程序触发)并在那里设置您的 observables 值:

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.2的原始演示JSFiddle
使用 Bootstrap 3.2.0 JSFiddle 更新了演示。

【讨论】:

  • @Kittoes 你应该向the author 询问...
  • 大声笑,对不起。只是想知道它背后是否有一些明显的原因,因为我对这个 javascript 东西很陌生。您的回答效果很好,可惜这样的事情是必要的。
  • 我很好奇除了单选按钮之外,如何让它在单个复选框上工作。
  • 优秀的帖子!这是复选框按钮组的改编:jsfiddle.net/MEA33/1
  • @Matus 类似的问题在这里。数据没问题,但 BS 和 KO 都切换了值。添加 && !$(element).prop('checked')) 条件为我更新修复了它
【解决方案2】:

我不能把这归功于我,因为有一次我的同事想出了它,但它真的很好用。

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

【讨论】:

  • 谢谢,这对于使用同一个 observable 的多个链接切换非常有用!
【解决方案3】:

找到了一个更简单的方法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>

【讨论】:

    【解决方案4】:

    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);
            }
        }
    };
    

    【讨论】:

      【解决方案5】:

      将@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);
          }
      }
      

      【讨论】:

        【解决方案6】:

        这里有一个简单的解决方案,我很惊讶还没有被提及。

        1) 从btn-group

        中删除data-toggle="buttons"

        现在淘汰赛应该可以工作了

        2) Bootstrap 为这些输入应用自定义 css 以隐藏它们,我们刚刚通过删除数据切换丢失了它们,因此将以下 css 应用于无线电输入

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

        3) 我们需要做的最后一件事是让所选选项的按钮具有活动类。将以下内容添加到标签按钮

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-03
          • 2016-12-10
          • 2016-05-10
          • 1970-01-01
          • 2014-05-07
          • 2016-12-01
          • 2014-02-26
          • 1970-01-01
          相关资源
          最近更新 更多