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
ExternalWrapperwould remove props from the parent component, now there is atempthat 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_optionalto Input and State to allow callbacks to work even if these components are not in the dash layout. - #3077 Add new parameter
assets_path_ignoretodash.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].