# 4.3 Displaying Data in Tables (Page Builder & Element Config)

BetterForms provides powerful options for displaying tabular data, from simple list views to advanced data tables. This section covers when to use each approach and how to implement them effectively.

## Two Main Approaches to Displaying Data

### 1. **List Views (`listrows`)**

* Simple multi-row data entry forms
* Add/remove rows functionality
* Good for data input scenarios
* Limited sorting and filtering

### 2. **Data Tables (`tables2`)**

* Advanced data display with sorting, filtering, pagination
* Read-only data presentation
* Complex interactions and custom formatting
* Based on Vue-Tables-2 library

## When to Use Each Approach

| **Use List Views When:**     | **Use Data Tables When:**              |
| ---------------------------- | -------------------------------------- |
| Users need to add/edit rows  | Displaying read-only data              |
| Simple data entry forms      | Large datasets requiring search/filter |
| Contact lists, address books | Reports and analytics                  |
| Product catalogs (editable)  | User management interfaces             |
| Max 20-30 rows               | Hundreds or thousands of rows          |

## List Views (`listrows`) Implementation

### Where to Add List Views in the IDE

1. **Open your page** in the BetterForms IDE
2. **Navigate to Page Builder tab**
3. **Search for "listrows"** in the snippets
4. **Copy and paste** into your page schema

### Basic List View Structure

```json
{
  "type": "listrows",
  "label": "Contact List",
  "model": "contacts",
  "min": 1,
  "max": 10,
  "styleClasses": "col-md-12",
  "schema": {
    "fields": [
      {
        "type": "input",
        "inputType": "text",
        "label": "First Name",
        "model": "firstName",
        "styleClasses": "col-md-4"
      },
      {
        "type": "input",
        "inputType": "text",
        "label": "Last Name",
        "model": "lastName",
        "styleClasses": "col-md-4"
      },
      {
        "type": "input",
        "inputType": "email",
        "label": "Email",
        "model": "email",
        "styleClasses": "col-md-4"
      }
    ]
  }
}
```

### Data Model for List Views

Your page's data model should include an array for the list data:

```json
{
  "contacts": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john@example.com"
    },
    {
      "firstName": "Jane",
      "lastName": "Smith",
      "email": "jane@example.com"
    }
  ]
}
```

### Advanced List View Example

```json
{
  "type": "listrows",
  "label": "Product Inventory",
  "model": "products",
  "min": 0,
  "max": 50,
  "styleClasses": "col-md-12",
  "schema": {
    "fields": [
      {
        "type": "input",
        "inputType": "text",
        "label": "Product Name",
        "model": "name",
        "styleClasses": "col-md-3",
        "validator": "string"
      },
      {
        "type": "input",
        "inputType": "number",
        "label": "Price",
        "model": "price",
        "styleClasses": "col-md-2",
        "validator": "number"
      },
      {
        "type": "input",
        "inputType": "number",
        "label": "Quantity",
        "model": "quantity",
        "styleClasses": "col-md-2",
        "validator": "number"
      },
      {
        "type": "select",
        "label": "Category",
        "model": "category",
        "styleClasses": "col-md-3",
        "values": [
          { "value": "electronics", "label": "Electronics" },
          { "value": "clothing", "label": "Clothing" },
          { "value": "books", "label": "Books" }
        ]
      },
      {
        "type": "checkbox",
        "label": "Active",
        "model": "isActive",
        "styleClasses": "col-md-2"
      }
    ]
  }
}
```

## Data Tables (`tables2`) Implementation

### Where to Add Data Tables in the IDE

1. **Open your page** in the BetterForms IDE
2. **Navigate to Page Builder tab**
3. **Search for "table"** in the snippets
4. **Copy and paste** into your page schema

### Basic Data Table Structure

```json
{
  "type": "tables2",
  "label": "Customer List",
  "model": "customers",
  "styleClasses": "col-md-12",
  "columns": ["firstName", "lastName", "email", "status"],
  "options": {
    "filterable": true,
    "sortable": true,
    "pagination": {
      "enabled": true,
      "perPage": 10
    }
  }
}
```

### Data Model for Data Tables

Your page's data model should include an array of objects:

```json
{
  "customers": [
    {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john@example.com",
      "status": "Active"
    },
    {
      "firstName": "Jane",
      "lastName": "Smith",
      "email": "jane@example.com",
      "status": "Inactive"
    }
  ]
}
```

### Advanced Data Table with Custom Formatting

```json
{
  "type": "tables2",
  "label": "Order Management",
  "model": "orders",
  "styleClasses": "col-md-12",
  "columns": ["orderNumber", "customer", "total", "status", "actions"],
  "options": {
    "filterable": ["customer", "status"],
    "sortable": ["orderNumber", "customer", "total"],
    "pagination": {
      "enabled": true,
      "perPage": 25
    },
    "texts": {
      "count": "Showing {from} to {to} of {count} orders",
      "filter": "Search orders:",
      "filterPlaceholder": "Type to search..."
    }
  },
  "slots": [
    {
      "slot": "total",
      "html": "<strong>${{props.row.total}}</strong>"
    },
    {
      "slot": "status",
      "html": "<span class=\"badge badge-{{props.row.status === 'Complete' ? 'success' : 'warning'}}\">{{props.row.status}}</span>"
    },
    {
      "slot": "actions",
      "html": "<button class=\"btn btn-sm btn-primary\" v-on:click=\"namedAction('editOrder', {orderId: props.row.id})\">Edit</button>"
    }
  ]
}
```

## Row Click Actions

### Adding Row Click Functionality

```json
{
  "type": "tables2",
  "label": "Customer List",
  "model": "customers",
  "styleClasses": "col-md-12",
  "columns": ["firstName", "lastName", "email"],
  "actions_onRowClick": [
    {
      "action": "path",
      "options": {
        "path_calc": "'/customer-detail?id=' + this.params.row.id"
      }
    }
  ]
}
```

### Using Named Actions with Row Data

```json
{
  "type": "tables2",
  "label": "Customer List",
  "model": "customers",
  "styleClasses": "col-md-12",
  "columns": ["firstName", "lastName", "email"],
  "actions_onRowClick": [
    {
      "action": "namedAction",
      "name": "viewCustomerDetail"
    }
  ]
}
```

**In your Named Action:**

```javascript
// Access the clicked row data
let customer = action.options.params.row;
let customerId = customer.id;
let customerName = customer.firstName + ' ' + customer.lastName;
```

## Custom Column Formatting with Slots

### Adding Custom HTML to Columns

```json
{
  "type": "tables2",
  "label": "Product Catalog",
  "model": "products",
  "columns": ["name", "price", "inventory", "status", "actions"],
  "slots": [
    {
      "slot": "price",
      "html": "<span class=\"text-success\">${{props.row.price}}</span>"
    },
    {
      "slot": "inventory",
      "html": "<span class=\"{{props.row.inventory < 10 ? 'text-danger' : 'text-success'}}\">{{props.row.inventory}} units</span>"
    },
    {
      "slot": "status",
      "html": "<span class=\"badge badge-{{props.row.status === 'active' ? 'success' : 'secondary'}}\">{{props.row.status}}</span>"
    },
    {
      "slot": "actions",
      "html": "<div class=\"btn-group\"><button class=\"btn btn-sm btn-primary\" v-on:click=\"namedAction('editProduct', {productId: props.row.id})\">Edit</button><button class=\"btn btn-sm btn-danger\" v-on:click=\"namedAction('deleteProduct', {productId: props.row.id})\">Delete</button></div>"
    }
  ]
}
```

### Child Row Expansion

Display additional details when a row is expanded:

```json
{
  "type": "tables2",
  "label": "Order List",
  "model": "orders",
  "columns": ["orderNumber", "customer", "total", "status"],
  "slots": [
    {
      "slot": "child_row",
      "html": "<div class=\"p-3\"><h5>Order Details</h5><p><strong>Shipping Address:</strong> {{props.row.shippingAddress}}</p><p><strong>Notes:</strong> {{props.row.notes}}</p></div>"
    }
  ]
}
```

## Integration with FileMaker

### Loading Data from FileMaker

Use the `onFormRequest` hook to populate table data:

**FileMaker Script:**

```javascript
// In your onFormRequest hook
If[Not IsEmpty($$BF_Query)]
  Set Variable [$action; Value:JSONGetElement($$BF_Query, "action")]
  If[$action = "loadCustomers"]
    // Perform find and return customer data
    Set Variable [$customers; Value:GetColumnAsArray("customers", "id", "1")]
    Set Variable [$result; Value:JSONSetElement("{}", "customers", $customers, JSONArray)]
    Exit Script [Result: $result]
  End If
End If
```

### Triggering FileMaker Actions from Tables

```json
{
  "type": "tables2",
  "label": "Customer List",
  "model": "customers",
  "columns": ["firstName", "lastName", "email", "actions"],
  "slots": [
    {
      "slot": "actions",
      "html": "<button class=\"btn btn-sm btn-success\" v-on:click=\"runUtilityHook({type: 'activateCustomer', customerId: props.row.id})\">Activate</button>"
    }
  ]
}
```

## Table Filtering and Sorting

### Basic Filtering

```json
{
  "type": "tables2",
  "model": "products",
  "columns": ["name", "category", "price"],
  "options": {
    "filterable": true,
    "filterByColumn": true,
    "pagination": {
      "enabled": true,
      "perPage": 20
    }
  }
}
```

### Custom Sort Functions

For numeric sorting of text fields:

```json
{
  "type": "tables2",
  "model": "products",
  "columns": ["name", "price", "date"],
  "options": {
    "sortable": true,
    "customSorting": {
      "price_function": "var ascending = arguments[0]; return function(a,b){if (ascending) return parseFloat(a.price) >= parseFloat(b.price) ? 1 : -1; return parseFloat(a.price) <= parseFloat(b.price) ? 1 : -1;}"
    }
  }
}
```

## Performance Considerations

### For Large Datasets

1. **Use Server-Side Pagination**
   * Load data in chunks from FileMaker
   * Implement search on the server side
2. **Optimize FileMaker Queries**
   * Use efficient finds
   * Limit returned fields
   * Use `getColumnAsArray` for better performance
3. **Consider Data Caching**
   * Cache frequently accessed data
   * Use development data model for testing

### Table Performance Tips

```json
{
  "type": "tables2",
  "model": "largeDataset",
  "columns": ["id", "name", "status"],
  "options": {
    "pagination": {
      "enabled": true,
      "perPage": 50
    },
    "filterable": ["name", "status"],
    "sortable": ["name", "id"],
    "texts": {
      "count": "Showing {from} to {to} of {count} records"
    }
  }
}
```

## Best Practices

### 1. **Choose the Right Component**

* Use `listrows` for data entry
* Use `tables2` for data display
* Consider user workflow needs

### 2. **Optimize User Experience**

* Provide clear column headers
* Use consistent formatting
* Add loading states for large datasets

### 3. **Handle Mobile Responsiveness**

* Test table behavior on mobile devices
* Consider hiding non-essential columns
* Use responsive design principles

### 4. **Security Considerations**

* Validate user permissions for row actions
* Sanitize data before display
* Use proper authentication for sensitive data

## Troubleshooting Common Issues

### List Views Not Showing

* Check that the data model contains the correct array
* Verify the `model` property matches your data structure
* Ensure `schema.fields` are properly configured

### Data Table Not Loading

* Verify the data model array structure
* Check that column names match data properties
* Ensure `model` property is correctly set

### Row Actions Not Working

* Check action syntax in `actions_onRowClick`
* Verify named actions are properly defined
* Test with simple actions first

## Next Steps

Now that you understand data display options, you can:

* **Style your tables** - See [4.4 Basic App Styling](/getting-started/ide-quick-tour/4.-common-customizations-and-expanding-your-app/4.4-basic-app-styling-site-styling-ui.md)
* **Integrate with authentication** - See [Authentication](/reference/authentication.md)
* **Explore advanced features** - See [Data Table Reference](/reference/components-overview/common/tables2.md)

{% hint style="info" %}
**Performance Tip:** For datasets with more than 100 rows, consider implementing server-side pagination and filtering in your FileMaker scripts.
{% endhint %}


---

# 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.klai.studio/getting-started/ide-quick-tour/4.-common-customizations-and-expanding-your-app/4.3-displaying-data-in-tables-page-builder-and-element-config.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.
