# Actions

Custom actions can be applied to 4 areas of an object-table: rows, cells, columns and the object-table itself.

## Empty action

In the event that a table contains no rows, an `<empty-action>` node can be used to add additional UI functionality.

```xml
<object-table ...>
    <empty-action icon="fa-angle-right" on-press="$:noResultsAction()" validate="false" />
    ...
</object-table>
```

```javascript
function noResultsAction(object){
    // Do something with the object
    console.log(JSON.stringify(object));
}
```

**Note:** This works in addition to the [`empty-message`](https://docs.journeyapps.com/reference/build/ui-components/all-ui-components/object-table/..#empty-message) attribute that is available on an `object-table`.

## Row actions

Similar to bind, a row can trigger an action by nesting a standard `action` tag in an `object-table`. For example:

```xml
<object-table ...>
    <action icon="fa-angle-right" on-press="$:rowAction($selection)" validate="false" />
    ...
</object-table>
```

```javascript
function rowAction(object){
    // Do something with the object
    console.log(JSON.stringify(object));
}
```

{% hint style="info" %}
Tip: It is good practice to place actions at the top of an `object-table`
{% endhint %}

When using a function to specify the `icon` attribute on `<action/>`, it has access to `$object` (note: not `$selection`), for example:

```xml
<object-table ...>
    <action icon="$:getIcon($object)" .../>
    ...
</object-table>
```

## Cell actions

When an `action` is specified within a `column`, the action will fire when the particular column is selected. The `icon`, in this case, is placed on the right - and should not be confused with a standard cell icon, which is independent of the `action` `icon`.

```xml
<object-table ...>
    <column display="{name}" heading="Name">
        <action icon="fa-angle-right" on-press="$:cellAction($selection)"/>
    </column>
    <column display="{surname}" heading="Surname">
        <action icon="fa-angle-right" on-press="$:cellAction($selection)" />
    </column>
    ...
</object-table>
```

```javascript
function cellAction(object){
    // Do something with the object
    console.log(JSON.stringify(object));
}
```

Much like a row action, this function also has access to `$selection`.

### Combining row and cell actions

In the case where both a row and cell action are present, the cell action will take precedence and prevent the row action from firing.

```xml
<object-table ...>
    <action icon="fa-angle-right" on-press="$:rowAction($selection)" />

    <!-- Clicking here will fire the row action -->
    <column heading="Name">{name}</column>

    <!-- Clicking here will fire the cell action only -->
    <column heading="Surname" display="{surname}">
        <action icon="fa-angle-right" on-press="$:cellAction($selection)" />
    </column>
</object-table>
```

## Column actions

A column action is displayed next to the column heading, in the same place the filter icon would normally be. If the action is specified, the `header-action` icon is used regardless if filters are enabled or not.

```xml
<object-table ...>
    <column heading="Name">
        <header-action icon="fa-angle-right" on-press="$:headerAction()" />
    </column>
</object-table>
```

**Note:** Unlike row and cell actions, this function does not have access to `$selection`, as they are only rendered once per column.

### Common patterns

Header actions can be used in interesting patterns, as follows:

#### Combo menu

In this example the header action displays a dropdown menu with a set of options, where and when pressed.

```javascript
function headerAction(){
    var option = actionSheet(["Setting 1", "Setting 2", "toggle-filter"]);
    // do something with the option
}
```

#### Combo menu with original filter capability

```javascript
function headerAction(){
    var option = actionSheet([
        "Setting 1",
        "Setting 2",
        "Toggle Filter" // <--- we want this menu item to have the original toggle-filter capability
     ]);

     if(option === 2){
         // so we specifically return 'show-filter' here
         // this is referred to as a 'return directive'
         return "show-filter"
      }
}
```

#### Pin frozen column

In this example, when the action is pressed, a view variable is set which indicates that the particular column should 'freeze' to the left.

```xml
<var name="frozen_column" type="string" />

<object-table ...>
    <column freeze="$:view.frozen_column === 'name'?'left':'none'" heading="Name">
      <header-action icon="fa-pin" on-press="$:headerAction()" />
    </column>
    ...
</object-table>
```

```javascript
function headerAction(){
    view.frozen_column = "name";
}
```

## `object-table` actions

{% hint style="info" %}
**Version compatibility**

`object-table` actions were introduced in version 4.34.6 of the JourneyApps Container.
{% endhint %}

`object-table` actions enable additional buttons to be appended to the controls area of an `object-table`. Additionally, these buttons can call functions passing the current data in the `object-table`.

For example:

```xml
<object-table label="Employees" limit="17" empty-message="Your items will appear here" query="employees" mode="paginate">
    <column heading="Name">{name}</column>
    <button-group>
        <button icon="fa-envelope" label="Email" on-press="exportCSV($filteredData, filteredDisplayData, controls)"/>
        <button icon="fa-download" label="Export CSV" on-press="exportCSV($filteredData, filteredDisplayData, controls)"/>
    </button-group>
</object-table>
```

![](https://2865107717-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9TCHLR67eLHBOjPvHhud%2Fuploads%2Fgit-blob-3150f21e7eeb72b7112de1aa5f2b2fe99c3fe6b4%2Fobject-table-actions-example.png?alt=media)

Each of the buttons call a function in their `on-press` attribute with the following three parameters:

1. `$filteredData`: An object of the form `{columns: string[], rows: DatabaseObject[]}`. `DatabaseObject` refers to the familiar JourneyApps object that can be used to update values in the database.
2. `filteredDisplayData`: An object of the form `{columns: string[], rows: string[][]}`. The rows correspond to the text currently displayed in the table. This object can be directly passed to CSV's `stringify()` function to create a CSV export of the table's data.
3. `controls`: An object of the form `{page: number, totalPages: number, limit: number, filters: {string: string[]}}`. This represents the current state of the search criteria and filters applied to the `object-table`.

In both parameters where data is passed to the function the sort order and current table filters are applied to the data.

{% hint style="info" %}
**Tip**: When you have more than one button in the `button-group` and would like to emphasize one of them, use `mode="split"`. This will display the first button in the group and place the rest in an [`actionSheet`](https://docs.journeyapps.com/reference/build/ui-components/all-ui-components/actionsheet).
{% endhint %}

### Example: Select all items on a page

{% code title="main.view\.xml" %}

```xml
<object-table label="Select one to view details" query="tickets" empty-message="Your items will appear here" limit="5">
    <column heading="Date">{date}</column>
    <column filter="true" heading="Customer">{customer}</column>
    <column filter="true" heading="Pad">{pad_name}</column>
    <column style-align="right" heading="Total">{$object.ticket_total}</column>
        
    <button-group>
        <button label="Select all" icon="fa-check" on-press="$:selectAll($filteredData, controls)" validate="false" style="outline" />
    </button-group>
</object-table>
```

{% endcode %}

{% code title="main.js" %}

```javascript
function selectAll(filteredData, controls) {
    var startIndex = (controls.page - 1) * controls.limit;
    var endIndex = controls.limit * controls.page;
    // slice the current visible page's objects out of the array of all objects currently rendered (sorting and filtering included)
    var selectedObjects = filteredData.rows.slice(startIndex, endIndex);
}
```

{% endcode %}

{% hint style="info" %}
`$filteredData` contains an array of all the DB objects currently rendered in the table across all pages. In the above example we ‘slice’ out the specific page that we are interested in - in this case it's the current page which is store in the `controls` parameter.
{% endhint %}

### Example: One-click CSV export

{% code title="schema.xml" %}

```xml
<model name="employee" label="Employee">
    <field name="name" label="Name" type="text:name" />
    <display>{name}</display>
</model>
```

{% endcode %}

{% code title="main.view\.xml" %}

```xml
<view title="CSV Export">
    <var name="employees" type="query:employee" />
    <object-table label="Employees" query="employees">
        <column heading="Name">{name}</column>
        <button-group>
            <button icon="fa-download" label="Export CSV" on-press="exportCSV($filteredData, filteredDisplayData, controls)"/>
        </button-group>
    </object-table>
</view>
```

{% endcode %}

{% code title="main.js" %}

```javascript
function exportCSV(filteredData, filteredDisplayData, controls) {
    var data = CSV.stringify(filteredDisplayData);
    journey.files.saveFile(data, 'data.csv');
}
```

{% endcode %}
