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

发布于 2024-12-29 11:36:29 字数 2320 浏览 1 评论 0原文

我在使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

憧憬巴黎街头的黎明 2025-01-05 11:36:29

您看到的问题是基于这样一个事实:如果给定一个数组,映射插件将创建一个 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 。

当时的另一个小变化是在计算的可观察量中使用 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:

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);

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/

唐婉 2025-01-05 11:36:29

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

它可以很好地处理视图模型到 js 数组的映射!

Or you could try knockout-data-projections

It hanldes view model to js arrays mappings quite well!!

身边 2025-01-05 11:36:29

您需要将 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);
};

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/

self.load = function () {
    ko.mapping.fromJS([
        { "name": "joey", "value": 1 },
        { "name": "anne", "value": 2 },
        { "name": "paul", "value": 3 },
        { "name": "mike", "value": 4 }
    ], {}, self.items);
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文