Lightning Datatable Inline Editing

lightning-datatable component use to display salesforce data in a table format. This component support inline editing. That means we don't need to navigate to the record in order to update the field value. I'll show you how we can use lightning-datatable in our project. Below is the sample output, How our component look like.

Output 1:
In this output I've updated the Opportunity Name and saved.

Output 2:
I've applied a check that Opportunity name cannot be more than 150 character.

OpportunityList.cmp

<aura:component controller="OpportunityListController">

       <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
       <aura:attribute name="data" type="Object"/>
       <aura:attribute name="columns" type="List"/>
       <aura:attribute name="errors" type="Object" />

       <div style="width:90%;height:400px;margin:0 auto">   

             <lightning:datatable aura:Id="dataTable"

                 keyField="Id"

                 data="{!v.data}"

                 columns="{!v.columns}"

                 Id="{dataTable+!index}"

                 hideCheckboxColumn="true"

                 oncellchange="{!c.handleEdit}"

                 showRowNumberColumn="false"

                 rowNumberOffset="{! v.rowNumberOffset }"

                 onsave="{! c.handleSave }"

                 oncancel="{! c.handleCancel }"

             errors="{!v.errors}" />   

      </div>

</aura:component>

I've created few aura attribute and a lightning datatable with some properties above.

  • data: This attribute holds the opportunity list.
  • columns: This attribute holds no of display columns.
  • errors: If there is any erroe while editing, This attribute would hold errors.

OpportunityListController.js

({

     doInit : function(component, event, helper) {

           helper.getOpportunityList(component); 

     },   

     handleSave:function(component,event,helper){

         helper.saveEditedRecords(component,event);   

     },

     handleCancel:function(component,event,helper){        },

     handleEdit:function(component,event,helper){

         helper.validateEditData(component,event);   

     }

})

I've implement all the event method here which we have used in our OpportunityList.cmp component. I've written all the main implmentation in helper class.


OpportunityListHelper.js

({

     getOpportunityList : function(component) {

         var action = component.get('c.getOpportunityList');

         action.setCallback(this,function(res){

              var state = res.getState();

              if(state == 'SUCCESS'){

                  var columns = [];

                  columns.push({

                    type: 'text',

                    label: 'Name',

                    fieldName: 'Name',

                    editable: true,

                    wrapText: false

                  });

 

                  columns.push({

                    type: 'text',

                    label: 'Stage',

                    fieldName: 'StageName',

                    editable: false,

                    wrapText: false

                  });

 

                  columns.push({

                    type: 'number',

                    label: 'Amount',

                    fieldName: 'Amount',

                    editable: true,

                    initialWidth: 110

                  });

                  component.set('v.columns', columns);

                  component.set('v.data',res.getReturnValue());  

              }

         });

         $A.enqueueAction(action);

     },

 

     showToast : function(msg,title){

         var toastEvent = $A.get("e.force:showToast");

         toastEvent.setParams({

              title:title,

              message:msg,

              key:'info_alt',

              type:'info',

              mode:'pester'

         });

         toastEvent.fire();

     },

 

     validateEditData : function(component,event){

         var v = event.getParam('draftValues');

         for(let i = 0; i < v.length; i++){

             var error = component.get('v.errors');

             if(v[i].Name != undefined) {

                 if(v[i].Name.length > 150) {

                     this.addErrorMessage(v[i].Id,component,"Name","Opportunity Name should be less than 150","Limit Error");

                 }

                 else if(error != null) {

                      this.updateErrorMessage(v[i].Id,component,error,"Name","Limit Error");

                 }

             }

         }

     },

 

     //If User entry invalid entry in textbox this method would help to render an error.

     addErrorMessage : function(rowId,component,colName,colMsg,title){

           var error = component.get('v.errors');
           if(error == undefined || error == null){
               error = { rows: {}, table: {} };
           }

 

           var colsArray = [];
           let msgArray = [];
           if(error.rows[rowId] != undefined){
              colsArray = error.rows[rowId].fieldNames;
              msgArray = error.rows[rowId].messages;
           }
           if(colsArray.indexOf(colName) == -1){
              colsArray.push(colName);
              msgArray.push(colMsg);
           }

           error.rows[rowId] = {
               title : title,
               messages : Array.from(msgArray),
               fieldNames : Array.from(colsArray)
           };

 

           //Check object data and based on that data error msg would be populated.
           let msg = new Set();
           let rows = JSON.parse(JSON.stringify(error.rows));
           let keys = Object.keys(rows);
           for(let i=0;i< keys.length;i++){
              let msgs = rows[keys[i]].messages;
              for(let c = 0; c< msgs.length;c++){
                    msg.add(msgs[c]);
              }
           }

           error = { rows: error.rows, table: {} };
           error.table.title = title;
           error.table.messages = Array.from(msg);
           component.set('v.errors',error);

     },

 

     //If user update the textbox entry then we would check if input is valid or not.

     updateErrorMessage : function(rowId,component,error,colName,title){

          let rows = JSON.parse(JSON.stringify(error.rows));

          //fieldsNames can have 2 values. So we would remove respective column value.
          if(rows[rowId].fieldNames.indexOf(colName) != -1){
             let index = rows[rowId].fieldNames.findIndex(x=> x == colName);
             rows[rowId].fieldNames.splice(index,1);
             rows[rowId].messages.splice(index,1);
          }

          if(rows[rowId].fieldNames.length == 0){
             delete rows[rowId]; //Deleting the error from the list.
          }

 

          //Check remaning object data and based on that data error msg would be populated.
         let msg = new Set();
         let keys = Object.keys(rows);
         for(let i=0;i< keys.length;i++){
            let msgs = rows[keys[i]].messages;
            for(let c = 0; c< msgs.length;c++){
                msg.add(msgs[c]);
            }
         }

 

         //If all erros are deleted then, Clear the error object.
         if(Object.keys(rows).length == 0){
             error = { rows: {}, table: {} };
         }
         else {
              error = { rows: rows, table: {} };
              error.table.title = title;
              error.table.messages = Array.from(msg);
         }
         component.set('v.errors',error);

     },

 

     saveEditedRecords : function(component,event,helper){

          //First check the error if there is no error then Proceed.

          let canSaveRecord = true;

          var error = component.get('v.errors');

          if(error != undefined || error != null){

              let rows = JSON.parse(JSON.stringify(error.rows));

              if(Object.keys(rows).length > 0){

                   canSaveRecord = false;

              }

          }

 

          if(canSaveRecord){

              var editedRecords = component.find('dataTable').get("v.draftValues");
              var updatedRecords = [];
              var action = component.get('c.updateOpportunityRecords');
              action.setParams({records : editedRecords});

              action.setCallback(this,function(response){

                   var state = response.getState();

                   let msg = response.getReturnValue();

                   if(state == 'SUCCESS' && msg == 'success'){

                       this.showToast('Record(s) has been Successfully Updated','Success');

                       $A.get('e.force:refreshView').fire();

                   }

                   else {

                       this.showToast(msg,'Error');

                   }

              });

              $A.enqueueAction(action);

          }

     } 

})


OpportunityListController.apex

public with sharing class OpportunityListController {


    @AuraEnabled

    public static List<Opportunity> getOpportunityList(){       

    //returning 'Closed Won' Opportunity list.   

   return [Select Id,Name,StageName,Amount FROM opportunity WHERE StageName = 'Closed Won'];   

   }       

 

  @AuraEnabled   

   public static string updateOpportunityRecords(List<Opportunity> records){       

       try {           

          upsert records;           

          return 'success';       

      }         

      catch(DmlException e){           

         return e.getMessage();       

      }   

   }

}

Upcoming Blog

  • lightning datatable inline edit picklist
  • lightning datatable inline edit lookup


Feel free to reachout to me if you face any issue or challange.

Thanks viewer for taking your time to reading this aritcle. Please comment if you find this article helpful.

Regards
Maksud

Comments

Popular posts from this blog

How to create custom popup | Lightning Popup | Using HTML/CSS and Javascript