基于可观察变化的切换功能

时间:2015-11-30 16:19:24

标签: jquery knockout.js toggle durandal observable

Durandal 中,我正在寻找一种通过分配viewOptions: ko.observable("small")来快速初始化活动切换按钮的方法。在发生viewOptions更改的同时,我希望使用相应的id切换按钮并关闭所有其他按钮。

在我尝试过的几种方法中,没有一种方法可以做到这一点。以下代码中缺少什么?

  • 框架是Durandal,它是Knockout JS的扩展
  • 我对观察到的变量的变化没有任何回应

HTML:

<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
    <button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
    <button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
    <button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>

JS:

define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
    return {
        viewOptions: ko.observable("small"),
        bindingComplete: function() { // called immediately after databinding occurs.
            alert("start"); //called
            console.log("validate: this:", this); // Object {__moduleId__: "app/view"}
            console.log("validate: viewOptions:",this.viewOptions); // OK, non-empty
            this.viewOptions.subscribe(function(newViewOptions){
                alert("Hello there!"); //never gets called
                alert(newViewOptions); //
            });
            alert("end"); //called
        }
    }
});

http://jsfiddle.net/ccjnj/179/(简化)

4 个答案:

答案 0 :(得分:2)

自定义绑定&#34; radio&#34;从数据属性&#34; data-value&#34;中驱动每个切换按钮的值,因此您必须为每个选项指定该值,如:

 <button type="button" id="small" class="btn btn-default" data-value="small"> ...

这应该照顾这个小提琴演示http://jsfiddle.net/ccjnj/171/

答案 1 :(得分:1)

您使用the css binding,告诉它在active(或中等或大)时应用viewOptions() == 'small'课程:

<button ... data-bind="css: {active: viewOptions() == 'small'}">...</button>

示例:

&#13;
&#13;
var viewModel = {
    viewOptions: ko.observable("small"),
};

ko.applyBindings(viewModel);

loop();

function loop() {
  viewModel.viewOptions("small");
  setTimeout(function() {
    viewModel.viewOptions("medium");
  }, 1000);
  setTimeout(function() {
    viewModel.viewOptions("large");
  }, 2000);
  setTimeout(loop, 3000);
}
&#13;
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
    <button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
    <button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
    <button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
&#13;
&#13;
&#13;

我从上面删除了订阅,因为我以为你只是用它来设置课程。如果您还想要订阅 ,只需将其添加回来(在浏览器控制台中查看):

&#13;
&#13;
var viewModel = {
    viewOptions: ko.observable("small"),
    attached: function() {
      this.viewOptions.subscribe(function(newViewOptions){
          console.log(newViewOptions);
      }, this); // <== No need for bind, note subscribe' 2nd arg
    }
};

ko.applyBindings(viewModel);
viewModel.attached();

var timeout = +new Date() + 10000;
loop();

function loop() {
  viewModel.viewOptions("small");
  setTimeout(function() {
    viewModel.viewOptions("medium");
  }, 1000);
  setTimeout(function() {
    viewModel.viewOptions("large");
  }, 2000);
  if (+new Date() < timeout) {
    setTimeout(loop, 3000);
  }
}
&#13;
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="radio: viewOptions">
    <button type="button" id="small" class="btn btn-default" data-bind="css: {active: viewOptions() == 'small'}"><span class="glyphicon glyphicon-stop">Small</button>
    <button type="button" id="medium" class="btn btn-default" data-bind="css: {active: viewOptions() == 'medium'}"><span class="glyphicon glyphicon-th-large">Medium</button>
    <button type="button" id="large" class="btn btn-default" data-bind="css: {active: viewOptions() == 'large'}"><span class="glyphicon glyphicon-th">Large</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:1)

T.J。克劳德有一个很好的答案。我只是认为在viewmodel中创建按钮项并使用foreach绑定来渲染它们似乎是个好主意。保存重复的HTML。

function buttonData(label, value) {
  return {
    label: label,
    value: value
  };
}

var viewModel = {
  buttons: [
    buttonData('Small', 'small'),
    buttonData('Medium', 'medium'),
    buttonData('Large', 'large')
  ],
  selectedButton: ko.observable("small"),
  setOption: function(data) {
    console.debug("Set option", data.value);
    viewModel.selectedButton(data.value);

  }
};

ko.applyBindings(viewModel);

viewModel.selectedButton.subscribe(function(newViewOptions) {
  alert("Hello there!");
  alert(newViewOptions);
});
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="btn-group right" id="view-selector" data-toggle="buttons" data-bind="foreach: buttons">
  <button type="button" class="btn btn-default" data-bind="click:$root.setOption, text:label, css: { active: value == $root.selectedButton()}"></button>
</div>

答案 3 :(得分:0)

将解决方案的各个方面结合起来就能找到答案。我意识到Durandal中的事件处理模型与Knockout略有不同,在我错过ko.bindingHandlers.radiobindingComplete之前需要以下代码:

define(['jquery', 'jquery-ui', 'bootstrap', 'knockout', 'durandal/app', 'plugins/router'], function($, jqueryui, bootstrap, ko, app, router) {
    ko.bindingHandlers.radio = {
        init: function(element, valueAccessor, allBindings, data, context) {
            var $buttons, $element, observable;
            observable = valueAccessor();
            if (!ko.isWriteableObservable(observable)) {
                throw "You must pass an observable or writeable computed";
            }
            $element = $(element);
            if ($element.hasClass("btn")) {
                $buttons = $element;
            }
            else {
                $buttons = $(".btn", $element);
            }
            var elementBindings = allBindings();
            $buttons.each(function() {
                var $btn, btn, radioValue;
                btn = this;
                $btn = $(btn);
                radioValue = $btn.attr("id");
                $btn.on("click", function() {
                    observable(ko.utils.unwrapObservable(radioValue));
                });
                return ko.computed({
                    disposeWhenNodeIsRemoved: btn,
                    read: function() {
                        $btn.toggleClass("active", observable() === ko.utils.unwrapObservable(radioValue));
                    }
                });
            });
        }
    };        
    var vm = {
        viewOptions: ko.observable(),
        bindingComplete: function() { 
            vm.viewOptions.subscribe(function(newViewOptions){
                alert("Hello there!"); 
                alert(newViewOptions);
            });
            vm.viewOptions("large");
        }
    }
    return vm;
});