[SalesForce] Visualforce pageBlockTable – update values in cells on load

I've been developing a page that displays dollar amounts in a table, and allows updates to those amounts. This table is read from and writes to a JSON string. Some of these dollar amounts have many decimal places, as they are worked out through formulas dividing up other values. I'd like to have these amounts display in the input field with two decimal places. As this needs to be an input field I'm not able to format these amounts as dollar values the simple way (value="{0, number, currency}". So I've taken the approach of using javascript and jquery, however I've had some trouble getting this working.

The goal: To have values in a pageBlockTable column updated on load to round up to 2 decimal places. Preferably this should effect display and not alter the field value itself, to avoid replacing data unnecessarily on save.

Issues: I haven't been able to get pageBlockTable cell entity IDs to retrieve values or write values back. I need to be able to retrieve these dynamically, but even testing with a hard coded entity Id (e.g. dp = document.getElementById('{!$Component.theBlock.theForm.TableSched.5.duePrinc}').value; ) results in an error in the console "Uncaught TypeError: Cannot read property 'value' of null". A similar argument referencing a normal inputText Id retrieves the value fine.

Vf page:

<apex:page standardController="Account" extensions="LoanScheduleTableController" docType="html-5.0" id="loansummary">
<apex:includeScript value="{!$Resource.jquery}"/> 

<script>
    $j = jQuery.noConflict(); 
    // test to set value through jquery
    $j(document).ready(function () {
        pid = {!$Component.theBlock.theForm.TableSched.5.duePrinc};
        princ = document.getElementById('pid').value;  
        princ = parseFloat(princ).toFixed(2);
        document.getElementById('pid').val(princ)
        alert('due principal to 2dp is ' + a);
    });
</script>
    <apex:pageBlock id="theBlock" title="Repayment Schedule">
        <apex:form id="theForm">
            <apex:pageBlockSection id="SecButton">
                <apex:commandButton action="{!updateSched}" value="Update Schedule" reRender="loansummary"/>
                <apex:commandButton value="Test" onclick="check()"/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="2" id="SecLoan">

                <apex:outputText label="Loan Amount" id="loanAmt" value="{0, number, currency}">
                    <apex:param value="{!ls.loanAmount}"/>
                </apex:outputText>
                <apex:outputText label="Interest Rate" value="{0, number, #%}">
                    <apex:param value="{!ls.interestRatePercent/100}" />
                </apex:outputText>
                <apex:outputText label="Estimated Interest" value="{0, number, currency}">
                    <apex:param value="{!ls.estimatedInterest}" />
                </apex:outputText> 
                <apex:outputText label="Origination Fee Percentage" value="{0, number, ###.0%}">
                    <apex:param value="{!ls.originationFeePercent}" />
                </apex:outputText> 
                <apex:outputText label="Origination Fee" value="{0, number, currency}">
                    <apex:param value="{!ls.originationFeeAmount}" />
                </apex:outputText>
                <apex:outputText label="Annual Fee Percentage" value="{0, number, ###.0%}">
                    <apex:param value="{!ls.annualFee}" />
                </apex:outputText>
                <apex:outputText label="Daily Fee Percentage" value="{0, number, ###.0%}">
                    <apex:param value="{!ls.dailyFee}" />
                </apex:outputText>
                <apex:outputText label="Estimated Fee" value="{0, number, currency}">
                    <apex:param value="{!ls.estimatedFee}" />
                </apex:outputText>
                <apex:outputText label="Actual Fee" value="{0, number, currency}">
                    <apex:param value="{!ls.actualFee}" />
                </apex:outputText>
                <apex:outputText label="Penalty Rate" value="{0, number, ###.0%}">
                    <apex:param value="{!ls.penaltyRate}" />
                </apex:outputText>
                <apex:outputText label="Additional Rate" value="{0, number, ###.0%}">
                    <apex:param value="{!ls.additionalRate}" />
                </apex:outputText>
            </apex:pageBlockSection>
            <apex:pageBlockTable value="{!schedules}" var="sitem" id="TableSched">
                <apex:column headerValue="Due Date">
                    <apex:input type="date" value="{!sitem.dueDate}" disabled="{!sitem.dueDate<TODAY()}"/>
                </apex:column>
                <apex:column headerValue="Effective Date">
                    <apex:outputText value="{!sitem.effDate}"/>
                </apex:column>
                <apex:column headerValue="Due Principal">
                    <apex:input id="duePrinc" value="{!sitem.duePrin}" type="text" disabled="{!sitem.dueDate<TODAY()}"/>
                </apex:column>
                <apex:column headerValue="Due Interest">
                    <apex:outputText value="{0, number, currency}">
                    <apex:param value="{!sitem.dueInt}"/>
                    </apex:outputText>
                </apex:column>
                <apex:column headerValue="Fee">
                    <apex:outputText value="{0, number, currency}">
                    <apex:param value="{!sitem.fee}"/>
                    </apex:outputText>
                </apex:column>
                <apex:column headerValue="Total Installment">
                    <apex:outputText value="{0, number, currency}">
                    <apex:param value="{!sitem.total}"/>
                    </apex:outputText>
                </apex:column>
            </apex:pageBlockTable>
        </apex:form>
    </apex:pageBlock>
</apex:page>

Controller:

public class LoanScheduleTableController {

    private Account acct;
    ApexPages.StandardController stdController;
    public LoanJSONHandler.loanSummary ls {get;set;} // wrapper class to structure json
    public Date dat {get;set;} 

    public LoanScheduleTableController (ApexPages.StandardController stdController) {
        stdController.addFields(new List<string>{'loan_json__c'});
        this.acct = (Account)stdController.getRecord();
        if(acct.loan_json__c!=null) ls = (LoanJSONHandler.loanSummary)JSON.deserialize(acct.loan_json__c, LoanJSONHandler.loanSummary.class);

    }

    public PageReference updateSched() {
        LoanJSONHandler.createLoanSchedfromJSON(ls);
        acct.loan_json__c = JSON.serializePretty(ls);
        update acct;
        return null; // stay on the same page 
    }

    public LoanJSONHandler.loanSummary getLoan(){
        return ls;
    }

    public list<LoanJSONHandler.schedule> getSchedules(){

        //if(acct.loan_json__c!=null) schedules = (list<Schedule>)JSON.deserialize(acct.loan_json__c, list<Schedule>.class);
        system.debug('schedules: '+ls.scheds);
        return ls.scheds;
    }


}

Wrapper classes:

public with sharing class LoanJSONHandler {

    public LoanJSONHandler() {}

    // Defines shape of JSON response
    public class loanSummary {
        public string clId {get;set;}
        public decimal loanAmount {get;set;}
        public decimal interestRatePercent {get;set;}   // should be 7.33%
        public decimal estimatedInterest {get;set;}
        public final decimal originationFeePercent {get;set;}
        public decimal originationFeeAmount {get;set;}  // should be loanAmount * originationFeeAmount
        public final decimal annualFee {get;set;}       
        public final decimal dailyFee {get;set;}
        public decimal estimatedFee {get;set;}          // should be loan days / 365 * 1.5 / 100 * (loanAmount + estimatedInterest)
        public decimal actualFee {get;set;}             // should be loan days * dailyFee * (loanAmount * adjusted interest total)
        public final decimal penaltyRate {get;set;}     // should be (1+(interestRatePercent*2)/2)squared - 1       look at Math.pow(value,powerOf) method
        public final decimal additionalRate {get;set;}

        public list<schedule> scheds = new list<schedule>();

        public loanSummary() {
            originationFeePercent = 0.015;
            annualFee = 0.015;
            dailyFee = annualFee/365;
            penaltyRate = 0.202;
            additionalRate = 0.5;
        }

    }

    public class schedule {
        public string schedId {get;set;}
        public date dueDate {get;set;}
        public date effDate {get;set;}
        public decimal duePrin {get;set;}
        public decimal dueInt {get;set;}
        public decimal fee {get;set;}
        public decimal total {get;set;}
    }
}

Best Answer

I tried your code in my dev org. The issue here as per my findings is <apex:input>. It is not returning the proper value to the Javascript or jQuery .value functions. I am not sure why or there may be another option to get and set the value out of apex:input.

What I would suggest is, change your apex:input to input like this:

<input type="text" id="duePrinc" value="{!sitem.duePrin}" disabled="{!sitem.dueDate<TODAY()}"/>

Then, in your jQuery code, instead using Javascript to get the value, use jQuery straightaway like:

$j = jQuery.noConflict(); 
    $j(document).ready(function () {
        var princ = $j('#duePrinc').val(); 
        alert('due principal to 2dp is '+princ);
        princ = parseFloat(princ).toFixed(2);
        $j('#duePrinc').val(princ);
        alert('due principal to 2dp is '+princ);
    });

This will give you the result you expected.

Shailesh