Salesforce LWC: Multi-Record Creation using Lightning Web Component
In this blog we will learn about one very common business use case. Most of the time we get requirement to create a table kind of view from which we can see most of the record's information and also we can manage creating updating and deleting record from same view.
So in this blog we will see how to create such kind of UI using Lightning Web Component. We will take one use case and will implement the same.
Use Case:
There will be one input field for Account selection. Once user select any account then user can see all related Contacts. Also user should have capability to add multiple new contacts for selected Account and user can delete existing Contacts.
Solutions:
To implement above solution first we need one input field where user can select Account Name. For this we can utilize Lightning Data Service. Once User will select Account we can pass the Account Id to Apex Class for getting related Contacts and show it on UI using HTML table.
Lets see some code for the UI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | <template> <lightning-card title=""> <div class="slds-p-horizontal_small"> <lightning-record-edit-form object-api-name="Contact"> <lightning-layout multiple-rows> <lightning-layout-item size="12" small-device-size="6" medium-device-size="4" large-device-size="6" padding="around-small"> <lightning-input-field field-name="AccountId" onchange={handleAccountId}></lightning-input-field> </lightning-layout-item> </lightning-layout> </lightning-record-edit-form><br/> <template if:true={selectedAccount}> <div> <table class="slds-table slds-table_cell-buffer slds-table_bordered"> <thead> <tr class="slds-line-height_reset"> <th class="" scope="col"> <div class="slds-truncate" title="Select Contact">Select Contact</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Contact First Name">Contact First Name</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Contact Last Name">Contact Last Name</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Account Name">Account Name</div> </th> <th class="" scope="col"> <div class="slds-truncate" title="Action">Action</div> </th> </tr> </thead> <tbody> <template if:true={contactDataWrp}> <template for:each={contactDataWrp} for:item="con" for:index="index"> <tr class="slds-hint-parent" key={con.Id}> <td data-label="Select Contact"> <div class="slds-truncate" title="Select Contact"> <lightning-input type="checkbox" disabled={disabledCheckbox} name="input1"></lightning-input> </div> </td> <td data-label="Contact First Name"> <div class="slds-truncate" title="First Name">{con.FirstName}</div> </td> <td data-label="Contact Last Name"> <div class="slds-truncate" title="Last Name">{con.LastName}</div> </td> <td data-label="Account Name"> <div class="slds-truncate" title="Account Name">{accountName}</div> </td> <td data-label="Action"> <div class="slds-truncate" title="Action"> <lightning-button label="Delete" variant="neutral" value={index} onclick={deleteRecord}></lightning-button> </div> </td> </tr> </template> </template> <template if:true={blankRow}> <template for:each={blankRow} for:item="con" for:index="ind"> <tr class="slds-hint-parent" key={con.Id}> <td data-label="Select Contact"> <div class="slds-truncate" title="Select Contact"> <lightning-input type="checkbox" name={ind} value={con.isChecked} onchange={setCheckBox}></lightning-input> </div> </td> <td data-label="Contact First Name"> <lightning-input type="text" value={con.FirstName} placeholder="Contact First Name" name={ind} onchange={setFirstName}></lightning-input> </td> <td data-label="Contact Last Name"> <lightning-input type="text" value={con.LastName} placeholder="Contact Last Name" name={ind} onchange={setLastName}>></lightning-input> </td> <td data-label="Account Name"> <lightning-record-view-form record-id={selectedAccount} object-api-name="Account"> <lightning-output-field field-name="Name" variant="label-hidden"></lightning-output-field> </lightning-record-view-form> </td> <td data-label="Action"> <lightning-button label="Remove" variant="neutral" value={ind} onclick={removeRow}></lightning-button> </td> </tr> </template> </template> </tbody> </table> </div> </template> </div> <div slot="footer"> <template if:true={selectedAccount}> <lightning-button name="multipleRowSave" label="Save Conacts" class="slds-p-around_xx-small" variant="brand-outline" onclick={saveData}></lightning-button> <lightning-button name="multipleRowAddition" label="Add Row" class="slds-p-around_xx-small" variant="brand" onclick={addRow}></lightning-button> <lightning-button name="multipleRowRemoval" class="slds-p-around_xx-small" label="Remove Row" variant="destructive" onclick={removeRow}></lightning-button> </template> </div> </lightning-card> </template> |
If you see above code, you will find it has two separate iteration over list of data. This is because first one is showing current Contacts of Account and second one is showing new Contacts which are not inserted yet into Salesforce.
Lets move to the Java Script Logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import { LightningElement, track, wire, api} from 'lwc'; import contactRecords from '@salesforce/apex/MultiRecordCreationHandler.getRelatedContacts'; import deleteContactHandler from '@salesforce/apex/MultiRecordCreationHandler.deleteContactHandler'; import insertContactData from '@salesforce/apex/MultiRecordCreationHandler.saveContactData'; import { getRecord } from 'lightning/uiRecordApi'; import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name'; export default class MultiRecordCreation extends LightningElement { @track contactDataWrp; @track blankRow = []; @track disabledCheckbox = true; @track index = 0; @track selectedAccount; @track accountName; @wire(getRecord, { recordId: '$selectedAccount', fields: [ ACCOUNT_NAME_FIELD ] }) savedRecordIdWire({error,data}) { if(data){ this.accountName = data.fields.Name.value; }else if(error){ window.alert(JSON.stringify(error)); } } handleAccountId(event){ let accountId = event.detail.value[0]; if(accountId !== undefined){ this.selectedAccount = accountId; this.contactDataWrp = []; contactRecords({accId : accountId}).then(result => { this.contactDataWrp = result; this.index = result.length; }).catch(error => { console.log(error); }) }else{ this.blankRow = []; this.index = 0; this.contactDataWrp = []; } } deleteRecord(event){ const selectedContact = this.contactDataWrp[event.target.value]; window.alert(JSON.stringify(this.contactDataWrp) + ' & ' + event.target.value + ' & ' + JSON.stringify(selectedContact)); deleteContactHandler({conId: selectedContact.Id, accId: selectedContact.AccountId}).then(result => { this.contactDataWrp = result; }).catch(error => { window.alert(JSON.stringify(error)); }) } addRow(event){ this.index++; let i = this.index; let newContact = new Object(); let blankRow = this.blankRow; newContact.Id = i; newContact.isChecked = false; blankRow.push(newContact); this.blankRow = blankRow; } removeRow(event){ const eventName = event.target.name; let blankRow = this.blankRow; if(eventName === 'multipleRowRemoval'){ for(let i = 0; i < blankRow.length; i++){ if(blankRow[i].isChecked){ blankRow.splice(i, 1); i--; } } }else{ blankRow.splice(event.target.value, 1); } this.blankRow = blankRow; } setFirstName(event){ const eventName = event.target.name; let blankRow = this.blankRow; blankRow[eventName].FirstName = event.target.value; this.blankRow = blankRow; } setLastName(event){ const eventName = event.target.name; let blankRow = this.blankRow; blankRow[eventName].LastName = event.target.value; this.blankRow = blankRow; } saveData(event){ let blankRow = this.blankRow; let contactDataList = []; for(let i = 0; i < blankRow.length; i++){ if(blankRow[i] !== undefined && blankRow[i].isChecked){ let conData = new Object(); conData.AccountId = this.selectedAccount; conData.FirstName = blankRow[i].FirstName; conData.LastName = blankRow[i].LastName; contactDataList.push(conData); } } if(contactDataList.length > 0){ insertContactData({contactDataString: JSON.stringify(contactDataList)}).then(result => { let newContactList = this.contactDataWrp; for(let i = 0; i < result.length; i++){ if(result[i] !== undefined){ let contactRecord = {'sobjectType' : 'Contact'}; contactRecord.Id = result[i].Id; contactRecord.FirstName = result[i].FirstName; contactRecord.LastName = result[i].LastName; contactRecord.AccountId = this.selectedAccount; newContactList.push(contactRecord); } } this.contactDataWrp = newContactList; this.blankRow = []; this.index = newContactList.length; }).catch(error => { window.alert('Please contact system admin: ' + JSON.stringify(error)); }) }else{ window.alert('Please select any row to insert data.'); } } setCheckBox(event){ let blankrow = this.blankRow; if(blankrow[event.target.name].isChecked){ blankrow[event.target.name].isChecked = false; }else{ blankrow[event.target.name].isChecked = true; } this.blankRow = blankrow; } } |
Let us talk about each method.
- wire Service: The purpose of wire service is to get selected Account information.
- handleAccountId: In this method once we select any Account then it will make server side call and get all related Contacts.
- deleteRecord: This method will delete selected record.
- addRow & removeRow: These two methods are used to add/remove new row for new contact creation.
- saveData: This method is used for save new records and after saving it will be part of existing contact records for selected Account.
- setCheckBox: This checkbox method is used for selecting multiple rows for performing actions.
Apex Class Logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public with sharing class MultiRecordCreationHandler { @AuraEnabled public static List<Contact> getRelatedContacts(Id accId){ return [Select Id, FirstName, LastName, Account.Name from Contact where AccountId = :accId]; } @AuraEnabled public static List<Contact> deleteContactHandler(Id conId, Id accId){ Database.delete(conId); return [Select Id, FirstName, LastName, AccountId, Account.Name from Contact where AccountId = :accId]; } @AuraEnabled public static List<Contact> saveContactData(String contactDataString){ List<Contact> contactList = (List<Contact>)System.JSON.deserializeStrict(contactDataString, List<Contact>.Class); Database.insert(contactList); return contactList; } } |
Demo:
Code Reference:
References:
Other Useful blogs:
- https://www.sfprompt.com/
- https://manish-porwal.blogspot.com/2019/11/draw-chart-in-lwc-using-chartjs.html
Comments
Post a Comment