Lightning Web Component First Example
Let’s discuss here the complete end to end application using the Lightning web components. This example, we will be seeing the application that will be used to maintain the issue log. The app which we will be building looks like below. Please refer to this link for environment setup.
1. Create an apex class
Create an Apex class that will fetch the list of issues from the database. This class fetches the list of issues from the database.
public with sharing class IssueController { @AuraEnabled(cacheable=true) public static List<Issue__c> getAllIssues() { return [SELECT Id, Name, Assigned_To__c, Description__c,Priority__c,Resolved__c FROM Issue__c Order by Name]; } }
2.Create an Issue Log LWC
Create a lightning component using the below SFDX command
This component will do
- Insert the new record into the issue log using wire services
- Fire an event with the newly created record.
- Show toast message on insert the new record.
sfdx force:lightning:component:create --type lwc --componentname issuelog --outputdir force-app\main\default\lwc
Use this code in issuelog.html
<template> <lightning-card title="Log Issue" icon-name="standard:record"> <div class="slds-m-around_medium"> <lightning-input label="Issue Description" type="text" onchange={handleChange} class="slds-m-bottom_x-small"></lightning-input> <lightning-combobox name="Priority" label="Priority" value="" placeholder="Select Priority" options={options} onchange={handleChange}></lightning-combobox> <lightning-button label="Log Issue" variant="brand" onclick={createIssue}></lightning-button> </div> </lightning-card> </template>
Use this code in issuelog.js
import { LightningElement, track, wire } from 'lwc'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; import { createRecord } from 'lightning/uiRecordApi'; import ISSUE_OBJECT from '@salesforce/schema/Issue__c'; import ASSIGNED_FIELD from '@salesforce/schema/Issue__c.Assigned_To__c'; import DESCRIPTION_FIELD from '@salesforce/schema/Issue__c.Description__c'; import PRIOITY_FIELD from '@salesforce/schema/Issue__c.Priority__c'; export default class Issuelog extends LightningElement { @track description; @track proirity; get options() { return [{ label: 'Low', value: 'Low' }, { label: 'Medium', value: 'Medium' }, { label: 'High', value: 'High' }, { label: 'Critical', value: 'Critical' }, ]; } handleChange(event) { if (event.target.label === 'Issue Description') { this.description = event.target.value; } if (event.target.label === 'Priority') { this.proirity = event.target.value; } } createIssue() { const fields = {}; fields[DESCRIPTION_FIELD.fieldApiName] = this.description; fields[PRIOITY_FIELD.fieldApiName] = this.proirity; const recordInput = { apiName: ISSUE_OBJECT.objectApiName, fields }; createRecord(recordInput) .then(issue => { console.log('issue' + JSON.stringify(issue)); const event = new CustomEvent('newrecord', { detail: { data: issue }, }); this.dispatchEvent(event); console.log('event fired'); this.dispatchEvent( new ShowToastEvent({ title: 'Success', message: 'Issue Logged with Record ', variant: 'success', }), ); this.description = ''; }) .catch(error => { console.log('error' + JSON.stringify(error, null, 2)); this.dispatchEvent( new ShowToastEvent({ title: 'Error creating record', message: error.message, variant: 'error', }), ); }); } }
Understand the issuelog.js
The below get method will be returning the lits of the options for combo box .insted of passing the hardcoded values to get method you can able to use the getPicklist Values wired Adapter
get options() { return [{ label: 'Low', value: 'Low' }, { label: 'Medium', value: 'Medium' }, { label: 'High', value: 'High' }, { label: 'Critical', value: 'Critical' }, ]; }
The below code will handle the change event from the description and priority fields changes.
handleChange(event) { if (event.target.label === 'Issue Description') { this.description = event.target.value; } if (event.target.label === 'Priority') { this.proirity = event.target.value; } }
The following code in the create issue method will be used to save the record and will be passed to createRecord wire adapter
const fields = {}; fields[DESCRIPTION_FIELD.fieldApiName] = this.description; fields[PRIOITY_FIELD.fieldApiName] = this.proirity; const recordInput = { apiName: ISSUE_OBJECT.objectApiName, fields };
createRecord will be used to save the record and in the same method, we will firing an event with a newly created record using the new CustomEvent.
createRecord(recordInput) .then(issue => { console.log('issue' + JSON.stringify(issue)); const event = new CustomEvent('newrecord', { detail: { data: issue }, }); this.dispatchEvent(event);
the following code will be used to dispatch the toast message to UI when the record is saved of failed
this.dispatchEvent( new ShowToastEvent({ title: 'Success', message: 'Issue Logged with Record ', variant: 'success', }), );
Use this issuelog.js-meta.xml file to configure the issue log.
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="helloWorld"> <apiVersion>45.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__AppPage</target> <target>lightning__RecordPage</target> <target>lightning__HomePage</target> </targets> </LightningComponentBundle>
3.Create an Issue List LWC
create a lightning component using the following sfdx command.
This component will be
- Container component for the issue log component
- Received an event that is the trigger by issue log component
- handler the resolved checkbox checked and unchecked and navigation logic.
sfdx force:lightning:component:create --type lwc --componentname issuelist --outputdir force-app\main\default\lwc
Use this issuelist.html code
<template> <lightning-card title="Add New Issue "> <c-issuelog onnewrecord={handleNewRecord}></c-issuelog> </lightning-card> <lightning-card title="List of Issues"> <div class="slds-m-horizontal_large"> <template if:true={issues}> <template if:true={issues}> <table class="slds-table slds-table_cell-buffer slds-no-row-hover slds-table_bordered"> <thead> <tr class="slds-line-height_reset"> <th class="" scope="col"> <div class="slds-truncate" title="Name">Name</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Description">Description</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Priority">Priority</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Resolved">Resolved</div> </th> </tr> </thead> <tbody> <template for:each={issues} for:item="issue"> <tr key={issue.Id}> <td data-label="Name"> <a value={issue.Id} data-recordid={issue.Id} onclick={handleNameClicked}> {issue.Name}</a> </td> <td data-label="Description"> {issue.Description__c}</td> <td data-label="Priority"> {issue.Priority__c}</td> <td data-label="Resolved"> <lightning-input data-recordid={issue.Id} type="checkbox" checked={issue.Resolved__c} value={issue.Resolved__c} onchange={handleChecked}></lightning-input> </td> </tr> </template> </tbody> </table> </template> <template if:false={issues.length}> No Data Found </template> </template> <template if:true={error}> Error While Loading data from Salesforce </template> </div> </lightning-card> </template>
use this code in issuelist.js
import { LightningElement, wire, api, track } from 'lwc'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; import { createRecord, generateRecordInputForUpdate, updateRecord, deleteRecord, } from 'lightning/uiRecordApi'; import { refreshApex, getSObjectValue } from '@salesforce/apex'; import getAllIssues from '@salesforce/apex/IssueController.getAllIssues'; import { NavigationMixin } from 'lightning/navigation'; export default class Issuelist extends NavigationMixin(LightningElement) { @track issues; @track error; wiredIssueResult; @track recordId; @api currentRecord @wire(getAllIssues) wiredIssue(result) { this.wiredIssueResult = result; if (result.data) { this.issues = result.data; this.error = undefined; } else if (result.error) { this.error = result.error; this.issues = undefined; } } handleNewRecord(evt) { return refreshApex(this.wiredIssueResult); } handleChecked(event) { event.preventDefault(); const recId = event.target.dataset.recordid; let record = { fields: { Id: recId, Resolved__c: event.target.checked, }, }; updateRecord(record) .then(() => { this.dispatchEvent( new ShowToastEvent({ title: 'Success', message: 'Record is updated', variant: 'success', }), ); }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: 'Error while updating record', message: error.message, variant: 'error', }), ); }); } handleNameClicked(event) { event.preventDefault(); const recId = event.target.dataset.recordid; this[NavigationMixin.Navigate]({ type: 'standard__recordPage', attributes: { recordId: recId, objectApiName: 'Issue__c', actionName: 'view' }, }); } }
understand the code
The following code is used to handle the event that is received from the issue log .here I am simply refreshing the wire adapter which will get the updated values
handleNewRecord(evt) { return refreshApex(this.wiredIssueResult); }
You can able to handle the event values as shown below as well and get the event values and parse it.
this.issues = [ ...this.issues, { "Id": evt.detail.data.id, "Name": evt.detail.data.fields.Name.value, "Assigned_To__c": evt.detail.data.fields.Assigned_To__c.value, "Description__c": evt.detail.data.fields.Description__c.value, "Priority__c": evt.detail.data.fields.Priority__c.value, "Resolved__c": evt.detail.data.fields.Resolved__c.value }, ];
The following code will be used to update the values when user click on the checkbox resolved
handleChecked(event) { event.preventDefault(); const recId = event.target.dataset.recordid; let record = { fields: { Id: recId, Resolved__c: event.target.checked, }, }; updateRecord(record) .then(() => { this.dispatchEvent( new ShowToastEvent({ title: 'Success', message: 'Record is updated', variant: 'success', }), ); }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: 'Error while updating record', message: error.message, variant: 'error', }), ); }); }
The following code is used to fire the navigation event
handleNameClicked(event) { event.preventDefault(); const recId = event.target.dataset.recordid; this[NavigationMixin.Navigate]({ type: 'standard__recordPage', attributes: { recordId: recId, objectApiName: 'Issue__c', actionName: 'view' }, }); }
Use the below issuelist.js-meta.xml code
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="helloWorld"> <apiVersion>45.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__AppPage</target> <target>lightning__RecordPage</target> <target>lightning__HomePage</target> </targets> </LightningComponentBundle>
4. Push changes
Push the changes to scratch org using the below SFDX command and add this component to the layout
sfdx force:source:push --forceoverwrite
Here is the final output of the code.