To get the most out of this page, make sure you’ve read about Basic Callbacks in the Dash Fundamentals.
dashNoUpdate
In certain situations, you don’t want to update the callback output. You can
achieve this by
returning dashNoUpdate()
in
the callback function.
library(dash)
library(dashHtmlComponents)
library(dashCoreComponents)
app <- Dash$new()
app$layout(htmlDiv(list(
htmlButton('Click here to see the content', id='show-secret'),
htmlDiv(id='body-div')
)))
app$callback(
output(id='body-div', property='children'),
params = list(
input(id='show-secret', property='n_clicks')
),
update_output <- function(n_clicks) {
if (is.null(n_clicks[[1]])) {
return(dashNoUpdate())
}
else {
return("Elephants are the only animal that can't jump")
}
}
)
app$run_server()
dashNoUpdate
This example illustrates how you can show an error while keeping the previous
input, using dashNoUpdate
to update only some of the callback outputs.
library(dash)
library(dashHtmlComponents)
library(dashCoreComponents)
app <- Dash$new()
app$layout(htmlDiv(list(
htmlP('Enter a composite number to see its prime factors'),
dccInput(id='num', type='number', debounce=TRUE, min=1, step=1),
htmlP(id='err', style=list('color' = 'red')),
htmlP(id='out')
)))
app$callback(
output=list(
output(id='out', property='children'),
output(id='err', property='children')
),
params = list(
input(id='num', 'value')
),
show_factors <- function(num) {
if(is.null(num[[1]])) {
# It can be used to prevent ALL outputs updating
return(dashNoUpdate())
}
factors = prime_factors(num[[1]])
if(length(factors) == 1) {
return(list(dashNoUpdate(), sprintf('%s is prime!', num[[1]])))
}
else {
return(list(sprintf('%s is %s', num[[1]], paste(factors, collapse = " * ")), ''))
}
}
)
prime_factors <- function(num) {
n <- num
i <- 2
out = list()
while(i*i <= n) {
if(n%%i == 0) {
n = as.integer(n/i)
out = append(out, i)
}
else {
if (i==2) i = i + 1 else i = i + 2
}
}
out = append(out, n)
return(unlist(out))
}
app$run_server()
Enter a composite number to see its prime factors
Input
Has Fired with callback_context
In addition to event properties like n_clicks
that change whenever an event happens (in this case a click), there is
a method app$callback_context
available only inside a callback. Using dash.callback_context
, you can determine which component/property pairs triggered a callback.
Below is a summary of properties of dash.callback_context
outlining the basics of when to use them. For more detail and examples see Determining Which Callback Input Changed.
callback_context
In Dash 2.4 and later,
dash.callback_context
(ordash.ctx
) has three additional properties to make it easier to work with.
triggered_id
: The id
of the component that triggered the callback.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.args_grouping
: A dictionary of the inputs used with flexible callback signatures. The keys are the variable names and the values are dictionaries containing:Dash 2.4 and earlier versions of Dash have the following properties
triggered
: list of all the Input
props that changed and caused the callback to execute.Input
prop got itstriggered
, unless the same action changes two props at once or the callbackInput
props that are all modified by another callback based on a single user action.More about empty
triggered
lists: For backward compatibility purposes, an empty
triggered
is not really empty. It’s
falsy so that you can useif triggered
to detect the initial call, but it still has a placeholder
element so thatctx.triggered[0]["prop_id"].split(".")
yields a blank ID and prop["", ""]
instead of an error.
inputs
and states
: allow you to access the callback params{ 'component_id.prop_name': value }
Here’s an example of how this can be done:
library(dash)
library(dashCoreComponents)
library(dashHtmlComponents)
library(jsonlite)
app <- Dash$new()
app$layout(
htmlDiv(
list(
htmlButton(children = 'Button 1', id='btn-1', n_clicks=0),
htmlButton(children = 'Button 2', id='btn-2', n_clicks=0),
htmlButton(children = 'Button 3', id='btn-3', n_clicks=0),
htmlDiv(id='container')
)
)
)
app$callback(output('container', 'children'),
list(input('btn-1', 'n_clicks'),
input('btn-2', 'n_clicks'),
input('btn-3', 'n_clicks')),
function(input1, input2, input3) {
inputs <- c(input1, input2, input3)
ctx <- app$callback_context()
most_recent_click <- if(ctx$triggered$value) {
unlist(strsplit(ctx$triggered$prop_id, "[.]"))[1]
} else "No clicks yet"
toJson <- jsonlite::toJSON(ctx, pretty = TRUE)
htmlDiv(
list(
htmlTable(
list(
htmlTr(
list(
htmlTh('Button 1'),
htmlTh('Button 2'),
htmlTh('Button 3'),
htmlTh('Most Recent Click')
)
),
htmlTr(
list(
htmlTd(input1),
htmlTd(input2),
htmlTd(input3),
htmlTd(most_recent_click)
)
)
)
),
htmlPre(toJson)
)
)
})
#app$run_server()
This section describes the circumstances under which the dash-renderer
front-end client can make a request to the Dash back-end server (or the
clientside callback code) to execute a callback function.
All of the callbacks in a Dash app are executed with the initial value
of their inputs when the app is first loaded. This is known as the
“initial call” of the callback. To learn how to suppress this behavior,
see the documentation for the
prevent_initial_call
attribute of Dash callbacks.
It is important to note that when a Dash app is initially loaded in a
web browser by the dash-renderer
front-end client, its entire callback
chain is introspected recursively.
This allows the dash-renderer
to predict the order in which callbacks
will need to be executed, as callbacks are blocked when their inputs are
outputs of other callbacks which have not yet fired. In order to unblock
the execution of these callbacks, first callbacks whose inputs are
immediately available must be executed. This process helps the
dash-renderer
to minimize the time and effort it uses, and avoid
unnecessarily redrawing the page, by making sure it only requests that
a callback is executed when all of the callback’s inputs have reached
their final values.
Examine the following Dash app:
library(dash)
library(dashHtmlComponents)
app <- Dash$new()
app$layout(
htmlDiv(
list(
htmlButton("execute callback", id="button_1"),
htmlDiv("callback not executed", id="first_output_1"),
htmlDiv("callback not executed", id="second_output_1")
)
)
)
app$callback(
list(output("first_output_1", "children"),
output("second_output_1", "children")),
params=list(input("button_1", "n_clicks")),
function(n_clicks) {
return(list(paste0("n_clicks is ", as.character(n_clicks)),
paste0("n_clicks is ", as.character(n_clicks))
)
)
}
)
app$run_server()
Notice that when this app is finished being loaded by a web browser and
ready for user interaction, the div
components do not say
“callback not executed” as declared in the app’s layout, but rather
“n_clicks is None” as the result of the
change_text()
callback being
executed. This is because the “initial call” of the callback occurred
with n_clicks
having the value of None
.
Most frequently, callbacks are executed as a direct result of user
interaction, such as clicking a button or selecting an item in a
dropdown menu. When such interactions occur, Dash components communicate
their new values to the dash-renderer
front-end client, which then
requests that the Dash server execute any callback function that has the
newly changed value as input.
If a Dash app has multiple callbacks, the dash-renderer
requests
callbacks to be executed based on whether or not they can be immediately
executed with the newly changed inputs. If several inputs change
simultaneously, then requests are made to execute them all.
Whether or not these requests are executed in a synchronous or
asynchronous manner depends on the specific setup of the Dash back-end
server. If it is running in a multi-threaded environment, then all of
the callbacks can be executed simultaneously, and they will return
values based on their speed of execution. In a single-threaded
environment however, callbacks will be executed one at a time in the
order they are received by the server.
In the example application above, clicking the button results in the
callback being executed.
When a user interacts with a component, the resulting callback might
have outputs that are themselves the input of other callbacks. The
dash-renderer
will block the execution of such a callback until the
callback whose output is its input has been executed.
Take the following Dash app:
library(dash)
library(dashHtmlComponents)
app <- Dash$new()
app$layout(
htmlDiv(
list(
htmlButton("execute fast callback", id="button_3"),
htmlButton("execute slow callback", id="button_4"),
htmlDiv("callback not executed", id="first_output_3"),
htmlDiv("callback not executed", id="second_output_3"),
htmlDiv("callback not executed", id="third_output_3")
)
)
)
app$callback(
list(output("first_output_3", "children")),
params=list(input("button_3", "n_clicks")),
function(n_clicks) {
return(list(paste("in the fast callback it is ",
toString(Sys.time())))
)
}
)
app$callback(
list(output("second_output_3", "children")),
params=list(input("button_4", "n_clicks")),
function(n_clicks) {
Sys.sleep(5)
return(list(paste("in the slow callback it is ", toString(Sys.time()))))
}
)
app$callback(
list(output("third_output_3", "children")),
params=list(input("first_output_3", "children"), input("second_output_3", "children")),
function(children_1, children_2) {
return(list(paste("in the third callback it is ", toString(Sys.time()))))
}
)
app$run_server()
The above Dash app demonstrates how callbacks chain together. Notice
that if you first click “execute slow callback” and then click “execute
fast callback”, the third callback is not executed until after the slow
callback finishes executing. This is because the third callback has the
second callback’s output as its input, which lets the dash-renderer
know that it should delay its execution until after the second callback
finishes.
It is possible for a callback to insert new Dash components into a Dash
app’s layout. If these new components are themselves the inputs to other
callback functions, then their appearance in the Dash app’s layout will
trigger those callback functions to be executed.
In this circumstance, it is possible that multiple requests are made to
execute the same callback function. This would occur if the callback in
question has already been requested and its output returned before the
new components which are also its inputs are added to the layout.
You can use the prevent_initial_call
attribute to prevent callbacks
from firing when their inputs initially appear in the layout of your
Dash application.
This attribute applies when the layout of your Dash app is initially
loaded, and also when new components are introduced into the layout when
a callback has been triggered.
This example has not been ported to R yet - showing the Python version instead.
Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21
from dash import Dash, html, Input, Output, callback
from datetime import datetime
import time
app = Dash()
app.layout = html.Div(
[
html.Button("execute callbacks", id="button_2"),
html.Div(children="callback not executed", id="first_output_2"),
html.Div(children="callback not executed", id="second_output_2"),
html.Div(children="callback not executed", id="third_output_2"),
html.Div(children="callback not executed", id="fourth_output_2"),
]
)
@callback(
Output("first_output_2", "children"),
Output("second_output_2", "children"),
Input("button_2", "n_clicks"), prevent_initial_call=True)
def first_callback(n):
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return ["in the first callback it is " + current_time, "in the first callback it is " + current_time]
@callback(
Output("third_output_2", "children"), Input("second_output_2", "children"), prevent_initial_call=True)
def second_callback(n):
time.sleep(2)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return "in the second callback it is " + current_time
@callback(
Output("fourth_output_2", "children"),
Input("first_output_2", "children"),
Input("third_output_2", "children"), prevent_initial_call=True)
def third_output(n, m):
time.sleep(2)
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
return "in the third callback it is " + current_time
if __name__ == '__main__':
app.run(debug=True)
However, the above behavior only applies if both the callback output and
input are present in the app layout upon initial load of the application.
It is important to note that prevent_initial_call
will not prevent a callback from firing in the case where the callback’s input is inserted
into the layout as the result of another callback after the app initially
loads unless the output is inserted alongside that input!
In other words, if the output of the callback is already present in the
app layout before its input is inserted into the layout,
prevent_initial_call
will not prevent its execution when the input is first inserted into the layout.
Consider the following example:
This example has not been ported to R yet - showing the Python version instead.
Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21
from dash import Dash, dcc, html, Input, Output, callback
app = Dash(__name__, suppress_callback_exceptions=True)
server = app.server
app.layout = html.Div([
dcc.Location(id='url'),
html.Div(id='layout-div'),
html.Div(id='content')
])
@callback(Output('content', 'children'), Input('url', 'pathname'))
def display_page(pathname):
return html.Div([
dcc.Input(id='input', value='hello world'),
html.Div(id='output')
])
@callback(Output('output', 'children'), Input('input', 'value'), prevent_initial_call=True)
def update_output(value):
print('>>> update_output')
return value
@callback(Output('layout-div', 'children'), Input('input', 'value'), prevent_initial_call=True)
def update_layout_div(value):
print('>>> update_layout_div')
return value
In this case, prevent_initial_call
will prevent the update_output()
callback from firing when its input is first inserted into the app
layout as a result of the display_page()
callback. This is because both the input and output of the callback are already
contained within the app layout when the callback executes.
However, because the app layout contains only the output of the
callback, and not its input, prevent_initial_call
will not prevent the update_layout_div()
callback from firing. Since suppress_callback_exceptions=True
is specified here,
Dash has to assume that the input is present in the app layout when the app is
initialized. From the perspective of the output element in this example,
the new input component is handled as if an existing input had been
provided a new value, rather than treating it as initially rendered.
As of dash v1.19.0
, you can create circular updates
within the same callback.
Circular callback chains that involve multiple callbacks are not supported.
Circular callbacks can be used to keep multiple inputs synchronized to
each other.
This example has not been ported to R yet - showing the Python version instead.
Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21
from dash import Dash, html, dcc, Input, Output, callback, ctx
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(
[
dcc.Slider(
id="slider-circular", min=0, max=20,
marks={i: str(i) for i in range(21)},
value=3
),
dcc.Input(
id="input-circular", type="number", min=0, max=20, value=3
),
]
)
@callback(
Output("input-circular", "value"),
Output("slider-circular", "value"),
Input("input-circular", "value"),
Input("slider-circular", "value"),
)
def callback(input_value, slider_value):
trigger_id = ctx.triggered[0]["prop_id"].split(".")[0]
value = input_value if trigger_id == "input-circular" else slider_value
return value, value
if __name__ == '__main__':
app.run(debug=True)
This example has not been ported to R yet - showing the Python version instead.
Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21
from dash import Dash, html, dcc, Input, Output, callback, ctx
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
html.Div('Convert Temperature'),
'Celsius',
dcc.Input(
id="celsius",
value=0.0,
type="number"
),
' = Fahrenheit',
dcc.Input(
id="fahrenheit",
value=32.0,
type="number",
),
])
@callback(
Output("celsius", "value"),
Output("fahrenheit", "value"),
Input("celsius", "value"),
Input("fahrenheit", "value"),
)
def sync_input(celsius, fahrenheit):
input_id = ctx.triggered[0]["prop_id"].split(".")[0]
if input_id == "celsius":
fahrenheit= None if celsius is None else (float(celsius) * 9/5) + 32
else:
celsius = None if fahrenheit is None else (float(fahrenheit) - 32) * 5/9
return celsius, fahrenheit
if __name__ == "__main__":
app.run(debug=True)
This example has not been ported to R yet - showing the Python version instead.
Visit the old docs site for R at: https://community.plotly.com/c/dash/r/21
from dash import Dash, dcc, html, Input, Output, callback, callback_context
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
options = ["New York City", "Montréal", "San Francisco"]
app.layout = html.Div(
[
dcc.Checklist(["All"], [], id="all-checklist", inline=True),
dcc.Checklist(options, [], id="city-checklist", inline=True),
]
)
@callback(
Output("city-checklist", "value"),
Output("all-checklist", "value"),
Input("city-checklist", "value"),
Input("all-checklist", "value"),
)
def sync_checklists(cities_selected, all_selected):
ctx = callback_context
input_id = ctx.triggered[0]["prop_id"].split(".")[0]
if input_id == "city-checklist":
all_selected = ["All"] if set(cities_selected) == set(options) else []
else:
cities_selected = options if all_selected else []
return cities_selected, all_selected
if __name__ == "__main__":
app.run(debug=True)