Simple Account creation Wizard using Lightning Web Components
Background
Lightning Web Components (LWC) is Salesforce's implementation of a framework built on top of web standards. Web standards provides a common component model, programming model, interoperable components, better performance. The better performance is a result of the core features (component and programming model) implemented natively in the javascript engine, instead of in framework abstractions.LWC provides a layer of specialized Salesforce services on top of the core stack, including base lightning components for scaffolding, lightning data service (lds) for easy access to salesforce data apis, and the user interface api that makes the base lighting components and the the lds metadata aware. Overall lightning web components allows Salesforce developers to work with a cutting-edge programming model that is built for the 2019 web stack with backwards interoperatability.
Resources
There are multiple resources provided by Salesforce that will help you get started. Personally I found the recipes collection to be really useful in learning the basic mechanisms that can be chained and enhanced. The dev guide is also really useful to learn the nitty gritty such as lifecycle hooks, event propagation, configuration targets, decorators etc.
- Lightning Web Components Dev Guide
- Trailhead Project: Quick Start with Lightning Web Components
- Lightning Web Components Recipes: a collection of easy-to-digest code examples
- Ebikes: new Lightning Web Components sample application
- Lightning Web Components Sample Gallery
- Lightning Web Components version of the Dreamhouse sample app
- Lightning Web Components version of the Dreaminvest sample app
- Lightning Web Components version of the PureAloe sample app
- Lightning Web Components version of the Easy Spaces sample app
Simple Account Creation Wizard
In order to get some familiarity with lwc, I decided to built a simple wizard without an apex side code. The wizard consists of four components. A container component (CustomAccountWizard), component to get account details (accountDetails), component to get contact details (contactDetails), and a review component (customAccountWizardReview). The diagram below shows the component composition.
The child components bubble events to change the status of the wizard and the container component manages the state and renders the child components. The code for all the components can be found in the following repository [lwc-customaccountwizard]. Below I have highlighted a few code snippets that I think make lightning web components a clear upgrade to lightning components.
Events
The following points make the event framework an upgrade from previous lightning components.- Custom events do not need to be defined in a separate construct and can be created dynamically.
- Events dispatched using the dispatchEvent method extended from LightningElement.
- Parameters can be added with ease and the complexity of the parameters is limitless.
contactDetails.js
import { LightningElement } from 'lwc'; import CONTACT_OBJECT from '@salesforce/schema/Contact'; export default class ContactDetails extends LightningElement { handleSubmit(event) { event.preventDefault(); //stop default action //Send event to parent to go to next page let fields = event.detail.fields; fields.AccountId = ''; let recordInput = { allowSaveOnDuplicate: true, apiName: CONTACT_OBJECT.objectApiName, fields }; this.dispatchEvent(new CustomEvent('next', {detail: recordInput})); } handlePrevious(event) { event.preventDefault(); //stop default action //Send event to parent to go to next page let fields = event.detail.fields; fields.AccountId = ''; let recordInput = { allowSaveOnDuplicate: true, apiName: CONTACT_OBJECT.objectApiName, fields }; this.dispatchEvent(new CustomEvent('previous', {detail: recordInput})); } }
- Parents can register for events on child components in their declaration in the markup.
CustomAccountWizard.js (snippet)
** Caveat: In some (many probably) cases you would want to communicate between sibling components that are not part of the same DOM. In that case a publish-subscribe service can be used or developed. There is one available in the lwc-recipes that can be plugged into a solution and used as a service.
<template id="contactDetailsDiv" if:true={showContactDetailsForm}> <c-contact-details onprevious={handlePrevious} onnext={handleNext}></c-contact-details> </template>
** Caveat: In some (many probably) cases you would want to communicate between sibling components that are not part of the same DOM. In that case a publish-subscribe service can be used or developed. There is one available in the lwc-recipes that can be plugged into a solution and used as a service.
Utility Code and Reusability
In the aura lightning framework utility code had to be included in a static resource or another component and had to be included in the consuming components markup. For external libraries included through static resources, they cannot be accessed in components init functions and can only be used after the afterscriptsloaded callback executes.
Lightning Data Service
Lightning Data Service is much improved in lighting web components and can function from the service can be easily imported and used without being included in component markup. In the below code chain creation of account and contact would be possible in lightning components but the lightning data service for account and contact would have to be included in the component markup and would have be accessed using dom selectors. The below code is much cleaner and easier to follow and implement.
customAccountWizardReview.js
import { LightningElement, api } from 'lwc'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; import { createRecord } from 'lightning/uiRecordApi'; export default class CustomAccountWizardReview extends LightningElement { @api contact; @api account; handleSubmit() { let accountToCreate = this.account; //Create Account and get Id createRecord(accountToCreate).then(account => { console.log('account: ' + JSON.stringify(account)); let accountId = account.id; //this took some finding //Create Contact and parent with Account Id const fields = {}; Object.assign(fields, this.contact.fields); fields.AccountId = accountId; let contactToCreate = { allowSaveOnDuplicate: true, apiName: 'Contact', fields }; console.log('contactToCreate: ' + JSON.stringify(contactToCreate)); //createContact createRecord(contactToCreate).then(contact => { let contactId = contact.id; this.dispatchEvent( new ShowToastEvent({ title: 'Success', message: 'Account and Contact created successfully!', variant: 'success', }), ); //Send Event to navigate let detail = {}; detail.accountId = accountId; detail.contactId = contactId; this.dispatchEvent( new CustomEvent('submit', {detail: detail}) ); }).catch(error => { this.dispatchEvent( new ShowToastEvent({ title: 'Error creating contact', message: error.message, variant: 'error', }), ); }); }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: 'Error creating account', message: error.message, variant: 'error', }), ); }); } handlePrevious() { //Send event to parent to go to next page this.dispatchEvent(new CustomEvent('previous')); } }
Comments
Post a Comment