问题
I am working on an MVC web application using the MVVM pattern, breeze.js and knockout.js. This is the first time I use these js libraries and I still have to grasp how they work.
One of the pages of the application has a grid where both columns and rows are generated dynamically. I need to add an additional column where for each row I have the total of values displayed in the following row cells. Here an example:
Data type | Comment | Fact 1 | Fact 2 | Total | Value 1 | Value 2 | Value 3 | Value 4
==============================================================================================
Item 1 | any comment | fact 1 | fact 2 | calc. sum | 10 | 20 | 30 | 40
The grid is generated by binding a breeze entity object (planningItems) to templates. The object has the properties DataTypeId, Comment, Member, Total, FactValues. Total is the calculated sum.
<script type="text/html" id="list-planning-template">
<tr data-bind="mouseOverButton: $data">
<td style="text-align: center">
<button class="actionbutton actionbutton-item" data-bind="selectItem: $root.selectedItems, itemId: FactId"></button>
</td>
<td data-bind="text: DataTypeId" />
<td data-bind="text: Comment().Text" />
<!-- ko foreach: FactMembers -->
<td data-bind="text: Member().Code"></td>
<!-- /ko -->
<td data-bind="text: Total" />
<!-- ko foreach: FactValues -->
<td style="width: 50px" data-bind="text: Value"></td>
<!-- /ko -->
</tr>
I have been trying to add the Total property by extending the breeze entity object in the following way:
var FactCtor = function () {
this.Total = ko.computed({
read: function () {
var sum = 0;
if (this.FactValues) {
this.FactValues().forEach(function (fv) {
sum += fv.Value();
});
}
return sum;
},
deferEvaluation: true
}, this);
};
manager.metadataStore.registerEntityTypeCtor("Fact", FactCtor);
Essentially, what this code is supposed to do is to extend the entity by adding a knockout computed observable named Total with deferred evaluation. The function iterates through the breeze observable array FactValues and adds the values. I have been mucking about different versions of this code to no avail. Can anyone give me a hint on what is wrong with this code?
回答1:
UPDATE:
We were not able to get the code posted in my previous post to work. We were eventually able to overcome the problem by using a custom binding with breeze. Here is the code:
ko.bindingHandlers.getFyTotal = {
update: function (element, valueAccessor) {
var sum = 0;
var fact = valueAccessor();
if (fact.FactValues()) {
fact.FactValues().forEach(function (fv) {
sum += parseFloat(fv.Value());
});
}
$(element).html(sum);
}
};
The custom binding is then referenced in the HTML code the following way:
<td data-bind="getFyTotal: $data" />
Hope this may help others.
REVISED VERSION:
We have updated the above code to take advantage of ko.utils functions:
ko.bindingHandlers.getFyTotal = {
update: function (element, valueAccessor) {
var sum = 0;
var fact = valueAccessor();
if (fact.FactValues()) {
ko.utils.arrayForEach(fact.FactValues(), function (fv) {
sum += parseFloat(fv.Value());
});
}
$(element).html(sum);
}
};
回答2:
I modeled your code outside of Breeze and it's working:
http://jsfiddle.net/DazWilkin/yGZ7g/7/
I made a small tweak of adding a reference to FactValues (observableArray) on your constructor to overcome what - I believe - is the looping/this issue in JavaScript.
However, I haven't tried this in Breeze and wanted to do something similar. I was unable to get a similar function working and ultimately, created the totals during the 'then' processing of my executeQuery:
...manager.executeQuery(....).then(function(data) {
...
Fact.Total(FactValues()
.map(function(fv){ return fv.Value(); })
.reduce(function (total,curr) { return total+curr; });
...
}
I will try to get back to working on my version of this today and, if I find a better solution, I'll report back.
来源:https://stackoverflow.com/questions/13879716/how-to-calculate-a-total-in-a-grid-with-breeze-js-extended-entities