Sharing a dataframe between tabs

I have a Dash App with multiple tabs. Is there a way to share a dataframe between tabs i.e. sharing a dataframe created in tab 1 with tab 2

Check out the chapter on “sharing data between callbacks” - https://plot.ly/dash/sharing-data-between-callbacks

1 Like

Hi @chriddyp, thank you for the response. I guess the problem I am facing is not completely solvable using hidden divs.

For Ex:

  • In Tab 1, I am storing data in a hidden div and then using that data, I am creating a bar chart
  • Next, I navigate to Tab 2 and show a line graph based on the same hidden data again.
  • However, when I navigate back to the first tab, the stored data in the hidden div is lost.

Below is the sample code.

import dash
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
import json

tab1_layout = [html.Div("Storing data to hidden div and displaying as bar chart"),
               html.Button("Store data into hidden div",
                            n_clicks=0,
                            id='store-data-hidden-div'),
               html.Button("Display data from hidden div as bar chart",
                           n_clicks=0,
                           id='create-bar-chart'),
               dcc.Graph(id = "bar-graph")
               ]

tab2_layout = [html.Div("retrieving data from hidden div and displaying as line chart"),
               html.Button("Display data from hidden div as line chart",
                           n_clicks=0,
                           id='create-line-chart'),
               dcc.Graph(id = "line-graph")
               ]

app = dash.Dash()
app.config['suppress_callback_exceptions'] = True

## css file
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

app.layout = html.Div([
    # hidden Div for storing data needed for graphs
    html.Div(id='graph-data-json-dump', style={'display': 'none'}),
    # Title
    html.Div("Sharing data between callbacks"),

    # tabs
    dcc.Tabs(
            tabs=[
                {'label': 'Tab 1', 'value': 1},
                {'label': 'Tab 2', 'value': 2}
              ],
            value=1,
            id='tabs'
        ),

    # Tab-layout
    html.Div(id='tab-layout')
])

#switching between tabs
@app.callback(dash.dependencies.Output('tab-layout', 'children'),
              [dash.dependencies.Input('tabs', 'value')])
def call_tab_layout(tab_value):
    if tab_value == 1:
        return tab1_layout
    elif tab_value == 2:
        return tab2_layout
    else:
        html.Div()

#sending data to hidden div from tab1
@app.callback(dash.dependencies.Output('graph-data-json-dump', 'children'),
              [dash.dependencies.Input('store-data-hidden-div', 'n_clicks')])
def store_data_to_hidden_div(nclick):
    if nclick >0:
        df = pd.DataFrame({'x_axis':[6,4,9],
                        'y_axis':[4,2,7]})
        return df.to_json(orient = 'split')

#plotting bar graph in same tab

@app.callback(dash.dependencies.Output('bar-graph', 'figure'),
              [dash.dependencies.Input('graph-data-json-dump', 'children'),
               dash.dependencies.Input('create-bar-chart', 'n_clicks')])
def create_bar_chart(json_dump, nclick):
    if nclick >0:
        json_dump = json.loads(json_dump)
        json_dump_df = pd.DataFrame(json_dump['data'], columns=json_dump['columns'])

        data = [go.Bar(
            x = json_dump_df['x_axis'].values,
            y = json_dump_df['y_axis'].values,
            name = "Bar Chart")]
        fig = go.Figure(data = data)
        return fig

#plotting line graph in next tab
@app.callback(dash.dependencies.Output('line-graph', 'figure'),
              [dash.dependencies.Input('graph-data-json-dump', 'children'),
               dash.dependencies.Input('create-line-chart', 'n_clicks')])
def create_line_chart(json_dump, nclick):
    if nclick > 0:
        json_dump = json.loads(json_dump)
        json_dump_df = pd.DataFrame(json_dump['data'], columns=json_dump['columns'])

        data = [go.Scatter(
            x=json_dump_df['x_axis'].values,
            y=json_dump_df['y_axis'].values,
            name="Line Chart")]
        fig = go.Figure(data=data)
        return fig


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

Thanks for the example! I see what’s happening now.

The problem is in this callback. Here’s what’s happening:

  1. When you navigate back to Tab 1, you are returning tab1_layout
  2. tab1_layout returns a two buttons where n_clicks=0
  3. Since the callback returns new components with IDs, Dash fires the appropriate callbacks. In this case, it fires the callbacks with n_clicks=0 for the newly rendered buttons, therefore resetting the data that you stored previously in your hidden div.

You have a couple of options:

  1. Prevent the update from happening by raising an exception in your callback when n_clicks=0
  2. Move the Button that triggers the datastore out of tab1_layout and keep it stored persistently throughout the app.
2 Likes

Hi chriddyp, I had the same problem. could you create example of how to do with your first option? many thanks.

Also, what is odd is when I switched between pages, only the html table shows the data of previous load, all figures are gone. How to let the figures show?

I have a similar problem @chriddyp

I’m using two tabs with two different graphs, Graphs get updated dynamically using callbacks.
When I navigate between tabs, and come back to the first tab, the graph is gone. Is there a way to handle this scenario to retain data in the layout when you switch back between tabs.
Appreciate you time and help.

Thanks!

2 Likes

@chriddyp, Thanks for all the wonderful work you do and for the great examples.

I am facing this problem as well: storing data in a hidden div, but losing it upon navigating across tabs. Do you mind providing code examples of the options you suggest?

Just curious if anyone found a solution to this. I’m having the same problem.

Same problem here. =/

I’ve already tried to use persistence as parameter and dcc.Store(), but nothing worked.

Hi @chriddyp,

I am only starting to use Dash and I am facing the same issue. Could you please provide an example for these two (or one of them!) that you mentioned?

Where should we prevent the update to happen? In the callback that is used to switch from one tab to the other? Here is the callback that I am using :

@app.callback(
        Output("tabs_content", "children")
        , Input("tabs_app", "value"))
def callback2(tab):
    if tab == "tab-1":
        return tab_1
    elif tab == "tab-2":
        return tab_2

Thanks a lot!
Mathieu

Hi,

I was searching for something similar and I think what @chriddyp refers to, is this one:

from dash.exceptions import PreventUpdate

The update in the applicable callback can be prevented by doing this:

if nclick == 0:
    raise PreventUpdate

I also changed the imports, used prevent_initial_call=True, and changed from Input to State when using the graph-data-json-dump.

For the sake of completeness, here the full code from the initial post:

import dash
from dash import dcc, html, Input, Output, State
from dash.exceptions import PreventUpdate

import pandas as pd
import plotly.graph_objs as go
import json

tab1_layout = [
    html.Div("Storing data to hidden div and displaying as bar chart"),
    html.Button(
        "Store data into hidden div",
        n_clicks=0,
        id='store-data-hidden-div'
    ),
    html.Button(
        "Display data from hidden div as bar chart",
        n_clicks=0,
        id='create-bar-chart'
    ),
    dcc.Graph(id="bar-graph")
]

tab2_layout = [
    html.Div("retrieving data from hidden div and displaying as line chart"),
    html.Button(
        "Display data from hidden div as line chart",
        n_clicks=0,
        id='create-line-chart'),
    dcc.Graph(id="line-graph")
]

app = dash.Dash(
    __name__,
    suppress_callback_exceptions=True,
)

app.layout = html.Div(
    [
        # hidden Div for storing data needed for graphs
        html.Div(id='graph-data-json-dump', style={'display': 'none'}),
        # Title
        html.Div("Sharing data between callbacks"),
        # tabs
        dcc.Tabs(
            id='tabs',
            children=[
                dcc.Tab(label='Tab 1', value='t1'),
                dcc.Tab(label='Tab 2', value='t2'),
            ],
            value='t1',
        ),
        # Tab-layout
        html.Div(id='tab-layout')
    ]
)

# switching between tabs
@app.callback(
    Output('tab-layout', 'children'),
    Input('tabs', 'value'),
)
def call_tab_layout(tab_value):
    if tab_value == 't1':
        return tab1_layout
    elif tab_value == 't2':
        return tab2_layout
    else:
        html.Div()

# sending data to hidden div from tab1
@app.callback(
    Output('graph-data-json-dump', 'children'),
    Input('store-data-hidden-div', 'n_clicks'),
    prevent_initial_call=True
)
def store_data_to_hidden_div(nclick):
    if nclick == 0:
        raise PreventUpdate

    df = pd.DataFrame(
        {'x_axis': [6, 4, 9],
         'y_axis': [4, 2, 7]
         }
    )
    return df.to_json(orient='split')


# plotting bar graph in same tab
@app.callback(
    Output('bar-graph', 'figure'),
    State('graph-data-json-dump', 'children'),
    Input('create-bar-chart', 'n_clicks'),
    prevent_initial_call=True
)
def create_bar_chart(json_dump, nclick):
    content = json.loads(json_dump)
    json_dump_df = pd.DataFrame(content['data'], columns=content['columns'])

    data = go.Bar(
        x=json_dump_df['x_axis'].values,
        y=json_dump_df['y_axis'].values,
        name="Bar Chart"
    )
    fig = go.Figure(data=data)
    return fig


# plotting line graph in next tab
@app.callback(
    Output('line-graph', 'figure'),
    State('graph-data-json-dump', 'children'),
    Input('create-line-chart', 'n_clicks'),
    prevent_initial_call=True
)
def create_line_chart(json_dump, nclick):
    json_dump = json.loads(json_dump)
    json_dump_df = pd.DataFrame(json_dump['data'], columns=json_dump['columns'])

    data = [go.Scatter(
        x=json_dump_df['x_axis'].values,
        y=json_dump_df['y_axis'].values,
        name="Line Chart")]
    fig = go.Figure(data=data)
    return fig


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

Anyways, I think the better way to handle this would be using a dcc.Store component.