Black Lives Matter. Please consider donating to Black Girls Code today.
Dash Enterprise delivers an incredible 21x cost savings 💸Download the e-book!

Tracing a variable

Hi Dash Community,

I just started learning how to make a Web App with Dash. Yet I have one question while reading the tutorial.
In the last example of the part 2 (https://plot.ly/dash/getting-started-part-2), we can change the available cities depending on the selected country, fair enough. Yet, I wonder if it is possible to remember the chosen options. For instance, if I select Cincinatti, then select Canada, then select back America, can Cincinatti be selected “by default”? I mean something similar to the “trace” method of tkinter variables.

Thanks for your answers!

Hey @Kefeng, welcome to Dash!

This is a great question. Most Dash apps don’t have a sense of “history” - their outputs are derived from exactly what you see on the page. This makes apps predictable in that you will always reach the same outputs no matter “how you got there”.

However, to solve this for this particular question, one solution would be to store the “history” of the app inside a hidden div (which ends up acting as a client-side session store) and reading from that when we switch options. Here’s an example:

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import json

app = dash.Dash()

options = {
    'America': ['NYC', 'Cincinatti'],
    'Canada': ['Montreal', 'Toronto'],
}

app.layout = html.Div([
    dcc.Dropdown(
        id='d1',
        options=[{'label': i, 'value': i} for i in options.keys()],
        value='Canada'
    ),
    dcc.Dropdown(
        id='d2'
    ),
    html.Div('{}', id='history', style={'display': 'none'})
])


@app.callback(Output('d2', 'options'), [Input('d1', 'value')])
def update_options(value):
    return [{'label': i, 'value': i} for i in options[value]]


@app.callback(Output('d2', 'value'),
              [Input('d2', 'options')],
              [State('d1', 'value'), State('history', 'children')])
def update_value(options, d1value, jhistory):
    history = json.loads(jhistory)
    if d1value in history:
        return history[d1value]
    return options[0]['value']


@app.callback(Output('history', 'children'),
              [Input('d1', 'value'), Input('d2', 'value')],
              [State('history', 'children')])
def update_history(d1value, d2value, jhistory):
    history = json.loads(jhistory)
    history[d1value] = d2value
    return json.dumps(history)


if __name__ == '__main__':
    app.run_server(debug=True)

On a similar note to “history”, we’ll also be adding the concept of “previous state” to Dash in https://github.com/plotly/dash-renderer/pull/25

1 Like

Really interesting and cool, @chriddyp ! I took a stab at this earlier this morning and got hung up. I’m feeling good, as I’m a dash noob but had a really similar idea! Maybe it’s starting to percolate into the brain…

That said, I was not getting the behavior I expected. I used the second example from the “Multiple Outputs” idea here. I stole the use of this:

all_options = {
    'America': ['New York City', 'San Francisco', 'Cincinnati'],
    'Canada': [u'Montréal', 'Toronto', 'Ottawa']
}

and created an empty dict to try and modify on the fly with the history:

last_choice = {'America': None, 'Canada': None}

I’d look for a change in countries-dropdown and cities-dropdown and then update that dictionary with the value, then return it to a hidden div called last-choice. Does that not work because of the whole “don’t use global variables” thing? I also likely didn’t have a clue how div children were stored, as I was just storing raw:

last_choice[country] = city
return last_choice

Does that not work? Thanks again for sharing the real way to do this!

Thanks @chriddyp, this is exactly the answer I was looking for. I went through the tutorial and saw a similar approach to “store” a variable with only one callback and pass it to other callbacks.