Wednesday, November 3, 2021

Dynamically Add/Remove rows using Lightning Web Components(LWC)

 This post explains how to add/remove the rows using the Lighting web component(LWC).

HTML Code:

<template>
   <lightning-card>
      <lightning-spinner if:true={isSpinner} variant="brand" size="large"> </lightning-spinner>
      <lightning-layout>
         <lightning-layout-item size="12">
            <lightning-button class="slds-float--right slds-m-around_small" variant="brand" label="Save"
               onclick={saveRows}>
            </lightning-button>
            <table class="slds-table slds-table_cell-buffer slds-table_bordered slds-border_left slds-border_right"
               aria-labelledby="element-with-table-label other-element-with-table-label">
               <thead>
                  <tr>
                     <th>Name</th>
                     <th>Industry</th>
                     <th>Phone</th>
                     <th>Email</th>
                     <th></th>
                  </tr>
               </thead>
               <tbody>
                  <template for:each={filterList} for:item="filterData" for:index="index">
                     <tr key={filterData}>
                        <td>
                           <lightning-input type="text" name="accName" data-index={index}
                              variant="label-hidden" placeholder="" onchange={handleChange}
                              value={filterData.Name}>
                           </lightning-input>
                        </td>
                        <td>
                           <lightning-combobox name="industry" data-index={index} variant="label-hidden"
                              placeholder="" onchange={handleChange} value={filterData.Industry}
                              options={industryOptions}>
                           </lightning-combobox>
                        </td>
                        <td>
                           <lightning-input type="text" name="accEmail" data-index={index}
                              variant="label-hidden" placeholder="" onchange={handleChange}
                              value={filterData.Email}>
                           </lightning-input>
                        </td>
                        <td>
                           <lightning-input type="text" name="accPhone" data-index={index}
                              value={filterData.Phone} variant="label-hidden" onchange={handleChange}>
                           </lightning-input>
                        </td>
                        <td>
                           <lightning-button-icon data-index={filterData.id} class="slds-float--right"
                              icon-name="action:new" onclick={handleAddRow}></lightning-button-icon>
                           <lightning-button-icon data-index={filterData.id} class="slds-float--right"
                              icon-name="action:delete" onclick={handleRemoveRow}></lightning-button-icon>
                        </td>
                     </tr>
                  </template>
               </tbody>
            </table>
         </lightning-layout-item>
      </lightning-layout>
   </lightning-card>
</template>

JS Code:
import { LightningElement, wire } from 'lwc';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import saveAccounts from '@salesforce/apex/LWCExampleController.saveAccounts';

export default class DynamicallyAddRow extends LightningElement {
    industryOptions = [{ value: '-None-', label: '' }];
    filterList = [];
    keyIndex = 0;
    isSpinner = false;

    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
    accountinfo;

    @wire(getPicklistValues, { recordTypeId: '$accountinfo.data.defaultRecordTypeId', fieldApiName: INDUSTRY_FIELD })
    industryValues({ data, error }) {
        if (data) {
            data.values.forEach(val => {
                this.industryOptions = [...this.industryOptions, { value: val.value, label: val.label }];
            });
        }
        else if (error) {
            this.processErrorMessage(error);
        }
    }

    connectedCallback() {
        this.handleAddRow();
    }

    handleChange(event) {
        if (event.target.name == 'accName') {
            this.filterList[event.currentTarget.dataset.index].Name = event.target.value;
        }
        else if (event.target.name == 'industry') {
            this.filterList[event.currentTarget.dataset.index].Industry = event.target.value;
        }
        else if (event.target.name == 'accEmail') {
            this.filterList[event.currentTarget.dataset.index].Email = event.target.value;
        }
        else if (event.target.name == 'accPhone') {
            this.filterList[event.currentTarget.dataset.index].Phone = event.target.value;
        }
    }

    handleAddRow() {
        let objRow = {
            Name: '',
            Industry: '',
            Phone: '',
            Email: '',
            id: ++this.keyIndex
        }

        this.filterList = [...this.filterList, Object.create(objRow)];
    }

    handleRemoveRow(event) {
        this.filterList = this.filterList.filter((ele) => {
            return parseInt(ele.id) !== parseInt(event.currentTarget.dataset.index);
        });

        if (this.filterList.length == 0) {
            this.handleAddRow();
        }
    }

    saveRows() {
        console.log('this.filterList => ', this.filterList);
        this.isSpinner = true;
        saveAccounts({ lstAccs: this.filterList }).then(result => {
            this.isSpinner = false;
            this.showToastMessage('success', 'Accounts Saved Successfully!!', 'Success');
            this.filterList = [];
            if (this.filterList.length == 0) {
                this.handleAddRow();
            }
            console.log('result ==> ', result);
        }).catch(error => {
            this.processErrorMessage(error);
            this.isSpinner = false;
        })
    }

    processErrorMessage(message) {
        let errorMsg = '';
        if (message) {
            if (message.body) {
                if (Array.isArray(message.body)) {
                    errorMsg = message.body.map(e => e.message).join(', ');
                } else if (typeof message.body.message === 'string') {
                    errorMsg = message.body.message;
                }
            }
            else {
                errorMsg = message;
            }
        }
        this.showToastMessage('error', errorMsg, 'Error!');
    }

    showToastMessage(variant, message, title) {
        this.dispatchEvent(
            new ShowToastEvent({
                title: title,
                message: message,
                variant: variant
            })
        );
    }
}

Apex Class:
public inherited sharing class LWCExampleController {

    @AuraEnabled
    public static List<Account> saveAccounts(List<Account> lstAccs) {
        try {
            insert lstAccs;
            return lstAccs;
        }
        catch(Exception ex) {
            throw new AuraHandledException(ex.getMessage());
        }
    }
}
Output: