Update : version 2.5.0 has been released since this was posted.
Good day Community Members,
We’re excited to announce that Dash 2.4.0 & 2.4.1 have been released. 2.4.0 contains the big feature items and 2.4.1 contains a quick bugfix. These are a backwards compatible releases and we recommend going straight to 2.4.1
.
pip install dash==2.4.1
Official Changelog Dash v2.4.0
Highlights
Improved Callback Context
Improved callback_context (ctx): Thank you @AnnMarieW for designing and developing this new feature.
This new feature simplifies the syntax for determining which Input triggered a callback by replacing the infamous ctx.triggered[0]["prop_id"].split(".")[0]
with a new ctx.triggered_id
:
Before:
from dash import callback_context
@app.callback(
Output('my-div', 'children'),
Input('button-1', 'n_clicks'),
Input('button-2', 'n_clicks'),
)
def update(b1, b2):
triggered_id = callback_context.triggered[0]['prop_id']
if triggered_id == 'button-1.n_clicks':
return 'Button 1 was clicked'
if triggered_id == 'button-2.n_clicks':
return 'Button 2 was clicked'
After:
from dash import ctx
@app.callback(
Output('my-div', 'children'),
Input('button-1', 'n_clicks'),
Input('button-2', 'n_clicks'),
)
def update(b1, b2):
if ctx.triggered_id == 'button-1':
return 'Button 1 was clicked'
if ctx.triggered_id == 'button-2':
return 'Button 2 was clicked'
In total, dash.ctx
has three additional properties to make it easier to work with (see the official docs):
ctx.triggered_id
: Theid
of the component that triggered the callback. For example:'my-button'
ctx.triggered_prop_ids
: A dictionary of the component ids and props that triggered the callback. Useful when multiple inputs can trigger the callback at the same time, or multiple properties of the same component can trigger the callback.ctx.args_grouping
: A dictionary of the inputs used with flexible callback signatures. The keys are the variable names and the values are dictionaries.
ctx.triggered_id
example - determining which input changed
The code below will allow us to determine which button triggered the callback. Notice that we import ctx
at the very top. See more about this example in the docs.
from dash import Dash, Input, Output, html, dcc, ctx
import plotly.express as px
import plotly.graph_objects as go
#Initialise the App
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# App Layout
app.layout = html.Div([
html.Button('Draw Graph', id='draw'),
html.Button('Reset Graph', id='reset'),
dcc.Graph(id='graph')
])
# Configure callbacks
@app.callback(
Output('graph', 'figure'),
Input(component_id='reset', component_property='n_clicks'),
Input(component_id='draw', component_property='n_clicks'),
prevent_initial_call=True
)
def update_graph(b1, b2):
triggered_id = ctx.triggered_id
print(triggered_id)
if triggered_id == 'reset':
return go.Figure()
elif triggered_id == 'draw':
df = px.data.iris()
return px.scatter(df, x=df.columns[0], y=df.columns[1])
# Run the App
if __name__ == '__main__':
app.run_server()
ctx.triggered_prop_ids
example - determining which property changed
Use ctx.triggered_prop_ids
when multiple properties of the same component (ID) can trigger the callback. For example, let’s build a scatter graph and display on the page data generated from the selectedData
and the clickData
properties of the graph.
from dash import Dash, Input, Output, html, dcc, ctx
import plotly.express as px
import plotly.graph_objects as go
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
df = px.data.iris()
fig = px.scatter(df, x=df.columns[0], y=df.columns[1])
app.layout = html.Div([
dcc.Markdown(id='content'),
dcc.Graph(id='graph', figure=fig)
])
@app.callback(
Output('content', 'children'),
Input(component_id='graph', component_property='selectedData'),
Input(component_id='graph', component_property='clickData'),
prevent_initial_call=True
)
def update_graph(selected, clicked):
triggered_prop_id = ctx.triggered_prop_ids
print(triggered_prop_id)
if 'graph.selectedData' in triggered_prop_id:
print(selected)
return 'The x range of the seclected data starts from {}'.format(selected['range']['x'][0])
elif 'graph.clickData' in triggered_prop_id:
print(clicked)
return 'The Sepal width of the clicked data is {}'.format(clicked['points'][0]['y'])
if __name__ == '__main__':
app.run_server()
ctx.args_grouping
example - flexible callback signatures
Use ctx.args_grouping
when working with flexible callback signatures. The args_grouping
is a dictionary, where the keys are the variable names and the values are dictionaries containing:
"id"
: the component ID. If it’s a pattern matching ID, it will be a dict."id_str"
: for pattern matching IDs, it’s the stringified dict ID with no white spaces."property"
: the component property used in the callback."value"
: the value of the component property at the time the callback was fired."triggered"
: a boolean indicating whether this input triggered the callback.
In the code example below, you will notice that the callback decorator has 4 inputs, but the callback function signature has only one argument.
from dash import Dash, dcc, html, Output, Input, ctx
app = Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(options=['one','two','three'], value='one', id='dropdown-1'),
dcc.Dropdown(options=['Apple','Banana','Citrus'], value='Apple', id='dropdown-2'),
html.Button('Submit-1', id='btn-1'),
html.Button('Submit-2', id='btn-2'),
html.P(),
html.Div(id='container')
])
@app.callback(
Output("container", "children"),
inputs={
"all_inputs": {
"btn1": Input("btn-1", "n_clicks"),
"btn2": Input("btn-2", "n_clicks"),
"dropdown1": Input("dropdown-1", "value"),
"dropdown2": Input("dropdown-2", "value"),
}
},
)
def display(all_inputs):
print(ctx.args_grouping)
print("-----------------------------------------------------------------")
c = ctx.args_grouping.all_inputs
if c.btn1.triggered:
return f"Button 1 clicked {c.btn1.value} times."
elif c.btn2.triggered:
return f"Button 2 clicked {c.btn2.value} times"
elif c.dropdown1.triggered:
return "Dropdown 1 was triggered."
elif c.dropdown2.triggered:
return [
"Dropdown 2 was triggered.",
f' **** Dropdown 2 property is: {c.dropdown2.property}',
f' **** Dropdown 2 value is: {c.dropdown2.value}.',
f' **** Dropdown 2 id is: {c.dropdown2.id}.',
]
else:
"no update"
if __name__ == '__main__':
app.run_server(debug=True)
ctx.args_grouping
with pattern matching callbacks example
Use args_grouping
combined with pattern matching to get the index of the button that triggered the callback.
import numpy as np
import plotly.express as px
from dash import Dash, Input, Output, ctx, dcc, html, ALL
N=5
app = Dash(__name__)
def make_figure(n):
x = np.linspace(0, 8, 81)
y = x ** int(n)
return px.scatter(x=x, y=y)
app.layout = html.Div(
[
html.Div(
[html.Button(f"x^{i}", id={"index":i}) for i in range(N)]
),
dcc.Graph(id="graph"),
]
)
@app.callback(
Output("graph", "figure"), dict(btns=Input({"index":ALL}, "n_clicks"))
)
def update_graph(btns):
# Since we are using {"index":ALL}, then
# ctx.args_grouping.btns is a list of dicts - one dict for each button.
for btn in ctx.args_grouping.btns:
if btn.triggered:
n = btn.id.index
print(n)
return make_figure(n)
return {}
if __name__ == "__main__":
app.run_server(debug=True)
link_target
in dcc.Markdown
By default, links in markdown open in the same page. Set dcc.Markdown(link_target='_blank')
to open the link in a new browser tab when clicked. For a list of link_target attribute values, see the MDN link target docs.
from dash import Dash, dcc, html
app = Dash(__name__)
app.layout = html.Div([
dcc.Markdown("[DuckDuckGo ](https://duckduckgo.com)", link_target="_blank")
])
if __name__ == '__main__':
app.run()
Support for Promises within clientside callbacks
Which means it’s now possible to make web requests in clientside. Thank you, @MrTeale for taking this on in PR #2009
from dash import Dash, dcc, html, Input, Output, dash_table
app = Dash(__name__)
app.layout = html.Div([
dcc.Dropdown(
options=[
{
"label": "Car-sharing data",
"value": "https://raw.githubusercontent.com/plotly/datasets/master/carshare_data.json",
},
{
"label": "Iris data",
"value": "https://raw.githubusercontent.com/plotly/datasets/master/iris_data.json",
},
],
value="https://raw.githubusercontent.com/plotly/datasets/master/iris_data.json",
id="data-select",
),
html.Br(),
dash_table.DataTable(id="my-table-promises", page_size=10),
dcc.Store(id="fetched-data", storage_type="session"),
])
app.clientside_callback(
"""
async function(value) {
const response = await fetch(value);
return await response.text();
}
""",
Output("fetched-data", "data"),
Input("data-select", "value"),
)
app.clientside_callback(
"""
function(data) {
return JSON.parse(data);
}
""",
Output("my-table-promises", "data"),
Input("fetched-data", "data"),
)
if __name__ == "__main__":
app.run_server(debug=True)
Added support for creating Dash components with TypeScript
TypeScript is a programming language that is a strict syntactical superset of JavaScript and adds optional static typing to the language. It transpiles to JavaScript.
Dash components can now be generated from React components written in TypeScript. This includes generating the Python API form TypeScript prop types. More on the discussion in PR #1956.
- See the docs for an example of how TypeScript is used to define props for an Audio component.
- See this repo to get started with creating Dash components with TypeScript.
Minor Ticks & Gridlines
Cartesian axes now support minor
ticks and gridlines:
import plotly.express as px
from dash import Dash, html, dcc
df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip", color="sex")
fig.update_xaxes(minor_ticks="outside", minor_showgrid=True)
fig.update_yaxes(minor_ticks="outside", minor_showgrid=True)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(figure=fig)
])
if __name__=='__main__':
app.run()
This lets you do some interesting things, for example having major ticks/grids on month boundaries and minor ones on week boundaries (the example below also shows off our new griddash
parameter for dashed gridlines) and this looks really great with the tick labels set to period
mode so that the month labels below are centered on the month they denote:
from dash import Dash, html, dcc
import plotly.express as px
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
df = df.loc[(df["Date"] >= "2016-07-01") & (df["Date"] <= "2016-12-01")]
fig = px.line(df, x='Date', y='AAPL.High')
fig.update_xaxes(ticks= "outside", ticklabelmode= "period",
tickcolor= "black", ticklen=10,
minor=dict(ticklen=4,
dtick=7*24*60*60*1000,
tick0="2016-07-03",
griddash='dot',
gridcolor='white')
)
app = Dash(__name__)
app.layout = html.Div([dcc.Graph(figure=fig)])
if __name__=='__main__':
app.run()
This feature requires Plotly 5.8.0
This feature was anonymously sponsored, so thanks to our sponsor !
app.run
Renamed app.run_server
to the simpler app.run
. We preserved app.run_server
for backwards compatibility.
Notable Bug Fixes
-
#2046 - Fix bug #2045, in which an an import error occurs with Dash v2.4.0 when using pytest but
dash[testing]
is not installed. -
#2029 - Restrict the number of props listed explicitly in generated component constructors - default is 250. This prevents exceeding the Python 3.6 limit of 255 arguments. The omitted props are still in the docstring and can still be provided the same as before, they just won’t appear in the signature so autocompletion may be affected.
-
#1968 - Fix bug #1877, in which code that uses
merge_duplicate_headers
andstyle_header_conditional
to highlight DataTable columns incorrectly highlights header cells. -
#2015 - Fix bug #1854 in which the combination of row_selectable=“single or multi” and filter_action=“native” causes a JS error.
-
#1976 - Fix bug #1962 in which DatePickerSingle and DatePickerRange are extremely slow when provided a long list of disabled_days.
-
#2035 - Fix bug #2033 in which the in-app error reporting shows HTML code instead of rendering the HTML.
-
#1970 dcc.Dropdown Refactor fixes:
Minor Changes
-
#1839 - The
callback
decorator returns the original function, not the wrapped function, so that you can still call these functions directly, for example in tests. Note that in this case there will be no callback context so not all callbacks can be tested this way. -
#2027 - Improve the error message when a user doesn’t wrap children in a list.
-
Plus lots of maintenance updates!
Previous Releases:
Dash 2.3.0 Release - MathJax and fillpattern option in scatter trace
Dash 2.2.0 Release - Adds ticklabelstep
to axes, and added dash.get_asset_url
Dash 2.1.0 Release - Autogenerated IDs and reärranged keyword arguments in Dash components
Dash 2.0 Prerelease Candidate Available!
Dash v1.21.0 Release - Plotly.js 2.0, Icicle Charts, Bar Chart Patterns, Clipboard Component, Bug Fixes