【问题标题】:Toggle function based on change of observable根据可观察的变化切换功能
【发布时间】:2016-03-04 08:32:05
【问题描述】:

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/(简体)

【问题讨论】:

    标签: jquery knockout.js toggle durandal observable


    【解决方案1】:

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

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

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

    【讨论】:

    • 您能否将这部分代码作为对viewOptions 更改的响应? function(newViewOptions){ alert("Hello there!"); alert(newViewOptions); }
    • 该代码抛出 js 异常,因为您试图从“this”而不是“viewModel”访问 viewOptions 我已在此小提琴中为您添加更正jsfiddle.net/ccjnj/173
    • 问题似乎是我没有定义viewOptions。不幸的是,我定义了viewOptions 作为回报,我将尝试在代码示例中反映这一点:return { ko.observable("large-data"); }
    • 我在这里有点困惑......该代码的目的是什么?如果您想在“viewOptions”每次更改时收到通知,那么一种方法是在这里jsfiddle.net/ccjnj/175
    【解决方案2】:

    你使用the css binding,告诉它在viewOptions() == 'small'(或中,或大)时应用active类:

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

    例子:

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

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

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

    【讨论】:

    • 目前我了解,但valueOptions 的变化也应该会触发一个观察者功能。这可能就是您的示例中缺少的全部内容。
    • @PeterGerhat:你有那部分,subscribe。我以为你在使用subscribe 来设置课程,所以我删除了它,因为它是不必要的。只需将其添加回来,然后在里面做任何你需要的额外工作。旁注:不需要.bind(this)subscribe 需要第二个参数,这是在回调中用作this 的值。
    • 诀窍似乎在于使其适用于 Durandal(或 Knockout JS)。更新后的 JS 反映了我的问题的核心,viewOptionsattached 生命周期回调中被调用时未定义。
    • @PeterGerhat:我不知道你在哪里打电话attached(因为它不在你的问题中;所有你的问题必须在问题中,而不是刚刚链接),但大概是在主对象初始化程序之后,因此viewOptions 存在。请参阅上面的第二个示例。
    • @PeterGerhat:Knockout 不会为您调用 applyBindings。如果您正在使用可以执行的操作,那么当然,您不想在自己的代码中执行此操作。
    【解决方案3】:

    T.J.克劳德给你一个很好的答案。我只是认为在视图模型中制作按钮项并使用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>

    【讨论】:

    • 好建议,但待主要问题解决后我会保存它
    【解决方案4】:

    结合一些解决方案让我找到了答案。在我错过ko.bindingHandlers.radiobindingComplete 之前,我意识到Durandal 中的事件处理模型与Knockout 略有不同,需要以下代码:

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

    【讨论】:

    • 很高兴看到你想通了,我想在这里指出的一件事是:返回可以是它自己的变量。所以你可以做 var vm ={...} 然后“return vm;”用那个代替“this”,你可以使用“vm”……只是需要考虑的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 2017-01-03
    • 2020-01-30
    • 2019-03-01
    • 2019-11-20
    • 2017-09-07
    相关资源
    最近更新 更多