Knockout mapping splits observable arrays

I am having a weird issue with the Nockout plugin.

If I populate the observed array through matching, I cannot iterate over the array or get its length, even if the user interface is updated correctly, the array seems empty.

Here you can find a working jsFiddle: http://jsfiddle.net/VsbsC/

This is the HTML caption:

<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 the "Download" button, all records and the total quantity 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 user interface is still being processed correctly, but the final value is displayed as zero, pressing the Check button also gives zero, and console.info -ing self.items gives an empty array.

How is this possible? I have read textbooks countless times, and I cannot understand what I am doing wrong.

Ps I need to populate the observable array through the mapping plugin, because on a real page, the values ​​come from an AJAX request.

+4
source share
3 answers

The problem you see is based on the fact that the mapping plugin creates an observable array if it is given an array. Thus, you set the self.items value of the observed array to the observed array (and not just the array). Thus, he wrapped extra time.

Here is one way to do this:

 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 observable array with the matching plugin, so it is ready to upgrade. Then in the update, you call ko.mapping.fromJS with the updated data and the updated associated object.

Another minor change at this point was simply to use value() in your computed observable now that it is observable from the matching plugin.

Example here: http://jsfiddle.net/rniemeyer/3La5K/

+10
source

Or you could try knockout prediction data

It displays the model well for js array mappings.

+1
source

You need to pass your items observableArray to the map function so that it can display the new values ​​in 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); }; 
0
source

Source: https://habr.com/ru/post/1393380/


All Articles