Replies: 2 comments 3 replies
-
Hi @kleynjan!
|
Beta Was this translation helpful? Give feedback.
-
@falkoschindler, inspired by your pandas grid example I finally settled on another approach, building the table using primitive elements, using a column-spec to drive what ui.control is used for every column. This way I give up Qtable functionality (pagination, sorting, etc) -- but some of these are better approached from the back-end anyway. And I get rid of the large blob of vue code. Regular value binding works (straight into the data dictionary, yea!) and validation is also easily integrated -- on the client side at least. Posting my "crud table" here in case someone else is facing the same struggle. from nicegui import ui
# expanding on https://github.com/zauberzeug/nicegui/discussions/905
# inspired by https://github.com/zauberzeug/nicegui/blob/main/examples/pandas_dataframe/main.py
my_data = [
{ "name": "Damien", "age": 18, },
{ "name": "Janine", "age": 19, },
{ "name": "John", "age": 20, },
]
my_columns = [
{
"name": "name",
"ui_type": ui.input,
"td_style": "width: 50%;",
"default_value": "...",
"parms": {"validation": {"Too short!": lambda value: len(value) > 5}},
},
{
"name": "age",
"ui_type": ui.number,
"td_style": "width: 20%;",
"default_value": 0,
"parms": {"format": "%.0f"},
},
]
def handle_save() -> None:
print(f"saving... {my_data}")
@ui.refreshable
def crud_table(
data: list[dict],
columns: list[dict],
table_classes="",
on_save: callable = None,
on_change: callable = None,
) -> None:
def delete_row(*, r: int) -> None:
data.pop(r)
crud_table.refresh()
def add_row() -> None:
data.append({colspec["name"]: colspec["default_value"] for colspec in columns})
crud_table.refresh()
with ui.element("table").classes(table_classes):
# table header
with ui.element("tr"):
for c in columns:
with ui.element("th"):
ui.label(c["name"])
with ui.element("th"):
ui.label("")
# table body
with ui.element("tbody") as tbody:
for row_index, row in enumerate(data):
with ui.element("tr"):
for col_spec in columns:
col_name = col_spec["name"]
cls = col_spec["ui_type"]
cls_parms = col_spec.get("parms", {})
cls_parms["value"] = row[col_name] # avoid triggering on_change when binding
if on_change:
cls_parms["on_change"] = lambda event, r=row_index, c=col_name: on_change(
r=r, c=c, value=event.value
)
# finally, add the td cell with the nicegui control...
with ui.element("td").style(col_spec.get("td_style", "")):
cls(**cls_parms).props("dense").bind_value(row, col_name)
with ui.element("td").classes("text-right"):
ui.button(
icon="delete", on_click=lambda event, r=row_index: delete_row(r=r)
).props("flat size=md dense")
# bottom row with add & save buttons
with ui.element("tr"):
with ui.element("td").props(f"colspan={len(columns)+1}").classes("text-right"):
ui.button(icon="add", on_click=add_row).props("flat size=md")
ui.button("Save", on_click=on_save).props("size=sm")
crud_table(my_data, my_columns, table_classes="text-left w-60", on_save=handle_save)
ui.run() Cheers, -Peter |
Beta Was this translation helpful? Give feedback.
-
Question
I am building heavily customized tables and struggling a bit with the "no man's land" between such big Quasar components and Nicegui. I find it difficult to boil this down to atomic questions, so I hope it's OK to post a list here.
Also see the minimal working example below. My questions can be summarized as "is there a way to do this in a more general and extensible way, for instance with the add_slot API"? And if not, "how do I get to the insides of a complex Quasar object"?
More specifically:
Binding. Is there a simple way (or 'best' way) to keep quasar table data bound to the columns and rows variables? In the example below, event handlers are used to sync all changes back to the 'rows' variable (otherwise, only 'props.rows' will be modified, not the 'rows' variable).
Alternatively, as was originally asked in 905, this sync'ing would be unnecessary if there was a way to access the actual quasar-side table data from the python-side. (But I guess that is a binding.)
Add_slot API. In the code below, like in many examples I've seen, the table.add_slot('body') is used to create one big "Quasar block" (q-tr->q-td->q-input). Instead, I'd like to use ui.input rather than the "literal" q-input.
In the discussions on PR 500 and 514 @dclause and others give a tantalizing glimpse, but the official add_slot example doesn't show how to override or extend the bread-and-butter th/td in the table body, using the data passed in as props. Can the API help me break up this big Quasar block and bring more of the logic of constructing the table in the Nicegui domain?
Validation. Quasar internal validation does not work, as I think is documented. Eg, :rules="email" on one of the columns below has no effect. @falkoschindler has added server-side validation in #336, but how do I add that to a big nested quasar slot as I am using below? So this is also related to point 2, I think.
Programmatic construction. In #668, @dclause showed very nicely how to construct the table by iterating through the colums ("v-for col in props.cols") and Vue ifs/elses to dynamically build the table. Ideally, I'd want to be able to do that in Nicegui, using (2) and (3). If not, I am back to point 1, and struggling to get access to the "col.value" from the nicegui side of my app.
I realize that maybe I should be digging in more deeply into Vue if I want to capitalize on the add_slot API. If that's so, I'd appreciate some pointers -- or other examples. Thanks!
Beta Was this translation helpful? Give feedback.
All reactions