Editable lightning:dataTable – Summer18 feature

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. 

Manish Choudhari

I am a certified Salesforce Application & System Architect and Developer working on Salesforce Technology since 2014. Currently, I have 14 Salesforce certifications along with OCPJP (Oracle Certified Profession JavaSE6 Programmer) working in Salesforce.com Hyderabad as a Technical Engineer. Writing technical blogs, learning new technologies and frameworks and sharing knowledge is my hobby.

This Post Has 67 Comments

  1. Avatar

    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.

    1. Avatar

      Hi Avineesh,

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

    2. Avatar

      Hi avaneesh,

      Please let us know your pain point. I have used this and its working.

  2. Avatar

    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.

  3. Avatar

    Good article, I have to implement sorting here what would be the best approach?

  4. Avatar

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

    -Ryan

    1. Avatar

      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 🙂

  5. Avatar

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

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

    1. Avatar

      draftValues is attribute defined in datatable itself which returns all the edited record list. You do not have to declare is specifically.

  6. Avatar

    Does this support deletion of records?

  7. Avatar

    Also how well can it handle pagination

  8. Avatar

    Is there any support for creating new records from a datatable?

    1. Avatar

      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.

  9. Avatar

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

    1. Avatar

      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.

  10. Avatar

    Thanks for this updates.

    Navigatation to detail record and relationship data is much needed in this functionality.

  11. Avatar

    Is it possible to edit Picklist values in lightning:dataTable?

  12. Avatar

    Is it possible to edit Picklist fields in lightning:dataTable?

    1. Avatar

      Hi Nathan, Picklist field can be rendered as text type and can be edited using plain text. The actual drop-down is still not supported.

  13. Avatar

    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.

    1. Avatar

      Hi Abhishek,

      Under columns properties (in controller.js file), Did you change the type to “number” for your field or is it “text”?

  14. Avatar

    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.

    1. Avatar

      Hi Shashank,

      You need to call ‘$A.enqueueAction(action);’ which will call your server method.

  15. Avatar

    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.

    1. Avatar

      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?

      1. Avatar

        Hi ,
        I am facing he same problem , can anyone help with his issue

  16. Avatar

    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

      1. Avatar

        Anyone got solution?

  17. Avatar

    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?

      1. Avatar

        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.

        1. Avatar

          can you share your response data object from above code along with your column forming code from controller init method?

          1. Avatar

            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);

          2. Avatar

            Hey Brad,

            I would be more interested to have a look at your column generation code as well, but can you please share complete code(component,controller,helper,apex) with me at sfdcfacts@gmail.com

  18. Avatar

    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

    1. Avatar

      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.

  19. Avatar

    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

    1. Avatar

      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.

  20. Avatar

    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?

    1. Avatar

      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.

      1. Avatar

        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..

        1. Avatar

          I have designed a table like this but that uses SLDS not actual lightning:datatable. If you like that example, I can share that with you.

          1. Avatar

            Hi Manish,

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

  21. Avatar

    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();
    }
    },
    })

      1. Avatar

        Manish,

        I’ve just sent the complete to to sfdcfacts@gmail.com. Could you please check it?

        Lokesh

        1. Avatar

          Hello Manish,

          Can u help on this please

          Lokesh

          1. Avatar

            Hi Lokesh,

            I will look into it as soon as possible.

  22. Avatar

    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?

  23. Avatar

    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

    1. Avatar

      Exact copy past will not work Naman. Please modify the object name and fields name as per your requirement.

  24. Avatar

    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.

  25. Avatar

    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.

    1. Avatar

      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();
      }
      },

  26. Avatar

    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?

    1. Avatar

      Hi Manish and Deepak,
      Do you know or have the answer for Deepak’s question above regarding showing an error on the cell when user enter an unexpected data?
      Looking to hearing back from you. Thanks

  27. Avatar

    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.)

    1. Avatar

      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!!

  28. Avatar

    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.

  29. Avatar

    Hi Manish,

    This post is really helpful!!

    I have one question – In my component I am fetching column labels from fieldset and storing in a list and passed it to client side. It is coming perfectly but i just want to make below piece of code dynamic:

    component.set(‘v.labels’, [
    {label: ret.lstLabel[0], fieldName: ret.lstfieldAPI[0],type: ‘text’,editable:’true’},
    {label: ret.lstLabel[1], fieldName: ret.lstfieldAPI[1],type: ‘text’,editable:’true’},
    {label: ret.lstLabel[2], fieldName: ret.lstfieldAPI[2],type: ‘text’,editable:’true’}
    ]);

    I tried to iterate a loop of length of returned value. But it is not working. Can you pls help.

    Thanks,
    Pratiksha

  30. Avatar

    Hi Manish,
    I have the same question asked by Deepak above, regrading showing the error message on the editable cell when user enter unexpected info (for example, negative number for quantity) in that cell Does datatable support this ?

    1. Avatar

      Hi Lean & Deepak,

      This is not supported by default if you are using lightning:dataTable, however you can use SLDS data table and implement this functionality.

  31. Avatar

    Hi.. I added your code in my component.. But The updated record is not getting saved in database. Can you please help in solving this issue.

Leave a Reply

Close Menu