KnockoutJS foreach绑定:不能将绑定多次应用于同一元素

时间:2017-07-12 20:50:24

标签: javascript knockout.js foreach binding jsfiddle

虽然我已经多次看到这个问题,但我还没有找到一个令人满意的答案,因为它与我的简单情况有关。

很简单,我在我的视图中使用knockoutjs,我想填充一个包含定期更新数据的列表。

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

我已经重新创建了我在下面尝试做的事情。在生成的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(?)
});

我这里有一个小提琴,说明只显示第一个数组:

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

任何人都可以告诉我需要做些什么更改才能让我的视图与第二个数据阵列正确更新,好吗?任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:1)

继承你的模型,你可以看到我摆脱了事件监听器,它可以在这种情况下更有效地完成淘汰,更好地将逻辑保存在一个地方。

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>

工作小提琴 https://jsfiddle.net/fkxgk7rc/3/

答案 1 :(得分:0)

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

在UI中设置视图模型后,您必须对其进行操作(或者为其提供可以调用的函数,以便它可以自行更新)。这是观察能力的重点 - 它们会发生变化,因此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]);
});

答案 2 :(得分:0)

如果在ko.cleanNode(...)之前添加ko.applyBindings(...),您可以修复错误,但这样做不好

@tyler_mitchell的回答是很好的解决方案https://stackoverflow.com/a/45080213/4433565