Inline edit support in custom component in Salesforce Lightning

Inline Edit Support in Custom Component in Salesforce Lightning

If you have a situation where you need to create a custom component with inline edit functionality then this article will help you. However inline edit support in custom component is not supported so far. I have created a component to display Account with inline edit functionality.

InlineEdit1

The idea behind the inline edit is that I am rendering two fields for each field one in output format and another in input format but only one visible at a time. When the component is loaded records will be shown in the output format. When the user clicks on the pencil icon the input format component is shown, at the same time Save button is activated. The user can change field value and save the record.

Follow the below sample code -

Apex Class (LightningInlineEditCltr). This is the controller class to fetch and save the records in the database.

public with sharing class LightningInlineEditCltr {
    @AuraEnabled
    public static List < account > accountsToDisplay() {
        List < Account > returnList = new List < Account > ();
        List < Account > lstOfAccount = [select id, Name, Rating, website from account ];
        for (Account acc: lstOfAccount) {
            returnList.add(acc);
        }
        return returnList;
    }
    // method for update records after inline editing 
    @AuraEnabled
    public static List < account > saveAccount(List<Account> listOfAccount) {
        update listOfAccount;
        return listOfAccount;
    }
    // method for fetch picklist values dynamic 
    @AuraEnabled
    public static List < String > getselectOptions(sObject objObject, string fld) {
        List < String > allOpts = new list < String > ();
        Schema.sObjectType objType = objObject.getSObjectType();
        Schema.DescribeSObjectResult objDescribe = objType.getDescribe();
        map < String, Schema.SObjectField > fieldMap = objDescribe.fields.getMap();
        list < Schema.PicklistEntry > values = fieldMap.get(fld).getDescribe().getPickListValues();
        for (Schema.PicklistEntry a: values) {
            allOpts.add(a.getValue());
        }
        allOpts.sort();
        return allOpts;
    }
}

Lightning Component (ChildInlineEdit). This component displays the records in the row.

<aura:component controller="LightningInlineEditCltr">
    <!-- To Get picklist values-->  
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="objInfoForPicklistValues" type="account" default="{sobjectType : 'Account'}" description="object info to get picklist"/>
    <aura:attribute name="ratingPicklistOpts" type="string[]" description="picklist options"/>
    <aura:attribute name="showSaveCancelBtn" type="boolean"/>
    <aura:attribute name="showErrorClass" type="boolean" default="false"/>
    <aura:attribute name="index" type="string" />
    <aura:attribute name="singleAccount" type="sobject" default="{'sobjectType' : 'account',
                                                                             'Name' : '',
                                                                             'Rating': ''
                                                                             }"/>
    <aura:attribute name="nameEditMode" type="boolean" default="false" />
    <aura:attribute name="ratingEditMode" type="boolean" default="false" />
   
    <tr>
        <td><div class="slds-truncate">{!v.index}</div></td>
        <td ondblclick="{!c.EditName}" class="{! v.showErrorClass == true ? 'slds-cell-edit slds-has-error' : 'slds-cell-edit'}">
            <span class="slds-grid slds-grid_align-spread">
                <aura:if isTrue="{!v.nameEditMode == false}">
                    <span class="slds-truncate" title="Name">{!v.singleAccount.Name}</span>
                    <button onclick="{!c.EditName}" class="slds-button slds-button_icon slds-cell-edit__button slds-m-left_x-small" tabindex="0" title="Edit Name">
                        <lightning:icon iconName="utility:edit" size="xx-small" alternativeText="edit"/>
                    </button>
                    <!-- Inline Edit Section in else case--> 
                    <aura:set attribute="else">
                        <section  tabindex="0" class="slds-popover slds-popover_edit" role="dialog" style="position: absolute; top: 0px">
                            <div class="slds-popover__body">
                                <div class="slds-form-element slds-grid slds-wrap">
                                    <div class="slds-form-element__control slds-grow">
                                        <ui:inputText class="slds-input"
                                                      labelClass="slds-form-element__label slds-form-element__label_edit slds-no-flex"
                                                      aura:id="inputId"
                                                      blur="{!c.closeNameBox}"
                                                      change="{!c.onNameChange}"
                                                      required="true"
                                                      label="Name"
                                                      value="{!v.singleAccount.Name}" />
                                    </div>
                                </div>
                            </div>
                            <span id="form-end" tabindex="0"></span>
                        </section> 
                    </aura:set> 
                </aura:if>
            </span>
        </td>
       
        <td ondblclick="{!c.inlineEditRating}" class="slds-cell-edit">
            <span class="slds-grid slds-grid_align-spread">
                <aura:if isTrue="{!v.ratingEditMode == false}">
                    <span class="slds-truncate" title="Rating">{!v.singleAccount.Rating}</span>
                    <button onclick="{!c.inlineEditRating}" class="slds-button slds-button_icon slds-cell-edit__button slds-m-left_x-small" tabindex="0" title="Edit Rating">
                        <lightning:icon iconName="utility:edit" size="xx-small" alternativeText="edit"/>
                    </button>
                    <!-- Inline Edit Section in else case--> 
                    <aura:set attribute="else">
                        <section  tabindex="0" class="slds-popover slds-popover_edit" role="dialog" style="position: absolute; top: 0px">
                            <div class="slds-popover__body">
                                <div class="slds-form-element slds-grid slds-wrap">
                                    <div class="slds-form-element__control slds-grow">
                                        <label class="slds-form-element__label">Rating</label>
                                        <ui:inputSelect aura:id="accRating"
                                                        class="slds-select"
                                                        blur="{!c.closeRatingBox}"
                                                        change="{!c.onRatingChange}"
                                                        value="{!v.singleAccount.Rating}"
                                                        />
                                    </div>
                                </div>
                            </div>
                            <span id="form-end" tabindex="0"></span>
                        </section> 
                    </aura:set> 
                </aura:if>
            </span>
        </td>
    </tr>
</aura:component>

ChildInlineEditController.js

({
    doInit: function(component, event, helper) {
        helper.fetchPickListVal(component, 'Rating', 'ratingPicklistOpts');
    },
    EditName : function(component,event,helper){  
        component.set("v.nameEditMode", true);
        setTimeout(function(){
            component.find("inputId").focus();
        }, 100);
    },
    inlineEditRating : function(component,event,helper){  
        component.set("v.ratingEditMode", true);
        component.find("accRating").set("v.options" , component.get("v.ratingPicklistOpts"));
        setTimeout(function(){
            component.find("accRating").focus();
        }, 100);
    },
    onNameChange : function(component,event,helper){
        if(event.getSource().get("v.value").trim() != ''){
            component.set("v.showSaveCancelBtn",true);
        }
    },
    onRatingChange : function(component,event,helper){
        component.set("v.showSaveCancelBtn",true);
    },    
    closeNameBox : function (component, event, helper) {
        component.set("v.nameEditMode", false);
        if(event.getSource().get("v.value").trim() == ''){
            component.set("v.showErrorClass",true);
        }else{
            component.set("v.showErrorClass",false);
        }
    },
    closeRatingBox : function (component, event, helper) {
        component.set("v.ratingEditMode", false);
    },
})

ChildInlineEditHelper.js

({
   // fetch picklist values dynamic from apex controller
    fetchPickListVal: function(component, fieldName, picklistOptsAttributeName) {
        var action = component.get("c.getselectOptions");
        action.setParams({
            "objObject": component.get("v.objInfoForPicklistValues"),
            "fld": fieldName
        });
        var opts = [];
        action.setCallback(this, function(response) {
            if (response.getState() == "SUCCESS") {
                var allValues = response.getReturnValue();
 
                if (allValues != undefined && allValues.length > 0) {
                    opts.push({
                        class: "optionClass",
                        label: "--- None ---",
                        value: ""
                    });
                }
                for (var i = 0; i < allValues.length; i++) {
                    opts.push({
                        class: "optionClass",
                        label: allValues[i],
                        value: allValues[i]
                    });
                }
                component.set("v." + picklistOptsAttributeName, opts);
            }
        });
        $A.enqueueAction(action);
    },
})

Lightning Component (ParentInlineEdit). This component fetches the data from the database and passes to the child component.

<aura:component controller="LightningInlineEditCltr" implements="flexipage:availableForAllPageTypes,force:hasRecordId,force:appHostable">  
    <!--Init handler To Get the records on load--> 
    <aura:handler name="init" value="{!this}"   action="{!c.getAccountsJS}"/>
    <aura:attribute name="AccountList" type="account[]" description="store account records list"/>
    <aura:attribute name="showSaveCancelBtn" type="boolean" description="show hide save and cancel" default="false"/>
   
    <div class="slds-m-around_large">
        <table class="slds-table slds-table_bordered slds-table_cell-buffer">
            <thead>
                <tr class="slds-text-title--caps">
                    <th scope="col"><div class="slds-truncate" title="Id">S.No</div></th>
                    <th scope="col"><div class="slds-truncate" title="Account Name">Account Name</div></th>
                    <th scope="col"><div class="slds-truncate" title="Rating">Rating</div></th>
                </tr>
            </thead>
            <tbody>
                <aura:iteration items="{!v.AccountList}" var="acc" indexVar="index">
                    <!-- Child Component -->
                    <c:ChildInlineEdit singleAccount="{!acc}"
                                     showSaveCancelBtn="{!v.showSaveCancelBtn}"
                                     index="{!index + 1}" />
                </aura:iteration>
            </tbody>
        </table>
        <aura:if isTrue="{!v.showSaveCancelBtn}">
            <lightning:buttonGroup class="slds-m-around_medium">
                <lightning:button label="Save" onclick="{!c.SaveAccount}" />
            </lightning:buttonGroup>
        </aura:if>
    </div>
</aura:component>

ParentInlineEditController.js-

({
    getAccountsJS : function(component, event, helper) {
        var action = component.get("c.accountsToDisplay");
        action.setCallback(this, function(response) {
            if (response.getState() === "SUCCESS" && component.isValid()) {
                component.set("v.AccountList", response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },
    SaveAccount : function(component, event, helper) {
        if (helper.requiredFieldValidation(component, event)){
            var action = component.get("c.saveAccount");
            action.setParams({
                'listOfAccount': component.get("v.AccountList")
            });
            action.setCallback(this, function(response) {
                if (response.getState() === "SUCCESS" && component.isValid()) {
                    component.set("v.AccountList", response.getReturnValue());
                    component.set("v.showSaveCancelBtn",false);
                    alert('Account Updated...');
                }
            });
            $A.enqueueAction(action);
        }
    },
})

ParentInlineEditHelper.js-

({
    requiredFieldValidation : function(component,event) {
        var allRecords = component.get("v.AccountList");
        var isValid = true;
        for(var i = 0; i < allRecords.length;i++){
            if(allRecords[i].Name == null || allRecords[i].Name.trim() == ''){
                alert('fill this field : Row No ' + (i+1) + ' Account Name is null' );
                isValid = false;
            } 
        }
        return isValid;
    },
})

Lightning Application (LearnLight)- Application contains the parent component to run the functionality.

<aura:application extends=”force:slds”>
    <c:ParentInlineEdit />
</aura:application>

InlineEdit2

Responses

Comments are closed.

Popular Salesforce Blogs