My LWC Service Component
Introduction
Let’s start from the introduction and finding out why the service component approach is so popular in LWC. The top 5 key benefits of using a separate service component in Salesforce Lightning Web Components (LWC):
- Modularity: Encourages modular code for reusability.
- Clean Codebase: Keeps code organized and clean.
- Effective Testing: Simplifies unit testing.
- Reduced Redundancy: Eliminates data access duplication.
- Maintenance: Streamlines updates and maintenance.
My Sevice Component
I use it in almost every LWC related project. You don’t need an HTML or CSS here so the component will contain only the .js file.
├── service
│ ├── service.js
│ └── service.js-meta.xml
There is the skeleton of the service/utility component:
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export {
//
}
As you can see it pretty easy for understanding, we will use ShowToastEvent so we need to import it first. Now let’s add some useful functions:
let self;
const setSelfVariable = (thisVariable) => {
self = thisVariable;
}
self variable will store our this context. It will be used in function we will add further. setSelfVariable() function will set this variable. Next function is doApexCall(), this is the main function for imperative apex calls:
const doApexCall = (apexMethod, params, callback) => {
apexMethod(params)
.then(result => { callback(result) } )
.catch(error => {
showLog(error);
hideSpinner();
showErrorMessage(reduceError(error));
});
}
As you can see doApexCall() uses some function that are were not added yet. showSpinner() and hideSpinner() will interact with spinnerClass var on the main component, showLog is simply put log to the browser log console.
const showSpinner = () => {
self.spinnerClass = 'slds-show';
}
const hideSpinner = () => {
self.spinnerClass = 'slds-hide';
}
const showLog = (message) => {
self.proxyErrorMessage = JSON.stringify(message);
console.log('Apex or Wire log === ', message);
}
const showInfoMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'info'
});
self.dispatchEvent(event);
}
const showErrorMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'error'
});
self.dispatchEvent(event);
}
const showWarningMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'warning'
});
self.dispatchEvent(event);
}
const showSuccessMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'success'
});
self.dispatchEvent(event);
}
showInfoMessage() and other show functions should be used to make a Toast Event notification.
const validateEmail = (email) => {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let isValid = re.test(String(email).toLowerCase());
if (!isValid) {
showErrorMessage( 'Error', 'Email validation error!');
}
return isValid;
}
const validatePhoneNumber = (phone) => {
const re = /^\(\d{3}\)-\d{3}-\d{4}$/;
let isValid = re.test(String(phone));
if (!isValid) {
showErrorMessage( 'Error', 'Phone number validation error!');
}
return isValid;
}
const reduceError = (errors) => {
if (!Array.isArray(errors)) {
errors = [errors];
}
return (
errors
.filter(error => !!error)
.map(error => {
if (Array.isArray(error.body)) {
return error.body.map(e => e.message);
}
else if (error.body && typeof error.body.message === 'string') {
return error.body.message;
}
else if (typeof error.message === 'string') {
return error.message;
}
return error.statusText;
})
.reduce((prev, curr) => prev.concat(curr), [])
.filter(message => !!message)
.join(',')
);
}
There are some function for the email and phone validation. And the third one is from the LWC Recipes. Salesforce suggest to use it for error handling.
Before moving to the next point. We need to add export for the all function we have.
export {
setSelfVariable,
showSpinner,
hideSpinner,
showLog,
showInfoMessage,
showErrorMessage,
showWarningMessage,
showSuccessMessage,
validateEmail,
validatePhoneNumber,
reduceError,
doApexCall,
}
At the end our service component should look like this:
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export {
setSelfVariable,
showSpinner,
hideSpinner,
showLog,
showInfoMessage,
showErrorMessage,
showWarningMessage,
showSuccessMessage,
validateEmail,
validatePhoneNumber,
reduceError,
doApexCall,
}
let self;
const setSelfVariable = (thisVariable) => {
self = thisVariable;
}
const showSpinner = () => {
self.spinnerClass = 'slds-show';
}
const hideSpinner = () => {
self.spinnerClass = 'slds-hide';
}
const showLog = (message) => {
self.proxyErrorMessage = JSON.stringify(message);
console.log('Apex or Wire log === ', message);
}
const showInfoMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'info'
});
self.dispatchEvent(event);
}
const showErrorMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'error'
});
self.dispatchEvent(event);
}
const showWarningMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'warning'
});
self.dispatchEvent(event);
}
const showSuccessMessage = (title, message) => {
const event = new ShowToastEvent({
title: title,
message: message,
variant: 'success'
});
self.dispatchEvent(event);
}
const validateEmail = (email) => {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let isValid = re.test(String(email).toLowerCase());
if (!isValid) {
showErrorMessage( 'Error', 'Email validation error!);
}
return isValid;
}
const validatePhoneNumber = (phone) => {
const re = /^\(\d{3}\)-\d{3}-\d{4}$/;
let isValid = re.test(String(phone));
if (!isValid) {
showErrorMessage( 'Error', 'Phone number validation error!');
}
return isValid;
}
const reduceError = (errors) => {
if (!Array.isArray(errors)) {
errors = [errors];
}
return (
errors
.filter(error => !!error)
.map(error => {
if (Array.isArray(error.body)) {
return error.body.map(e => e.message);
}
else if (error.body && typeof error.body.message === 'string') {
return error.body.message;
}
else if (typeof error.message === 'string') {
return error.message;
}
return error.statusText;
})
.reduce((prev, curr) => prev.concat(curr), [])
.filter(message => !!message)
.join(',')
);
}
const doApexCall = (apexMethod, params, callback) => {
apexMethod(params)
.then(result => { callback(result) } )
.catch(error => {
showLog(error);
hideSpinner();
showErrorMessage(reduceError(error));
});
}
Conclusion
My Service Component has been at the heart of my LWC projects, transforming the way I approach development tasks. With an efficiency, this component enables you to harness the full potential of LWC, ensuring your applications shine on both mobile and desktop platforms.