5

I have a pageblocktable with one editable column in which numeric value for quantity will be entered. the next column to this is the Rate column which will have values from Salesforce. I want the Product of these 2 to be displayed in the last column as Total and also the sum of the Total column in Javascript so that on entering values in the inputtext, the total dynamically changes.

Below is my page :

<apex:page controller="DetailController" sidebar="false" id="pg">
<script>
function findTotal(){
alert(document.getElementById('pg:frmId:pb:pbt:0:qty').value);
//    alert(document.getElementById('pg:frmId:pb:pbt:0:j_id38').txt);
var qty = document.getElementById('pg:frmId:pb:pbt:0:qty').value;
var rate = document.getElementById('pg:frmId:pb:pbt:0:j_id38').text; 
var totallineItem = qty*rate;
//    document.getElementById('pg:frmId:pb:pbt:0:qty').value
document.getElementById("pg:frmId:pb:pbt:0:j_id26").innerHTML = totallineItem ; 
}



</script>
<apex:form id="frmId">


<apex:pageBlock title="Order Line Items" rendered="{!showDetails}" id="pb">
<apex:pageblockButtons location="bottom">
<apex:commandButton action="{!createOrder}" value="Save"/>
</apex:pageblockButtons>
<apex:pageBlockTable value="{!DispWrapper}" var="dw" id="pbt">
<apex:column value="{!dw.lineItemObj.Product__c}"/>
<apex:column value="{!dw.lineItemObj.Order__c }"/>
<apex:column headerValue="Delivered Quantity" >
<apex:inputtext value="{!dw.quantity}" id="qty" onchange="findTotal();"   styleClass="count-me"/>
 </apex:column>
 <apex:column value="{!dw.lineItemObj.Rate__c }" id="rate"/>
 <apex:column headerValue="Total">
 <apex:outputtext id="litotal"></apex:outputtext>
 </apex:column>
 </apex:pageBlockTable>
 </apex:pageBlock>

 </apex:form>
 </apex:page>

I am not familiar with Javascript and so this code is all I have which I tried. Please point me in the right direction to proceed with this!

The sample image below shows a typical table. The last column and the summary (Totals) need to be populated via Javascript. Sample Table

Adrian Larson
  • 149,971
  • 38
  • 239
  • 420
Sid
  • 340
  • 5
  • 17

1 Answers1

6

The DOM Id values in Visualforce tables include a row number (the :0: in your JavaScript) so your logic needs to incorporate that. The approach that I think works best is to use a CSS class (that has no markup associated with it) instead of the DOM Id:

<apex:column headerValue="Delivered Quantity" >
    <apex:inputtext value="{!dw.quantity}" styleClass="quantityMarker"/>
</apex:column>
<apex:column value="{!dw.lineItemObj.Rate__c }" styleClass="rateMarker"/>
<apex:column headerValue="Total">
    <apex:outputtext styleClass="totalMarker"/>
    <apex:facet name="footer">
        <apex:outputtext styleClass="grandTotalMarker"/>
    </apex:facet>
</apex:column>

While you could achieve what you want with just JavaScript, I would recommend adding (as a static resource) and using jQuery in your page. The code then becomes something like this (that you should add to the end of the page so the page rendering is not held up):

<script src="{!$Resource.jQuery}"></script>
<script>
(function($) {
    $(document).ready(function() {
        $('.quantityMarker').change(function() {

            var defaultedFloat = function(s) {
                if (s !== null && s !== '') {
                    var r = parseFloat(s);
                    if (!isNaN(r)) return r;
                }
                return 0;
            };

            // Recalculate the row
            var q = $(this);
            var r = q.closest('tr').find('.rateMarker');
            var t = q.closest('tr').find('.totalMarker');
            // Better handling of rounding may be needed
            t.text(defaultedFloat(q.val()) * defaultedFloat(r.text()));

            // Recalculate total of all rows
            var total = 0;
            $('.totalMarker').each(function() {
                var t = $(this);
                total += defaultedFloat(t.text());
            });
            $('.grandTotalMarker').text(total);
        });
    });
})(jQuery.noConflict());
</script>

This code:

  • Executes jQuery.noConflict() so any existing value of $ in the page is preserved
  • Waits until the DOM is ready
  • Then adds a function that is notified of changes on all elements that have class .quantityMarker
  • When a change happens, the function finds the class rateMarker element in the same row and the totalMarker element in the same row
  • Then sets the product in the totalMarker element = Then sums all the row totals into the grand total
Keith C
  • 135,775
  • 26
  • 201
  • 437
  • Thanks for the quick reply Keith. I have included jQuery file in my static resource and then tried your code. is there something else that needs to be done to populate the Totals column as its still not getting populated - do i need to include javascript code like document.getElementById('total').value= value obtained from jQuery? – Sid Aug 01 '16 at 10:14
  • I've typed this code in without trying it myself so there are probably mistakes in it but I think its pretty much complete. The first thing to do is look at your browser's console for errors and you can add console.log calls to the above code as the most simple debugging technique. But if you are going to write JavaScript you need to learn about debugging techniques for it e.g. How do I start to debug my own Visualforce/JavaScript? as most of us makes mistakes when writing it. – Keith C Aug 01 '16 at 10:20
  • I just pasted the original code into http://jshint.com/ and fixed two problems in the code - I've updated the code in the answer. Doesn't guarantee the code will work, but at least makes sure its syntactically correct. – Keith C Aug 01 '16 at 10:27
  • Thanks for the suggestion Keith. I was able to do a bit of debugging and the code is working fine now as I am getting the desired result in the Totals column. However, can you please also guide me to get the value of Total of the Totals column below the table (could be part of table itself or separately). I believe the grandtotalmarker is supposed to calculate that, but how do i display the same on my page? (really sorry for asking very basic questions!!!!) – Sid Aug 01 '16 at 10:33
  • I've added to the Visualforce making use of the apex:facet name="footer" to add a grandTotalMarker cell at the bottom of the total column. – Keith C Aug 01 '16 at 10:41
  • Thats great! Just one small issue I am still facing - If the table has multiple entries, on entering the quantity for the first line item i am getting NaN as the result in total and then on entering the second one it works fine. Is there a way to make that as 0 or the correct value instead of NaN – Sid Aug 01 '16 at 11:00
  • See addition of defaultedFloat function to answer. – Keith C Aug 01 '16 at 11:17
  • Thats awesome Keith..Just one last thing - How do I store the value available in Totals in a Custom Object field. I already have a save function with me, but I am not sure how to pass the value which appears in the Totals column each time for each row. Tried using apex:param in Outputtext, but its coming out to be null. Do i need to set that as well in the jQuery script? – Sid Aug 01 '16 at 11:37
  • You can add an apex:inputHidden next to the totalMarker field and also update that (using the val function). Or you can just repeat the calculation at the server side as its trivial. – Keith C Aug 01 '16 at 11:41
  • Thanks a ton Keith! I replicated the calculation server side to resolve this. – Sid Aug 01 '16 at 11:50
  • noConflict isn't necessary when using closures. – Adrian Larson Aug 01 '16 at 13:41
  • @AdrianLarson Not sure what you mean by that. If you examine the source, you will see that the noConflictfunction restores the value of $ that jQuery has set to itself using window.$ = _$. So executing the function immediately after jQuery is pulled in is the safe approach (though rarely necessary). – Keith C Aug 01 '16 at 13:47
  • You're passing it by argument. You're not referencing window.$ within the closure. So it's not necessary. – Adrian Larson Aug 01 '16 at 13:48
  • @AdrianLarson Yes those lines of code are using the $ passed in. But you are missing the point that without noConflict lines of JavaScript following this code will have $ defined to be jQuery when those lines of code may expect $ to be something else. The point is to avoid jQuery making a global change. – Keith C Aug 01 '16 at 13:51
  • Hmm fair enough. It won't break your code, but it might break someone else's. – Adrian Larson Aug 01 '16 at 13:54