2012-01-27 80 views
4

我遇到了Knockout的映射插件的一个奇怪的问题。淘汰赛的映射打破可观察阵列

如果我通过映射填充可观察数组,则无法迭代数组或获取其长度,即使UI已正确更新,数组似乎为空。

你可以在这里找到工作的jsfiddle:http://jsfiddle.net/VsbsC/

这是HTML标记:

<p><input data-bind="click: load" type="button" value="Load" /></p> 
<p><input data-bind="click: check" type="button" value="Check" /></p> 
<table> 
    <tbody data-bind="foreach: items"> 
     <tr> 
      <td data-bind="text: name"></td> 
      <td data-bind="text: value"></td> 
     </tr> 
    </tbody> 
</table> 
<p data-bind="text: total"></p> 

这是JavaScript代码:

var ViewModel = function() { 
    var self = this; 
    self.items = ko.observableArray(); 
    self.load = function() { 
     self.items([ 
      { "name": "joey", "value": 1 }, 
      { "name": "anne", "value": 2 }, 
      { "name": "paul", "value": 3 }, 
      { "name": "mike", "value": 4 } 
     ]); 
    }; 
    self.check = function() { 
     alert(self.items().length); 
    }; 
    self.total = ko.computed(function() { 
     var total = 0; 
     for (var i = 0; i < self.items().length; i++) { 
      total += self.items()[i].value; 
     } 
     return total; 
    }); 
}; 
var viewModel = new ViewModel(); 
ko.applyBindings(viewModel); 

当我点击加载按钮记录和总数都正确显示,当我点击Check按钮我得到正确的项目编号。

但是,如果我改变

self.items([ 
    { "name": "joey", "value": 1 }, 
    { "name": "anne", "value": 2 }, 
    { "name": "paul", "value": 3 }, 
    { "name": "mike", "value": 4 } 
]); 

self.items(ko.mapping.fromJS([ 
    { "name": "joey", "value": 1 }, 
    { "name": "anne", "value": 2 }, 
    { "name": "paul", "value": 3 }, 
    { "name": "mike", "value": 4 } 
])); 

的UI仍然可以正确地呈现,但总显示零,点击检查收益率也为零,并console.info -ing self.items产生一个空数组。

这怎么可能?我无数次重读了这些教程,并且我不明白我做错了什么。

P.s.我需要通过映射插件填充可观察数组,因为在实际页面中,值来自AJAX请求。

回答

10

您所看到的问题基于这样一个事实,即映射插件会在给定数组的情况下创建observableArray。因此,您将observableArray的值设置为等于observableArray(而不仅仅是一个数组)。所以,这是一个额外的时间。

下面是做到这一点的一种方法:

var ViewModel = function() { 
    var self = this; 
    self.items = ko.mapping.fromJS([]); 
    self.load = function() { 
     ko.mapping.fromJS([ 
      { "name": "joey", "value": 1 }, 
      { "name": "anne", "value": 2 }, 
      { "name": "paul", "value": 3 }, 
      { "name": "mike", "value": 4 } 
     ], self.items); 
    }; 
    self.check = function() { 
     alert(self.items().length); 
    }; 
    self.total = ko.computed(function() { 
     var total = 0, items = self.items(); 
     for (var i = 0; i < items.length; i++) { 
      total += items[i].value(); 
     } 
     return total; 
    }); 
}; 
var viewModel = new ViewModel(); 
ko.applyBindings(viewModel); 

所以,你初始化使用映射插件原来observableArray,所以它已准备好进行更新。然后,在更新时,您可以使用更新后的数据和要更新的映射对象调用ko.mapping.fromJS

此时的另一个小改动就是在计算的observable中使用value(),因为它是映射插件的可观察值。

样品在这里:http://jsfiddle.net/rniemeyer/3La5K/

+0

您的代码完美无缺。问题出在'self.items = ko.mapping.fromJS([]);':在我测试之前,我已经尝试过你写的东西,但是我用'self.items = ko.observableArray( );',并不是那种方式。由于我无法在[映射文档](http://knockoutjs.com/documentation/plugins-mapping.html)中找到任何内容,我打开了[ticket](https://github.com/SteveSanderson/knockout/)问题/ 297)要求添加解释。 – Albireo 2012-01-27 16:44:15

+0

是的,您的用户界面已经绑定到原始的observableArray,所以当您用数据更新它时,您需要确保您能够更新原始数组,而不是用新数组替换它。 – 2012-01-27 16:55:05

0

你需要通过你的itemsobservableArray映射函数,以便它可以在新的值映射到它。

工作的解决方案在这里: http://jsfiddle.net/unklefolk/j9pdX/

self.load = function() { 
    ko.mapping.fromJS([ 
     { "name": "joey", "value": 1 }, 
     { "name": "anne", "value": 2 }, 
     { "name": "paul", "value": 3 }, 
     { "name": "mike", "value": 4 } 
    ], {}, self.items); 
};