diff --git a/nicegui/element.py b/nicegui/element.py index 1ba1edd87..d757ee58b 100644 --- a/nicegui/element.py +++ b/nicegui/element.py @@ -484,6 +484,18 @@ def run_method(self, name: str, *args: Any, timeout: float = 1, check_interval: return self.client.run_javascript(f'return runMethod({self.id}, "{name}", {json.dumps(args)})', timeout=timeout, check_interval=check_interval) + def get_computed_prop(self, prop_name: str, *, timeout: float = 1) -> AwaitableResponse: + """Return a computed property. + + This function should be awaited so that the computed property is properly returned. + + :param prop_name: name of the computed prop + :param timeout: maximum time to wait for a response (default: 1 second) + """ + if not core.loop: + return NullResponse() + return self.client.run_javascript(f'return getComputedProp({self.id}, "{prop_name}")', timeout=timeout) + def _collect_descendants(self, *, include_self: bool = False) -> List[Element]: elements: List[Element] = [self] if include_self else [] for child in self: diff --git a/nicegui/elements/table.py b/nicegui/elements/table.py index 5804e0ec8..4f6cc16f6 100644 --- a/nicegui/elements/table.py +++ b/nicegui/elements/table.py @@ -226,6 +226,18 @@ def update_rows(self, rows: List[Dict], *, clear_selection: bool = True) -> None self.selected.clear() self.update() + async def get_filtered_sorted_rows(self, *, timeout: float = 1) -> List[Dict]: + """Asynchronously return the filtered and sorted rows of the table.""" + return await self.get_computed_prop('filteredSortedRows', timeout=timeout) + + async def get_computed_rows(self, *, timeout: float = 1) -> List[Dict]: + """Asynchronously return the computed rows of the table.""" + return await self.get_computed_prop('computedRows', timeout=timeout) + + async def get_computed_rows_number(self, *, timeout: float = 1) -> int: + """Asynchronously return the number of computed rows of the table.""" + return await self.get_computed_prop('computedRowsNumber', timeout=timeout) + class row(Element): def __init__(self) -> None: diff --git a/nicegui/static/nicegui.js b/nicegui/static/nicegui.js index e42754659..08d0d4948 100644 --- a/nicegui/static/nicegui.js +++ b/nicegui/static/nicegui.js @@ -62,6 +62,19 @@ function runMethod(target, method_name, args) { } } +function getComputedProp(target, prop_name) { + if (typeof target === "object" && prop_name in target) { + return target[prop_name]; + } + const element = getElement(target); + if (element === null || element === undefined) return; + if (prop_name in element) { + return element[prop_name]; + } else if (prop_name in (element.$refs.qRef || [])) { + return element.$refs.qRef[prop_name]; + } +} + function emitEvent(event_name, ...args) { getElement(0).$emit(event_name, ...args); } diff --git a/tests/test_table.py b/tests/test_table.py index cae6341b7..206ad6308 100644 --- a/tests/test_table.py +++ b/tests/test_table.py @@ -204,3 +204,29 @@ def test_problematic_datatypes(screen: Screen): screen.should_contain('5 days') screen.should_contain('(1+2j)') screen.should_contain('2021-01') + + +def test_table_computed_props(screen: Screen): + all_rows = rows() + filtered_rows = [row for row in all_rows if 'e' in row['name']] + filtered_sorted_rows = sorted(filtered_rows, key=lambda row: row['age'], reverse=True) + + @ui.page('/') + async def page(): + table = ui.table( + columns=columns(), + rows=all_rows, + row_key='id', + selection='multiple', + pagination={'rowsPerPage': 1, 'sortBy': 'age', 'descending': True}) + table.filter = 'e' + + await ui.context.client.connected() + assert filtered_sorted_rows == await table.get_filtered_sorted_rows() + assert filtered_sorted_rows[:1] == await table.get_computed_rows() + assert len(filtered_sorted_rows) == await table.get_computed_rows_number() + + screen.open('/') + screen.should_contain('Lionel') + screen.should_not_contain('Alice') + screen.should_not_contain('Bob') diff --git a/website/documentation/content/table_documentation.py b/website/documentation/content/table_documentation.py index 2641a10b4..d3118fd02 100644 --- a/website/documentation/content/table_documentation.py +++ b/website/documentation/content/table_documentation.py @@ -239,6 +239,37 @@ def handle_pagination_changes() -> None: ) +@doc.demo('Computed props', ''' + You can access the computed props of a table within async callback functions. +''') +def computed_props(): + async def show_filtered_sorted_rows(): + ui.notify(await table.get_filtered_sorted_rows()) + + async def show_computed_rows(): + ui.notify(await table.get_computed_rows()) + + table = ui.table( + columns=[ + {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left', 'sortable': True}, + {'name': 'age', 'label': 'Age', 'field': 'age', 'align': 'left', 'sortable': True} + ], + rows=[ + {'name': 'Noah', 'age': 33}, + {'name': 'Emma', 'age': 21}, + {'name': 'Rose', 'age': 88}, + {'name': 'James', 'age': 59}, + {'name': 'Olivia', 'age': 62}, + {'name': 'Liam', 'age': 18}, + ], + row_key='name', + pagination=3, + ) + ui.input('Search by name/age').bind_value(table, 'filter') + ui.button('Show filtered/sorted rows', on_click=show_filtered_sorted_rows) + ui.button('Show computed rows', on_click=show_computed_rows) + + @doc.demo('Computed fields', ''' You can use functions to compute the value of a column. The function receives the row as an argument.