Creating a Custom List View in Salesforce Using LWC and Apex

In this blog post, we’ll guide you through the process of creating a custom list view in Salesforce using Lightning Web Components (LWC) and Apex. This will enable you to fetch, display, and print Task records based on specific filters. We’ll cover the step-by-step development of the Apex controller, LWC component, and the creation of a list layout button to enhance your Salesforce interface.

Prerequisites

  • Basic understanding of Salesforce development.
  • Familiarity with Apex and Lightning Web Components.
  • Access to a Salesforce org with appropriate permissions.

Step 1: Create a Visualforce Page

Start by creating a Visualforce page that connects to the LWC, named ListviewPage.

htmlCopy code<apex:page standardController="Task" recordSetVar="tasks" extensions="CustomListViewInLwcCtrl">
    <apex:includeLightning />
    <style>
        #lightning {
            height: 100vh;
        }
        @media print {
            #lightning {
                height: auto;
                overflow: visible;
            }
            .print-section {
                height: auto;
                overflow: visible;
            }
        }
    </style>
    <div id="lightning"></div>
    <script>
        console.log('work6');
        var filterId = '{!filterId}';
        console.log('Filter ID:', filterId);

        $Lightning.use(
            "c:ExampleLWCApp",
            function() {
                $Lightning.createComponent(
                    "c:listviewpage",
                    {
                        'filterId': filterId
                    },
                    "lightning"
                );
            }
        );
    </script>
</apex:page>

Step 2: Create an Aura Component

htmlCopy code<aura:application extends="ltng:outApp">
    <aura:dependency resource="listviewpage" />
</aura:application>

Step 3: Create an Apex Controller

Next, you’ll need an Apex controller to manage the fetching of list views and their associated records.

apexCopy codepublic with sharing class CustomListViewInLwcCtrl {
    private String filterId;

    public CustomListViewInLwcCtrl(ApexPages.StandardSetController controller) {
        filterId = controller.getFilterId();
        System.debug('FilterId--> ' + filterId);
    }

    public String getFilterId() {
        return filterId;
    }

    @AuraEnabled(cacheable = true)
    public static List<ListView> fetchTaskListView(String objectApiName) {
        try {
            return [
                SELECT Id, Name, DeveloperName
                FROM ListView
                WHERE SObjectType = :objectApiName
                ORDER BY DeveloperName ASC
            ];
        } catch (Exception e) {
            System.debug('Error fetching list views: ' + e.getMessage());
            return new List<ListView>();
        }
    }

    @AuraEnabled(cacheable = true)
    public static List<sObject> getTaskListviewRecord(String objectName, String listViewId, String limitsize, String offsize) {
        // Logic to fetch Task records
    }

    @AuraEnabled(cacheable = true)
    public static List<Map<String, String>> getTaskListviewLabel(String objectName, String listViewId) {
        // Logic to fetch Task record labels
    }
}

Step 4: Create a Lightning Web Component

Create the LWC listviewPage that will interact with the Apex controller.

HTML Template

htmlCopy code<template>
    <div class="slds-grid slds-wrap" style="width: 280px;">
        <div class="slds-m-around_medium">
            <lightning-combobox
                name="listViewSelect"
                label="Select List View"
                value={selectedListView}
                placeholder="Select a List View"
                options={listViewOptions}
                onchange={handleListViewChange}>
            </lightning-combobox>
        </div>
    </div>
    <br>
    <div class="slds-grid slds-wrap">
        <div class="slds-col slds-size_1-of-4"></div>
        <div class="slds-col slds-size_3-of-4 slds-text-align_right">
            <lightning-button variant="brand" label="Print" onclick={handlePrint}></lightning-button>
        </div>
    </div>
    <br>
    <div if:true={isLoading}>
        <lightning-spinner alternative-text="Loading"></lightning-spinner>
    </div>
    <template if:false={isLoading}>
        <div class="print-section">
            <template if:true={records.length}>
                <lightning-datatable key-field="Id" data={records} columns={columns} hide-checkbox-column></lightning-datatable>
                <div class="slds-m-top_medium slds-text-align_center">
                    <lightning-button-group>
                        <lightning-button class="previous" label="Previous" onclick={handlePrevious} disabled={disablePrevious}></lightning-button>
                        <lightning-button class="next" label="Next" onclick={handleNext} disabled={disableNext}></lightning-button>
                    </lightning-button-group>
                </div>
            </template>
            <template if:false={records.length}>
                <div class="slds-text-align_center">
                    No records to display
                </div>
            </template>
        </div>
    </template>
</template>

JavaScript Controller

javascriptCopy codeimport { LightningElement, track, wire, api } from 'lwc';
import fetchListView from '@salesforce/apex/CustomListViewInLwcCtrl.fetchTaskListView';
import getTaskListviewRecord from '@salesforce/apex/CustomListViewInLwcCtrl.getTaskListviewRecord';
import getTaskListviewLabel from '@salesforce/apex/CustomListViewInLwcCtrl.getTaskListviewLabel';

const PAGE_SIZE = 100;

export default class ListviewPage extends LightningElement {
    @api filterId;
    @track listViewOptions = [];
    @track selectedListView = '';
    @track records = [];
    @track columns = [];
    @track isLoading = true;
    @track limitsize = PAGE_SIZE;
    @track offset = 0;

    connectedCallback() {
        console.log('Filter ID:', this.filterId);
    }

    @wire(fetchListView, { objectApiName: 'Task' })
    fetchListViewHandler({ data, error }) {
        if (data) {
            this.listViewOptions = data.map(listView => ({
                label: listView.Name,
                value: listView.Id
            }));
            this.selectedListView = this.filterId || this.listViewOptions[0].value;
            this.fetchRecords();
        } else if (error) {
            console.error('Error fetching list views:', error);
        }
    }

    fetchRecords() {
        this.isLoading = true;
        getTaskListviewRecord({
            objectName: 'Task',
            listViewId: this.selectedListView,
            limitsize: this.limitsize.toString(),
            offsize: this.offset.toString()
        })
        .then(result => {
            console.log(`${result.length} records`, result);
            this.records = this.formatRecords(result);
            this.fetchLabels();
        })
        .catch(error => {
            console.error('Error fetching records:', error);
            this.records = [];
            this.isLoading = false;
        });
    }

    formatRecords(records) {
        return records.map((record, index) => ({
            ...record,
            Count: this.offset + index + 1,
            // Additional field mappings
        }));
    }

    fetchLabels() {
        getTaskListviewLabel({
            objectName: 'Task',
            listViewId: this.selectedListView
        })
        .then(labels => {
            this.columns = [
                { label: ' ', fieldName: 'Count', type: 'number' },
                ...labels.map(labelInfo => ({
                    label: labelInfo.label,
                    fieldName: labelInfo.fieldApiName,
                    type: 'text'
                }))
            ];
            this.isLoading = false;
        })
        .catch(error => {
            console.error('Error fetching labels:', error);
            this.isLoading = false;
        });
    }

    handleListViewChange(event) {
        this.selectedListView = event.detail.value;
        this.offset = 0;
        this.fetchRecords();
    }

    handlePrint() {
        if (confirm('Are you sure you want to print?')) {
            window.print();
        }
    }

    handlePrevious() {
        if (this.offset >= PAGE_SIZE) {
            this.offset -= PAGE_SIZE;
            this.fetchRecords();
        }
    }

    handleNext() {
        this.offset += PAGE_SIZE;
        this.fetchRecords();
    }

    get disablePrevious() {
        return this.offset === 0;
    }

    get disableNext() {
        return this.records.length < PAGE_SIZE;
    }
}

Step 5: Handle Remote Site Settings

To allow your Apex class to make callouts, add your Salesforce org’s URL to the Remote Site Settings:

  1. Navigate to Setup.
  2. Search for “Remote Site Settings” in the Quick Find box.
  3. Click “New Remote Site.”
  4. Fill in the required fields (e.g., Remote Site Name: MyOrgSite, Remote Site URL: https://myorg.my.salesforce.com).
  5. Save the settings.

Step 6: Create a List Layout Button

To create a button that opens your custom list view Visualforce page:

  1. Go to Salesforce Setup.
  2. Navigate to Object Manager and select the Task object.
  3. Go to Buttons, Links, and Actions.
  4. Click “New Button or Link.”
  5. Choose the appropriate settings:
    • Label: Custom List View
    • Name: Custom_List_View
    • Display Type: Detail Page Button
    • Behavior: Display in a new window
    • Content Source: URL
  6. Use the following URL as the content source: /apex/ListviewPage?filterId={!Task.FilterId}

Conclusion

This custom list view component in Salesforce allows for enhanced record handling, display, and printing, offering greater flexibility than the standard list views. By leveraging LWC and Apex, you can create a tailored experience for your users, improving their efficiency and overall satisfaction.

If this was tl;dr, contact Tectonic for assistance today.

Related Posts
Who is Salesforce?
Salesforce

Who is Salesforce? Here is their story in their own words. From our inception, we've proudly embraced the identity of Read more

Salesforce Marketing Cloud Transactional Emails
Salesforce Marketing Cloud

Salesforce Marketing Cloud Transactional Emails are immediate, automated, non-promotional messages crucial to business operations and customer satisfaction, such as order Read more

Salesforce Unites Einstein Analytics with Financial CRM
Financial Services Sector

Salesforce has unveiled a comprehensive analytics solution tailored for wealth managers, home office professionals, and retail bankers, merging its Financial Read more

AI-Driven Propensity Scores
AI-driven propensity scores

AI plays a crucial role in propensity score estimation as it can discern underlying patterns between treatments and confounding variables Read more