Building simple LWC components - Part 1
In this series, we will learn how to create simple LWC components without writing any apex code. There are various ways provided by Salesforce to fetch data from the database without the developer requiring to write even a single line of Apex.
In this blog, we will have a look at wire service to retrieve data from the server. LWC uses a reactive wire service that is built on Lightning Data Service. Components use @wire annotation in the JS class file to read data from one of the wire adapters in lightning/ui*Api modules.
We will build a simple LWC component that will get the account record id from the Account record page and fetch the account data using wire service. The account details will be displayed in a header component styled using SLDS. We will also use the lightning-map component to show the account address on the google map.
Below is the HTML markup for the LWC component
accountInfo.html
To display the map, I have made use of lightning-map component which is available as a standard OOTB component. The map-markers attribute of this component is populated in the JS class file with the account address. The account address is retrieved using the wire service provided by LWC.
Next, we will have a look at the JS class file.
accountInfo.js
In this blog, we will have a look at wire service to retrieve data from the server. LWC uses a reactive wire service that is built on Lightning Data Service. Components use @wire annotation in the JS class file to read data from one of the wire adapters in lightning/ui*Api modules.
We will build a simple LWC component that will get the account record id from the Account record page and fetch the account data using wire service. The account details will be displayed in a header component styled using SLDS. We will also use the lightning-map component to show the account address on the google map.
Below is the HTML markup for the LWC component
accountInfo.html
<template>
<div class="slds-page-header">
<div class="slds-page-header__row">
<div class="slds-page-header__col-title">
<div class="slds-media">
<div class="slds-media__figure">
<span
class="slds-icon_container slds-icon-standard-account"
title="account"
>
<svg class="slds-icon slds-page-header__icon" aria-hidden="true">
<use
xlink:href="/assets/icons/standard-sprite/svg/symbols.svg#account"
></use>
</svg>
<span class="slds-assistive-text">account</span>
</span>
</div>
<div class="slds-media__body">
<div class="slds-page-header__name">
<div class="slds-page-header__name-title">
<h1>
<span class="slds-page-header__name-meta">
Account
</span>
</h1>
</div>
</div>
<p class="slds-page-header__title slds-truncate">
{name} • {phone} • {type}
</p>
</div>
</div>
</div>
</div>
</div>
<template if:true={account}>
<lightning-map map-markers={mapMarkers} zoom-level="18"></lightning-map>
</template>
</template>
The HTML for this component is pretty simple. I have used the slds classes to style the account header. The variables name, phone and type are defined in the JS class file with @track annotation. @track annotation makes the variables reactive, which is similar to two way binding of attributes in aura.<div class="slds-page-header">
<div class="slds-page-header__row">
<div class="slds-page-header__col-title">
<div class="slds-media">
<div class="slds-media__figure">
<span
class="slds-icon_container slds-icon-standard-account"
title="account"
>
<svg class="slds-icon slds-page-header__icon" aria-hidden="true">
<use
xlink:href="/assets/icons/standard-sprite/svg/symbols.svg#account"
></use>
</svg>
<span class="slds-assistive-text">account</span>
</span>
</div>
<div class="slds-media__body">
<div class="slds-page-header__name">
<div class="slds-page-header__name-title">
<h1>
<span class="slds-page-header__name-meta">
Account
</span>
</h1>
</div>
</div>
<p class="slds-page-header__title slds-truncate">
{name} • {phone} • {type}
</p>
</div>
</div>
</div>
</div>
</div>
<template if:true={account}>
<lightning-map map-markers={mapMarkers} zoom-level="18"></lightning-map>
</template>
</template>
To display the map, I have made use of lightning-map component which is available as a standard OOTB component. The map-markers attribute of this component is populated in the JS class file with the account address. The account address is retrieved using the wire service provided by LWC.
Next, we will have a look at the JS class file.
accountInfo.js
import { LightningElement, api, track, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord } from 'lightning/uiRecordApi';
const FIELDS = ['Account.Name', 'Account.Phone', 'Account.Type', 'Account.BillingStreet', 'Account.BillingCity', 'Account.BillingState'];
export default class AccountContactInfo extends LightningElement {
@api recordId;
@track account;
@track name;
@track phone;
@track type;
@track mapMarkers = [];
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
wiredRecord({ error, data }) {
if (error) {
let message = 'Unknown error';
if (Array.isArray(error.body)) {
message = error.body.map(e => e.message).join(', ');
} else if (typeof error.body.message === 'string') {
message = error.body.message;
}
this.dispatchEvent(
new ShowToastEvent({
title: 'Error loading account',
message,
variant: 'error',
}),
);
} else if (data) {
this.account = data;
this.name = this.account.fields.Name.value;
this.phone = this.account.fields.Phone.value;
this.type = this.account.fields.Type.value;
this.mapMarkers = [
{
location: {
Street: this.account.fields.BillingStreet.value,
City: this.account.fields.BillingCity.value,
State: this.account.fields.BillingState.value,
},
title: this.account.fields.Name.value,
icon: 'standard:account',
},
];
}
}
}
Now let us try to understand the JS part of this LWC component. At the top, we have imported all the required modules from LWC.import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord } from 'lightning/uiRecordApi';
const FIELDS = ['Account.Name', 'Account.Phone', 'Account.Type', 'Account.BillingStreet', 'Account.BillingCity', 'Account.BillingState'];
export default class AccountContactInfo extends LightningElement {
@api recordId;
@track account;
@track name;
@track phone;
@track type;
@track mapMarkers = [];
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
wiredRecord({ error, data }) {
if (error) {
let message = 'Unknown error';
if (Array.isArray(error.body)) {
message = error.body.map(e => e.message).join(', ');
} else if (typeof error.body.message === 'string') {
message = error.body.message;
}
this.dispatchEvent(
new ShowToastEvent({
title: 'Error loading account',
message,
variant: 'error',
}),
);
} else if (data) {
this.account = data;
this.name = this.account.fields.Name.value;
this.phone = this.account.fields.Phone.value;
this.type = this.account.fields.Type.value;
this.mapMarkers = [
{
location: {
Street: this.account.fields.BillingStreet.value,
City: this.account.fields.BillingCity.value,
State: this.account.fields.BillingState.value,
},
title: this.account.fields.Name.value,
icon: 'standard:account',
},
];
}
}
}
- LightningElement - This is the base module that needs to be imported for every LWC component
- api - This is required to get the record id from the record page
- track - This is required to make the JS properties reactive. Remember the two-way binding that we discussed at the start of this blog.
- wire - This is required for making use of the wire service to retrieve the data from the backend.
Here we have used the getRecord adaptor available in the wire service to retrieve data. Below is the basic syntax for using wire service.
import { adapterId } from 'adapterModule';
@wire(adapterId, adapterConfig)
propertyOrFunction;
@wire(adapterId, adapterConfig)
propertyOrFunction;
- In our case, the adapterId is getRecord. There are various other adapterId available such as createRecord, deleteRecord, updateRecord and so on.
- The adapterModule used in this particular example is lightning/uiRecordApi
- In the adapterConfig, you must specify the record id. With record id, you can either specify the array of fields or you can specify the layout. If you specify the layout, then all the fields present in that particular layout would be automatically queried.
- The last thing is the propertyOrFunction - this will receive the output of your operation. The output can either be stored in a JS property or in a function. It is better to use a function because you can include error handling in the function.
The wiredRecord is the function that has two parameters - error and data. Only one of them would be populated at a time. In logic, we check first for the error. If the error parameter is populated, then we display the error using a toast message. In LWC, you need to fire the event called ShowToastEvent to display a toast. If there is no error, we populate the attributes with the account information. Since the attributes are reactive, so as soon as the value of the attribute is changed, it gets reflected in the HTML component.
If you have any questions or comments, please drop a message in the comments section. I will be soon writing the Part 2 of this series, any suggestions are welcomed.
Comments
Post a Comment