Lightning Web Component Dialog With Inputs



During latest development I came for the stage that I was needed to open basic modal dialog in lightning component.

Unfortunately we cannot use most of the JQeury libraries that provide beautiful and rich dialog, therefore the solution should be using other component. 


Still, this is very common request, so I thought:

1.Use 1 component that will receive parameter and will be reused in my app

2.Search in the web for solutions

I saw several good article/git and choose this A LWC Confirmation Dialog, mainly because it is simple and easy to understand.


However after several use cases, I came to the case where I needed to get also user inputs in the dialog. I searched again the web, but didn't find suitable examples, therefore I decide to expand the component that I cloned.

The solution is posted in open git lwc-dialog-with-parametersAll you need to get is the component customDialogModal, the second component is just for demo.


*Notice importance of the css file, without it if you will use input of type combobox, then the options might be invisible.

 .slds-modal__content{  

   overflow: initial;  

 }  


Also notice the difference input section in the code for combobox/checkbox, as it needed some other attributes vs other input types.


Full component code (can also be found in git):

html:

<template>
    <template if:true={visible}>
        <div class="slds-container_small">
            <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
                <div class="slds-modal__container">
                    <header class="slds-modal__header">
                        <h2 data-id="titile" class="slds-text-heading_medium slds-hyphenate">{title}</h2>
                    </header>
                    <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
                        <p id="data-id">{message}</p>

                        <template for:each={inputList} for:item="inp">
                            <template if:true={inp.isCombo}>
                                <lightning-combobox
                                    key={inp.name}
                                    label={inp.label}
                                    value={inp.value}
                                    options={inp.options}
                                    data-keyid={inp.name} 
                                    required={inp.required} 
                                    onchange={handleInputAdded}>
                                </lightning-combobox>
                            </template>

                            <template if:true={inp.isInput}>
                                <lightning-input 
                                    value={inp.value} key={inp.name} type={inp.type} label={inp.label} data-keyid={inp.name} required={inp.required} onchange={handleInputAdded}>

                                </lightning-input>
                            </template>

                            <template if:true={inp.isCheckbox}>
                                <lightning-input 
                                    key={inp.name} type='checkbox' label={inp.label} data-keyid={inp.name} required={inp.required} onchange={handleInputAdded} checked={inp.value}>

                                </lightning-input>
                            </template>
                        </template>
                    </div>

                    <footer class="slds-modal__footer">
                        <lightning-button variant="neutral"
                                            name="cancel"
                                            label={cancelLabel}
                                            title={cancelLabel}
                                            onclick={handleClick}>

                        </lightning-button>

                        <lightning-button variant="brand"
                                          name="confirm"
                                          label={confirmLabel}
                                          title={confirmLabel}
                                          onclick={handleClick} >
                        </lightning-button>
                    </footer>
                </div>
            </section>
            <div class="slds-backdrop slds-backdrop_open"></div>
        </div>
    </template>
</template>

js:

import { LightningElement, api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class CustomDialogModal extends LightningElement {
    @api visible = false;
    @api title = '';
    @api name;          //reference name of the component
    @api message = '';
    @api confirmLabel = 'Yes';
    @api cancelLabel = 'No';
    @api originalMessage;   //any event/message/detail to be published back to the parent component
    @api inputList = [];

    userInput = {};

    //handle button clicks
    handleClick(event){
        if(event.target.name == 'confirm'){
            const isInputsCorrect = [...this.template.querySelectorAll('lightning-input')]
                .reduce((validSoFar, inputField) => {
                    inputField.reportValidity();
                    return validSoFar && inputField.checkValidity();
                }, true);

            if(! isInputsCorrect){
                return;
            }
        }

        let finalEvent = {
            originalMessage : this.originalMessage,
            status : event.target.name
        };

        for(let inpItem in this.inputList){
            //If user added input get it, otherwise taking the original value that was sent
            finalEvent[this.inputList[inpItem].name] = this.inputList[inpItem].name in this.userInput ? 
                                                        this.userInput[this.inputList[inpItem].name] : 
                                                        this.inputList[inpItem].value;
        }

        this.dispatchEvent(new CustomEvent('modalaction', {detail : finalEvent}));
    }

    handleInputAdded(event){
        for(let inpItem in this.inputList){
            if(this.inputList[inpItem].name == event.target.dataset.keyid){
                if(this.inputList[inpItem].isCheckbox == true){
                    this.userInput[this.inputList[inpItem].name] = event.detail.checked;
                }
                else{
                    this.userInput[this.inputList[inpItem].name] = event.detail.value;
                }
            }
        }
    }
}






Lightning Web Component for Merge Records




In previous post I examine the usability of the flow with all its new features by building flow that allow to merge 2 accounts. The main conclusion was that the flow is very powerful tool and can be used to build complex processes. It does missing flexibility when working with sObject or when want to apply enhance styling to your application.

In this post I decided to try and build similar process with web component-  that is component can be embedded on any record page and allow to merge other record into the current record. 

The full code can be found in this repository

How it will work? The component - mergeRecordsComp - can be placed on any record page. It will show search input where the user can type and search other record. For this purpose I'm using additional custom component - customLookupComp - as Salesforce still doesn't have build-in input for that. 

After the input record was selected popup is opened that shows in table the values from the main record (record were we started the process) and the values from the second record (record that was selected in the search) in the second column.

By default all the values for the merge will be taken from the main record, but user can click on specific fields from the second record that he want to set in the merged record.

At the bottom, user can choose if he want to merge also the child records. 

*For this demo I just added 1 checkbox that used  as merge all child data all  none, but can quite easily add higher flexibility, like getting dynamically all the sObject  related list using ChildRelationship class and allowing  the  user  to choose which  he want merge.  in  apex you can find object related list with the following code:


Schema.DescribeSObjectResult dsr = Schema.getGlobalDescribe().get('Account').getDescribe();

for(Schema.ChildRelationship childRelation : dsr.getChildRelationships()){
    
    System.debug('getChildSObject:: ' + childRelation.getChildSObject());
    System.debug('getRelationshipName:: ' + childRelation.getRelationshipName());
    System.debug('getField:: ' + childRelation.getField());
}

Also note that in the current implementation there is loop on the relation object and query/update on each relevant entity, which is in general bad practice. If you except to have many relation entities you might need to limit the merge relation objects, or call batch process to split the work..


What about merging more than 2 records? In the flow it seems that this will be very cumbersome, as it require too manage flow variables and decisions. In the component it is not that  complicate, just need o provide a way for selecting additional record (can show the search input again or provide button 'add more') and after getting the input record, query the record data and add additional column in the table that compare the data.

Conclusion? 
As expected, writing web component require more skills and efforts compare to the flow, but it does provide higher flexibility and reusability.


Retire of Permission on Profiles

If you are working as a Salesforce admin/developer you've probably heard somewhere that Salesforce is planning to make a significant cha...