Saving State of Tabs in Dash

Hi,
I am unable to save the state of the tabs, i.e. if I have 2 tabs and if I make changes in the 1st tab and then move on to the 2nd tab, make some changes there and when I return to my 1st tab, all the changes are lost. If I have selected some options from my dropdown, then they are all restored to default when I switch between tabs! Is there a way to prevent this? Also can we have interaction between two tabs?

Hey SagarK

I’ve done something similar, with a 3 tab tabs. I hope it helps.

Basically, I just store the components state from one tab in a dcc.Store, and when this tab is reached again, I populate it with the stored data.

dash_layout_components = {
    'time_slider_app2': 'value',
    'backtest_choice_app2': 'values',
    'asset_selection_app2': 'value',
    'graph_selection_app2': 'values'
}

set_back_and_display_graph_input = {
    'store_layout_data': 'modified_timestamp',
    'tabs': 'value'
}


@app.callback(
    Output('store_layout_data', 'data'),
    [Input(key, value) for key, value in dash_layout_components.items()]
)
def store_layout(time_slider_value, backtest_choice_values, assets_selection_values, graph_selection_values):

    data_json = {
        'time_slider_value': time_slider_value,
        'backtest_choice_values': backtest_choice_values,
        'asset_selection_value': assets_selection_values,
        'graph_selection_values': graph_selection_values
    }
    return data_json


@app.callback(
    Output('store_layout_data', 'clear_data'),
    [Input('bt_erase_layout_storage_app2', 'n_clicks_timestamp')],
    [State('tabs', 'value')]
)
def erase_layout_data(bouton_ts, tab_value):

    if tab_value != '/app2':
        raise PreventUpdate
    return True


for component_id, component_property in dash_layout_components.items():
    @app.callback(
        Output(component_id, component_property),
        [Input(key, value) for key, value in set_back_and_display_graph_input.items()],
        [State('store_layout_data', 'data'), 
         State(component_id, 'id')]
    )
    def set_back_component(bouton_ts, tabs_value, layout_state_data, component):  
        if tabs_value != '/app2':
           raise PreventUpdate

        if layout_state_data is None:
            return []

        else:
            stored_layout_value_name = [key[:key.rfind('a')] + value for key, value in dash_layout_components.items()]
            store_layout_component_name = stored_layout_value_name[list(dash_layout_components.keys()).index(component)]
            return layout_state_data[store_layout_component_name]

Note that in the third callback, the second State and the content of the else statement are there to be able to find the index of the stored data corresponding to the layout component id.

stored_layout_value_name = ['time_slider_value', 'backtest_choice_values', 'asset_selection_value', 'graph_selection_values']

store_layout_component_name = the name of the stored value of the current layout component.
return the stored value corresponding to the current component.

I had to do this “trick” to be able to associate each component to the right stored value, as we don’t have access to the iterators (component_id and component_property) in the callbacks

I guess it should work for your use case

1 Like

Thanks for pointing me to the Store component from Dash and thanks for a detailed explanation! My code works well now :slight_smile:

@jajarbins Hey Where do you define your dcc.Store data? Could i not just save the whole tab in a dcc.Store component?, and check if i switch the tab if the store component is none or not? Tried it but dont work

I’m not sure to understand what you mean by “your dcc.Store data”.
If you mean the data that I upload in the dcc.Store, in this callback:
@app.callback(
Output(‘store_layout_data’, ‘data’),
[Input(key, value) for key, value in dash_layout_components.items()]
)
def store_layout(time_slider_value, backtest_choice_values, assets_selection_values, graph_selection_values):

data_json = {
    'time_slider_value': time_slider_value,
    'backtest_choice_values': backtest_choice_values,
    'asset_selection_value': assets_selection_values,
    'graph_selection_values': graph_selection_values
}
return data_json

if you mean “where do you put the dcc.Store component” → in the app.layout

@jajarbins thank you, seah I meant both :D. Could you maybe provide a small full example, I would really appreciate it. If I. Understand it right your method works with the tab method 1,whcih means that the tabs content can be defined in callbacks, right?