Knockoutjs. How to compute data changed inside observable array

匿名 (未验证) 提交于 2019-12-03 08:36:05

问题:

Please, look at my text. I try to use observableArray of knockoutjs and foreach to compute data of array. Example 1 works fine: total sum computed if you change data in the fields. But Example 2 is not working.

<html> <head>                                                                              <title></title>                                                                 <script type='text/javascript' src='/js/jquery-1.8.2.min.js'></script>          <script type='text/javascript' src='/js/knockout-2.1.0.debug.js'></script>  </head>                                                                         <body> <p>Example 1</p> <div>     <p>     <input data-bind="value: fnum1" />     <input data-bind="value: fnum2" />     <span data-bind="text: ftotsum"></span>     </p> </div>     <p>Example 2</p> <div>     <p>     <!-- ko foreach: fields -->     <input data-bind="value: $data" />     <!-- /ko -->     <span data-bind="text: ltotsum"></span>     </p> </div> </body> <script>     function vm(){         //Calc Example 1         var self = this;         self.fnum1 = ko.observable(1);         self.fnum2 = ko.observable(2);         self.ftotsum = ko.computed(function(){             return parseFloat(self.fnum1()) + parseFloat(self.fnum2());         });         //Calc Example 2         self.fields = ko.observableArray([1, 2]);         self.ltotsum = ko.computed(function(){             var total = 0;             ko.utils.arrayForEach(self.fields(), function(item) {                 total += parseFloat(item);             })             return total;         });      };      ko.applyBindings(new vm()); </script> </html>

回答1:

EDIT: Got fiddle working, Raffaele is correct in saying you need to wrap the observable inside an object, but you can do it within the array creation itself and I like to use the ko.utils to unwrap my observables, it does the same thing for observables but it won't crash if there is a non-observable passed to it. See fiddle for full example.

An observableArray doesn't make the values passed observable, this is a common mistake. An observableArray just observes the modifications to the array and not the values. If you want to have your values inside your array be observable you have to make them so.

function vm(){     //Calc Example 1     var self = this;     self.fnum1 = ko.observable(1);     self.fnum2 = ko.observable(2);     self.ftotsum = ko.computed(function(){         return parseFloat(self.fnum1()) + parseFloat(self.fnum2());     });     //Calc Example 2     self.fields = ko.observableArray([{"num":ko.observable(1)},{"num":ko.observable(2)}]);     self.ltotsum = ko.computed(function(){         var total = 0;         ko.utils.arrayForEach(self.fields(), function(item) {             total += parseFloat(ko.utils.unwrapObservable(item.num));         });         return total;     });  }; ko.applyBindings(new vm());

Should work with the example above now.



回答2:

The documentation says:

Key point: An observableArray tracks which objects are in the array, not the state of those objects

Simply putting an object into an observableArray doesn’t make all of that object’s properties themselves observable. Of course, you can make those properties observable if you wish, but that’s an independent choice. An observableArray just tracks which objects it holds, and notifies listeners when objects are added or removed.

Your second example doesn't work because the value of the input fields is not bound to the values in the array. That values in the array are used only once, in the foreach binding, but when you type in the input boxes, nothing triggers KO.

Here is a working fiddle with a solution implemented. I used a helper ObsNumber

function vm(){     var self = this;      var ObsNumber = function(i) {         this.value = ko.observable(i);     }      self.fields = ko.observableArray([new ObsNumber(1) ,                                       new ObsNumber(2)]);      self.sum = ko.computed(function(){         var total = 0;         ko.utils.arrayForEach(self.fields(), function(item) {             total += parseFloat(item.value());         });         return total;     }); };  ko.applyBindings(new vm());

and the following markup



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!