How to Build a Versatile Lightning Web Component (LWC) for Dynamic Record Display in Salesforce
Are you looking to create a flexible and reusable Lightning Web Component (LWC) in Salesforce that can dynamically display record details for any object type? This blog post will guide you through building a powerful LWC that fetches and displays record data organized into layout sections, similar to Salesforce's standard page layouts. This approach ensures that the component can adapt to any object, providing a highly customizable and user-friendly solution.
Why Build a Generic LWC for Record Display?
Salesforce developers often need to create components that can work across multiple objects without hardcoding details for each specific object. A generic LWC can dynamically handle different object types and layouts, making it a highly efficient tool in your Salesforce toolkit. By leveraging Salesforce's getRecordUi
wire adapter, we can fetch both the layout and field details dynamically, ensuring maximum flexibility and reusability.
Step-by-Step Guide to Building the Component
Step 1: Create the LWC Component
First, create a new LWC component named recordDetailLayout
. The file structure for this component should look like this:
lwc/
└── recordDetailLayout/
├── recordDetailLayout.html
├── recordDetailLayout.js
└── recordDetailLayout.js-meta.xml
Step 2: Implement the Code
We'll need to create three files: recordDetailLayout.html
, recordDetailLayout.js
, and recordDetailLayout.js-meta.xml
. Let's dive into each file.
recordDetailLayout.html
This HTML file defines the component's layout using lightning-accordion
to display sections dynamically.
<template>
<lightning-card title="Record Details" icon-name="standard:record">
<template if:true={sectionsAvailable}>
<lightning-accordion allow-multiple-sections-open active-section-name={activeSections}>
<template for:each={sections} for:item="section">
<lightning-accordion-section label={section.heading} key={section.id} name={section.id}>
<div class="slds-grid slds-wrap slds-gutters">
<template for:each={section.layoutRows} for:item="row">
<template for:each={row.layoutItems} for:item="item">
<div key={item.key} class="slds-col slds-size_1-of-2 slds-p-around_x-small">
<div class="slds-form-element">
<label class="slds-form-element__label">{item.fieldLabel}</label>
<div class="slds-form-element__control">
<lightning-formatted-text value={item.fieldValue}></lightning-formatted-text>
</div>
</div>
</div>
</template>
</template>
</div>
</lightning-accordion-section>
</template>
</lightning-accordion>
</template>
<template if:true={error}>
<p class="slds-text-color_error slds-p-around_medium">{error}</p>
</template>
</lightning-card>
</template>
recordDetailLayout.js
This JavaScript file contains the logic for fetching record and layout data. It dynamically handles different objects by using the getRecordUi
wire adapter.
import { LightningElement, api, wire, track } from 'lwc';
import { getRecordUi } from 'lightning/uiRecordApi';
export default class RecordDetailLayout extends LightningElement {
@api recordId;
@api objectApiName;
@track sections = [];
@track sectionsAvailable = false;
activeSections = [];
error;
@wire(getRecordUi, { recordIds: '$recordId', layoutTypes: ['Full'], modes: ['View'] })
wiredRecordUI({ error, data }) {
if (data) {
try {
const objectLayouts = data.layouts?.[this.objectApiName];
const records = data.records?.[this.recordId]?.fields;
if (objectLayouts && records) {
const layoutId = Object.keys(objectLayouts)[0];
const layoutData = objectLayouts[layoutId];
if (layoutData?.Full?.View?.sections) {
this.sectionsAvailable = true;
this.sections = layoutData.Full.View.sections.map((section, sectionIndex) => ({
id: section.id,
heading: section.heading,
layoutRows: section.layoutRows.map((row, rowIndex) => ({
key: `row-${sectionIndex}-${rowIndex}`,
layoutItems: row.layoutItems.map((item, itemIndex) => {
const fieldApiName = item.layoutComponents.find(component => component.apiName)?.apiName;
const fieldLabel = item.label || '';
const fieldValue = fieldApiName ? records[fieldApiName]?.value : null;
return {
key: `item-${sectionIndex}-${rowIndex}-${itemIndex}`,
fieldLabel: fieldLabel,
fieldValue: fieldValue
};
})
}))
}));
// Set active sections to open the first two sections by default
this.activeSections = this.sections.slice(0, 2).map(section => section.id);
} else {
throw new Error('Full or View layout structure is missing.');
}
} else {
throw new Error('No layout or records found for the specified object API name and record ID.');
}
} catch (e) {
console.error('Error processing layout data:', e);
this.error = 'An error occurred while processing the layout data.';
}
} else if (error) {
console.error('Error fetching record layout data:', error);
this.error = 'Failed to retrieve record layout information.';
}
}
}
recordDetailLayout.js-meta.xml
This metadata file configures the component’s API version and availability in different contexts.
<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>Record Detail Layout</masterLabel>
<apiVersion>60.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__RecordHome</target>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<property name="recordId" type="String" label="Record Id"/>
<property name="objectApiName" type="String" label="Object API Name"/>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Salesforce Account Page Layout Example
Below is an example of a Salesforce Account page layout displayed in a modal. This layout shows various fields and sections relevant to account management.
How It Works
The component utilizes Salesforce's getRecordUi
wire adapter to fetch both the layout metadata and the record data dynamically. The fetched layout sections are then displayed using a lightning-accordion
, allowing users to easily navigate through different sections of a record.
Key Features:
- Dynamic Layout Handling: The component adapts to any Salesforce object by fetching layout and field details dynamically based on the
objectApiName
andrecordId
properties. - User-Friendly Interface: Displays record details in expandable sections, providing a clean and organized view.
- Error Handling: Robust error handling to manage scenarios where data retrieval fails or layout structures are missing.
Conclusion
By following this guide, you can create a versatile Lightning Web Component that dynamically displays record details for any Salesforce object. This generic LWC is not only flexible and reusable but also enhances user experience by organizing information into collapsible sections.
Feel free to customize this component further based on your specific needs or reach out if you have any questions. Happy coding!
0 Comments