Sharing data between Multi-page Apps

I have a multi-page app, and I am using the file upload component. I would like to upload a file one time, do some simple processing, then keep that file available to pass between the other URLs without having to re-upload. The hidden Div concept does not seem to hold when passing between the URLs. As soon as I navigate off the index page, the file is lost. In this example, I am just passing the filename to keep it simple, although ultimately I need to pass the data frame JSON along with the filename. Any tips to get this working?

from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import base64
import io
import os
import pandas as pd

print(dcc.__version__) # 0.6.0 or above is required

app = dash.Dash()
app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

index_page = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '30%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=False
    ),
    html.Div(id='output-data-upload'),
    html.Br(),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
])

page_1_layout = html.Div([
    dcc.Dropdown(
        id='page-1-dropdown',
        options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']],
        value='LA'
    ),
    html.Div(id='page-1-content'),
    html.Br(),
    html.Div(id='output-data-upload'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
    html.Br(),
    dcc.Link('Go back to home', href='/'),

])

def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])
    
    return filename,df
    
@app.callback(Output('page-1-content', 'children'),
              [Input('page-1-dropdown', 'value')])
def page_1_dropdown(value):
    return 'You have selected "{}"'.format(value)


page_2_layout = html.Div([
    dcc.RadioItems(
        id='page-2-radios',
        options=[{'label': i, 'value': i} for i in ['Orange', 'Blue', 'Red']],
        value='Orange'
    ),
    html.Div(id='page-2-content'),
    html.Br(),
    html.Div(id='output-data-upload'),
    html.Br(),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go back to home', href='/')
])

@app.callback(Output('page-2-content', 'children'),
              [Input('page-2-radios', 'value')])
def page_2_radios(value):
    return 'You have selected "{}"'.format(value)


# Update the index
@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/page-1':
        return page_1_layout
    elif pathname == '/page-2':
        return page_2_layout
    else:
        return index_page
    # You could also return a 404 "URL not found" page here


@app.callback(Output('output-data-upload', 'children'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename'),
               Input('upload-data', 'last_modified')])
def update_output(contents, filename, date):
    if contents is not None:
        #parse_contents(contents, filename, date)
        filename, df = parse_contents(contents, filename, date)
        #df.to_json(orient = 'split')
        return html.Div([
                html.H6(filename)
                ])
        
external_css = ["https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css",
                "https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css",
                "https://fonts.googleapis.com/css?family=Work+Sans",
                "https://bootswatch.com/3/paper/bootstrap.css"]
                #'https://codepen.io/chriddyp/pen/bWLwgP.css']

for css in external_css:
    app.css.append_css({"external_url": css})
    
if 'DYNO' in os.environ:
    app.scripts.append_script({
        'external_url': 'https://cdn.rawgit.com/chriddyp/ca0d8f02a1659981a0ea7f013a378bbd/raw/e79f3f789517deec58f41251f7dbb6bee72c44ab/plotly_ga.js'
    })
    
if __name__ == '__main__':
    app.run_server(debug=True)```
2 Likes

I have solved this by persisting the hidden div across all pages, and passing sub-divs for each filename and json data.

from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import base64
import io
import os
import pandas as pd

print(dcc.__version__) # 0.6.0 or above is required

app = dash.Dash()
app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    # This is the 'hidden div' however its really a container for sub-divs, some hidden, some not 
    html.Div(id='output-data-upload'),
    html.Br(),
    html.Div(id='page-content')
])

index_page = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '30%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
    ),
    
    html.Br(),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
])

page_1_layout = html.Div([
    # This button will pull the JSON data into a new callback
    html.Button('Submit', id='button'),
    html.Div(id='output-container-button'),
    html.Div(id='page-1-content'),
    dcc.Link('Go to Page 2', href='/page-2'),
    html.Br(),
    dcc.Link('Go back to home', href='/'),

])

def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])
    
    return filename,df
    
page_2_layout = html.Div([
    html.Div(id='page-2-content'),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go back to home', href='/')
])


# Update the index
@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/page-1':
        return page_1_layout
    elif pathname == '/page-2':
        return page_2_layout
    else:
        return index_page
    # You could also return a 404 "URL not found" page here


@app.callback(Output('output-data-upload', 'children'),
              [Input('upload-data', 'contents'),
               Input('upload-data', 'filename'),
               Input('upload-data', 'last_modified')])
def update_output(contents, filename, date):
    if contents is not None:
        filename, df = parse_contents(contents, filename, date)
        # This is the key to the hidden div formatting
        return html.Div([
                html.Div(['Tank Stats file: ' + filename]),
                html.Div([df.to_json(orient = 'split')], id='tankStats',style={'display': 'none'})
                ],style={'padding-top': '60px','align': "center"})

@app.callback(Output('output-container-button', 'children'),
              [Input('button', 'n_clicks'),
               Input('tankStats', 'children')])
def update_graph(n_clicks,tankStats):
    if n_clicks is not None:
        dff = pd.read_json(tankStats[0], orient='split')
        print(dff)
        #figure = create_figure(dff)
        return 'pass success'
    
       
external_css = ["https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css",
                "https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css",
                "https://fonts.googleapis.com/css?family=Work+Sans",
                "https://bootswatch.com/3/paper/bootstrap.css"]
                #'https://codepen.io/chriddyp/pen/bWLwgP.css']

for css in external_css:
    app.css.append_css({"external_url": css})
    
if 'DYNO' in os.environ:
    app.scripts.append_script({
        'external_url': 'https://cdn.rawgit.com/chriddyp/ca0d8f02a1659981a0ea7f013a378bbd/raw/e79f3f789517deec58f41251f7dbb6bee72c44ab/plotly_ga.js'
    })
    
if __name__ == '__main__':
    app.run_server(debug=True)```

Is there any other way to navigate the value to multi pages? I have to pass the value from the table to another page. For my case, sharing the whole div element to multi pages doesn’t seem a good idea!

I’m just copy and pasting this answer from another relevant private discussion.

I do it by returning each page layout as a function with arguments.

In app1.py I would have:

def getLayout(hidden_client,
          hidden_permission,
          hidden_user):
return [
    html.Div(hidden_client,id='hidden-client-div-1',style={'display':'none'}),
    html.Div(hidden_permission,id='hidden-permission-div-1',style={'display':'none'}),
    html.Div(hidden_user,id='hidden-user-div-1',style={'display':'none'}),

    PAGE CONTENT HERE
]

In index.py I have something like:

@app.callback(Output('page-content', 'children'),
          [Input('url', 'pathname'),
          Input('hidden-client-div', 'children'),
          Input('hidden-permission-div', 'children'),
          Input('hidden-user-div', 'children')])
def display_page(pathname,
             hidden_client,
             hidden_permission,
             hidden_user):
   if pathname=="app1": 
       return app1.getLayout(hidden_client,
                             hidden_permission,
                             hidden_user)
   else:
      etc.
2 Likes

what if we get the Hyperlink value store in hidden div and pass that hidden value to another page?
Is this solution works? This idea is similar to your idea I guess!

1 Like

Hello,
I am a new user of Dash. My problem and the topic are connected, so I post here.
I try to solve the problem of loading data from a large file, which is about 150MB. I can not divide files into smaller ones.
I select the appropriate file based on the value from dcc.Dropdown, load it in memory and present only a part of the data in the graph. However, a few options from dcc.Dropdown point to the same file, so it is reasonable to store it in memory for as long as use data from this file.
Loading the whole file into a hidden div stops the application. I tried to load data into the cache using Flask Caching (‘CACHE_TYPE’: ‘filesystem’). The application works as long as it is running from the command line. However, it should work on the server with IIS using WSGI, where it, unfortunately, does not start.
Does anyone have experience and can suggest how to solve this problem?

Hi. How have you solved the problem ? can you please share the solution.

I just tested to get value of dropdown from index page to other pages. Here is app layout. (App entry point is index.py)

Multipage
 |-- app.py
 |-- index.py
 |-- apps
      |-- app1.py
      |-- app2.py
      |-- __init__.py

app.py

import dash

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

index.py

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

from app import app
from apps import app1, app2


app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content',children=dcc.Dropdown(id='dd',options=[
            {'label': 'From Index Page - {}'.format(i), 'value': i} for i in [
                'PivotPy', 'PivotPy-Dash', 'Vasp2Visual'
            ]])),
    html.Div(id = 'display-page'),
    dcc.Link('Go to App 1 ', href='/apps/app1'), html.Br(), html.Br(),
    dcc.Link('Go to App 2 ', href='/apps/app2')
        ])


@app.callback(Output('display-page', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/apps/app1':
        return app1.layout
    elif pathname == '/apps/app2':
        return app2.layout
    else:
        return ''

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

app1.py

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

from app import app

layout = html.Div([
    html.H3('App 1'),
    html.Div(id='d2')
])

@app.callback(Output('d2','children'),[Input('dd','value')])
def return_1(value):
    print("App 1 Clicked with value {}".format(value))
    return html.H1("App 1 Clicked with value {}".format(value))

app2.py

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from time import sleep

from app import app

layout = html.Div([
    html.H3('App 2'),
    html.Div(id='d1')
])

@app.callback(Output('d1','children'),[Input('dd','value')])
def return_2(value):
    print('Sleeping')
    sleep(2)
    print("App 2 Clicked with value {}".format(value))
    return html.H1("App 2 Clicked with value {}".format(value))

Result


Only the visible page updated as I see print out put in terminal.

Hi @claudiaann1 I’ve been doing some research, and yes, the hidden div is useful for not multi-page dash apps. It’s useful for sharing data between callbacks in the same dash route. Have you solved your issue? Have you tested @daveishere solution?

Hi @daveishere where did you find this solution?

So basically this is a hack but can I have nested callbacks within app1.py def getLayout’s return value layout? Otherwise they are just static pages right?