[SalesForce] Lightning:datatable dynamically disable rowactions

I have a lightning:datatable, which is rendering a set of objects:

<lightning:datatable data="{!v.propertyMappings}" 
                           columns="{!v.columns}" 
                           keyField="Id" 
                           onrowaction="{!c.handleRowAction}"
                           hideCheckboxColumn="true" 
                           resizeColumnDisabled="true" />

I want to dynamically enable/disable some rows based on an attribute on Custom_Object__c that is loaded into {!v.propertyMappings}.

I tried adding a disabled attribute to the object data when it was loaded in my helper:

    var responseValues = response.getReturnValue();
    for (var i = 0; i < responseValues.length; i++) {
      responseValues[i]["disabled"] = true;
    } 
    component.set("v.propertyMappings", responseValues);

But it didn't seem to have any effect…?

My row-actions are still available (from {!v.columns}):

{ type: "action", typeAttributes: { rowActions: actions } }

Should I just use another component?

Best Answer

You need to handle the disabled attribute in your getRowActions handler (you do have one, right?). Here's a self-contained example that demonstrates this.

Application

<aura:application extends="force:slds">
    <aura:attribute name="data" type="List" />
    <aura:attribute name="columns" type="List" />

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

    <div style="height: 300px">
    <lightning:datatable data="{!v.data}"
                         columns="{!v.columns}"
                         keyField="Id"
                         onrowaction="{!c.handleAction}"
                         hideCheckboxColumn="true"
                         resizeColumnDisabled="true" />
    </div>
</aura:application>

Controller

({
    init: function(component, event, helper) {
        // callback binding
        var actions = helper.getRowActions.bind(this, component);
        // sample data
        component.set("v.data", [
            { Id: "1", intValue: 0 },
            { Id: "2", intValue: 0 },
            { Id: "3", intValue: 0 },
            { Id: "4", intValue: 0 },
            { Id: "5", intValue: 0 },
            { Id: "6", intValue: 0 }
        ]);
        // column data
        component.set("v.columns", [
            { label: "value", fieldName: "intValue", type: "number" },
            { type: "action", typeAttributes: { rowActions: actions }}
        ]);
    },
    // handle action
    handleAction: function(component, event, helper) {
        var action = event.getParam("action"),
            row = event.getParam("row"),
            data = component.get("v.data"),
            rowIndex = data.indexOf(row);
        switch(action.name) {
            case 'inc': data[rowIndex].intValue++;
                break;
        }
        component.set("v.data", data);
    }
})

Helper

({
    getRowActions: function(component, row, cb) {
        var actions = [];
        actions.push({
            label: "Increment", 
            name: "inc",
            // allow three increments per row
            disabled: row.intValue > 2 });
        cb(actions);
    }
})

Here, I'm using row-based data to determine if the menu item is disabled or not. This is done dynamically at the moment the menu button is clicked. You can make this as simple or complicated as need be.