Reputation: 71
One of my bright ideas is looking a little less bright than I first thought.
Background - I needed to do the classic repeat control solution to the view with DBLookups in one of the columns. I need to have totals for the columns, so I decided that I would use a viewScope variable to hold the subtotal, and add the values as I compute them in the rows of the repeat control.
Sounds good in theory, but for some reason, if I use a Computed Field inside the repeat control, the value is computed (and appended to the subtotal) twice. I have established this with code along the following lines, using both a computed field and an edit box with the same code inside the repeat:
<xp:text escape="true" id="computedField9">
<xp:this.value><![CDATA[#{javascript:
//some calculations, nothing to see here*
var subTotal = viewScope.get("valueColumnTotal");
viewScope.put("valueColumnTotal", subTotal + sumVals);
sessionScope.put("Testing", sessionScope.get("Testing") + "~" + sumVals);
return sumVals;
}]]></xp:this.value>
//converters and stuff, nothing to see here
</xp:text>
<xp:inputText id="inputText1">
<xp:this.value><![CDATA[#{javascript:
//Same as the computed field above
</xp:this.value>
</xp:inputText>
For values of 1,2 and 3, my sessionScope variable shows 0~1~1~1~2~2~2~3~3~3 and the subtotal comes out as 18 If I remove the text box, I get 0~1~1~2~2~3~3 and a subtotal of 12
In other words, I'm getting double values for the computed field and single values for the edit box. I'm also getting a zero in there that I can't explain, but I'm assuming that's just an initial value and not something to worry about.
I thought it might have been a case of the repeat being refreshed by something, but then I would expect 1~2~3~1~2~3.
I'm guessing that I just don't understand something fundamental about when computed fields are computed, but I'm stumped. I guess I could switch to edit boxes, but I think Computed Fields are more appropriate here.
Can anybody shed some light on this one?
Upvotes: 1
Views: 626
Reputation: 79
If I read the requirement correctly, the requirement is to show totals for a column. If the view has categorized columns, here is the solution I used, based off of Notes In 9 – 019 – Xpages: Getting Column Totals From a View
A datacontext is used to pull in the column total from the view: {** This line of code if (entry==null) {return false}else{return entry} returns a zero if there are no categories for the key value}
The display of the totals in the table used to display the values using a row within a repeat control, is done using a xp:facet with the below code as the Total line:
With the entire facet looking like: {** Column #4, 5th column over, in the view was the column showing the category total}
Cut-N-Paste Below {**Only to be used by professional Cut-n-Paste'ers}
<xp:dataContext var="contractCategory">
<xp:this.value>
<![CDATA[#{javascript:
try{
var nav:NotesViewNavigator = contractView.createViewNav();
var query = new java.util.Vector();
query.addElement(sGrantId);
var entry:NotesViewEntry = contractView.getEntryByKey(query);
var entry = nav.getPrev(entry);
if (entry==null) {return false}else{return entry}
}catch(e){
return false;
}
}]]>
</xp:this.value>
</xp:dataContext>
<xp:this.facets>
<xp:panel xp:key="footer" id="TotalDisplayArea">
<xp:tr style="background-color:#c9c9c9">
<xp:this.rendered>
<![CDATA[#{javascript:getComponent("repeat1").getRowCount();}]]>
</xp:this.rendered>
<xp:td colspan="2" styleClass="budget_grid_body lotusBold" style="text-align:left">
Totals:</xp:td>
<xp:td styleClass="budget_grid_body" style="text-align:right"> </xp:td>
<xp:td styleClass="budget_grid_body" style="text-align:center;">
<xp:div style="text-align:left;float:left;padding-left:20px;">
<xp:span>$</xp:span>
</xp:div>
<xp:div style="float:right;padding-right:5px;">
<xp:text escape="true" id="totalCostDisp">
<xp:this.converter>
<xp:convertNumber pattern="#,##0.00"></xp:convertNumber>
</xp:this.converter>
<xp:this.value>
<![CDATA[#{javascript:
(operatingCategory)?
operatingCategory.getColumnValues()[4]:
"CodeError:4:2"}]]>
</xp:this.value>
</xp:text>
</xp:div>
</xp:td>
<xp:td style="background-color:#ffffff"></xp:td>
</xp:tr>
</xp:panel>
</xp:this.facets>
Upvotes: 0
Reputation: 20384
The JSF Lifecycle sheds some light on the challenge you are facing. The formula you use is executed in multiple phases as Per pointed out. The easiest way to ensure that they are computed only once is to use this code:
if (view.isRenderingPhase() {
var subTotal = viewScope.get("valueColumnTotal");
viewScope.put("valueColumnTotal", subTotal + sumVals);
sessionScope.put("Testing", sessionScope.get("Testing") + "~" + sumVals);
return sumVals;
}
You just need to make sure to reset the value at the appropriate time (e.g. beforeRenderResponse
)
The other option to be independent from cycles (you might want to compute a value even if you don't render the page) is to use a small Java class:
public class SumItUp {
private HashMap<String, Integer> totals = new HashMap<String,Integer>();
public void add(String key, int value) {
Integer i = new Integer(value);
this.totals.put(key,i);
}
public int getTotal() {
int result = 0;
for (Map.Entry<String, Integer> me : this.totals.entrySet()) {
result += me.getValue().toInt(); // <-- check that one - off my memory
}
}
Use this as data context or initialize it in your SSJS. Regardless how often the computation is called. When you submit to a Hashmap there won't be duplicate values. Use the clientid (not the id, that's the same for all controls in a repeat) or the document's UNID as key - they are unique
Upvotes: 1