2

I am currently having an issue with an apex:pageBlockTable, where I need some dynamic functionality in each row.

I am currently trying to recreate a more condensed version of the Quote creation, more specifically, creating QuoteLineItem records.

I am currently able to auto-populate the rows with the following code.

In My VisualForce
(JavaScipt)

function UpdatePartInList(partThatChanged){
    var elemenetIdForColumn = partThatChanged.id;

    var lastColon = elemenetIdForColumn.lastIndexOf(':');
    var secondToLastColon = elemenetIdForColumn.lastIndexOf(':', lastColon - 1);
    var columnNumber = (elemenetIdForColumn.substr(secondToLastColon + 1, (lastColon - secondToLastColon) - 1));

    var newId = document.getElementById(elemenetIdForColumn + "_lkid").value;
    AssignPartInformation(newId, columnNumber);
} 

(The Actual VisualForce - within a form tag)

<apex:actionFunction name="AssignPartInformation" action="{!PopulatePartInformation}" reRender="partInformation" immediate="true">
    <apex:param value="" name="newProductId"/>
    <apex:param value="" name="columnNumber"/>
</apex:actionFunction>
.... Later in the VisualForce Page...
<apex:pageBlockTable value="{!PartsOrMachinesList}" var="lineItem">

    <apex:column>
        <apex:facet name="header">
            Part Selected
        </apex:facet>
        <apex:inputField value="{!lineItem.LineItem.Product2Id}" onchange="UpdatePartInList(this)"/>
    </apex:column>
    <apex:column>
        <apex:facet name="header">
            List Price
        </apex:facet>
        <apex:outputText value="{!lineItem.ListPrice}"/>
    </apex:column>
    <apex:column>
        <apex:facet name="header">
            Sales Price
        </apex:facet>
        <apex:inputText value="{!lineItem.LineItem.UnitPrice}">
            <apex:actionSupport event="onchange" reRender="partInformation"/>
        </apex:inputText>
    </apex:column>
    <apex:column>
        <apex:facet name="header">
            Quantity
        </apex:facet>
        <apex:inputField value="{!lineItem.LineItem.Quantity}">
            <apex:actionSupport event="onchange" reRender="partInformation"/>
        </apex:inputField>
    </apex:column>
    <apex:column>
        <apex:facet name="header">
            Discount
        </apex:facet>
        <apex:inputField value="{!lineItem.LineItem.Discount}">
            <apex:actionSupport event="onchange" reRender="partInformation"/>
        </apex:inputField>
    </apex:column>
    <apex:column>
        <apex:facet name="header">
            Subtotal
        </apex:facet>
        <apex:outputText value="{!lineItem.SubTotal}"/>
    </apex:column>

</apex:pageBlockTable>

Now last, but not least...
The Apex Controller Code

/*A simple wrapper class*/
public with sharing class PartOrMachineItem
{
    public QuoteLineItem LineItem { get; set; }
    public Decimal ListPrice { get { return LineItem.UnitPrice; } }
    public String ForTestPurposes { get; set; }
    public Decimal SubTotal 
    { 
        get
        {
            /*
                Condition 1: If No Product is Selected 
                Condition 2: If Price a negative number
                Condition 3: If Discount is greater than 100 and less than 0
            */
            if((ListPrice  == null) || (ListPrice < 0) || (LineItem.Discount > 100 || LineItem.Discount < 0))
                return 0;

            return (LineItem.Discount != 0) ? (LineItem.UnitPrice * LineItem.Quantity * (1 - LineItem.Discount * .01)) : LineItem.UnitPrice * LineItem.Quantity;

        }
    }

    public PartOrMachineItem(QuoteLineItem currentLineItem)
    {
        LineItem = currentLineItem;
    }
}

/*The actionfunction method's code*/
public PageReference PopulatePartInformation()
{
   if(ApexPages.currentPage().getParameters().get('newProductId') != '000000000000000' && ApexPages.currentPage().getParameters().get('columnNumber').isNumeric())
   {
        Integer columnNumber = Integer.valueOf(ApexPages.currentPage().getParameters().get('columnNumber'));
        PartsOrMachinesList[columnNumber].LineItem.Product2Id = (Id)ApexPages.currentPage().getParameters().get('newProductId');
        PricebookEntry selectedProduct = [SELECT UnitPrice FROM PricebookEntry WHERE Product2Id = : PartsOrMachinesList[columnNumber].LineItem.Product2Id AND Pricebook2Id = :DefaultPriceBookId];

        PartsOrMachinesList[columnNumber].LineItem.putSObject('PriceBookEntry', selectedProduct);

        PartsOrMachinesList[columnNumber].LineItem.UnitPrice = selectedProduct.UnitPrice;
        PartsOrMachinesList[columnNumber].LineItem.Quantity = 1;
        PartsOrMachinesList[columnNumber].LineItem.Discount = 0;
    }
    return null;
}

Almost everything works the way I expect it to. When I select a product, the correct fields are populated and the subtotal is calculated correctly. I have even (soft) tested this by modifying values in the PopulatePartInformation method.

Now, what I would like, is an update to the Sales Price, Quantity, or Discount field to reRender the table row with the correct subtotal value. However, the values in the view state are not changing, so the subtotal retain the old value.

I imagine it has to do with the fact that most of the changes were made with an immediate actionFunction, but after that I am rather stumped.

Is there a possible solution? If so, is there a solution without resorting to a heavy amount of JavaScript/JQuery (although I am not opposed to it, if it solves the issue)?

  • I have found a solution after a bit of fidgeting with different things. I will post the answer later during the day when I am freed up. Basically, I am redoing the same methods but in a different fashion to achieve the desired results. – Programmable Medley Mar 3 '15 at 7:55
0

The answer is basically, to create immediate actionFunctions and JavaScript that are very similar to UpdatePartInList and AssignPartInformation. So I simply added the following code:

In JavaScript

function UpdateSalesPriceInList(priceThatChanged){
    var elemenetIdForColumn = priceThatChanged.name;
    var lastColon = elemenetIdForColumn.lastIndexOf(':');
    var secondToLastColon = elemenetIdForColumn.lastIndexOf(':', lastColon - 1);
    var columnNumber = (elemenetIdForColumn.substr(secondToLastColon + 1, (lastColon - secondToLastColon) - 1));
    AssignPriceInformation(priceThatChanged.value, columnNumber);
}

function UpdateQuantityInList(quantityThatChanged){
    var elemenetIdForColumn = quantityThatChanged.name;
    var lastColon = elemenetIdForColumn.lastIndexOf(':');
    var secondToLastColon = elemenetIdForColumn.lastIndexOf(':', lastColon - 1);
    var columnNumber = (elemenetIdForColumn.substr(secondToLastColon + 1, (lastColon - secondToLastColon) - 1));
    AssignQuantityInformation(quantityThatChanged.value, columnNumber);
}

function UpdateDiscountInList(discountThatChanged){
    var elemenetIdForColumn = discountThatChanged.name;
    var lastColon = elemenetIdForColumn.lastIndexOf(':');
    var secondToLastColon = elemenetIdForColumn.lastIndexOf(':', lastColon - 1);
    var columnNumber = (elemenetIdForColumn.substr(secondToLastColon + 1, (lastColon - secondToLastColon) - 1));
    AssignDiscountInformation(discountThatChanged.value, columnNumber);
}

In My VisualForce Page

<!-- within form tags obviously --!>
<apex:actionFunction name="AssignPriceInformation" action="{!AssignNewPriceInformation}" reRender="partInformation" immediate="true">
    <apex:param value="" name="newSalesPrice"/>
    <apex:param value="" name="columnNumber"/>
</apex:actionFunction>

<apex:actionFunction name="AssignQuantityInformation" action="{!AssignNewQuantityInformation}" reRender="partInformation" immediate="true">
    <apex:param value="" name="newQuantity"/>
    <apex:param value="" name="columnNumber"/>
</apex:actionFunction>

<apex:actionFunction name="AssignDiscountInformation" action="{!AssignNewDiscountInformation}" reRender="partInformation" immediate="true">
    <apex:param value="" name="newDiscount"/>
    <apex:param value="" name="columnNumber"/>
</apex:actionFunction>
<!-- within my pageBlockTable --!>
<apex:column >
    <apex:facet name="header">
        Sales Price
    </apex:facet>
    <apex:inputText value="{!lineItem.LineItem.UnitPrice}" onchange="UpdateSalesPriceInList(this)"/>
</apex:column>
<apex:column >
    <apex:facet name="header">
        Quantity
    </apex:facet>
    <apex:inputField value="{!lineItem.LineItem.Quantity}" onchange="UpdateQuantityInList(this)" />
</apex:column>
<apex:column >
    <apex:facet name="header">
        Discount
    </apex:facet>
    <apex:inputField value="{!lineItem.LineItem.Discount}"  onchange="UpdateDiscountInList(this)"/>
</apex:column>

Last but not least, the controller code

public PageReference AssignNewPriceInformation()
{
    if(IsDecimalValue(ApexPages.currentPage().getParameters().get('newSalesPrice')) && ApexPages.currentPage().getParameters().get('columnNumber').isNumeric())
    {
        Integer columnNumber = Integer.valueOf(ApexPages.currentPage().getParameters().get('columnNumber'));
        Decimal newSalesPrice = Decimal.valueOf(ApexPages.currentPage().getParameters().get('newSalesPrice'));
        PartsOrMachinesList[columnNumber].LineItem.UnitPrice = (newSalesPrice > 0) ? newSalesPrice : 0;
    }

    return null;
}

public PageReference AssignNewQuantityInformation()
{
    if(ApexPages.currentPage().getParameters().get('newQuantity').isNumeric() && ApexPages.currentPage().getParameters().get('columnNumber').isNumeric())
    {
        Integer columnNumber = Integer.valueOf(ApexPages.currentPage().getParameters().get('columnNumber'));
        Integer newQuantity = Integer.valueOf(ApexPages.currentPage().getParameters().get('newQuantity'));
        PartsOrMachinesList[columnNumber].LineItem.Quantity = (newQuantity > 0) ? newQuantity : 0;
    }

    return null;
}

public PageReference AssignNewDiscountInformation()
{
    if(ApexPages.currentPage().getParameters().get('newDiscount').isNumeric() && ApexPages.currentPage().getParameters().get('columnNumber').isNumeric())
    {
        Integer columnNumber = Integer.valueOf(ApexPages.currentPage().getParameters().get('columnNumber'));
        Integer newDiscountValue = Integer.valueOf(ApexPages.currentPage().getParameters().get('newDiscount'));
        PartsOrMachinesList[columnNumber].LineItem.Discount = (newDiscountValue > 0 && newDiscountValue < 100) ? newDiscountValue : 0;
    }

    return null;
}

Everything seems to be responsive and it works great for what I need so far. I do feel that there is a more elegant solution to solve this, but for now, this will suffice.

I am curious if there are any other approaches that may be more beneficial.

| improve this answer | |
  • I see your actionFunctions are rerendering (reRender="partInformation"), but I can't see what partInformation actually is. Are you rerendering the whole table? A page element that contains the table (form, pageBlock, etc)? Wouldn't rerendering the whole table cause the focus to be lost from the current input? I'm currently trying to figure out if its possible to just rerender a row. – Jason Clark Jan 20 '16 at 22:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.