The top portion of your form is a good spot for pydantic form, as all the inputs would be read in a single dictionary just like the wrapper you are talking about.
Here is an example flow based upon the image you gave:
from dash import Dash, dcc, html, Input, Output, set_props, ALL, MATCH, ctx, no_update
from dash_pydantic_form import ModelForm
import dash_mantine_components as dmc
import dash_ag_grid as dag
import pandas as pd
# Initialize Dash app
app = Dash(__name__, external_stylesheets=dmc.styles.ALL)
# Sample data for the table
data = {
"Header A": ["Cell A"] * 6,
"Header B": ["Cell B"] * 6,
"Header C": ["Cell C"] * 6,
}
df = pd.DataFrame(data)
# Pydantic model for the form
from pydantic import BaseModel, Field, create_model
# Define fields dynamically
fields = {
"general_input": (str, Field(..., title="General Input")),
"specific_input": (str, Field(..., title="Specific Input")),
"general_custom_aio": (str, Field(..., title="General Custom AIO")),
"additional_settings1": (bool, Field(False, title="Additional Settings")),
"additional_setting1_details": (dict, Field(default_factory=dict,
title="Additional Setting 1 Details",
repr_kwargs={"visible": [("additional_settings1", '==', True)]})),
"grid_settings_1": (bool, Field(False, title="Grid Settings 1")),
"grid_settings_2": (bool, Field(False, title="Grid Settings 2")),
}
# Create the model dynamically
def create_grid_form(fields):
return create_model('FormModel', **fields)
def action_button(**kwargs):
_id = kwargs.pop('id')
return dmc.Button(id={'index': _id, 'type': 'button_function'}, **kwargs)
def undo():
print("Undo action triggered")
def redo():
print("Redo action triggered")
def action1():
set_props('dialog-title', {'children': "Action 1 Title"})
print("Action 1 triggered")
def action2():
print("Action 2 triggered")
def action3():
set_props('dialog-title', {'children': f"{ctx.triggered_id.index} Title"})
print("Action 3 triggered")
action_map = {
"undo-btn": undo,
"redo-btn": redo,
"action1-btn": action1,
"action2-btn": action2,
"action3-btn": action3,
}
# Layout
app.layout = dmc.MantineProvider([
html.H2("Dialog Title", id="dialog-title"),
ModelForm(
create_grid_form(fields),
aio_id="pydantic-form",
form_id="form1"
),
html.Hr(),
html.Div([
action_button(children="Undo", id="undo-btn"),
action_button(children="Redo", id="redo-btn"),
action_button(children="Action 1", id="action1-btn"),
action_button(children="Action 2", id="action2-btn"),
action_button(children="Action 3", id="action3-btn"),
]),
html.Div([
dcc.Tabs(id="tabs", value="tab-1", children=[
dcc.Tab(label="Grid View 1", value="tab-1"),
dcc.Tab(label="Grid View 2", value="tab-2"),
dcc.Tab(label="Grid View 3", value="tab-3"),
dcc.Tab(label="Grid View 4", value="tab-4"),
]),
html.Div(id="tabs-content")
]),
html.Div([
html.Button("Cancel", id="cancel-btn"),
html.Button("Save", id="save-btn", style={"background-color": "blue", "color": "white"}),
]),
])
@app.callback(
Output("tabs-content", "children"),
Input("tabs", "value")
)
def render_tab_content(tab_value):
# Render the table for the tabs
return dag.AgGrid(
id=f"grid-{tab_value}",
columnDefs=[{"headerName": col, "field": col, 'flex': 1} for col in df.columns],
rowData=df.to_dict("records"),
defaultColDef={"editable": True, "sortable": True},
dashGridOptions={"animateRows": False}
)
@app.callback(
Output("dialog-title", "children"),
Input({'type': 'button_function', 'index': ALL}, 'n_clicks'),
)
def handle_action(n_clicks):
if not n_clicks or all(click is None for click in n_clicks):
return "Dialog Title"
triggered_id = ctx.triggered_id
if triggered_id and triggered_id['type'] == 'button_function':
action_func = action_map.get(triggered_id['index'])
if action_func:
action_func()
return no_update
@app.callback(
Input(ModelForm.ids.main('pydantic-form', 'form1'), "data")
)
def on_form_change(data):
print("Form data changed:", data)
# Run the Dash app
if __name__ == "__main__":
app.run(debug=True)
It’s not pretty, but its more about getting an example of the functionality