Editable lightning:dataTable – Summer18 feature

#Summer18 release was a power-packed release and has a lot to offer. One of the features introduced is lightning is the editable lightning data table.

Now you can edit your record from within the lightning:dataTable itself. Check out gif above.👆

Not sure how you can do this, no worries, this post is all about this. Follow this post and create your own editable lightning data table.

Read all summer18 lightning feature here

Component – Markup of your component

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes"
                access="global"
                controller="AccountController">
    <aura:attribute name="data" type="Object"/>
    <aura:attribute name="columns" type="List"/>
    <aura:attribute name="recordId" type="String"/>
    <!-- This attribute will hold the update records from data table-->
    <aura:attribute name="updatedRecord" type="Object[]" />

    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

    <!-- You must define keyField as 'Id' to save the record back in Salesforce
'onsave' attribute will executed when user clicks on save button -->
    <lightning:card title="Account Editable Datatable">
        <lightning:datatable
                             aura:id="accountDataTable"
                             columns="{! v.columns }"
                             data="{! v.data }"
                             keyField="Id"
                             onsave ="{!c.onSave}"
                             hideCheckboxColumn="true"
                             onrowaction="{! c.handleRowAction }" />
    </lightning:card>
</aura:component>

Client Controller – Handles event of your markup

({
    /*
     * This finction defined column header
     * and calls getAccounts helper method for column data
     * editable:'true' will make the column editable
     * */
doInit : function(component, event, helper) {        
        component.set('v.columns', [
            {label: 'Name', fieldName: 'Name', editable:'true', type: 'text'},
            {label: 'Phone', fieldName: 'Phone', editable:'true', type: 'phone'},
            {label: 'Rating', fieldName: 'Rating', editable:'true', type: 'text'},
            {label: 'Custom Field', fieldName: 'My_Custom_Field__c', editable:'true', type: 'text'}
        ]);        
        helper.getAccounts(component, helper);
    },

    /*
     * This function is calling saveDataTable helper function
     * to save modified records
     * */
    onSave : function (component, event, helper) {
        helper.saveDataTable(component, event, helper);
    }
})

Clie

Client Helper – Handles server interaction and reusable code

({
    getAccounts : function(component, event, helper) {
        var action = component.get("c.getAccounts");
        action.setCallback(this,function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.data", response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },

    /*
     * This function get called when user clicks on Save button
     * user can get all modified records
     * and pass them back to server side controller
     * */
    saveDataTable : function(component, event, helper) {
        var editedRecords =  component.find("accountDataTable").get("v.draftValues");
        var totalRecordEdited = editedRecords.length;
        var action = component.get("c.updateAccounts");
        action.setParams({
            'editedAccountList' : editedRecords
        });
        action.setCallback(this,function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                //if update is successful
                if(response.getReturnValue() === true){
                    helper.showToast({
                        "title": "Record Update",
                        "type": "success",
                        "message": totalRecordEdited+" Account Records Updated"
                    });
                    helper.reloadDataTable();
                } else{ //if update got failed
                    helper.showToast({
                        "title": "Error!!",
                        "type": "error",
                        "message": "Error in update"
                    });
                }
            }
        });
        $A.enqueueAction(action);
    },

    /*
     * Show toast with provided params
     * */
    showToast : function(params){
        var toastEvent = $A.get("e.force:showToast");
        if(toastEvent){
            toastEvent.setParams(params);
            toastEvent.fire();
        } else{
            alert(params.message);
        }
    },

    /*
     * reload data table
     * */
    reloadDataTable : function(){
    var refreshEvent = $A.get("e.force:refreshView");
        if(refreshEvent){
            refreshEvent.fire();
        }
    },
})

Server Controller – Returns and update record

public class AccountController {
@AuraEnabled
    public static List<Account> getAccounts(){
        return [SELECT
               Id, Name, Phone, Rating, My_Custom_Field__c
               FROM Account LIMIT 200];
    }

    @AuraEnabled
    public static boolean updateAccounts(List<Account> editedAccountList){
        try{
            update editedAccountList;
            return true;
        } catch(Exception e){
            return false;
        }
    }
}

So this is how you can create your own editable lightning data table. Below are few considerations and facts about lightning:dataTable:

  • lightning:dataTable is not supported on mobile devices
  • The keyField attribute is required to hold unique row id. You should always define keyField=”Id” which means row id will be same as record id and it will make easier to update, view and edit records.
  • You component version needs to be at least on API version 43 for the editable data table. However if you are not using editable columns, then you can run data table from api version 41 onwards.

Salesforce is really making lightning more easier to use with new component and new features in exiting components with every release and #Summer18 is perfect example of that. 

(Visited 9951 Times)

63 thoughts on “Editable lightning:dataTable – Summer18 feature

  1. AVINEESH KUMAR MISHRA

    Hello Manish,

    Thanks for this post but when i am using this inline edit lightning Data table for custom object then it’s not working.

    Reply
    1. Manish Choudhari Post author

      Hi Avineesh,

      Can you please post your code here? I have tested it with custom object and it is working fine.

      Reply
  2. Dhananjay Kumar

    Hi,

    I would like to share few points whoever implementing this.
    1. want to show relationship data
    2. want to show url to navigate to parent record as we see in standard component.

    solutions:
    1. want to show relationship data
    – data table not supported for relationship data and it will show blank data
    – create a formula field for that relationship then you only you can show i.e record__r.Name ==> parentName__c

    2. want to show url in data table to navigate to parent record
    – right now there is no support to generate lookup url to navigate through record in datatable
    – create a formula field “RecordId__c” : “/”&Id to hold record id. This will used to generate url
    – call this field in SOQL
    – in lighting component where you are creating ‘Columns’ use as :
    component.set(‘v.columns’, [
    {label: ‘Name’, fieldName: ‘Name’, editable:’true’, type: ‘text’},
    {label: ‘Custom Field’, fieldName: ‘My_Custom_Field__c’, editable:’true’, type: ‘text’},
    {label: ‘Code’, fieldName: ‘RecordId__c’, type: ‘url’ , typeAttributes: { label: { fieldName: ‘parentName__c’ }, target: ‘_blank’ }}
    ]);

    and just like switch it will turn the parent record data to a url in datatable and you can navigate.

    🙂
    thanks.

    Reply
  3. congmingwudi@gmail.com

    Hi Manish, do you have any idea when the datatable will be supported on mobile?

    -Ryan

    Reply
    1. Manish Choudhari Post author

      Hi Ryan,

      I have looked at the pipeline for Winter19 release, I do not see this feature listed there. So I guess, they are not focusing on this part for now. But not sure, they might surprise us 🙂

      Reply
  4. Nitin

    var editedRecords = component.find(“accountDataTable”).get(“v.draftValues”);

    In the above line where is draftvalues attribute declared in Component?

    Reply
    1. Manish Choudhari Post author

      It’s not supported till summer 18. Let’s see if future release gives this flexibility.

      For now, you can use another component along with your datatable component, and use LDS to create a new record. Once the record is created, refresh the data table again to see the new record.

      Reply
  5. Homerlex

    Is there any way to hook up the save operation with Lightning Data Service instead of sending update through an APEX controller?

    Reply
    1. Manish Choudhari Post author

      Hi Mike,

      Yes, LDS can be used to save the data, however, the limitation is that LDS only works on single record id. So if you want to perform save operation on multiple records like in this example, you have to use your own apex controller.
      Hope this answers your question.

      Reply
  6. Abhishek Kumar

    Hi Manish,
    I copied and pasted your code with just one modification. I added NoOfEmployyes standard field in place of your custom field. However, resulting datatable is not rendering properly.
    I can’t paste the screen shot here. It is displaying big rectangular boxes for each column in each row. It is not displaying any value for fields of type number inside those boxes but for text it is showing the values.

    Reply
  7. Shashank Gupta

    Hi Manish,

    Very helpful post.
    One issue i am facing is, when i use below code:

    var editedRecords = component.find(“accountDataTable”).get(“v.draftValues”);
    var totalRecordEdited = editedRecords.length;
    var action = component.get(“c.updateAccounts”);
    action.setParams({
    ‘editedAccountList’ : editedRecords
    });

    @AuraEnabled
    public static boolean updateAccounts(List editedAccountList){
    try{
    update editedAccountList;
    return true;
    } catch(Exception e){
    return false;
    }
    }

    it is not calling me apex controller method at all. Controller specified in the component I checked everything is in place. except for the object name.

    Reply
  8. Shashank Gupta

    Hi Manish,

    A very good article, very helpful.
    I am facing some problem with this code:

    used :
    var editedRecords = component.find(“accountDataTable”).get(“v.draftValues”);
    var totalRecordEdited = editedRecords.length;
    var action = component.get(“c.updateAccounts”);
    action.setParams({
    ‘editedAccountList’ : editedRecords
    });
    action.setCallback(this,function(response) {
    var state = response.getState();
    if (state === “SUCCESS”) {
    //if update is successful
    if(response.getReturnValue() === true){
    helper.showToast({
    “title”: “Record Update”,
    “type”: “success”,
    “message”: totalRecordEdited+” Account Records Updated”
    });
    helper.reloadDataTable();
    } else{ //if update got failed
    helper.showToast({
    “title”: “Error!!”,
    “type”: “error”,
    “message”: “Error in update”
    });
    }
    }
    });
    $A.enqueueAction(action);

    @AuraEnabled
    public static boolean updateAccounts(List editedAccountList){
    try{
    update editedAccountList;
    return true;
    } catch(Exception e){
    return false;
    }
    }

    but in my case it is not calling the apex method. Everything is copy pasted from here.

    Reply
    1. Manish Choudhari Post author

      Hi Shashank,

      Have you mapped your Apex Controller in your component? In the first line of your component, have you written “controller=”YouApexControllerName”.

      If yes, do you see any error on the screen or in console logs?

      Reply
  9. Anil

    Hi,
    Thanks for the code but , when i pass the same code for the custom object the apex class is not called can you please help me

    Reply
  10. Stuck

    Let’s say I have a custom component with a custom phone number field in a junction object (I want to keep tabs on several phones lent to employees).

    I’d also like to know what the previous phone number, if any, was assigned to a specific employee.

    I have an old number field, and a new number field. Is there a way of showing the old number before inserting the new one?

    I have a custom object with all the phones, a custom object with the employee’s name and a junction with MD relationships to the previous objects and the two number fields.

    How can this be done?

    Reply
      1. Brad Stucker

        Yeah, I’ve tried that but no go.

        I’m putting each of these into a similar function (1 function for each attribute) in my helper class:
        getE : function(component, event, helper) {
        var action = component.get(“c.getE”);
        action.setCallback(this,function(response) {
        var state = response.getState();
        console.log(response.getReturnValue());
        if (state === “SUCCESS”) {
        component.set(“v.data”, response.getReturnValue());
        }
        });
        $A.enqueueAction(action);
        }

        I can get the new value, but I can’t see the old one.

        Reply
          1. Brad Stucker

            I think this is what you’re looking for. I can email you the whole thing if necessary.

            APEX CLASS
            public class PhoneTable {
            @AuraEnabled
            public static List getA() {
            return [Select PhoneID__c,
            (select CAmm__c from Loan__c order by CreatedDate Desc limit 1)
            from Phone__c
            ];
            }

            @AuraEnabled
            public static List getPA() {
            return [Select CAmm__c
            from Loan__c
            order by CreatedDate
            Desc
            limit 1
            ];
            }
            HELPER
            @AuraEnabled
            public static Boolean newS(String ammountListing) {
            List loanList = new List ();
            List wrapperList = (List ) JSON.deserialize(ammountListing, List .class);
            Lent__c newLending = new Lent__c();
            insert newLending;
            for (wt lendingList: wrapperList) {
            Loan__c listRecord = new Loan__c(Phones__c = lendingList.Id,
            Records__c = newLending.id,
            CAmm__c = lendingList.CAmm,
            PreviousA__c = lendingList.PreviousA);
            loanList.add(listRecord);

            }
            try {
            insert loanList;
            } catch (Exception E) {
            return false;
            }
            return true;
            }

            public class wt {
            @AuraEnabled public Decimal CAmm {
            get;
            set;
            }
            @AuraEnabled public Decimal PreviousA {
            get;
            set;
            }
            @AuraEnabled public String Id {
            get;
            set;
            }
            }
            }

            getA: function(component, event, helper) {
            var action = component.get(“c.getA”);
            action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === “SUCCESS”) {
            component.set(“v.data”, response.getReturnValue());
            }
            });
            $A.enqueueAction(action);
            },
            getPA: function(component, event, helper) {
            var action = component.get(“c.getPA”);
            action.setCallback(this, function(response) {
            var state = response.getState();
            console.log(response.getReturnValue());
            if (state === “SUCCESS”) {
            component.set(“v.olddata”, response.getReturnValue());
            }
            });
            $A.enqueueAction(action);

  11. Kumar

    HI Manish,

    Upon clicking save button , Is it possible to show updated values in the datatable itself,
    I don’t need to send them to server. After clicking save button, i tried to read the values in the data attribute, which are still showing old values not updated one.

    Can you help me how to achieve this

    Reply
    1. Manish Choudhari Post author

      Hi Kumar,

      Yes, you can do that. However, I am not able to think of a business scenario where this would be useful.

      Actually you have to replace the data of you data-table which is still old when you click on the save button. Do something like this:
      > When you click on save button, get the latest changed data.
      > Get all existing data from data table (old one)
      > Loop through the old data and replace the column values where needed

      This way you will not send the data to server but would be able to display new data in datatable.

      Reply
  12. Chen

    Hi Manish,

    thanks for posting this!

    everything is working great except when I do var refreshEvent = $A.get(“e.force:refreshView”);
    it returns an undefined object and I have to manually refresh the page to see the changes.

    Any thoughts?

    thanks,
    Xi

    Reply
    1. Manish Choudhari Post author

      Are you using the same code?
      You must be using it standalone lightning app or lightning out, which is not supported. This event is only supporting LEX and Mobile experience.

      Also try to check for null/undefined before you fire the event to avoid any exception.

      Reply
  13. Xi

    Hi Manish,

    This post is great! Thanks very much!

    one question tho, my refreshView returns an undefined object so when i call Fire it runs into an error.

    any thoughts?

    Reply
    1. Manish Choudhari Post author

      Are you using the same code?
      You must be using it standalone lightning app or lightning out, which is not supported. This event is only supporting LEX and Mobile experience.

      Also try to check for null/undefined before you fire the event to avoid any exception.

      Reply
      1. Xi

        Thanks Manish. You are right about standalone lighting app not supporting the functionality.

        Another quick question, do you have any suggestion of implementing dynamic search on multiple fields? (Search based on number of fields user put in). I have 14 possible fields in total..

        Reply
          1. Xi

            Hi Manish,

            Thanks for the reply. but I came up with my own implementation which I just modified the SOQL dynamically.

  14. Lokesh

    Hi Manish,

    I am trying to implement inline edit and call from client controller isnt reaching Server apex. This is the code:

    COMPONENT:
    ———————————————————————

    {!v.loadMoreStatus}

    <!–

    –>

    CLIENT CONTROLLER:
    ———————————–
    onSave : function(component, event, helper) {
    // var editedRecords = component.find(‘dataTable’).get(‘v.draftValues’);
    // alert(‘Number of records edited:’+editedRecords.length);
    helper.onEdit(component, event, helper);
    }

    CLIENT HELPER:
    —————————————–
    ({
    onEdit : function(component, event, helper) {
    var editedRecords = event.getParam(‘draftValues’);
    alert(‘Number of records edited:’+editedRecords.length);
    var totalRecordEdited = editedRecords.length;
    var action = component.get(“c.updateIncidents”);
    action.setParams({
    “editedIncidents” : editedRecords
    });
    action.setCallback(this, function(response) {
    var state = response.getState();
    if(state === ‘SUCCESS’) {
    //alert(‘return value:::’+response.getReturnValue());
    if(response.getReturnValue() === true) {
    helper.showToast({
    “title”: “Record Update”,
    “type”: “success”,
    “message”: totalRecordEdited+” Account Records Updated”
    });
    helper.reloadDataTable();
    }
    else {

    helper.showToast({
    “title”: “Error!!”,
    “type”: “error”,
    “message”: “Error in update”
    });
    }
    }
    });
    $A.enqueueAction(action);
    },

    showToast : function(params){
    var toastEvent = $A.get(“e.force:showToast”);
    if(toastEvent){
    toastEvent.setParams(params);
    toastEvent.fire();
    } else{
    alert(params.message);
    }
    },

    reloadDataTable : function(){
    var refreshEvent = $A.get(“e.force:refreshView”);
    if(refreshEvent){
    refreshEvent.fire();
    }
    },
    })

    Reply
  15. santhi

    hi,

    this is fine but we have a requirement which says upon click of tab it should navigate between all editable fields. Eventhough we can use arrow keys to navigate between tabs user requires tab to work for navigation.

    Can you please suggest how is this possible?

    Reply
  16. Naman Jain

    Hi Manish,

    I am not able to see the table as it is which is shown in sample. I am not able to attach the sceenshot here, but I will mail you the screenshot.
    I have copy paste the same code then also not able to view the exact data table.
    can you please help.

    Regards,
    Naman

    Reply
  17. Anusha

    Hi Manish,’
    I wrote a VF page to return PDF form, and I created a Button & QuickAction with this page.
    I am trying to download this page from Salesforce1 Mobile App.
    I tried different ways, it is downloading from desktop, but not for Mobile app.
    My main doubt is, Is it possible to download a PDF VF page from Salesforce1?
    If u have any idea, can you please share.

    Thankyou.

    Reply
  18. Likitha

    Hi Manish,

    I have tried a part of this code. I am editing the data table as given above,Problem is apex is getting called but the data datatable is not updating with the new edited values.Any help will be much appreciated.

    Reply
    1. Manish Choudhari Post author

      Hi Likhita, you need to refresh your lightning component once you get the response from the server. Something like this:
      reloadDataTable : function(){
      var refreshEvent = $A.get(“e.force:refreshView”);
      if(refreshEvent){
      refreshEvent.fire();
      }
      },

      Reply
  19. Deepak

    Hi Manish Sir, this is Deepak.

    I want to show validations like the required field, phone number validations. the errors will show on the cell.
    suppose if here is column name in data table if we left this blank then the error should show on the cell. like when we enter the wrong email address in data table cell shows error on that particular cell. Can we do like this?

    Reply
  20. Anusha

    Hi Manish,

    I am using editable functionality in my code.I am facing the below issues.
    1.I need to pass the columns dynamically with out using following code.
    component.set(‘v.columns’, [
    {label: ‘Name’, fieldName: ‘Name’, editable:’true’, type: ‘text’},
    {label: ‘Phone’, fieldName: ‘Phone’, editable:’true’, type: ‘phone’},
    {label: ‘Rating’, fieldName: ‘Rating’, editable:’true’, type: ‘text’},
    {label: ‘Custom Field’, fieldName: ‘My_Custom_Field__c’, editable:’true’, type: ‘text’}
    ]);
    I should not use field sets also.Hence i am fetching the data from custom settings,
    i am using the following code for fetching the data from custom settings.

    Map values = customsettingname.getAll();
    Map options = new Map();

    for (String key: values.keyset()) {
    options.put(key, values.get(key));
    }
    system.debug(‘—options’+options);
    return options;
    from apex controller i am getting the values,but how to pass this values to colums is what the challange i am facing.Pls suggest any solution.
    2.Second thing is i need to display more than 50k records in the same page without using the pagination and offset.Any solution is there in lightning to display all the records in single page.(I am using infinite loading too.)

    Reply
    1. Manish Choudhari Post author

      You can surely set the column dynamically based on server response. Just make sure you provide valid object array to “v.column”.
      Also, displaying more than 50k record on the same page will definitely make your page a lot slower and your may not respond at all. Too much of data on same page consumes lot browser memory and results in page crash sometimes!!

      Reply
  21. Anusha

    Hi Manish,

    Any possibility is there to display 50k records in the same page without using (pagination,batch class,readonly attribute) in lightning.May it will be combination of jquery also.with out using pagination,batchclass and readonly attribute i need to handle more than 50k records in datatable of lightning.Please Let me know whether it is possible or not.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *