function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Ashley@WCELAshley@WCEL 

Change interface implementation from a visualforce page

Hello all.

 

I have an interface class called ReceiptBatch with two implementations: ReceiptBatch_charitable and ReceiptBatch_convertExisting. Most of the time I will use _charitable.  _convertExisting is a temporary implementation which will be used only by the admin (me) for a very short period of time to convert existing records to the new format.

 

I am trying to create a way to stealthily change the implementation used when accessing the visualforce page. My current attempt is to pass a URL query parameter when accessing the page, as in /apex/ReceiptBatch?rtype=convert. If that parameter exists in the URL then the controller extension will instantiate the _convertExisting implementation, otherwise it will default to the _charitable implementation.

 

Here's a summary of my current logic.

 

  1. The visualforce page uses StandardSetController to select a list of Opportunities for processing
  2. In the controller extension's constructor, check for the querystring parameter. If it exists, assign to a public variable which is bound to a hidden field on the form to maintain the parameter between page posts
  3. If the hidden field contains a value, then use that to instatiate the appropriate implementation, otherwise instantiate the default

Using the logs, I have verified that the parameter is being read from the query string and saved to the hidden field but it doesn't carry through to the final form submission and the processing is always completed using the default implementation. How can I get that parameter to persist to the final form submission? Any help is greatly appreciated.

 

Here's the code. I have underlined the parts most relevant to this question.

 

Visualforce Page:

<apex:page standardController="Opportunity" extensions="ReceiptBatch_controller" recordSetVar="opportunities" action="{!validateOpps}">
    
    <apex:form >
        <apex:pageBlock rendered="{!cleanOpps.size > 0}">
            <p style="background-color:green;color:white;padding:3px;"><b>Receipts may be generated for the following opportunities</b></p><br/>
            <apex:pageBlockTable value="{!cleanOpps}" var="c" id="cleanList">
                <apex:column value="{!c.CharitableReceipt__c}"/>
                <apex:column value="{!c.Account.Name}"/>
                <apex:column value="{!c.Name}"/>
                <apex:column value="{!c.Amount}"/>
                <apex:column value="{!c.CloseDate}"/>
                <apex:column value="{!c.IsWon}"/>
            </apex:pageBlockTable>

            Address the receipt to: 
            <apex:selectList multiselect="false" size="1" value="{!addresseeType}">
                <apex:selectOptions value="{!addresseeTypeList}"></apex:selectOptions>
            </apex:selectList>
            <apex:inputHidden value="{!ReceiptType}"/>
            <apex:commandButton action="{!issueNew}" value="Yes, create receipts for these opportunities"/>
        </apex:pageBlock>
</apex:form>
    
    <apex:pageBlock rendered="{!dirtyOpps.size > 0}">
        <p style="background-color:red;color:white;padding:3px;"><b>The following opportunities are invalid because:</b> {!ValidationMessage}<br/>
        You may correct any errors and rebuild your selection below</p><br/>
        
        <apex:pageBlockTable value="{!dirtyOpps}" var="d" id="dirtyList">
            <apex:column value="{!d.CharitableReceipt__c}"/>
            <apex:column value="{!d.Account.Name}"/>
            <apex:column value="{!d.Name}"/>
            <apex:column value="{!d.Amount}"/>
            <apex:column value="{!d.CloseDate}"/>
            <apex:column value="{!d.IsWon}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>   

    <apex:pageBlock >
        <apex:listViews type="Opportunity"/>
    </apex:pageBlock>  
    
</apex:page>

 

Controller Extension:

public with sharing class ReceiptBatch_controller{
    
    private ApexPages.StandardSetController itsStandardSetController;
    private List<Opportunity> itsSelectedOpps;
    private List<Opportunity> itsCleanOpps;
    private List<Opportunity> itsDirtyOpps;
    private List<CharitableReceipt__c> itsNewReceipts;
    private String itsValidationMessage;
    private ReceiptBatch_interface rbatch;

    public String receiptType{get;set;}
    public String addresseeType{get;set;}
    
    public List<SelectOption> addresseeTypeList{
        get{
            List<SelectOption> a = new List<SelectOption>();
            a.add(new SelectOption('Account','Account'));
            a.add(new SelectOption('Contact','Contact'));
            a.add(new SelectOption('Household','Household'));
            return a;
        }
    }
    
    public List<Opportunity> cleanOpps{
        get{return itsCleanOpps;}
        private set;
    }
    
    public List<Opportunity> dirtyOpps{
        get{return itsDirtyOpps;}
        private set;
    }  
    
    public String validationMessage{
        get{return itsValidationMessage;}
        private set;
    }
    
    public List<CharitableReceipt__c> newReceipts{
        get{return itsNewReceipts;}
        private set;
    }
    
    public ReceiptBatch_controller(ApexPages.StandardSetController sC){
        itsStandardSetController = sC;
        
        if (ApexPages.currentPage().getParameters().get('rtype') != null){
            receiptType = ApexPages.currentPage().getParameters().get('rtype');
        }
        
        if(receiptType == 'convert'){
rbatch = new ReceiptBatch_convertExisting();
} else if(receiptType == 'charitable'){ rbatch = new Receiptbatch_charitable(); } else { rbatch = new Receiptbatch_charitable(); } } public void validateOpps(){ itsSelectedOpps = itsStandardSetController.getSelected(); rbatch.setOpportunities(itsSelectedOpps); itsCleanOpps = rbatch.getCleanOpportunities(); itsDirtyOpps = rbatch.getDirtyOpportunities(); itsValidationMessage = rbatch.getValidationMessage(); } public void issueNew(){ rbatch.setAddresseeType(addresseeType); rbatch.issueReceipts(); itsNewReceipts = rbatch.getNewReceipts(); } }

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
Ashley@WCELAshley@WCEL

Figured it out.

 

I was treating custom list buttons as if they were visualforce form buttons. The form button on my visualforce page does a postback and triggers the view state mechanism, but when selecting the opportunities that need to be processed from a list, I use a custom opportunity list button which does a get request on the visualforce page and does not trigger the view state mechanism. So although the querystring parameter was being captured when I manually entered the URL, it was being lost again on the subsequent get request triggered by the custom button.

 

The solution was to set up two custom buttons, each pointing to the visualforce URL and each containing the appropriate recordType parameters (e.g. /apex/ReceiptBatch?rtype=convert and /apex/ReceiptBatch?rtype=charitable).  Works like a charm! And I can show or hide the temporary conversion button on the list view as needed.

 

Thank you for your debugging suggestions. They helped me think through the problem differently.

 

And to anyone else that finds this thread in the future, my description of view state above is basically correct although the hidden form field is not needed. You can just save the parameter to a member variable in the controller and as long as it is not marked as 'transient', it will persist in the view state. See here for a full explanation of how state is maintained on a visualforce page.

 

http://wiki.developerforce.com/page/An_Introduction_to_Visualforce_View_State

 

ashley

All Answers

vbsvbs
Couple of q's:
1. What is the value of the 'receiptType' variable in validateOpps function?
2. Have you added debug statements to confirm the calls in the methods getCleanOpportunities and getDirtyOpportunities to confirm what instance is being referred here?
Ashley@WCELAshley@WCEL

Thank you for your response. I have not added debug statements as you suggest but I can tell from the behaviour of the page that it is always referring to the default instance and not the one specified by the querystring.

 

Your questions gave me a debugging idea. I changed the inputHidden field to an inputText so that I could actually see the value of receyiptType as I click the action button and refresh the page. When I first load the Visualforce page using the querystring ?rtype=convert, this value gets stored in the input field as expected. But when I select an opportunity to process from the list view and click "Issue Receipt" (which simply posts the form data back to the same Visualforce page for confirmation), the value of receiptType disappears.

 

This leads me to question my understanding of how state is managed on a Visualforce page. Saving the querystring to a hidden form field was based on the belief that the" view state" mechanism in visualforce would save the controller's state so that it could be rebuilt in the same state when the page is posted to itself. Am I on the right path here in thinking that the controller can maintain state after a page refresh? If so, then it's just a bug in my implementation. If not, then my entire design is wrong. I guess my main question for this forum is: which is it?

Ashley@WCELAshley@WCEL

Figured it out.

 

I was treating custom list buttons as if they were visualforce form buttons. The form button on my visualforce page does a postback and triggers the view state mechanism, but when selecting the opportunities that need to be processed from a list, I use a custom opportunity list button which does a get request on the visualforce page and does not trigger the view state mechanism. So although the querystring parameter was being captured when I manually entered the URL, it was being lost again on the subsequent get request triggered by the custom button.

 

The solution was to set up two custom buttons, each pointing to the visualforce URL and each containing the appropriate recordType parameters (e.g. /apex/ReceiptBatch?rtype=convert and /apex/ReceiptBatch?rtype=charitable).  Works like a charm! And I can show or hide the temporary conversion button on the list view as needed.

 

Thank you for your debugging suggestions. They helped me think through the problem differently.

 

And to anyone else that finds this thread in the future, my description of view state above is basically correct although the hidden form field is not needed. You can just save the parameter to a member variable in the controller and as long as it is not marked as 'transient', it will persist in the view state. See here for a full explanation of how state is maintained on a visualforce page.

 

http://wiki.developerforce.com/page/An_Introduction_to_Visualforce_View_State

 

ashley

This was selected as the best answer