Knockout的映射打破了可观察的数组

时间:2012-01-27 15:25:47

标签: knockout.js observer-pattern knockout-mapping-plugin

我遇到了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);

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

但是,如果我改变

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。我需要通过映射插件填充observable数组,因为在实际页面中,值来自AJAX请求。

3 个答案:

答案 0 :(得分:11)

您所看到的问题基于以下事实:如果给定数组,映射插件将创建observableArray。因此,您将self.items 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(),因为它是映射插件中的一个observable。

此处示例:http://jsfiddle.net/rniemeyer/3La5K/

答案 1 :(得分:1)

或者您可以尝试knockout-data-projections

它很好地将视图模型应用于js数组映射!!

答案 2 :(得分:0)

您需要将items observableArray传递给映射函数,以便将新值映射到其中。

这里的工作解决方案: 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);
};