为什么KO根据表达式创建observable?

时间:2016-06-16 15:31:01

标签: knockout.js

使用类似

的绑定
<div data-bind="myBinding: { ... }, enable: isEnable() && isThatEnabled()"></div>

在调试JS代码时,当我查看myBinding的init中的allBindings时,通常定义如下:

init: function (element, valueAccessor, allBindings, viewModel, bindingContext)

我看到allBindings.enable是一个布尔值,它是isEnable() && isThatEnabled()的实际计算值。它不是一个可观察的,就像我期望KO基于布尔表达式创建一样。

我的问题是为什么它不是一个可观察的?

是否可以遵守“启用”功能。在这种情况下从myBinding绑定?

我看到3个选项:

  1. 定义自定义enable绑定:

    <div data-bind="myBinding: { ... }, myEnable: isEnable() && isThatEnabled()"></div>

  2. 有一个&#39;启用&#39; myBinding的参数:

    <div data-bind="myBinding: { enable: isEnable() && isThatEnabled() }"></div>

  3. 在HTML中使用计算的observable:

    data-bind="myBinding: bar, enable: ko.computed(function () { return foo() && foo2(); })"

  4. 第二个对我来说似乎最合理。

    &#13;
    &#13;
    var model = {
        foo: ko.observable(true),
        foo2: ko.observable(true),
        bar: ko.observable(1)
    };
    
    ko.bindingHandlers.myBinding = {
      init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
        var valueUnwrapped = ko.unwrap(value);
            
        element.innerHTML += ko.isObservable(allBindings().enable);
        
        // how unfortunate, 'enable' binding value is not observable
        // how to listen to 'enable' binding changes right here?
      },
    
      update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var value = valueAccessor();
        var valueUnwrapped = ko.unwrap(value);
      }
    };
    
    function onload() {
      ko.applyBindings(model);
    }
    &#13;
    <!DOCTYPE html>
    <html>
    <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body onload="onload()">
       <input type="checkbox" data-bind="checked: foo">
       <input type="checkbox" data-bind="checked: foo2">
       <button data-bind="myBinding: bar, enable: foo() && foo2()">Is 'enable' binding observable: </button> 
    </body>
    </html>
    &#13;
    &#13;
    &#13;

1 个答案:

答案 0 :(得分:1)

来自ko来源:

// Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated
function allBindings() {
    return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);
}
// The following is the 3.x allBindings API
allBindings['get'] = function(key) {
    return bindings[key] && evaluateValueAccessor(getValueAccessor(key));
};
allBindings['has'] = function(key) {
    return key in bindings;
};

如果你致电allBindings(),它会解开所有绑定的值。因此,如果您从计算函数访问它(例如从绑定的update方法),计算函数将订阅所有绑定更改的更新。

您无法直接访问observable,但可以使用ko.computed

进行包装
var enableObs = ko.computed(function() {return allBindings().enable;});

注意:不推荐使用allBindings 作为功能!因为如果从update方法调用它,它将在其他绑定中的任何更新上执行。所以最好:

var enableObs = ko.computed(function() {return allBindings.get('enable');});

查看myBindingmyBinding2行为的差异。 您也可以从update方法订阅更新而不计算(myBinding3)。

var model = {
    foo: ko.observable(true),
    foo2: ko.observable(true),
    text: ko.observable("changeme"),
    log: ko.observableArray([])
};

ko.bindingHandlers.myBinding = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var value = valueAccessor();
    var num=0;
    var valueUnwrapped = ko.unwrap(value);
        
    var enableObs = ko.computed(function() {
      // Note this computed executes on update of any binding. Not only for `enable` binding. Increment for show it.
      return allBindings().enable.toString() + num++;
    });
    enableObs.subscribe(function() {
      // if you remove counter from computed function this function will execute only on `enable` binding updates.
      element.innerHTML = ko.unwrap(enableObs).toString();
    });
  }
};

ko.bindingHandlers.myBinding2 = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var value = valueAccessor();
    var num=0;
    var valueUnwrapped = ko.unwrap(value);
        
    var enableObs = ko.computed(function() {
      return allBindings.get("enable").toString() + num++;
    });
    enableObs.subscribe(function() {
      element.innerHTML = ko.unwrap(enableObs).toString();
    });
  }
};

var tmp = 0;
ko.bindingHandlers.myBinding3 = {
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {     
      element.innerHTML = allBindings.get('enable').toString() + tmp++;
  }
};

function onload() {
  ko.applyBindings(model);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body onload="onload()">
  <input type="text" data-bind="value: text, valueUpdate: 'keyup'">
   <input type="checkbox" data-bind="checked: foo">
   <input type="checkbox" data-bind="checked: foo2">
   <button data-bind="myBinding, enable: foo() && foo2(), value: text()">Do smth</button>
  <button data-bind="myBinding2, enable: foo() && foo2(), value: text()">Do smth</button>
  <button data-bind="myBinding3, enable: foo() && foo2(), value: text()">Do smth</button>
</body>
</html>

<强> OLD:

init: function (element, valueAccessor, allBindings, viewModel, bindingContext)

如果您想要订阅 - 请改用update功能。 请参阅示例:http://jsbin.com/foxezidada/edit

和文档:http://knockoutjs.com/documentation/custom-bindings.html

  

我的问题是为什么它不是一个可观察的?

https://github.com/knockout/knockout/blob/master/src/binding/bindingAttributeSyntax.js

var getValueAccessor = bindingsUpdater
    ? function(bindingKey) {
        return function() {
            return evaluateValueAccessor(bindingsUpdater()[bindingKey]);
        };
    } : function(bindingKey) {
        return bindings[bindingKey];
    };

它在这里评估。