2

I'm using a StandardSetController to grab all the ListViews for the logged-in user and displaying them in a Lightning component. When I call the method via anonymous Apex, it works fine, but when I call it from my Lightning component, I get an "Internal Salesforce.com Error." (I don't think the "anonymous apex" part is relevant. I'll confirm by calling it from a test method and update this post.)

As an ugly work-around, I'm writing the result of the StandardSetController.getListViewOptions() method out to a visualforce page, parsing it, serializing it, and sending it back to the Lightning component. It's working fine, but I don't feel good about it! ;)

I'd love to understand what's causing the difference in behavior. I know there are some differences in user context between Lightning and regular o'l Salesforce -- different session IDs, for example -- but I can't think of any that would cause this...or what the "this" is! (There is no reference number with the internal sfdc error.)

Lightning Component Controller:

public with sharing class RecordHomeHeaderCtrl {
    public static final String START_MARKER = '###START###';
    public static final String END_MARKER = '###END###';

    @AuraEnabled
    public static String getAvailableListviews(String sObjectApiName){

        // When loaded, this visualforce page displays a JSON string containing the     
        // details of every listview available to the currently logged-in user.
        PageReference listviewsPage = Page.RecordHomeHeaderGetListviews;
        listviewsPage.getParameters().put('sobj', sObjectApiName);

        String pageContent = listviewsPage.getContent().toString();

        // Find the JSON string; remove everything else
        Integer startJson = pageContent.indexOf(START_MARKER) + START_MARKER.length();
        Integer endJson = pageContent.indexOf(END_MARKER);

        return pageContent.substring(startJson, endJson);

    }

Visualforce Page:

<apex:page controller="RecordHomeHeaderGetListviewsCtrl" showHeader="false" sidebar="false">
    {!startMarker}{!listviewsJson}{!endMarker}
</apex:page>

Visualforce Page Controller:

public with sharing class RecordHomeHeaderGetListviewsCtrl {
    public String listviewsJson {get; private set;}

    public String startMarker {get; private set;}
    public String endMarker {get; private set;}

    public RecordHomeHeaderGetListviewsCtrl() {
        // set a start and end string to make it easier to parse the returned data.
        startMarker = RecordHomeHeaderCtrl.START_MARKER;
        endMarker = RecordHomeHeaderCtrl.END_MARKER;

        String sObjectApiName = ApexPages.currentPage().getParameters().get('sobj');

        if(sObjectApiName == null){
            throw new RecordHomeHeaderGetListviewsException('An sObject API name was not included in the page querystring');
        }

        String.escapeSingleQuotes(sObjectApiName);

        // The StandardSetController takes a list of sObjects as a parameter
        String qString = 'SELECT Id FROM ' + sObjectApiName + ' LIMIT 1';
        list<sObject> objList = Database.query(qString);

        // Create an instance of the StandardSetController for this sObject. 
        ApexPages.StandardSetController stdSetCtrl = new ApexPages.StandardSetController(objList);
        list<SelectOptionWrapper> selectOptions = new list<SelectOptionWrapper>();

        for(System.SelectOption opt : stdSetCtrl.getListViewOptions()){
            if(!opt.getDisabled()){
                selectOptions.add(new SelectOptionWrapper(opt));
            }
        }

        // serialize the wrapped selectOptions, and store them in the listviewsJson property, 
        // which will be rendered (between the start and end marker strings) in the body of 
        // the RecordHomeHeaderGetListviews Visualforce page
        listviewsJson = JSON.serialize(selectOptions);

        system.debug('listviewsJson = ' + listviewsJson);

    }               

    public class SelectOptionWrapper{
        String label {get; private set;}
        String value {get; private set;}

        public selectOptionWrapper(System.SelectOption opt){
            this.label = opt.getLabel();
            this.value = opt.getValue();
        }
    }

    public class RecordHomeHeaderGetListviewsException extends Exception{ }

}
6

ApexPages.StandardSetController is designed to be run in the context of Visualforce and not Lightning Experience or async contexts (e.g. batchable, queueable, futures).

To get list view options, I recommend having your Apex controller run SOQL query on ListView object filtered as needed.

For example,

@AuraEnabled
public static List<ListView> getListViews( String objectName ) {
    return [
        SELECT
            Id, Name
        FROM
            ListView
        WHERE
            SObjectType = :objectName
        ORDER BY
            Name ASC
    ];
}

To query the records behind the list view, then you may need to make http callouts:

  • ui-api to the endpoint /services/data/{apiVersion}/ui-api/list-records/${listViewId}, or

  • rest-api to the endpoint /services/data/{apiVersion}/sobjects/{sobjectType}/listviews/{listViewID}/results

To render a list view, check out the lightning:listView component or perhaps customizing the display with a lightning:datatable component.

  • While this is the recommended approach, but the only issue here is that ListView does not provide the list views specific to a User. – Jayant Das Jul 11 '18 at 3:20
  • The SOQL query does respect the context user's access to the list views. The query will not return private list views not owned by the user, but rather only public or ones shared to the context user. To filter to ones that the context user "owns", then can add a filter on CreatedById. – Doug Ayers Jul 11 '18 at 3:38
  • +1, That’s a valid point. By just looking at the fields on the object, on the first instance thought it that way that there’s no filter criteria to filter based on the User. – Jayant Das Jul 11 '18 at 3:48
  • @JayantDas, honestly, I actually thought the same as you until you made your comment and I went to test it =) – Doug Ayers Jul 11 '18 at 3:49
  • This finding actually helps a lot! It’s not well documented and caused confusion. But now that you have verified it, it actually helps to build the Lightning equivalent of the “old school approach of StandardSetController#getListViewOptions()”. Honestly because of this reason, I was hesitant on answering this as wasn’t really able to test it out. Thanks for this perspective, really helpful! – Jayant Das Jul 11 '18 at 3:52

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.