Hi everyone,
A summary of Dash version 3.1 updates can be found at the end of the post. However, I wanted to draw your attention to a few interesting new changes and features.
Allow Optional
Thank you to @jinnyzor for contributing this new feature
In some cases you may have components in your app that don’t exist when a callback runs. For example, if you have a component that is conditionally rendered. By default, Dash will throw an error if you use a non-existent component as an Input
or State
for a callback. You’ll see an error like: 'A nonexistent object was used...'
You can set these Input
or State
objects to be optional using the allow_optional
parameter. With allow_optional=True
, the callback will receive None
for any inputs or states that use it that don’t exist.
The following code example shows a button that is conditionally rendered from one callback and used as an optional input on another callback. See complete example in the docs.
from dash import Dash, html, Output, Input, State, no_update
app = Dash(suppress_callback_exceptions=True)
app.layout = html.Div([
html.Div([
html.Button(id="optional-inputs-button-1", children="Button 1", className="button"),
html.Div(id="optional-inputs-container"),
html.Div(id="optional-inputs-output", className="output")
], className="container")
])
@app.callback(
Output("optional-inputs-container", "children"),
Input("optional-inputs-button-1", "n_clicks"),
State("optional-inputs-container", "children"),
prevent_initial_call=True
)
def add_second_button(_, current_children):
if not current_children:
return html.Button(id="optional-inputs-button-2", children="Button 2", className="button")
return no_update
@app.callback(
Output("optional-inputs-output", "children"),
Input("optional-inputs-button-1", "n_clicks"),
Input("optional-inputs-button-2", "n_clicks", allow_optional=True),
prevent_initial_call=True
)
def display_clicks(n_clicks1, n_clicks2):
return f"Button 1 clicks: {n_clicks1} - Button 2 clicks: {n_clicks2}"
if __name__ == '__main__':
app.run(debug=True)
Keep Dropdown Open on Select (closeOnSelect=False
)
Thank you to 12rambau for their first time contribution
With Dash 3.1 you can set closeOnSelect=False
on a dropdown to keep it open after each selection. Example code below. See complete example in the docs.
from dash import Dash, dcc, html, Input, Output,callback
app = Dash()
app.layout = html.Div([
dcc.Dropdown(['NYC', 'MTL', 'SF'], 'NYC', id='close-on-select-dropdown', multi=True,
closeOnSelect=False),
html.Div(id='close-on-select-output')
])
@callback(
Output('close-on-select-output', 'children'),
Input('close-on-select-dropdown', 'value')
)
def update_output(value):
return f'You have selected {value}'
if __name__ == '__main__':
app.run(debug=True)
Using Async/Await in Callbacks
Thank you to @jinnyzor for the contribution
Dash supports using async
/await
in callbacks. To use async
/await
in callbacks, pip install "dash[async]"
.
Using async
/await
is useful when making requests, communicating with databases, or when using async-first libraires such as aiofiles
and tortoise-orm
.
The following example demonstrates using async
/ await
in a callback.
import time
from dash import html, Input, Output, Dash
import asyncio
app = Dash()
server = app.server
def get_sync_data(iteration):
time.sleep(1)
return f"Result ({iteration}) from synchronous API call"
async def get_async_data(iteration):
await asyncio.sleep(1)
return f"Result ({iteration}) from asynchronous API call"
app.layout = html.Div([
html.H2("Synchronous vs. Asynchronous Functions in Dash"),
html.Button("Run Sync Tasks", id="sync-btn", style={'margin-right': '10px'}),
html.Button("Run Async Tasks", id="async-btn"),
html.Hr(),
html.Div(id="sync-output", style={'color': 'red', 'font-weight': 'bold'}),
html.Div(id="async-output", style={'color': 'green', 'font-weight': 'bold'}),
])
@app.callback(
Output("sync-output", "children"),
Input("sync-btn", "n_clicks"),
prevent_initial_call=True,
)
def sync_callback_example(n_clicks):
if n_clicks:
results = [get_sync_data(i) for i in range(5)]
return html.Div([html.Div(result) for result in results])
return ""
@app.callback(
Output("async-output", "children"),
Input("async-btn", "n_clicks"),
prevent_initial_call=True,
)
async def async_callback_example(n_clicks):
if n_clicks:
coros = [get_async_data(i) for i in range(5)]
results = await asyncio.gather(*coros)
return html.Div([html.Div(result) for result in results])
return ""
if __name__ == "__main__":
app.run(debug=True)
When you install dash[async]
, Dash installs Flask async’s dependencies.
Note: The gevent
worker class with gunicorn
is not supported. Using gunicorn
with gthread
worker class is supported. For example: gunicorn app:app -k gthread --threads=2
Callback Returned Values Correctly Stored in Persistence
Thank you to @petar-pejovic for the contribution
This fix ensures that the callback output value is written (instead of being pruned) within the browser’s persistence storage for its dcc
components that have persistence=True
set. For more details, please see the PR and the issue that Petar’s contribution solves.
Fixed
- #3341 Fixed query string parsing regression introduced in 2.18.2 where values containing unencoded
&
characters were being truncated. #3106 - #3279 Fix an issue where persisted values were incorrectly pruned when updated via callback. Now, callback returned values are correctly stored in the persistence storage. Fix #2678
- #3298 Fix dev_only resources filtering.
- #3315 Fix pages module is package check.
- #3319 Fix issue where
ExternalWrapper
would remove props from the parent component, now there is atemp
that is passed to check if it should be removed on unmount. - #3108 Fix layout as list for pages.
- #1906 Make graph height more responsive.
- #2927 Fix unexpected behaviour of the cursor in dcc.Input
- #3344 Fix dcc.Loading target_components with * prop.
Added
- #3294 Added the ability to pass
allow_optional
to Input and State to allow callbacks to work even if these components are not in the dash layout. - #3077 Add new parameter
assets_path_ignore
todash.Dash()
. Closes #3076 - #3202 expose the closeOnSelect option in dropdown component
- #3089 adding support for async callbacks and page layouts, install with
pip install dash[async]
.