检查事件有时不会在chrome中的knockout呈现的复选框上触发

时间:2017-06-15 20:42:37

标签: javascript knockout.js

我创建了一个简单的jsfiddle示例:https://jsfiddle.net/c2wus7qg/3/

在这里,您将看到带有复选框的四个列表项。如果您取消选中其中任何一项,则更改事件将不会触发。然后,如果您取消选中剩余的项目,开火。

此外,如果您检查任何未检查的项目并再次取消选中,则此情况下也不会触发更改事件。

这似乎只发生在chrome中。它在firefox和safari中按预期工作。

以下是代码示例(简化):

HTML:

<ul>
  <!-- ko foreach: categoriesComputed -->
  <li>
    <input data-bind="attr:{value: id}, checked: state,
                                            event: {change: $root.categoryChanged}" type="checkbox">
    <span data-bind="text: name"></span>
  </li>
  <!-- /ko -->
</ul>

的javascript:

function viewModel() {
    var self = this;

  self.categories = ko.observableArray();

  self.Category = function(id, name, state) {
    this.id = id;
    this.name = name;
    this.state = ko.observable(state);
  };

  self.categoriesComputed = ko.computed(function() {
    return self.categories()
      .sort(function(a, b) {
        return a.state() < b.state() ? 1 : -1;
      })
      .slice(0, 4);
  });

  self.categoryChanged = function() {
    console.log('Yep, definitely changed!');
  }
}

var KO = new viewModel();
ko.applyBindings(KO);

setTimeout(function() {
    var categories = [];
    for ( var i = 1; i <= 20; i++ ) {
    //set state to true only for some elements
    var state = i >= 5 && i <= 6;
    categories.push(new KO.Category(i, 'category #' + i, state));
  }
  KO.categories(categories);
}, 1000);

2 个答案:

答案 0 :(得分:0)

我认为这里发生的事情是你在“更改”事件可以触发之前取消绑定复选框。您的计算仅显示前4位,但是一旦取消选中其中一个选项,它就会从前4个中排序并从DOM中删除。

如果从计算函数中删除.slice(0, 4);,则每次都应该看到事件正常触发。

修改 根据您的用例,一种可能的解决方案是使用根模型上的数组来存储选定的选项,而不是每个选项上的可观察状态。

self.selectedCategories = ko.observableArray([5,6]);

选中的绑定将使用数组存储选定的模型,或者,当与checkedValue绑定配对时,将使用该数组仅存储所选选项的属性值。例如,如果您使用:

<input data-bind="attr:{value: id}, checked: $root.selectedCategories, checkedValue: id" type="checkbox">

selectedCategories数组将包含选项对象的ID(开头为5和6)。

这样,您可以在该可观察数组上创建单个订阅,以监视对任何复选框的更改,并绕过依赖于在DOM中呈现的模型的事件绑定。

  self.categoryChanged = function() {
    console.log('Yep, definitely changed!');
  }
  self.selectedCategories.subscribe(self.categoryChanged);

当然,您的sort函数也必须更新,以检查所选数组中是否存在元素,而不是检查对象的“状态”。这可能看起来像:

function(a, b) {
    var indexA = self.selectedCategories().indexOf(a.id);
    var indexB = self.selectedCategories().indexOf(b.id);
    if(indexB == indexA){
        return Number(a.id) - Number(b.id);
    } else { 
        return indexB - indexA;
    }
}

示例fiddle

答案 1 :(得分:0)

这是一个时间问题(正如Jason所说),在更改事件发生之前,DOM会更新。我修改了您的代码,以添加echoState成员,该成员在短暂延迟后获得state的值。我基于此categoriesComputed,结果是在更改事件触发之前,DOM未更新。

我怀疑这对任何现实世界的情况都有用。 :)

&#13;
&#13;
function viewModel() {
  var self = this;

  self.categories = ko.observableArray();

  self.Category = function(id, name, state) {
    this.id = id;
    this.name = name;
    this.state = ko.observable(state);
    this.echoState = ko.observable(state);

    this.state.subscribe((newValue) => {
      setTimeout(() => {
        this.echoState(newValue)
      }, 0);
    });
  };

  self.categoriesComputed = ko.computed(function() {
    return self.categories
      .sort(function(a, b) {
        return a.echoState() < b.echoState() ? 1 : -1;
      })
      .slice(0, 4);
  });

  self.categoryChanged = function(data) {
    console.log('Yep, definitely changed!', data);
  }
}

var KO = new viewModel();
ko.applyBindings(KO);

setTimeout(function() {
  var categories = [];
  for (var i = 1; i <= 20; i++) {
    //set state to true only for some elements
    var state = i >= 5 && i <= 6;
    categories.push(new KO.Category(i, 'category #' + i, state));
  }
  KO.categories(categories);
}, 1000);
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Uncheck any of first two checkboxes and the change event won't fire!
<ul>
  <!-- ko foreach: categoriesComputed -->
  <li>
    <input data-bind="attr:{value: id}, checked: state,
											event: {change: $root.categoryChanged}" type="checkbox">
    <span data-bind="text: name"></span>
  </li>
  <!-- /ko -->
</ul>
&#13;
&#13;
&#13;