Wil je een intuïtieve gebruikersinterface ontwikkelen, dan is Lightning Web Components (LWC) de uitkomst! Zo kun je hele webapplicaties in de gebruikersinterface integreren in de nieuwste webstandaarden. Daarbij biedt het betere prestaties dan het Aura-framework en alle componenten zijn herbruikbaar! In deze use-case willen gebruikers eenvoudig een e-mailtemplate kunnen selecteren en de gekozen template moet opgeslagen kunnen worden en weer leeggehaald kunnen worden. Een mooie LWC use case om hier verder toe te lichten.
Front-end
Voordat we in de code gaan duiken, is het goed om te zien wat we gaan bouwen. Met deze combobox kan er een template geselecteerd worden en deze wordt vervolgens opgeslagen. Vervolgens kan het template Id gebruikt worden in Flows om de mail te versturen.Een nieuw LWC Project in Visual Studio Code
Dit project gaan we volledig in VS Code bouwen. We hebben onze sandbox al verbonden met VS Code en gaan nu via de ingebouwde terminal een nieuw LWC project maken.
sfdx force:lightning:component:create --type lwc --name emailTemplateSelector --output-dir force-app/main/default/lwc
Met deze code geef je aan dat er een nieuw LWC project gemaakt wordt in VS Code met de naam “emailTemplateSelector” in de directory “lwc”. Als het goed is heb je nu de volgende mappenstructuur:
MySalesforceProject
└───force-app
└───main
└───default
└───lwc
└───emailTemplateSelector
│ emailTemplateSelector.html
│ emailTemplateSelector.js
│ emailTemplateSelector.js-meta.xml
Zoals je ziet wordt er automatisch een .html, een .js en een .xml bestand aangemaakt. Deze bestanden gaan we zo meteen aanpassen naar de use case, maar we hebben ook nog een APEX class nodig. Deze class EmailTemplateController.cls
plaatsen we in de map classes
en daarbij maken we ook een .xml bestand aan: EmailTemplateController.cls-meta.xml
. We hebben nu dus 5 bestanden om mee te werken:
- emailTemplateSelector.html: Definieert de gebruikersinterface van de component.
- emailTemplateSelector.js: Bevat de logica en functionaliteit van de component.
- emailTemplateSelector.js-meta.xml: Specificeert metadata en waar de component gebruikt kan worden in Salesforce.
- EmailTemplateController.cls: Apex-klasse die server-side logica en data-handling biedt voor de component.
- EmailTemplateController.cls-meta.xml: Bevat metadata voor de Apex-klasse, zoals toegangsniveaus en API-versie.
APEX voor de LWC
Laten we starten met de APEX class en de bijbehorende .xml:
public with sharing class EmailTemplateController {
@AuraEnabled(cacheable=true)
public static List getEmailTemplates() {
return [SELECT Id, Name FROM EmailTemplate];
}
}
58.0
Active
Hier wordt een Apex-klasse genaamd EmailTemplateController
gedefinieerd die rekening houdt met de gedeelde beveiligingsregels van de gebruiker. De @AuraEnabled(cacheable=true) is een Apex-annotatie die aangeeft dat de methode toegankelijk moet zijn voor Lightning componenten, zoals Lightning Web Components (LWC) en Aura Components. Zonder deze annotatie kan de methode niet rechtstreeks vanuit deze componenten worden aangeroepen. Daarbij is de methode cachebaar om prestaties te verbeteren. Daarna wordt er een statische methode getEmailTemplates
gedefinieerd die een lijst van EmailTemplate
objecten retourneert.
In de xml staat alleen welke API-versie er gebruikt is en dat de status “Active” is, zodat de APEX-klasse gebruikt kan worden. Laten we gelijk kijken naar de andere xml, emailTemplateSelector.js-meta.xml.
58.0
true
lightning__AppPage
lightning__RecordPage
lightning__HomePage
lightning__FlowScreen
XML van de LWC
Dit bestand is niet ingewikkeld, maar wel heel belangrijk. Hier geef je namelijk aan waar het Lightning Web Component (LWC) geplaatst kan worden. De <isExposed> geeft aan dat het Lightning component openbaar is en beschikbaar is voor gebruik binnen Salesforce-toepassingen. Vervolgens worden de toepassingen daaronder gezet met de <target>Paginanaam</target>
. De <targetConfig>
-tag in een Lightning component bundel wordt gebruikt om specifieke configuratie-instellingen toe te passen op een bepaald doel (target) zoals een pagina of schermtype. Hier wordt aangegeven dat de target “lightning__RecordPage” geldt voor het object “Evenement__c”. Op een ander object zal dit dus niet werken. Mocht je de LWC voor meerdere objecten willen gebruiken kun je deze aanvullen, bijv zo:
58.0
true
lightning__AppPage
lightning__RecordPage
lightning__HomePage
lightning__FlowScreen
Het HTML-bestand van de LWC
Het HTML bestand ziet er als volgt uit:
Dit stukje code is een Lightning Web Component (LWC) template dat een formulier bevat voor het bewerken van records van het object “Evenement__c” in Salesforce. In het HTML-bestand zitten een aantal interessante coderegels. Om te beginnen de bovenste regel:
- object-api-name=”Evenement__c”: Specificeert dat het formulier bedoeld is voor het bewerken van records van het Salesforce-object “Evenement__c”.
- record-id={recordId}: Bindt de variabele
recordId
aan het attribuutrecord-id
, wat aangeeft welk specifiek record wordt bewerkt. - onsuccess={handleSuccess}: Definieert de functie
handleSuccess
die wordt aangeroepen wanneer het bewerken van het record succesvol is afgerond.
Lightning-messages toont eventuele foutmeldingen of succesberichten als deze er zijn. Dan hebben we de combobox, waar de keuze uit het template gemaakt wordt:
Het <lightning-combobox>
component is een Lightning Web Component (LWC) binnen Salesforce.
- name=”emailTemplates”: Geeft de naam van het combobox-veld aan, wat handig is bij het verwerken van formuliergegevens in JavaScript of Apex.
- label=”Select Email Template”: Deze naam wordt boven de combobox geplaatst.
- value={selectedTemplateId}: Bindt de variabele
selectedTemplateId
aan het geselecteerde item in het combobox-veld. Wanneer een gebruiker een optie selecteert, wordtselectedTemplateId
automatisch bijgewerkt met de waarde van de geselecteerde optie. - placeholder=”Select an Email Template”: Geeft een voorlopige tekst weer in het combobox-veld wanneer er nog geen optie is geselecteerd.
- options={emailTemplateOptions}: Dit attribuut verwijst naar een variabele
emailTemplateOptions
, die een lijst van opties bevat die beschikbaar zijn om te selecteren in het combobox-veld. Elke optie kan bijvoorbeeld een object zijn met eenlabel
envalue
. Dit komt terug in het javascript. - onchange={handleChange}: Dit definieert de JavaScript-functie
handleChange
, die wordt aangeroepen telkens wanneer de geselecteerde waarde in het combobox-veld verandert. Dit stelt je in staat om dynamisch te reageren op de keuzes van de gebruiker.
Verder worden er nog 2 knoppen gedefinieerd “Opslaan” en “Clear” die volgens mij voor zich spreken.
Javascript voor de LWC
En dan nu het laatste bestand, maar wel degene met de meeste code:
import { LightningElement, track, api, wire } from 'lwc';
import getEmailTemplates from '@salesforce/apex/EmailTemplateController.getEmailTemplates';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord, updateRecord } from 'lightning/uiRecordApi';
import EMAIL_TEMPLATE_FIELD from '@salesforce/schema/Evenement__c.EmailTemplate_Bevestiging__c';
const FIELDS = [EMAIL_TEMPLATE_FIELD];
export default class EmailTemplateSelector extends LightningElement {
@track emailTemplateOptions = [];
@track selectedTemplateId = '';
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
wiredRecord({ error, data }) {
if (data) {
this.selectedTemplateId = data.fields.EmailTemplate_Bevestiging__c.value;
} else if (error) {
console.error('Error fetching record:', error);
}
}
@wire(getEmailTemplates)
wiredEmailTemplates({ error, data }) {
if (data) {
this.emailTemplateOptions = data.map(template => {
return { label: template.Name, value: template.Id };
});
} else if (error) {
console.error('Error fetching email templates:', error);
}
}
handleChange(event) {
this.selectedTemplateId = event.detail.value;
}
handleSave() {
const fields = {};
fields[EMAIL_TEMPLATE_FIELD.fieldApiName] = this.selectedTemplateId;
fields['Id'] = this.recordId;
const recordInput = { fields };
updateRecord(recordInput)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Email Template saved successfully',
variant: 'success'
})
);
})
.catch(error => {
console.error('Error updating record:', error);
this.dispatchEvent(
new ShowToastEvent({
title: 'Error updating record',
message: error.body.message,
variant: 'error'
})
);
});
}
handleClear() {
const fields = {};
fields[EMAIL_TEMPLATE_FIELD.fieldApiName] = '';
fields['Id'] = this.recordId;
const recordInput = { fields };
updateRecord(recordInput)
.then(() => {
this.selectedTemplateId = '';
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Email Template cleared successfully',
variant: 'success'
})
);
})
.catch(error => {
console.error('Error updating record:', error);
this.dispatchEvent(
new ShowToastEvent({
title: 'Error updating record',
message: error.body.message,
variant: 'error'
})
);
});
}
}
Laten we bovenaan beginnen:
import { LightningElement, track, api, wire } from 'lwc';
import getEmailTemplates from '@salesforce/apex/EmailTemplateController.getEmailTemplates';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord, updateRecord } from 'lightning/uiRecordApi';
import EMAIL_TEMPLATE_FIELD from '@salesforce/schema/Evenement__c.EmailTemplate_Bevestiging__c';
const FIELDS = [EMAIL_TEMPLATE_FIELD];
import { LightningElement, track, api, wire } from 'lwc';
: Importeert basis Lightning Web Component functionaliteit zoals lifecycle hooks (LightningElement
), track decorator voor variabele wijzigingen (track
) (component opnieuw wordt gerenderd om de interface bij te werken), api decorator (api
) om gegevens door te geven aan het component vanuit een bovenliggende component of om gegevens uit de component te halen, en wire decorator voor wire-adapters (wire
) om gegevens van Salesforce Apex-methoden op te halen.import getEmailTemplates from '@salesforce/apex/EmailTemplateController.getEmailTemplates';
: Importeert de Apex-methodegetEmailTemplates
vanuit deEmailTemplateController
klasse om e-mailsjablonen op te halen van de server. Zie ook het APEX script.import { ShowToastEvent } from 'lightning/platformShowToastEvent';
: Importeert deShowToastEvent
klasse voor het tonen van toastberichten (kleine pop-upmeldingen of notificaties) in de gebruikersinterface.import { getRecord, updateRecord } from 'lightning/uiRecordApi';
: Importeert functies voor het ophalen (getRecord
) en bijwerken (updateRecord
) van Salesforce records via de UI API.import EMAIL_TEMPLATE_FIELD from '@salesforce/schema/Evenement__c.EmailTemplate_Bevestiging__c';
: Importeert het veldschemaEmailTemplate_Bevestiging__c
van het Salesforce-objectEvenement__c
. Dit stelt de component in staat om specifiek toegang te krijgen tot en dit veld te gebruiken om het Id in op te slaan.
const FIELDS = [EMAIL_TEMPLATE_FIELD];
: Definieert een constante array FIELDS
die het veldschema EMAIL_TEMPLATE_FIELD
bevat. Deze constante wordt vaak gebruikt bij het werken met recorddata om specifieke velden te specificeren die moeten worden opgehaald of bijgewerkt.
export default class EmailTemplateSelector extends LightningElement {
@track emailTemplateOptions = [];
@track selectedTemplateId = '';
@api recordId;
- export default: Om de code herbruikbaar te maken en de modulariteit te verbeteren, voegen we
export default
toe, zodat deze klasse standaard geëxporteerd wordt. Dit betekent dat wanneer een andere module deze module importeert, deze automatisch deze class zal importeren. - class EmailTemplateSelector:Dit definieert een nieuwe JavaScript class genaamd
EmailTemplateSelector
. Deze class definieert de logica en het gedrag van de component en kan in de HTML gebruikt worden. - extends LightningElement: De class
EmailTemplateSelector
erft vanLightningElement
, wat betekent dat het alle functionaliteit van een standaard Lightning Web Component heeft.LightningElement
is de basisclass voor alle LWCs en biedt methoden en eigenschappen die specifiek zijn voor het Lightning Web Component framework.
- De
@track
decorator wordt gebruikt om eigendommen (properties) reactief te maken dus zodra de waarde verandert, wordt het component opnieuw gerenderd. - emailTemplateOptions: Dit is een array die wordt getrackt om een lijst van e-mailsjabloonopties op te slaan.
- selectedTemplateId: Deze string wordt getrackt en houdt het ID bij van het geselecteerde e-mailsjabloon.
- De
@api
decorator wordt gebruikt om een eigenschap publiek te maken, zodat deze eigenschap toegankelijk is vanuit andere componenten of vanuit de omgeving waarin de component wordt gebruikt. Het recordId: is een publieke eigenschap die het ID van een record bevat.
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
- getRecord: Dit is een adapter die wordt geleverd door het Lightning Data Service (LDS) om gegevens van een specifiek Salesforce-record op te halen. Via de @wire kan APEX ontvangen worden.
- recordId: ‘$recordId’: De
$recordId
is een dynamische waarde die verwijst naar derecordId
eigenschap van het component. WanneerrecordId
verandert, wordt de wire service opnieuw uitgevoerd. - fields: FIELDS: Dit geeft de velden aan die moeten worden opgehaald voor het record.
FIELDS
is een constante array die de API-namen van de gewenste velden bevat. Zie ook de toelichting hierboven.
wiredRecord({ error, data }) {
if (data) {
this.selectedTemplateId = data.fields.EmailTemplate_Bevestiging__c.value;
} else if (error) {
console.error('Error fetching record:', error);
}
}
Deze functie controleert in eerste instantie of er data object aanwezig is. Zo niet wordt er een error in de console getoond. Als er wel een data object is, wordt de variabele selectedTemplateId (let op, daar zit een @track op) gevuld met de waarde uit het veld EmailTemplate_Bevestiging__c
.
@wire(getEmailTemplates)
wiredEmailTemplates({ error, data }) {
if (data) {
this.emailTemplateOptions = data.map(template => {
return { label: template.Name, value: template.Id };
});
} else if (error) {
console.error('Error fetching email templates:', error);
}
}
Wanneer de e-mailsjablonen succesvol zijn opgehaald, worden ze gemapt naar een array van objecten (bovenaan gedefinieerd) die elk een label
en value
property bevatten. Deze array wordt vervolgens toegewezen aan emailTemplateOptions
van het component.
handleChange(event) {
this.selectedTemplateId = event.detail.value;
}
De handleChange
-functie heeft betrekking op het bijwerken van de geselecteerde e-mailsjabloon wanneer een gebruiker een keuze maakt in een combobox. this.selectedTemplateId
wordt bijgewerkt met de waarde uit event.detail.value
. Dit betekent dat wanneer een gebruiker een nieuw e-mailsjabloon selecteert in de combobox, de selectedTemplateId
property van de component wordt bijgewerkt naar de ID van de geselecteerde sjabloon.
handleSave() {
const fields = {};
fields[EMAIL_TEMPLATE_FIELD.fieldApiName] = this.selectedTemplateId;
fields['Id'] = this.recordId;
const recordInput = { fields };
updateRecord(recordInput)
.then(() => {
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Email Template saved successfully',
variant: 'success'
})
);
})
.catch(error => {
console.error('Error updating record:', error);
this.dispatchEvent(
new ShowToastEvent({
title: 'Error updating record',
message: error.body.message,
variant: 'error'
})
);
});
}
- Een leeg object
fields
wordt aangemaakt (constante). - De
selectedTemplateId
wordt toegevoegd aan hetfields
object onder de key van de API naam van het e-mailsjabloon veld (EMAIL_TEMPLATE_FIELD.fieldApiName
). - Het
fields
object krijgt ook deId
van het record dat moet worden bijgewerkt, met de key ‘Id’ en de waardethis.recordId
. - Het
recordInput
object bevat hetfields
object. Dit is de structuur die nodig is voor deupdateRecord
functie. updateRecord(recordInput)
wordt aangeroepen om het record bij te werken met de nieuwe gegevens. Deze functie retourneert een promise.- Bij succes en bij error wordt er een Toast bericht getoond (standaard Salesforce melding).
- De handleClear() functie doet eigenlijk hetzelfde als de handleSave(), maar dan wordt “fields” leeggemaakt en opgeslagen.
Conclusie
Hopelijk zijn de stappen duidelijk en helpt dit om een LWC (Lightning Web Component) te bouwen van scratch. Dit component is in mijn use case nog niet klaar en krijgt nog een aantal updates, maar het doel van dit artikel was het maken van een LWC en anders wordt het volgens mij te complex. Vragen of opmerkingen, zet ze in de comments!