reflex/docs/datatable_tutorial/add_interactivity.md
2024-02-26 17:18:28 +01:00

224 lines
7.2 KiB
Markdown

```python exec
import reflex as rx
from docs.datatable_tutorial.datatable_tutorial_utils import DataTableState, DataTableState2
```
# Adding Interactivity to our DataTable
Now we will add interactivity to our datatable. We do this using event handlers and event triggers.
The first example implements a handler for the `on_cell_clicked` event trigger, which is called when the user clicks on a cell of the data editor. The event trigger receives the coordinates of the cell.
```python
class DataTableState(rx.State):
clicked_cell: str = "Cell clicked: "
...
def get_clicked_data(self, pos: tuple[int, int]) -> str:
self.clicked_cell = f"Cell clicked: \{pos}"
```
The state has a var called `clicked_cell` that will store a message about which cell was clicked. We define an event handler `get_clicked_data` that updates the value of the `clicked_cell` var when it is called. In essence, we have clicked on a cell, called the `on_cell_clicked` event trigger which calls the `get_clicked_data` event handler, which updates the `clicked_cell` var.
```python demo
rx.text(DataTableState.clicked_cell)
```
```python demo
rx.data_editor(
columns=DataTableState.cols,
data=DataTableState.data,
on_cell_clicked=DataTableState.get_clicked_data,
)
```
The event handler `on_cell_context_menu` can be used in the same way as `on_cell_clicked`, except here the event trigger is called when the user right clicks, i.e. when the cell should show a context menu.
## Editing cells
Another important type of interactivity we will showcase is how to edit cells. Here we use the `on_cell_edited` event trigger to update the data based on what the user entered.
```python
class DataTableState(rx.State):
clicked_cell: str = "Cell clicked: "
edited_cell: str = "Cell edited: "
...
def get_clicked_data(self, pos) -> str:
self.clicked_cell = f"Cell clicked: \{pos}"
def get_edited_data(self, pos, val) -> str:
col, row = pos
self.data[row][col] = val["data"]
self.edited_cell = f"Cell edited: \{pos}, Cell value: \{val["data"]}"
```
The `on_cell_edited` event trigger is called when the user modifies the content of a cell. It receives the coordinates of the cell and the modified content. We pass these into the `get_edited_data` event handler and use them to update the `data` state var at the appropriate position. We then update the `edited_cell` var value.
```python demo
rx.text(DataTableState.edited_cell)
```
```python demo
rx.data_editor(
columns=DataTableState.cols,
data=DataTableState.data,
on_cell_clicked=DataTableState.get_clicked_data,
on_cell_edited=DataTableState.get_edited_data,
)
```
## Group Header
We can define group headers which are headers that encompass a group of columns. We define these in the `columns` using the `group` property such as `"group": "Data"`. The `columns` would now be defined as below. Only the `Title` does not fall under a group header, all the rest fall under the `Data` group header.
```python
class DataTableState2(rx.State):
"""The app state."""
...
cols: list[dict] = [
{\"title": "Title", "type": "str"},
{
"title": "Name",
"type": "str",
"group": "Data",
"width": 300,
},
{
"title": "Birth",
"type": "str",
"group": "Data",
"width": 150,
},
{
"title": "Human",
"type": "bool",
"group": "Data",
"width": 80,
},
{
"title": "House",
"type": "str",
"group": "Data",
},
{
"title": "Wand",
"type": "str",
"group": "Data",
"width": 250,
},
{
"title": "Patronus",
"type": "str",
"group": "Data",
},
{
"title": "Blood status",
"type": "str",
"group": "Data",
"width": 200,
},
]
...
```
The table now has a header as below.
```python demo
rx.data_editor(
columns=DataTableState2.cols,
data=DataTableState2.data,
on_cell_clicked=DataTableState2.get_clicked_data,
on_cell_edited=DataTableState2.get_edited_data,
)
```
There are several event triggers we can apply to the group header.
```python
class DataTableState2(rx.State):
"""The app state."""
right_clicked_group_header : str = "Group header right clicked: "
...
def get_group_header_right_click(self, index, val):
self.right_clicked_group_header = f"Group header right clicked at index: \{index}, Group header value: \{val['group']}"
```
```python demo
rx.text(DataTableState2.right_clicked_group_header)
```
```python demo
rx.data_editor(
columns=DataTableState2.cols,
data=DataTableState2.data,
on_cell_clicked=DataTableState2.get_clicked_data,
on_cell_edited=DataTableState2.get_edited_data,
on_group_header_context_menu=DataTableState2.get_group_header_right_click,
)
```
In this example we use the `on_group_header_context_menu` event trigger which is called when the user right-clicks on a group header. It returns the `index` and the `data` of the group header. We can also use the `on_group_header_clicked` and `on_group_header_renamed` event triggers which are called when the user left-clicks on a group header and when a user renames a group header respectively.
## More Event Triggers
There are several other event triggers that are worth exploring. The `on_item_hovered` event trigger is called whenever the user hovers over an item in the datatable. The `on_delete` event trigger is called when the user deletes a cell from the datatable.
The final event trigger to check out is `on_column_resize`. `on_column_resize` allows us to respond to the user dragging the handle between columns. The event trigger returns the `col` we are adjusting and the new `width` we have defined. The `col` that is returned is a dictionary for example: `\{'title': 'Name', 'type': 'str', 'group': 'Data', 'width': 198, 'pos': 1}`. We then index into `self.cols` defined in our state and change the `width` of that column using this code: `self.cols[col['pos']]['width'] = width`.
```python
class DataTableState2(rx.State):
"""The app state."""
...
item_hovered: str = "Item Hovered: "
deleted: str = "Deleted: "
...
def get_item_hovered(self, pos) -> str:
self.item_hovered = f"Item Hovered type: \{pos['kind']}, Location: \{pos['location']}"
def get_deleted_item(self, selection):
self.deleted = f"Deleted cell: \{selection['current']['cell']}"
def column_resize(self, col, width):
self.cols[col['pos']]['width'] = width
```
```python demo
rx.text(DataTableState2.item_hovered)
```
```python demo
rx.text(DataTableState2.deleted)
```
```python demo
rx.data_editor(
columns=DataTableState2.cols,
data=DataTableState2.data,
on_cell_clicked=DataTableState2.get_clicked_data,
on_cell_edited=DataTableState2.get_edited_data,
on_group_header_context_menu=DataTableState2.get_group_header_right_click,
on_item_hovered=DataTableState2.get_item_hovered,
on_delete=DataTableState2.get_deleted_item,
on_column_resize=DataTableState2.column_resize,
)
```