7

So, I was experimenting with ways to reduce CPU usage, and I came across a strange scenario: the StandardSetController did not seem to use the "prototype" object the way it should (that is, at all). Based on things I've seen around the Internet, I figured I was doing everything right, yet nothing seems to work.

Part 1: The Easy Way

First, I thought I'd go with the most direct route. Using the prototype object without doing anything fancy.

Account[] a = new Account[0];
while(a.size() < 100) a.add(new Account(Name='Test '+a.size()));
ApexPages.StandardSetController c = new ApexPages.StandardSetController(a);
Account t = (Account)c.getRecord();
t.Industry = 'Technology';
c.save();

This, I theorized, should create about 100 accounts, named Test 0 through Test 99, each with an Industry of Technology. The name was set correctly, but the Industry was not.

Part 2: The Slightly Windier Path

So, I next theorized that I couldn't do this on an insert, so I tried using an update:

Account[] a = new Account[0];
while(a.size() < 100) a.add(new Account(Name='Test '+a.size()));
ApexPages.StandardSetController c = new ApexPages.StandardSetController(a);
c.save();
Account t = (Account)c.getRecord();
t.Industry = 'Technology';
c.save();

Again, this didn't work.

Part 3: The Selected Path

After that, I thought perhaps I needed to "select" the records beforehand, so I also tried:

Account[] a = new Account[0];
while(a.size() < 100) a.add(new Account(Name='Test '+a.size()));
ApexPages.StandardSetController c = new ApexPages.StandardSetController(a);
c.save();
a = c.getRecords();
c.setSelected(a);
Account t = (Account)c.getRecord();
t.Industry = 'Technology';
c.save();

Again, the records would all be created, but they would again refuse to set the Industry value.

Part 4: The Queried Path

Finally, I thought I needed to use a query first, so I again altered my code:

Account[] a = new Account[0];
while(a.size() < 100) a.add(new Account(Name='Test '+a.size()));
ApexPages.StandardSetController c = new ApexPages.StandardSetController(a);
c.save();
a = [SELECT Id FROM Account WHERE Id IN :c.getRecords()];
c = new ApexPages.StandardSetController(a);
c.setSelected(a);
Account t = (Account)c.getRecord();
t.Industry = 'Technology';
c.save();

This, too, failed to work the way I expected. The prototype object seemed to be completely ignored. Is this feature broken, or does it only work in certain contexts? How exactly does one use the prototype object? Is there a way to make this work in triggers, execute anonymous, etc?


Edit: the documentation states that getRecord has the following purpose:

Returns the sObject that represents the changes to the selected records. This retrieves the prototype object contained within the class, and is used for performing mass updates.

However, I can't seem to actually get this to work as the documentation describes.

3
  • Is there a reason you are using getRecord() instead of getRecords()?
    – Lance Shi
    Mar 10, 2016 at 1:15
  • @LanceShi Because the documentation says I can use the object from getRecord to go about "performing mass updates." I've edited that in to the question.
    – sfdcfox
    Mar 10, 2016 at 2:25
  • 1
    it is possible (?) that the expression 'represents the changes to the selected records' implies usage in a VF context where the changes were xmitted via a POST to the controller - although I can't explain why name is right and industry isn't
    – cropredy
    Mar 10, 2016 at 2:36

1 Answer 1

1

Yep, seems to do absolutely nothin' via Apex. I played around with the order of operations and couldn't get anything to bind to the selected records either. Seems like a bug, but I would put money on support claiming WAD.

It seems like the only way to bind anything to the prototype is through Visualforce as demonstrated here. A cleaned up gist:

<apex:page standardController="Account" recordSetVar="accounts">
    <apex:form>
        <apex:pageBlock>
            <apex:commandButton value="Save" action="{!save}" />
            <apex:commandButton value="Cancel" action="{!cancel}" immediate="true" />
            <apex:pageBlockSection>
                <apex:inputField value="{!Account.Industry}">
                    <apex:actionSupport event="onchange" rerender="records"/>
                </apex:inputField>
            </apex:pageBlockSection>
            <apex:pageBlockTable value="{!accounts}" var="record" id="records">
                <apex:column value="{!record.Name}" />
                <apex:column value="{!record.Industry}" />
            </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Interestingly, if I add the following extension, the Industry change is reflected in the inputField, but not in the pageBlockTable.

public class MassUpdateExtension
{
    public MassUpdateExtension(ApexPages.StandardSetController controller) {
        controller.getRecord().put(Account.Industry, 'Agriculture');
    }
}

I even tried binding the prototype to a controller property. Updating it via the inputField works, but still no luck via Apex.

public class MassUpdateExtension
{
    public Account prototype { get; private set; }
    public MassUpdateExtension(ApexPages.StandardSetController controller) {
        prototype = controller.getRecord();
    }
    public PageReference updateIndustry()
    {
        prototype.Industry = 'Agriculture';
        return null;
    }
}

Neither of the following caused the recordSetVar to be affected:

<apex:page standardController="Account" recordSetVar="accounts"
    extensions="MassUpdateExtension" action="{!updateIndustry}">
    ....
</apex:page>

nor

<apex:page standardController="Account" recordSetVar="accounts"
    extensions="MassUpdateExtension">
    <apex:form>
        <apex:commandButton value="Update" action="{!updateIndustry}" />
        ...
    </apex:form>
</apex:page>
1
  • I remember looking at that SFDC help link and while getting it to work, was totally confused by the relationship between recordSetVar and getRecords() and the singular {!Account...} and getRecord(). There were use cases wherein magic happens and you don't need an extension but once you add an extension you ended up providing implementations of all the SSC methods to your VF page. I could simply be not smart enough.
    – cropredy
    Mar 10, 2016 at 3:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .