# Grid - Custom Action

## Introduction

GridMate offers out of the box a set of actions like Mass Assign, Mass Approve...However there is some situation where a custom component is required to address the business needs like implementing a specific logic or calling an external system.&#x20;

To address this type of use cases, GridMate defines an interface which is a set of properties and events.&#x20;

### **Aura Properties**

```html
<!-- The object of the grid -->
<aura:attribute name="relatedObjectName" type="String" access="global" />
<!-- The list of displayed columns -->
<aura:attribute name="columns" type="Object[]" access="global" />
<!-- The list of selected items -->
<aura:attribute name="selectedItems" type="Object[]" access="global" />
<!-- The Id of the master record (record page or master record) -->
<aura:attribute name="recordId" type="String" access="global" />
```

### **Aura Events**

```html
<!-- Event to be fired when the action is executed successfully-->
<aura:registerEvent name="onsuccess" type="c:DataGridActionEvent" />
<!-- Event to be fired when the user cancel the action-->
<aura:registerEvent name="oncancel" type="c:DataGridActionEvent" />
```

## Aura Implementation

### Aura Component

Let's say that we want to move to close date by one day for the selected items. Let's go ahead and start our custom action using an Aura component.

This action will be triggered from an Opportunity grid. Let's create an Aura component **MoveCloseDateComponent.** The component has all the required properties described above.

Also the component will fire **onsucess** or **oncancel** based on the user flow when the user submit the transaction or cancel the screen.

{% tabs %}
{% tab title="MoveCloseDateComponent.cmp" %}

```html
<aura:component implements="force:hasRecordId" controller="MoveCloseDateController" access="global">
    <!-- Aura Props -->
    <aura:attribute name="relatedObjectName" type="String" access="global" />
    <aura:attribute name="columns" type="Object[]" access="global" />
    <aura:attribute name="selectedItems" type="Object[]" access="global" />

    <!-- Aura Events -->
    <aura:registerEvent name="onsuccess" type="gmpkg:DataGridActionEvent" />
    <aura:registerEvent name="oncancel" type="gmpkg:DataGridActionEvent" />

    <!-- Internal flag to control the spinner-->
    <aura:attribute name="isWorking" type="Boolean" access="global" />

    <!-- overlayLib API -->
    <lightning:overlayLibrary aura:id="overlayLib" />

    <div class="slds-theme_default">
        <div class="content-wrapper">
            <lightning:input type="date" name="closeDate" label="Close Date" />
        </div>

        <div class="slds-modal__footer actions-wrapper">
            <button class="slds-button slds-button--neutral" onclick="{!c.handleCancel}">Cancel</button>
            <button class="slds-button slds-button--brand" onclick="{!c.handleSubmit}">Submit</button>
        </div>

        <aura:if isTrue="{! v.isWorking }">
            <lightning:spinner variant="brand" alternativeText="Processing" style="background: transparent" />
        </aura:if>
    </div>
</aura:component>
```

{% endtab %}

{% tab title="MoveCloseDateComponentController.js" %}

```javascript
({
    handleSubmit: function (component, event, helper) {
        component.set('v.isWorking', true);

        //Submit the action to the Salesforce
        let action = component.get('c.moveCloseDate');
        action.setParams({
            idList: component.get('v.selectedItems').map((x) => x.Id),
            closeDate: component.get('v.closeDate')
        });

        action.setCallback(this, function (res) {
            component.set('v.isWorking', false);

            if (res.getState() === 'SUCCESS') {
                //Show a toast for the end user
                let toastEvent = $A.get('e.force:showToast');

                toastEvent
                    .setParams({
                        title: 'Success',
                        type: 'success',
                        message: 'Action Executed Successfully'
                    })
                    .fire();

                component
                    .getEvent('onsuccess')
                    .setParams({
                        action: 'ActionExecuted'
                    })
                    .fire();

                //Close the modal
                component.find('overlayLib').notifyClose();
            } else if (res.getState() === 'ERROR') {
                console.log(res.getError());
            }
        });

        $A.enqueueAction(action);
    },
    handleCancel: function (component, event, helper) {
        component
            .getEvent('oncancel')
            .setParams({
                action: 'ActionCancelled'
            })
            .fire();

        component.find('overlayLib').notifyClose();
    }
});
```

{% endtab %}
{% endtabs %}

### Apex Controller

The Aura component is ready, let's go ahead and implement the Apex controller. In our example, the job of the controller is straightforward; update the close date of the selected record&#x73;**.**&#x20;

{% tabs %}
{% tab title="MoveCloseDateController" %}

```apex
public with sharing class MoveCloseDateController {
    @AuraEnabled
    public static boolean moveCloseDate(List<Id> idList, Date closeDate) {
        List<Opportunity> oppList = new List<Opportunity>();
        for (Id oppId : idList) {
            oppList.add(new Opportunity(Id = oppId, CloseDate = closeDate));
        }

        update oppList;
        return true;
    }
}
```

{% endtab %}
{% endtabs %}

### Action Configuration

Our Aura component and the Apex class are ready for use. Let's go ahead and configure the grid action as below:

```json
[
    {
        "name": "Move",
        "label": "Move",
        "component": "c:MoveCloseDateComponent"
    }
]
```

<figure><img src="/files/FQTokcXNA9jjSBBmqOPy" alt=""><figcaption></figcaption></figure>

## LWC Implementation

### LWC Component

LWC is also an option to implement a custom action. GridMate provides an Aura wrapper component to launch any LWC component. Below is the LWC component to update the close date same as the aura component above.

{% tabs %}
{% tab title="moveCloseDateLWC.html" %}

```html
<template>
    <div class="slds-theme_default">
        <div class="content-wrapper">
            <lightning-input
                data-id="closeDate"
                type="date"
                label="Close Date"
            ></lightning-input>
        </div>

        <div class="slds-modal__footer actions-wrapper">
            <button
                class="slds-button slds-button--neutral"
                onclick={handleCancel}
            >
                Cancel
            </button>
            <button
                class="slds-button slds-button--brand"
                onclick={handleSubmit}
            >
                Submit
            </button>
        </div>
        <lightning-spinner
            if:true={isWorking}
            variant="brand"
            alternative-text="Processing"
            style="background: transparent"
        >
        </lightning-spinner>
    </div>
</template>

```

{% endtab %}

{% tab title="moveCloseDateLWC.js" %}

```javascript
/* eslint-disable no-unused-vars */
import { LightningElement, api, track } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

import moveCloseDate from '@salesforce/apex/MoveCloseDateController.moveCloseDate';

export default class MoveCloseDateLWC extends LightningElement {
    //api properties
    @api recordId;
    @api relatedObjectName;
    @api columns;
    @api selectedItems;

    @track isWorking;

    get closeDate() {
        return this.template.querySelector('[data-id="closeDate"]');
    }

    handleSubmit(event) {
        this.isWorking = true;

        moveCloseDate({
            idList: this.selectedItems.map((x) => x.Id),
            closeDate: this.closeDate.value
        })
            .then((result) => {
                this.isWorking = false;
                //Show a toast for the end user
                const toastEvent = new ShowToastEvent({
                    title: 'Success',
                    message: 'Action executed successfully!',
                    variant: 'success'
                });
                this.dispatchEvent(toastEvent);

                //Notify the Aura Wrapper
                const submitEvent = new CustomEvent('actionexecuted', {
                    detail: {
                        status: 'success',
                        action: 'moveCloseDateExecuted'
                    }
                });

                this.dispatchEvent(submitEvent);
            })
            .catch((error) => {
                this.isWorking = false;
                console.log(error);
            });
    }

    handleCancel(event) {
        const cancelEvent = new CustomEvent('cancel', {
            detail: {
                action: 'moveCloseDateCanceled'
            }
        });

        this.dispatchEvent(cancelEvent);
    }
}

```

{% endtab %}
{% endtabs %}

### Action Configuration

The LWC component is using the same Apex class. Let's go ahead and configure the grid action as below:

```json
[
     {
        "name": "MoveLWC",
        "label": "Move LWC",
        "component": "gmpkg:DataGridCallLWCComponent",
        "attributes": {
            "componentDefs": [
                {
                    "component": "c:moveCloseDateLWC",
                    "attributes": {}
                }
            ]
        }
    }
]
```

And obviously we get the same result as a above.

<figure><img src="/files/uCrm8e95x051weSB1XG8" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gridmate.io/advanced-guides/grid-custom-action.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
