• WHO WE ARE
  • WHAT WE DO
    • Salesforce
      • Implementations
        • Sales Cloud
        • Service Cloud
        • CPQ
        • Field Service Lightning
        • Field Service for SMEs
      • Developments
        • Salesforce Customization
        • Custom Application Development
        • AppExchange Product Development
      • Migrations
        • Classic to Lightning Migration
        • Other Systems to Salesforce Migration
      • Integrations
    • Data Science
      • BI Solutions
      • AI/ML solutions
      • Agentic AI
  • HOW WE DO
    • Delivery Model
    • Our Works
  • REACH US
    • Contact Us
    • Careers
  • BLOG
    • WHO WE ARE
    • WHAT WE DO
      • Salesforce
        • Implementations
          • Sales Cloud
          • Service Cloud
          • CPQ
          • Field Service Lightning
          • Field Service for SMEs
        • Developments
          • Salesforce Customization
          • Custom Application Development
          • AppExchange Product Development
        • Migrations
          • Classic to Lightning Migration
          • Other Systems to Salesforce Migration
        • Integrations
      • Data Science
        • BI Solutions
        • AI/ML solutions
        • Agentic AI
    • HOW WE DO
      • Delivery Model
      • Our Works
    • REACH US
      • Contact Us
      • Careers
    • BLOG
  • [email protected]
  • (+91) 44-49521562
Merfantz - Salesforce Solutions for SMEs
Merfantz - Salesforce Solutions for SMEs
  • WHO WE ARE
  • WHAT WE DO
    • Salesforce
      • Implementations
        • Sales Cloud
        • Service Cloud
        • CPQ
        • Field Service Lightning
        • Field Service for SMEs
      • Developments
        • Salesforce Customization
        • Custom Application Development
        • AppExchange Product Development
      • Migrations
        • Classic to Lightning Migration
        • Other Systems to Salesforce Migration
      • Integrations
    • Data Science
      • BI Solutions
      • AI/ML solutions
      • Agentic AI
  • HOW WE DO
    • Delivery Model
    • Our Works
  • REACH US
    • Contact Us
    • Careers
  • BLOG

Enhancing Salesforce List Views with Lightning Web Components and Apex

  • June 27, 2024
  • Tech Blogger
  • Salesforce Developments Tutorial, Salesforce Lightning
  • 0

Introduction:

In this blog post, we’ll explore how to create 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 will walk through the Apex controller and LWC code step by step. Additionally, we’ll cover how to create a list layout button, a custom list view, and how to pass the current list view ID to an LWC.

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

First, we need a Visualforce page to connect to the LWC (ListviewPage).

<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

<aura:application extends=”ltng:outApp” >
<aura:dependency resource=’listviewpage’/>
</aura:application>

Step 3: Create an Apex Controller

First, we need an Apex controller to handle the fetching of list views and their records. Here’s the CustomListViewInLwcCtrl Apex controller:

public with sharing class CustomListViewInLwcCtrl {
private String filterId;
    public CustomListViewInLwcCtrl(ApexPages.StandardSetController controller) {
        // Assuming you’re passing a filter ID as a parameter
        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) {
        List<sObject> result = new List<sObject>();
        try {
            // Construct endpoint URL
            String baseUrl = URL.getOrgDomainUrl().toExternalForm();
            String endPointURL = baseUrl + ‘/services/data/v50.0/sobjects/’ + objectName + ‘/listviews/’ + listViewId + ‘/describe’;
            // Create HTTP request
            HttpRequest req = new HttpRequest();
            req.setEndpoint(endPointURL);
            req.setMethod(‘GET’);
            // Check if session ID is valid
            String sessionId = UserInfo.getSessionId();
            if (String.isEmpty(sessionId)) {
                System.debug(‘Invalid session ID’);
                return result;
            }
            req.setHeader(‘Authorization’, ‘Bearer ‘ + sessionId);
            // Send HTTP request
            Http http = new Http();
            HttpResponse response = http.send(req);
            if (response.getStatusCode() == 200) {
                Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
                System.debug(‘View—>’ + responseMap);
                if (responseMap.containsKey(‘query’)) {
                    String query = (String) responseMap.get(‘query’);
                    // Build the query dynamically with limitsize and offsize
                    query += ‘ LIMIT ‘ + limitsize + ‘ OFFSET ‘ + offsize;//OFFSET remining record to jump
                    System.debug(‘query==>’ + query);
                    // Execute the query
                    result = Database.query(query);
                    System.debug(‘record size–>’ + result.size());
                } else {
                    System.debug(‘Error: Expected “query” key not found in response.’);
                }
            } else {
                System.debug(‘HTTP Status Code: ‘ + response.getStatusCode());
            }
        } catch (Exception e) {
            System.debug(‘Error fetching list view records: ‘ + e.getMessage());
        }
        return result;
    }
    @AuraEnabled(cacheable = true)
    public static List<Map<String, String>> getTaskListviewLabel(String objectName, String listViewId) {
        List<Map<String, String>> labels = new List<Map<String, String>>();
        try {
            // Construct endpoint URL
            String baseUrl = URL.getOrgDomainUrl().toExternalForm();
            String endPointURL = baseUrl + ‘/services/data/v50.0/sobjects/’ + objectName + ‘/listviews/’ + listViewId + ‘/describe’;
            // Create HTTP request
            HttpRequest req = new HttpRequest();
            req.setEndpoint(endPointURL);
            req.setMethod(‘GET’);
            // Check if session ID is valid
            String sessionId = UserInfo.getSessionId();
            if (sessionId == null || sessionId == ”) {
                system.debug(‘Invalid session ID’);
            }
            req.setHeader(‘Authorization’, ‘Bearer ‘ + sessionId);
            // Send HTTP request
            Http http = new Http();
            HttpResponse response = http.send(req);
            if (response.getStatusCode() == 200) {
                Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
                System.debug(‘View—>’ + responseMap);
                if (responseMap.containsKey(‘columns’)) {
                    List<Object> columns = (List<Object>) responseMap.get(‘columns’);
                    for (Object column : columns) {
                        Map<String, Object> columnMap = (Map<String, Object>) column;
                        Boolean isSortable = (Boolean) columnMap.get(‘sortable’);
                        if (isSortable != null && isSortable) {
                            String fieldNameOrPath = (String) columnMap.get(‘fieldNameOrPath’);
                            String label = (String) columnMap.get(‘label’);
                            Map<String, String> labelInfo = new Map<String, String>();
                        labelInfo.put(‘label’, label);
                            if(fieldNameOrPath==’Who.Name’){
                                labelInfo.put(‘fieldApiName’, ‘WhoId’);
                            }else if(fieldNameOrPath==’What.Name’){
                                labelInfo.put(‘fieldApiName’, ‘WhatId’);
                            }else if(fieldNameOrPath==’Owner.NameOrAlias’){
                                labelInfo.put(‘fieldApiName’, ‘NameOrAlias’);
                            }else if(fieldNameOrPath==’LastModifiedBy.Alias’){
                                labelInfo.put(‘fieldApiName’, ‘LastModifiedBy’);
                            }else if(fieldNameOrPath==’Account__r.Name’){
                                labelInfo.put(‘fieldApiName’, ‘Account__c’);
                            }else if(fieldNameOrPath==’Owner.Name’){
                                labelInfo.put(‘fieldApiName’, ‘Owner’);
                            }else{
                        labelInfo.put(‘fieldApiName’, fieldNameOrPath);
                        }
                        labels.add(labelInfo);
                            system.debug(‘Key Val1’+labels);
                        }
                    }
                }
            } else {
                System.debug(‘HTTP Status Code: ‘ + response.getStatusCode());
            }
        } catch (Exception e) {
            System.debug(‘Error fetching list view labels: ‘ + e.getMessage());
        }
        return labels;
    }
}

Step 4: Create a Lightning Web Component

Next, let’s create the Lightning Web Component that will interact with our Apex controller to fetch and display the Task records. Here’s the listviewPage LWC:

HTML Template

<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”>
        <!– Image column –>
        <div class=”slds-col slds-size_1-of-4″>
        </div>
        <!– Print button column –>
        <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>
                <!– Pagination buttons –>
                <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

import { 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; // Adjust this based on your pagination needs
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 ID5:’, this.filterId);
    }
    @wire(fetchListView, { objectApiName: ‘Task’ })
    fetchListViewHandler({ data, error }) {
        if (data) {
            this.listViewOptions = data.map(listView => ({
                label: listView.Name,
                value: listView.Id
            }));
            if (this.filterId) {
                this.selectedListView = this.filterId;
            } else {
                this.selectedListView = 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() // Pass offset correctly
        })
        .then(result => {
            console.log(result.length + ‘ records’, result);
            const startIndex = this.offset + 1;
            this.records = result.map((record, index) => ({
                …record,
                Count: startIndex + index,
                WhatId: record.What ? record.What.Name : ”,
                WhoId: record.Who ? record.Who.Name : ”,
                Owner: record.Owner ? record.Owner.Name : ”,
                Subject: record.Subject ? record.Subject : ”,
                Status: record.Status ? record.Status : ”,
                Priority: record.Priority ? record.Priority : ”,
                NameOrAlias: record.Owner ? record.Owner.NameOrAlias : ”,
                LastModifiedBy: record.LastModifiedBy ? record.LastModifiedBy.Alias : ”,
                LastModifiedDate: record.LastModifiedDate ? this.formatDate(record.LastModifiedDate) : ”,
                TaskDescription: record.Task_Description ? record.Task_Description : ”,
                Account__c: record.Account__r ? record.Account__r.Name : ”,
                Inventory__c: record.Inventory__r ? record.Inventory__r.Name : ”,
                Phone__c: record.Phone__c ? record.Phone__c : ”,
                Invoice_Follow_Up_Stage__c: record.Invoice_Follow_Up_Stage__c ? record.Invoice_Follow_Up_Stage__c : ”,
                Terms__c: record.Terms__c ? record.Terms__c : ”,
                Sage_100_SO_Invoice_Number__c: record.Sage_100_SO_Invoice_Number__c ? record.Sage_100_SO_Invoice_Number__c : ”,
                Product_Name__c: record.Product_Name__c ? this.parsedValue(record.Product_Name__c) : ”,
                Production_Type__c: record.Production_Type__c ? record.Production_Type__c : ”,
                Stock_Number__c: record.Stock_Number__c ? record.Stock_Number__c : ”,
                Tech_Name__c: record.Tech_Name__c ? record.Tech_Name__c : ”,
                Priority__c: record.Priority__c ? record.Priority__c : ”,
                Tube_Tech_Name__c: record.Tube_Tech_Name__c ? record.Tube_Tech_Name__c : ”,
                RelatedTo_Type__c: record.RelatedTo_Type__c ? record.RelatedTo_Type__c : ”,
                RecordType: record.RecordType ? record.RecordType.Name : ”,
            }));
            this.fetchLabels();
        })
        .catch(error => {
            console.error(‘Error fetching records:’, error);
            this.records = [];
            this.isLoading = false;
        });
    }
    parsedValue(htmlString) {
        const div = document.createElement(‘div’);
        div.innerHTML = htmlString;
        const anchorTag = div.querySelector(‘a’);
        return anchorTag ? anchorTag.textContent : htmlString;
    }
    formatDate(dateString) {
        const options = {
            month: ‘short’,
            day: ‘numeric’,
            year: ‘numeric’,
            hour: ‘numeric’,
            minute: ‘2-digit’,
            hour12: true
        };
        return new Date(dateString).toLocaleDateString(‘en-US’, options);
    }
    fetchLabels() {
        getTaskListviewLabel({
            objectName: ‘Task’,
            listViewId: this.selectedListView
        })
        .then(labels => {
            console.log(‘Label’, labels);
            this.columns = [
                { label: ‘ ‘, fieldName: ‘Count’, type: ‘number’ }, // Add Count column
                …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; // Reset offset when list view changes
        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;
    }
}

PAGE_SIZE;}}

Handle Internal Errors

If an internal error occurs while fetching the records, we will display an appropriate error message to the user. The catch block in the fetchRecords method handles this by setting the error property.

Steps 1 Add Remote Site Settings

To allow your Apex class to make callouts, you need to add your Salesforce org’s URL to the Remote Site Settings. Here’s how to do it:

  1. Navigate to Setup:
    • Go to the Setup menu in Salesforce.
  2. Search for Remote Site Settings:
    • In the Quick Find box, type “Remote Site Settings” and select it.
  3. Create a New Remote Site:
    • Click the “New Remote Site” button.
    • Fill in the required fields:
      • Remote Site Name: Give it a name like “MyOrgSite”.
      • Remote Site URL: Enter your Salesforce org URL (e.g., https://myorg.my.salesforce.com).
    • Save the settings.

By adding the remote site settings, you ensure that your Salesforce org can make callouts to itself, which is necessary for the Apex class to function correctly.

Creating a List Layout Button

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

  1. Go to Salesforce Setup.
  2. Navigate to Object Manager.
  3. Select the Task object (or the desired object).
  4. Under Buttons, Links, and Actions, click New Button or Link.
  5. Fill in the details:
    • Label: Custom List View
    • Name: Custom_List_View
    • Display Type: List Button
    • Behavior: Display in existing window without sidebar or header
    • Content Source: URL
    • URL: /apex/customListViewPage
  6. Click Save.
  7. Add the button to the Search Layouts for the object.

Getting the Current List View ID in LWC

To get the current list view ID in the LWC, you can use the filterId parameter passed from the Visualforce page. This parameter is set in the connectedCallback method and used to fetch the relevant data.

Putting It All Together

  1. Create the Apex controller, Visualforce page, and LWC component with the functionalities described above.
  2. Deploy the code to your Salesforce org.
  3. Create the list layout button and add it to the object.
  4. Access the Visualforce page from the Lightning Experience.
  5. You should see a dropdown to select a list view, followed by the corresponding data displayed in a table format.

Final Output
custom list view

Conclusion

By following these steps, you can create a custom list view in Salesforce using Lightning Web Components and Apex. This solution allows you to fetch and display Task records dynamically and print the list view. Adjust the code as needed to fit your specific requirements.

How to Create Animation using Lightening Component

Aura Components Vs Lightning Web Components

Author Bio

Tech Blogger
+ Recent Posts
  • Comprehensive Overview of Salesforce Agent-Force Platform
    June 12, 2025
    Comprehensive Overview of Salesforce Agent-Force Platform
  • Salesforce GraphQL Revolutionizing Data Queries in CRM
    June 6, 2025
    Salesforce GraphQL: Revolutionizing Data Queries in CRM
  • Unlocking the Power of Dynamic Forms in Salesforce Lightning App Builder
    May 29, 2025
    Unlocking the Power of Dynamic Forms in Salesforce Lightning App Builder
  • Einstein Activity Capture Boosting Productivity & Relationship Intelligence
    May 22, 2025
    Einstein Activity Capture: Boosting Productivity & Relationship Intelligence
Tags: Salesforce Lightning DevelopmentVisualforce
  • Previous Top Challenges to Anticipate During Your Salesforce Migration
  • Next How to Embed a Salesforce Flow in a Visualforce Page
Merfantz Technologies is a leading Salesforce consulting firm dedicated to helping small and medium enterprises transform their operations and achieve their goals through the use of the Salesforce platform. Contact us today to learn more about our services and how we can help your business thrive.

Discover More

Terms and Conditions
Privacy Policy
Cancellation & Refund Policy

Contact Info

  • No 96, 2nd Floor, Greeta Tech Park, VSI Industrial Estate, Perungudi, Chennai 600 096, Tamil Nadu, INDIA
  • (+91) 44-49521562
  • [email protected]
  • 9:30 IST - 18:30 IST

Latest Posts

Comprehensive Overview of Salesforce Agent-Force Platform
Comprehensive Overview of Salesforce Agent-Force Platform June 12, 2025
Salesforce GraphQL Revolutionizing Data Queries in CRM
Salesforce GraphQL: Revolutionizing Data Queries in CRM June 6, 2025
Unlocking the Power of Dynamic Forms in Salesforce Lightning App Builder
Unlocking the Power of Dynamic Forms in Salesforce Lightning App Builder May 29, 2025

Copyright @2023 Merfantz Technologies, All rights reserved