KnockoutJS foreach 绑定:无法将绑定多次应用于同一元素

KnockoutJS foreach binding: cannot apply bindings multiple times to same element

虽然我在很多场合看到过这个问题,但由于它与我的简单情况有关,所以我还没有找到满意的答案。

很简单,我在我看来使用的是 knockoutjs,我想用要定期更新的数据填充一个列表。

我似乎无法协调何时 应该应用绑定。起初我认为只在 window 负载上应用绑定就足够了,但我的列表无法更新,因为不清楚如何影响视图中的更改。如果我尝试再次调用绑定,则会出现多重绑定错误。我想关键是我不明白视图应该如何自动更新。

我在下面重新创建了我正在尝试做的事情。在生成的 html 表单中,我有一个可点击的按钮,当点击时它会显示 2 个不同的数组,但第二个确实(数组:['Tic','Toc'])没有出现。

这是我的样本 html:

<!DOCTYPE html>
<html>

<head>
  <title>Knockout Test</title>
</head>

<body>

  <div>
    <p>Stuff Here:</p>
    <ul id = "junk" data-bind="foreach: myList">
       <li>
        <span data-bind="text: $data"></span>
       </li>
    </ul>
  </div>

  <div class="row" id="launch">
    <button type="submit" id="go">GO</button>
  </div>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
  <script type="text/javascript" src="../static/assets/js/kotest.js"></script> 

</body>

</html>

这是我的 JS:

var data = ['Ping','Pong'];
var go = document.getElementById('go');
var cnt = 0;

function prepData(queryResults) {
    listings = queryResults;
    // line below gives me an error on 2nd try - but how should this work otherwise?
    ko.applyBindings(new viewModel(), document.getElementById("junk")); 
}

function viewModel() {
  var self = this;  
  self.myList = ko.observableArray(listings);  
}

go.addEventListener('click',function() {   
   if ( cnt > 0 ) {
      data = ['Tic','Toc'];
   }   
   cnt++;   
   console.log("data is: "+data+"...");
   prepData(data); // would prefer to call: viewModel(data) instead but not sure how this would work(?)
});

我这里有一个 fiddle 说明只显示第一个数组:

https://jsfiddle.net/devEngine/fkxgk7rc/1/

任何人都可以告诉我需要进行哪些更改才能使我的视图正确更新第二个数据数组吗?任何帮助将不胜感激。

首先,您需要在 prepData 可用的范围内保留 viewModel 的句柄。然后,您所要做的就是 viewModel.myList.removeAll() 清除可观察数组并用 data 填充它。

在 UI 中设置视图模型后,您必须对其进行操作(或为其提供可以调用的函数,以便它可以自行更新)。这就是拥有 observables 的全部意义所在——它们会改变,UI 也会因此改变。

如果您创建了视图模型的一个实例,然后立即将其释放,您之后将无法对其进行操作。


var data = ['Ping','Pong'];
var go = document.getElementById('go');
var cnt = 0;
var viewModel = new viewModel();
ko.applyBindings(viewModel, document.getElementById("junk")); 
// snip
go.addEventListener('click',function() {   
   if ( cnt > 0 ) {
      data = ['Tic','Toc'];
   }   
   cnt++;   
   console.log("data is: "+data+"...");
   viewModel.myList.remove();
   for(var i = 0; i < data.length; i++)
       viewModel.myList.push(data[i]);
});

这是你的模型,如你所见,我去掉了事件侦听器,对于这种情况可以更有效地在淘汰赛中完成,最好将逻辑放在一个地方。

var data = ['Ping','Pong'];
function viewModel() {
  var self = this;  
  self.myList = ko.observableArray(data); 

  self.addToList = function (listData) {
     for (var i = listData.length; i-- > 0;)
        self.myList.push(listData[i]);
  }

  self.replaceList = function (listData) {
     self.myList(listData);
  }
}

全局变量是关键:

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

HTML 按钮:

<div class="row" id="launch">
   <button data-bind="click: addToList.bind($data, ['Tic','Toc'])">Add</button>
   <button data-bind="click: replaceList.bind($data, ['Tic','Toc'])">Replace</button>
</div>

工作Fiddle https://jsfiddle.net/fkxgk7rc/3/

如果在 ko.applyBindings(...) 之前添加 ko.cleanNode(...) 可以修复错误,但这是错误的方法

@tyler_mitchell 的回答是很好的解决方案