Based on this feature request, I have been playing around with a new callback syntax that resembles the function signature more closely. To illustrate the idea, consider this (very simple) app,
from dash import callback, html, dcc, Input, Output, State, Dash
@callback([Output("hello", "children"), Output("click", "children")],
Input("btn", "n_clicks"), State("name", "value"))
def hello(n_clicks: int, name):
return f"Hello {name}!", f"Click count is {n_clicks}"
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
dcc.Input(placeholder="Enter your name here", id="name"),
html.Button("Click me!", id="btn"),
html.Div(id="hello"), html.Div(id="click")
])
if __name__ == '__main__':
app.run_server()
that prints hello and click count messages on click. With the new syntax, the code would be,
from dash_extensions.enrich import DashProxy, callback, html, dcc, prop
@callback
def hello(n_clicks: prop("btn", "n_clicks", int),
name: prop("name", "value", trigger=False)) \
-> tuple[prop("hello", "children"), prop("click", "children")]:
return f"Hello {name}!", f"Click count is {n_clicks}"
app = DashProxy(prevent_initial_callbacks=True)
app.layout = html.Div([
dcc.Input(placeholder="Enter your name here", id="name"),
html.Button("Click me!", id="btn"),
html.Div(id="hello"), html.Div(id="click")
])
if __name__ == '__main__':
app.run_server()
A few notes on the new syntax,
- The
Input
/Output
/State
objects are replaced by a commonprop
function that takes component id and component property as mandatory arguments, and optionally a type definition - Argument props are interpreted as
Input
, orState
iftrigger=False
- Return props are interpreted as
Output
, orServersideOutput
ifserverside=True
On thing I like about the new syntax is that the binding to the prop happens just next to the argument name definition, e.g.
n_clicks: prop("btn", "n_clicks", int)
That means that you donât have to look elsewhere in the code (with the current syntax, in a list above that could be arbitrarily long) to figure out what property (i.e. ân_clicksâ of âbtnâ) the ân_clicksâ argument binds to. I think that improves code readability.
Secondly, the new syntax is more compact (at least in the current form). Rather than importing âInput/Output/Stateâ, you only need a single âpropâ import. And the dependency type (i.e. âInputâ, âOuputâ, etc.) is derived from the function signature. Especially for people that know Python, I think this might feel more ânaturalâ as compared to learning a syntax just for Dash (i.e. the current âInput/Output/Stateâ syntax).
You should be able to try out the new syntax with the following rc release,
Let me know what you think. Any suggestions for improvements are very welcome