Created
January 24, 2018 23:17
-
-
Save jtpio/1aeb0d850dcd537a5b244bcf5aeaa75b to your computer and use it in GitHub Desktop.
Dash URL State example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import dash | |
import dash_core_components as dcc | |
import dash_html_components as html | |
from urllib.parse import urlparse, parse_qsl, urlencode | |
from dash.dependencies import Input, Output | |
app = dash.Dash() | |
app.config.suppress_callback_exceptions = True | |
app.layout = html.Div([ | |
dcc.Location(id='url', refresh=False), | |
html.Div(id='page-layout') | |
]) | |
def apply_default_value(params): | |
def wrapper(func): | |
def apply_value(*args, **kwargs): | |
if 'id' in kwargs and kwargs['id'] in params: | |
kwargs['value'] = params[kwargs['id']] | |
return func(*args, **kwargs) | |
return apply_value | |
return wrapper | |
def build_layout(params): | |
layout = [ | |
html.H2('URL State demo', id='state'), | |
apply_default_value(params)(dcc.Dropdown)( | |
id='dropdown', | |
options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']], | |
value='LA' | |
), | |
apply_default_value(params)(dcc.Input)( | |
id='input', | |
placeholder='Enter a value...', | |
value='' | |
), | |
apply_default_value(params)(dcc.Slider)( | |
id='slider', | |
min=0, | |
max=9, | |
marks={i: 'Label {}'.format(i) for i in range(10)}, | |
value=5, | |
), | |
html.Br(), | |
] | |
return layout | |
def parse_state(url): | |
parse_result = urlparse(url) | |
params = parse_qsl(parse_result.query) | |
state = dict(params) | |
return state | |
@app.callback(Output('page-layout', 'children'), | |
inputs=[Input('url', 'href')]) | |
def page_load(href): | |
if not href: | |
return [] | |
state = parse_state(href) | |
return build_layout(state) | |
component_ids = [ | |
'dropdown', | |
'input', | |
'slider' | |
] | |
@app.callback(Output('url', 'search'), | |
inputs=[Input(i, 'value') for i in component_ids]) | |
def update_url_state(*values): | |
state = urlencode(dict(zip(component_ids, values))) | |
return f'?{state}' | |
if __name__ == '__main__': | |
app.run_server(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dash==0.20.0 | |
dash-renderer==0.11.2 | |
dash-html-components==0.8.0 | |
dash-core-components==0.18.0 |
Hi @GGPay,
Have you tried using the date
parameter for dcc.DatePickerSingle
?
Something like this:
Input('date-picker-id', 'date')
For dcc.DatePickerRange
, you can use start_date
and end_date
.
Hi, I changed the code a bit to work with arbitrary attributes (not just 'value'), allow for saving lists of items as well (e.g. for multi dropdowns) and save the whole thing in a base64 encoded json:
(Also nicely saves the particular tab that you're on, when working with tabs)
def apply_default_value(params):
def wrapper(func):
def apply_value(*args, **kwargs):
if 'id' in kwargs and kwargs['id'] in params:
kwargs[params[kwargs['id']][0]] = params[kwargs['id']][1]
#kwargs['value'] = params[kwargs['id']][1]
return func(*args, **kwargs)
return apply_value
return wrapper
def parse_state(url):
parse_result = urlparse(url)
query_string = parse_qsl(parse_result.query)
if query_string:
encoded_state = query_string[0][1]
state = dict(json.loads(urlsafe_b64decode(encoded_state)))
else:
state = dict()
return state
@app.callback(Output('page-layout', 'children'),
inputs=[Input('url', 'href')])
def page_load(href):
if not href:
return []
state = parse_state(href)
return build_layout(state)
component_ids = {
'tabs' : 'value',
'tab1_multidropdown' : 'value',
'tab2_datepicker' : 'start_date'
}
@app.callback(Output('url', 'search'),
inputs=[Input(id, param) for id, param in component_ids.items()])
def update_url_state(*values):
state = dict(zip(list(component_ids.keys()), list(zip(component_ids.values(), values))))
encoded = urlsafe_b64encode(json.dumps(state).encode())
params = urlencode(dict(params=encoded))
return f'?{params}'
The original gist does not work anymore with the stricter type enforcement of recent Dash:
The low-footprint fix I tried is to try to coerce the vale into its existing type (won't work if it's init with None):
def apply_default_value(params):
def wrapper(func):
def apply_value(*args, **kwargs):
if 'id' in kwargs and kwargs['id'] in params:
hval = kwargs['value'] # hardcoded value
sval = params[kwargs['id']] # saved value
ctor = None if hval is None or isinstance(hval, str) else type(hval)
kwargs['value'] = ctor(sval) if ctor else sval
return func(*args, **kwargs)
return apply_value
return wrapper
It works but it's brittle (so I moved to another approach: plotly/dash#188 (comment) )
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Jeremy,
Thank you so much for share your solution. It works as a charm. How to make that code works with dcc.DatePickerSingle where is no parameter value? I can't resolve the problem with building the url - it always say ?date=None&dropdown=LA and insert the date into DatePicker field. Any help would be much appreciate it.
Thanks