Skip to content

Commit

Permalink
Adding methods to get computed props (#3395)
Browse files Browse the repository at this point in the history
* Adding methods to get computed props

Adding methods to get computed props from Quasar elements, using the QTable as a proof of principle

* Adding methods to get computed props

Adding methods to get computed props from Quasar elements, using the QTable as a proof of principle

* Adding methods to get computed props

Adding methods to get computed props from Quasar elements, using the QTable as a proof of principle

* Adding methods to get computed props

Adding methods to get computed props from Quasar elements, using the QTable as a proof of principle

* Adding methods to get computed props

Adding methods to get computed props from Quasar elements, using the QTable as a proof of principle

* code review

* convert async properties into async methods

* make timeout a keyword argument

* add timeout argument to table methods

---------

Co-authored-by: Falko Schindler <falko@zauberzeug.com>
  • Loading branch information
srobertson86 and falkoschindler authored Jul 26, 2024
1 parent 05d432d commit 04649eb
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 0 deletions.
12 changes: 12 additions & 0 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions nicegui/elements/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
13 changes: 13 additions & 0 deletions nicegui/static/nicegui.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
26 changes: 26 additions & 0 deletions tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
31 changes: 31 additions & 0 deletions website/documentation/content/table_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 04649eb

Please sign in to comment.