Knockout 的映射破坏了可观察数组
我在使用 Knockout 的地图插件时遇到了一个奇怪的问题。
如果我通过映射填充可观察数组,则无法迭代该数组或获取其长度,即使 UI 已正确更新,该数组看起来还是空的。
您可以在这里找到一个可用的 jsFiddle: http://jsfiddle.net/VsbsC/
这是 HTML 标记 - up:
<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);
当我单击加载按钮时,记录和总数都会正确显示,当我单击检查按钮时,我会得到正确的结果项目编号。
但是,如果我更改
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 仍然可以正确呈现,但总数显示为零,则单击 Check 也会产生零,并且 console.info
-ing self. items
产生一个空数组。
这怎么可能?我已经无数次地重新阅读了教程,但我不明白我做错了什么。
PS 我需要通过映射插件填充可观察数组,因为在真实页面中,值来自 AJAX 请求。
I'm experiencing a strange problem with Knockout's mapping plug-in.
If I fill an observable array through mapping I can't iterate the array or get its length, even though the UI is updated correctly, the array seems empty.
You can find a working jsFiddle here: http://jsfiddle.net/VsbsC/
This is the HTML mark-up:
<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>
This is the JavaScript code:
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);
When I click on the Load button both the records and the total are displayed correctly, and when I click the Check button I get the correct item number.
However, if I change
self.items([
{ "name": "joey", "value": 1 },
{ "name": "anne", "value": 2 },
{ "name": "paul", "value": 3 },
{ "name": "mike", "value": 4 }
]);
to
self.items(ko.mapping.fromJS([
{ "name": "joey", "value": 1 },
{ "name": "anne", "value": 2 },
{ "name": "paul", "value": 3 },
{ "name": "mike", "value": 4 }
]));
the UI still get rendered correctly, but the total shows zero, clicking Check yields zero too, and console.info
-ing self.items
yields an empty array.
How is this possible? I've re-read the tutorials countless times, and I can't understand what I'm doing wrong.
P.s. I need to fill the observable array through the mapping plug-in because in the real page the values are coming from an AJAX request.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您看到的问题是基于这样一个事实:如果给定一个数组,映射插件将创建一个 observableArray。因此,您将 self.items observableArray 的值设置为等于 observableArray(而不仅仅是一个数组)。所以,它被额外包裹了一次。
这是一种方法:
因此,您使用映射插件初始化原始的 observableArray,以便可以更新它。然后,在更新时,您可以使用更新的数据和要更新的映射对象调用 ko.mapping.fromJS 。
当时的另一个小变化是在计算的可观察量中使用
value()
,因为它是来自映射插件的可观察量。示例如下:http://jsfiddle.net/rniemeyer/3La5K/
The issue that you are seeing is based on the fact that the mapping plugin will create an observableArray if it is given an array. So, you are setting the value of the
self.items
observableArray equal to an observableArray (rather than just an array). So, it is wrapped an extra time.Here is one way to do it:
So, you initialize the original observableArray using the mapping plugin, so it is ready to be updated. Then, on the update you call
ko.mapping.fromJS
with the updated data and the the mapped object to update.The other minor change at that point was just to use
value()
in your computed observable now that it is an observable from the mapping plugin.Sample here: http://jsfiddle.net/rniemeyer/3La5K/
或者你可以尝试 knockout-data-projections
它可以很好地处理视图模型到 js 数组的映射!
Or you could try knockout-data-projections
It hanldes view model to js arrays mappings quite well!!
您需要将
items
observableArray
传递给映射函数,以便它可以将新值映射到其中。工作解决方案在这里:
http://jsfiddle.net/unklefolk/j9pdX/
You need to pass your
items
observableArray
to the mapping function so that it can map the new values into it.Working solution here:
http://jsfiddle.net/unklefolk/j9pdX/