更改可观察数组时不调用Knockout订阅

时间:2017-08-04 09:00:07

标签: javascript jquery knockout.js

拿这段代码:

var koEvents = new ko.subscribable();

var viewModel = function() {
   var self = this;

   self.data = ko.observableArray([{
      valid: true
   }, {
      valid: true
   }]);

   self.isValid = ko.computed(function() {
      var isValid = true;
      ko.utils.arrayForEach(self.data(), function(item) {
         console.log(item.valid);
         if (!item.valid) {
            isValid = false;
            return;
         };
      });
      return isValid;
   }, this).subscribe(function(newValue) {
      alert("Subscribe called!");
      koEvents.notifySubscribers(newValue, "dataChanged");
   }.bind(this));

   return {
      data: self.data,
      isValid: self.isValid,
   };
}

var vm = new viewModel();
ko.applyBindings(vm, document.getElementById("container"));

vm.data()[0].valid = false;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="container">
   <div data-bind="text: isValid ? 'valid': 'invalid'">
   </div>
</div>

我有两个问题......

  1. 当我执行此操作self.isValid时,为什么vm.data()[0].valid = false;没有被调用?
  2. 为什么在最初的isValid为true且稍后设置为false时未调用subscribe(alert("Subscribe called!");)?我希望在我的代码中调用两次。
  3. 由于

1 个答案:

答案 0 :(得分:2)

该代码存在一些问题,但与您提出的问题相关的主要问题是更改可观察数组中对象的非可观察属性(valid)并未改变可观察对象数组,只是其中对象的属性。所以自然没有通知。如果您需要通知,则需要观看valid属性(这反过来意味着它必须是可观察的)。

其他问题:

  1. Knockout的一个问题是它有时会为你展开observables / computeds,但是如果它们是表达式的一部分,就不会 - 你必须这样做(()):

    <div data-bind="text: isValid() ? 'valid': 'invalid'"></div>
    <!-- ------------------------^^                          -->
    

    您的代码正在测试isValid(不是isValid())是否真实。它总是如此,因为它是一个功能参考。

    当标识符不是表达式的一部分时,KO仅为您自动解包。例如,这有效:

    <!-- Works -->
    <div data-bind="visible: isValid">...</div>
    

    但这不是:

    <!-- Doesn't work -->
    <div data-bind="visible: !isValid">...</div>
    

    (当isValid是可观察/计算的时候)。

  2. 您将self.isValid设置为订阅句柄,而不是计算出来的,因为您已经过度链接了。 :-)您需要在调用computed结束后完成作业,然后订阅:

    self.isValid = ko.computed(function() {
       // ...
    }, this); // <=== End the assignment here
    self.isValid.subscribe(function(newValue) {
       // ...
    }.bind(this));
    
  3. 您正在使用self = this,但随后又将this传递给computed,并将bindsubscribe一起使用。这是无害的,但毫无意义。您只需要一个或另一个。

  4. 无需创建新的单独对象作为VM构造函数的返回值;你已经有了一个对象(new创建的对象)。

  5. 以下是各种变化的示例。第一个条目的valid属性在800ms后设置为false

    var koEvents = new ko.subscribable();
    
    var viewModel = function() {
       this.data = ko.observableArray([{
          valid: ko.observable(true)
       }, {
          valid: ko.observable(true)
       }]);
    
       this.isValid = ko.computed(function() {
          var isValid = true;
          ko.utils.arrayForEach(this.data(), function(item) {
             console.log(item.valid());
             if (!item.valid()) {
                isValid = false;
                return;
             };
          });
          return isValid;
       }, this);
       this.isValid.subscribe(function(newValue) {
          alert("Subscribe called!");
          koEvents.notifySubscribers(newValue, "dataChanged");
       });
    }
    
    var vm = new viewModel();
    ko.applyBindings(vm, document.getElementById("container"));
    
    setTimeout(function() {
      vm.data()[0].valid(false);
    }, 800);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div id="container">
       <div data-bind="text: isValid() ? 'valid' : 'invalid'"></div>
    </div>