New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dcc.Location refresh=False doesn't fire the callback for derived URL properties #925
Comments
Note that it looks like my original implemention of I'll dig into the commit history and see if there was a valid reason that this changed. |
OK this wasn't quite right actually. I believe what's happening is that the callback is fired if the exact URL property was updated, but not if a derived URL property was updated. So, if you update |
As a workaround, users might be able to restructure their callbacks to target the exact same properties as inputs and outputs. I recall that there was an issue doing this (circular callbacks?) with the example above.... I'll try to reproduce now. |
OK, here's an example that demonstrates the current behavior well. The radio items & dropdowns update # This app demonstrates how URLs can be updated via Dropdowns and RadioItems
# and content on pages can be read.
import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
import urllib
app = dash.Dash(__name__, suppress_callback_exceptions=True)
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='layout-div'),
html.H1('Inner Content'),
html.Div('''
This content should change when navigating the pages via the
dcc.Link or the radio items & dropdowns.
'''),
html.Div(id='content', style={
'border': 'thin hotpink solid',
'padding': 10,
'margin': 10
}),
html.Hr(),
html.H1('Preset A Links'),
html.Div('''
These html.A (<a href="..."/>) links should force the page to reload
no matter what.
'''),
html.Div(html.A('Site 1 in Imperial', href='/site-1?unit=Imperial')),
html.Div(html.A('Site 3 in Metric', href='/site-1?unit=Metric')),
html.Div(html.A('Site 2 with no units in URL', href='/site-2')),
html.H1('Preset dcc.Link Links'),
html.Div('''
These dcc.Link (<a href="..."/>) links should update the inner content
without refreshing the page
'''),
html.Div(dcc.Link('Site 1 in Imperial', href='/site-1?unit=Imperial')),
html.Div(dcc.Link('Site 3 in Metric', href='/site-1?unit=Metric')),
html.Div(dcc.Link('Site 2 with no units in URL', href='/site-2')),
])
# the `no_update` below will prevent the callback chain from executing,
# including this callback. So, we have to target different parts of the URL
@app.callback(Output('content', 'children'), Input('url', 'href'))
def display_page(href):
o = list(urllib.parse.urlparse(href))
q = dict(urllib.parse.parse_qsl(o[4]))
pathname = o[2]
return html.Div([
dcc.RadioItems(
id='unit-input',
value=q.get('unit', 'Imperial'),
options=[{'label': i, 'value': i} for i in ['Imperial', 'Metric']]
),
dcc.Dropdown(
id='site-input',
value=o[2][1:] or 'site-0',
options=[
{'label': f'site-{i}', 'value': f'site-{i}'}
for i in range(5)
]
),
html.Div(id='dummy'),
html.Div(id='output')
])
@app.callback(
Output('url', 'href'),
Input('site-input', 'value'),
Input('unit-input', 'value'),
Input('dummy', 'children'),
State('url', 'href'))
def update_pathname(site, unit, dummy, href):
if len(dash.callback_context.triggered) == 2:
# site-input rendered, it didn't change
return dash.no_update
o = urllib.parse.urlparse(href)
o = o._replace(path=f'/{site}')
q = dict(urllib.parse.parse_qsl(o.query))
q['unit'] = unit
query_string = urllib.parse.urlencode(q)
o = o._replace(query=query_string)
return o.geturl()
@app.callback(
Output('output', 'children'),
Input('url', 'href'),
State('url', 'pathname'),
State('url', 'search'))
def update_output_based_off_url(href, pathname, search):
return html.Div([
html.B('Callback inputs tell me that:'),
html.Pre(f'href={href}\npathname={pathname}\nsearch={search}')
])
if __name__ == '__main__':
app.run_server(debug=True) The good news is that this use case works if you extract |
Hello @chriddyp , First of all thank you for your comments and research on this problem. I have a multi-page application, and as soon as the refresh property of dcc.Location is set to False, the url is updated according to my choice in a dropdown (via a callback), but the content of the page is not updated. I've been looking for a solution for several days but I can't find it. I hope you can help me with my problem. I thank you in advance. |
Consider a set of components that update the URL, like a dropdown allowing the user to change websites or radio items allowing users to change units from metric to imperial.
With
dcc.Link
, you can update the URL without refreshing the page and the rest of the content in the page can update dynamically by listening todcc.Location
properties.However, if these components are dropdowns or radio items (or tabs) that updatedcc.Location
directly then there isn't a way to not refresh the page and update the content.dcc.Location(refresh=False)
sort of does what we want in that it updates the URL without refreshing the page but it doesn't fire the callback, makingdcc.Location
unable to update the content.Actually this isn't quite right, see comment below.
Here is an example:
With
refresh=True
(the default) - The page gets reloaded instead of just the content. This makes sense given the prop name:With
refresh=False
- The URL updates (good) but the callback doesn't fire and so the content doesn't update.Proposed Fix
Option 1 - Update the logic with
refresh=False
to actually fire the callback. This could break certain apps out there that are depending on the current behavior but it's easier to understand and more useful in the long run.Option 2 - Introduce a new prop like
fire_callback
which would only be used ifrefresh=False
I prefer Option 1, but I might be overlooking real, valid usecases for this option.
The text was updated successfully, but these errors were encountered: