5

I've started work on implementing Visualforce pages to use in list view buttons using the approach How to convert mass actions on list view records from Classic to Lightning Experience?. We have many of these buttons, and they have lots in common to such an extent that there are just two constants that need to change from button to button. So while I need to create many pages, most of the rest of the code can be common.

The first approach was to do most of the work in a common component so each page could set the appropriate constants:

<apex:page
        standardController="MyObject__c"
        extensions="MyController"
        recordSetVar="unused"
        >
    <c:ListActions
            ...
            operation="MyObjectActions.move"
            value='last'
            ...
            />
</apex:page>

and that works fine for cases where the page is displayed.

But now for some cases, I'd like to not render the page. That can be done via the apex:page action attribute, once the work is done, returning the StandardSetController cancel() PageReference to go back to the list view page:

<apex:page
        standardController="MyObject__c"
        extensions="MyController"
        recordSetVar="unused"
        action="{! init }"
        >
</apex:page>

My problem is how to inject the two constants for this case. About the only attribute of apex:page that perhaps I could abuse is the extensions, but I'd have to create multiple marker classes. Or I could add many methods to the controller that set the required permutations of the constants. Or is there a better way to go?

PS

It isn't elegant but I guess I can live with:

<apex:page
        standardController="MyObject__c"
        extensions="MyController"
        recordSetVar="unused"
        action="{! execute_BenefitActions_move_last }"
        >

and:

public PageReference execute_BenefitActions_move_first() {
    return execute('BenefitActions.move', 'first');
}
public PageReference execute_BenefitActions_move_up() {
    return execute('BenefitActions.move', 'up');
}
public PageReference execute_BenefitActions_move_down() {
    return execute('BenefitActions.move', 'down');
}
public PageReference execute_BenefitActions_move_last() {
    return execute('BenefitActions.move', 'last');
}

...

private PageReference execute(String operation, String value) {
    this.operation = operation;
    this.value = value;
    ...

But before I add this ugliness, if you have a better approach, please share.

PPS

Query string parameters can't be used in this case - see Is a "Content Source" of "Visualforce Page" required for Ids for the checked list view items to be received by a StandardSetController?

  • How are you determining what those constants are? Are you keeping all those buttons? Would url parameters help? What is your entry point in the process? – Eric Aug 30 '18 at 4:50
  • @eric I have (naively) chosen the "Content Source" of "Visualforce Page" rather than "URL" and so was going to end up with many pages with the constants in each page. Seems a bit off to hard-code the '/apex' prefix of the page name in a URL but way cleaner otherwise. Is this something you do? – Keith C Aug 30 '18 at 7:02
  • @eric As sfdcx would say, this question suffers from the XY problem. Checking our code base, we already hard code Visualforce page URLs elsewhere... Duh. Post your comment as the answer and I will accept it. – Keith C Aug 30 '18 at 7:46
  • @eric Need to check if this solution will work; another dimension of this is that the type of the set controller needs defining so I may need a page per SObjectType to set the standardController="MyObject__c". – Keith C Aug 30 '18 at 10:39
  • If I am making a button to a VF page the yes I use the /apex all the time. If you are advertising to that, assuming I understand your requirement, create pages say Constant1, Constant2, Constant3. The sole purpose of those pages would be to redirect to the final page passing in the parameters. You could use a single controller, determine the Page name hit, then redirect to the main page with the appropriate parameters. This preserves the "Visualforce Page" of the button and removes the /apex as each button would hit the appropriate ConstantX redirect. – Eric Aug 30 '18 at 10:46
2

The best route I can think of - and I tried this out in a sandbox; it works - is introspecting off the Visualforce page's name itself, embedding the parameters therein via some delimiter.

While it's neither beautiful nor elegant, it does streamline the controller somewhat (no extra action methods) and allows the use of a single page per sObject, saved an arbitrary number of times under different names.

Visualforce Page TestPage_param_param2.vfp

<apex:page standardController="Account"
           extensions="TestController"
           recordSetVar="unused"
           action="{! runAction }">
</apex:page>

Controller TestController.apxc

public class TestController {
    private ApexPages.StandardSetController c;

    public TestController(ApexPages.StandardSetController c) {
        this.c = c;    
    }

    public PageReference runAction() {
        List<String> parameters = ApexPages.currentPage().getURL().substringAfterLast('/').substringBefore('?').split('_');

        System.assertEquals(3, parameters.size());
        String operation = parameters[1];
        String value = parameters[2];

        System.debug(String.format('Performing operation {0} with value {1} on selected records {2}', 
                                   new List<String> { operation, value, String.join(c.getSelected(), ', ') }));

        return c.cancel();
    }
}

Result

When run from a list view, this yields

16:32:07:113 USER_DEBUG [15]|DEBUG|Performing operation param with value param2 on selected records [Account (Id:0013600001sxEUmAAM, RecordTypeId:01236000000STbiAAG)]

  • I agree - just finished my first example of exactly that - Question_Move_Up. But I am also going to steal your use of substringAfterLast etc which is much clearer than the multiple split calls I am using. Thanks! – Keith C Aug 30 '18 at 20:54

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.