3

I'm a beginner to Visualforce - apologies if I've missed a thread that would answer this question but one has eluded me so far. This has been driving me out of my mind for a good week...

I had a variable in my controller class that was bound to an input component, which was working just fine - the controller variable was updated when I entered a new value in the input - until I stuck it inside an apex:repeat. I'm just repeating an output panel (containing my input) and two pageblock tables, n times on the page. The issue is that only the nth iteration of the repeated input actually binds to the controller. Debugging in the console shows that submitting a new value in any one of the inputs besides the last does not update the value of the variable in the controller, only by updating the last input rendered on the page can I pass a new value back. The user should be able to set the number of rows in the 'olis' table independently for each repeated section.

My code is below, hoping someone can help me understand where I'm going wrong. The goal is to create a single page where users can clone multiple copies of an opportunity, and add as many line items to each clone as they wish.

Thanks -

Visualforce page (troublesome input id is 'numOlisInput')

    <apex:page title="Clone N Opportunity Line Items" controller="OpportunityCloneNCtrlr" sidebar="false" tabStyle="Opportunity" showHeader="true" docType="HTML-5.0">
<style>
    .colStyle {
        display: fixed;
        max-height: 60px;
        overflow: auto;
    }
    .panelStyle{
        font-family:Palatino;
        font-weight:lighter;
    }
</style>

<apex:form id="N">
    <apex:pageBlock id="pb" mode="inlineEdit">
        <apex:pageBlockSection id="pbs" columns="1">
            <apex:commandButton id="cancelBtn" action="{!cancel}" value="Cancel"/>
            <apex:repeat id="oppAndOlis" var="opp" value="{!clonesList}">

                <apex:outputPanel id="thePanel" styleClass="panelStyle">
                    How many line items would you like to create? &nbsp;
                        <apex:input id="numOlisInput" style="width:20px" value="{!numOlis}" onkeydown="if(event.keyCode==13){this.blur();}">
                            <apex:actionSupport event="onchange" action="{!buildOlisList}" reRender="olis"/>
                        </apex:input> &nbsp;
                </apex:outputPanel>

                <apex:pageBlockTable id="opps" value="{!opp}" var="r" rows="1">
                    <apex:column headerValue="Name">
                        <a href="https://myinstance.salesforce.com/{!r.Id}">{!r.Name}</a>
                    </apex:column>
                    <apex:column headerValue="Programme Code" value="{!r.Programme_Code__c}"/>
                    <apex:column headerValue="Start Date" value="{!r.Start_Date__c}"/>
                    <apex:column headerValue="End Date" value="{!r.End_Date__c}"/>
                </apex:pageBlockTable>

                <apex:pageBlockTable id="olis" value="{!allOliLists[opp]}" var="oli">
                    <apex:repeat value="{!oliLabelToAPIMap}" var="label">
                        <apex:column headerValue="{!label}">
                            <div class="colStyle">
                                <apex:outputField value="{!oli[oliLabelToAPIMap[label]]}">
                                    <apex:inlineEditSupport event="ondblclick"/>
                                </apex:outputField>
                            </div>
                        </apex:column>

                    </apex:repeat>

                </apex:pageBlockTable>
            </apex:repeat>
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:form>

And my controller:

    public class OpportunityCloneNCtrlr {

public String page1 {get; set;}
public String page2 {get; set;}
public PageReference currentPage {get; set;}

public Integer numRecords;
public Integer getNumRecords(){
    return numRecords;
}
public void setNumRecords(Integer n){
    numRecords = n;
}

public Integer numOlis;
public Integer getNumOlis(){
    return numOlis;
}
public void setNumOlis(Integer n){
    numOlis = n;
}

public Id recordToClone{
    get{
        return ApexPages.currentPage().getParameters().get('Id');
    }
    set;
}

public Opportunity originalOpp {get; set;}
public List<Opportunity> clonesList {get; set;}
public Related_Programmes__c relatedProgsRec {get; set;}

public OpportunityLineItem oliPrototype {get; set;}
public List<OpportunityLineItem> oliPrototypes {get; set;}

public String queryString = '';

public Map<String, Schema.sObjectField> oppFieldMap {get; set;}
public Map<String, Schema.sObjectField> oppFieldMapHelper {get; set;}
public Map<String, String> oppLabelToAPIMap {get; set;}

public Map<String, Schema.sObjectField> oliFieldMap {get; set;}
public Map<String, Schema.sObjectField> oliFieldMapHelper {get; set;}
public Map<String, String> oliLabelToAPIMap {get; set;}

public Set<String> oppFieldNames {get; set;}
public Set<String> oliFieldNames {get; set;}

public Map<Opportunity, List<OpportunityLineItem>> allOliLists {get; set;}

public List<String> fieldsToRemove = new List<String>{
    // some fields
};

public List<String> oliFieldsToRemove = new List<String>{
    // some fields
};

//Constructor
public OpportunityCloneNCtrlr(){

    setNumRecords(numRecords);
    setNumOlis(numOlis);

    oppFieldMap = new Map<String, Schema.sObjectField>();
    oppFieldMapHelper = new Map<String, Schema.sObjectField>();
    oppFieldMapHelper = Schema.sObjectType.Opportunity.fields.getMap();

    for(String s : oppFieldMapHelper.keySet()){
        if(!oppFieldMapHelper.get(s).getDescribe().isCalculated() && oppFieldMapHelper.get(s).getDescribe().isUpdateable()) oppFieldMap.put(s, oppFieldMapHelper.get(s));
        else continue;
    }

    oppLabelToAPIMap = new Map<String, String>();
    for(String fieldAPI : oppFieldMap.keySet()){
        if(!fieldsToRemove.contains(fieldAPI)){
            oppLabelToAPIMap.put(oppFieldMap.get(fieldAPI).getDescribe().getLabel(), fieldAPI);
        } else{
            continue;
        }
    }

    oliFieldMap = new Map<String, Schema.sObjectField>();
    oliFieldMapHelper = new Map<String, Schema.sObjectField>();
    oliFieldMapHelper = Schema.sObjectType.OpportunityLineItem.fields.getMap();

    for(String s : oliFieldMapHelper.keySet()){
        if((!oliFieldMapHelper.get(s).getDescribe().isCalculated() && oliFieldMapHelper.get(s).getDescribe().isUpdateable()) || s == 'product2Id') oliFieldMap.put(s, oliFieldMapHelper.get(s));
        else continue;
    }

    oliLabelToAPIMap = new Map<String, String>();
    for(String fieldAPI : oliFieldMap.keySet()){
        if(!oliFieldsToRemove.contains(fieldAPI)){
            oliLabelToAPIMap.put(oliFieldMap.get(fieldAPI).getDescribe().getLabel(), fieldAPI);
        } else{
            continue;
        }
    }

    page1 = 'System.PageReference[/apex/OpportunityCloneN?' + String.valueOf(ApexPages.currentPage().getParameters().get('Id')) + ']';
    page2 = 'System.PageReference[/apex/OpportunityLineItemCloneN';
    currentPage = ApexPages.currentPage();

    if(String.valueOf(currentPage) == page1) buildOppsList();
    else {
        if(String.valueOf(currentPage).contains(page2)) buildOlisList();
    }
}

public void buildOppsList(){

    oppFieldNames = new Set<String>();
    for(String s : oppFieldMap.keySet()){
        if(!oppFieldMap.get(s).getDescribe().isCalculated() && oppFieldMap.get(s).getDescribe().isUpdateable()) oppFieldNames.add(s);
        else continue;
    }

    for(String s : oppFieldNames){
        queryString = queryString + s + ', ';
    }

    queryString = queryString.trim();
    queryString = queryString.removeEnd(',');

    String soqlQuery = 'SELECT ' + queryString + ' FROM Opportunity WHERE Id = :recordToClone';
    queryString = '';
    originalOpp = Database.query(soqlQuery);

    clonesList = new List<Opportunity>();

    Integer i;
    for(i = 0; i < numRecords; i++){
        Opportunity potentialInsert = new Opportunity();
        potentialInsert = originalOpp.clone();
        clonesList.add(potentialInsert);
    }
}

public PageReference saveWithProducts(){

    if(originalOpp.Related_Programmes__c == null){

        relatedProgsRec = new Related_Programmes__c();
        relatedProgsRec.OwnerId = originalOpp.OwnerId;
        relatedProgsRec.CurrencyIsoCode = originalOpp.CurrencyIsoCode;
        insert relatedProgsRec;

        this.relatedProgsRec.Id = relatedProgsRec.Id;

        originalOpp.Related_Programmes__c = relatedProgsRec.Id;
        update originalOpp;

        for(Opportunity o : clonesList){
            o.Related_Programmes__c = relatedProgsRec.Id;
        }

        insert clonesList;

        PageReference productPage1 = new PageReference('/apex/OpportunityLineItemCloneN');
        productPage1.getParameters().put('relatedProgramme',relatedProgsRec.Id);
        productPage1.getParameters().put('clonedRecord',originalOpp.Id);
        productPage1.setRedirect(true);
        return productPage1;

    } else{
        relatedProgsRec = [SELECT Id FROM Related_Programmes__c WHERE Id = :originalOpp.Related_Programmes__c];
        insert clonesList;
        PageReference productPage2 = new PageReference('/apex/OpportunityLineItemCloneN');
        productPage2.getParameters().put('relatedProgrammes',relatedProgsRec.Id);
        productPage2.getParameters().put('clonedRecord',originalOpp.Id);
        productPage2.setRedirect(true);
        return productPage2;
    }
}

public PageReference saveWithoutProducts(){

    if(originalOpp.Related_Programmes__c == null){

        relatedProgsRec = new Related_Programmes__c();
        relatedProgsRec.OwnerId = originalOpp.OwnerId;
        relatedProgsRec.CurrencyIsoCode = originalOpp.CurrencyIsoCode;
        insert relatedProgsRec;

        this.relatedProgsRec.Id = relatedProgsRec.Id;

        originalOpp.Related_Programmes__c = relatedProgsRec.Id;
        update originalOpp;

        for(Opportunity o : clonesList){
            o.Related_Programmes__c = relatedProgsRec.Id;
        }

        insert clonesList;

        PageReference theRP1 = new PageReference('/' + relatedProgsRec.Id);
        return theRP1;

    } else{
        insert clonesList;
        PageReference theRP2 = new PageReference('/' + originalOpp.Related_Programmes__c);
        return theRP2;
    }
}

public PageReference cancel(){
    PageReference theOpp = new PageReference('/' + originalOpp.Id);
    return theOpp;
}

public void buildOlisList(){
    setNumOlis(numOlis);
    String rpId = ApexPages.currentPage().getParameters().get('relatedProgrammes');
    String crId = ApexPages.currentPage().getParameters().get('clonedRecord');
    clonesList = [SELECT Id, Name, Programme_Code__c, Start_Date__c, End_Date__c FROM Opportunity WHERE Related_Programmes__c = :rpId AND Id != :crId];
    oliFieldNames = new Set<String>();

    for(String s : oliFieldMap.keySet()){
        if(!oliFieldMap.get(s).getDescribe().isCalculated() && oliFieldMap.get(s).getDescribe().isUpdateable()) oliFieldNames.add(s);
        else continue;
    }

    allOliLists = new Map<Opportunity,List<OpportunityLineItem>>();

    Integer i;
    for(Opportunity o : clonesList){
            oliPrototype = new OpportunityLineItem();
            oliPrototype.OpportunityId = o.Id;
            oliPrototypes = new List<OpportunityLineItem>();

            for(i = 0; i < numOlis; i++){
                oliPrototypes.add(oliPrototype);
            }
            allOliLists.put(o,oliPrototypes);
    }
}

}

There is also a previous page where Opportunity clones are created.

    <apex:page title="Clone N Opportunities" controller="OpportunityCloneNCtrlr" sidebar="false" tabStyle="Opportunity" showHeader="true" docType="HTML-5.0">

<style>
    .colStyle {
        display: fixed;
        max-height: 60px;
        overflow: auto;
    }
    .panelStyle{
        font-family:Palatino;
        font-weight:lighter;
    }
</style>

<apex:form id="N">
    <apex:pageBlock id="pb" mode="inlineEdit">
        <apex:pageBlockSection id="pbs" columns="1">
            <apex:outputPanel id="thePanel" styleClass="panelStyle">
                How many clones would you like to create (not including the original record)? &nbsp;
                <apex:input id="records" style="width:16px" value="{!numRecords}" onkeydown="if(event.keyCode==13){this.blur();}">
                    <apex:actionSupport event="onchange" action="{!buildOppsList}" reRender="clones,saveBtnProds,saveBtnNoProds" status="loading" />
                </apex:input> &nbsp;
                <apex:commandButton id="saveBtnProds" value="Save & Add Products" action="{!saveWithProducts}" onclick="if(!confirm('You are about to create {!numRecords} records. Do you want to continue?')) return false;" status="saving" reRender="N" />&nbsp;
                <apex:commandButton id="saveBtnNoProds" value="Save Without Products" action="{!saveWithoutProducts}" onclick="if(!confirm('You are about to create {!numRecords} records. Do you want to continue?')) return false;" status="saving" reRender="N" />&nbsp;
                <apex:commandButton action="{!cancel}" value="Cancel"/>&nbsp;
                <apex:actionStatus id="loading" startText=" One moment, please. "/>
                <apex:actionStatus id="saving" startText=" The clones are coming... "/>
            </apex:outputPanel>
        </apex:pageBlockSection>
        <apex:pageBlockSection id="table">
            <apex:pageBlockTable id="clones" value="{!clonesList}" var="c">
                <apex:repeat value="{!oppLabelToAPIMap}" var="label">
                    <apex:column headerValue="{!label}">
                        <div class="colStyle">
                            <apex:outputField value="{!c[oppLabelToAPIMap[label]]}">
                                <apex:inlineEditSupport event="ondblclick"/>
                            </apex:outputField>
                        </div>
                    </apex:column>
                </apex:repeat>
            </apex:pageBlockTable>
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:form>

The green input below updates the controller, the red does not: enter image description here

2

You cannot put multiple values in a single variable. For example:

Integer value = 5;
value = 10;
System.debug(value); // output is 10

Notice how the "5" is lost when a new value is assigned.

What you need is a collection, a way to hold a series of values.

So, you're going to need a wrapper class:

public class oppWrapper {
  public Integer numOlis { get; set; }
  public Opportunity record { get; set; }
}

Which you create a list of:

public List<oppWrapper> clonesList {get; set;}

Which you then bind to in Visualforce:

<apex:input id="numOlisInput" style="width:20px" value="{!opp.numOlis}" onkeydown="if(event.keyCode==13){this.blur();}">

You will need to adjust the remainder of your code to use the wrapper class.

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.