Multipage persistence

Hi. I’ve looked through the user manual, and at all posts related to multi page apps. I think I’m missing the concept of making an object persist across multiple pages in html.

I would like to process some data on the initial ‘index’ page, and have it available to other tabs. In this example, I like the dataframe created on the index page to be available to page_1_layout and page_2_layout for further manipulation. Why doesn’t something like this work?

I have spent several days on this. So any help is appreciated. Thanks

import dash
import pandas as pd
import numpy as np
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

app.scripts.config.serve_locally = True
app.config.suppress_callback_exceptions = True

app.layout = html.Div([
dcc.Location(id=‘url’, refresh=False),
html.Div(id=‘page-content’,className=“container”),
html.Div(id=‘data’,style={‘display’:‘none’})
])

index_page = html.Div([
html.Div(dcc.Dropdown(id =‘df’, options=[{‘label’:str(i), ‘value’: i } for i in [‘df1’,‘df2’]])),
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([
html.H1(‘Page 1’),
dcc.Dropdown(
id=‘page-1-dropdown’,
options=[{‘label’: i, ‘value’: i} for i in [‘LA’, ‘NYC’, ‘MTL’]],
value=‘LA’
),

html.Div(dcc.Checklist(id ='dfdrop', values=[] )),
html.Div(id='page-1-content'), 
html.Br(),
dcc.Link('Go to Page 2', href='/page-2'),
html.Br(),
dcc.Link('Go back to home', href='/'),

])

@app.callback(dash.dependencies.Output(‘page-1-content’, ‘children’),
[dash.dependencies.Input(‘page-1-dropdown’, ‘value’),
])
def page_1_dropdown(value):
return ‘You have selected “{}”’.format(value)

@app.callback(dash.dependencies.Output(‘dfdrop’,‘options’),
[dash.dependencies.Input(‘data’,‘children’)
])
def option1(child):
df = pd.read_json(child)
return [{‘label’:i, ‘value’:i} for i in df.columns]

page_2_layout = html.Div([
html.H1(‘Page 2’),
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(),
dcc.Link(‘Go to Page 1’, href=’/page-1’),
html.Br(),
dcc.Link(‘Go back to home’, href=’/’)
])

@app.callback(dash.dependencies.Output(‘page-2-content’, ‘children’),
[dash.dependencies.Input(‘page-2-radios’, ‘value’)])
def page_2_radios(value):
return ‘You have selected “{}”’.format(value)

@app.callback(dash.dependencies.Output(‘data’,‘children’),
[dash.dependencies.Input(‘df’,‘value’)])
def create_df(val):
df = pd.DataFrame()
if val == ‘df1’:
df = pd.DataFrame(np.random.randint(0,100,size=(100,4)),columns=list(‘ABCD’))
if val == ‘df2’:
df = pd.DataFrame(np.random.randint(0,1000,size=(100,4)),columns=list(‘ABCD’))
return df.to_json()# Update the index

# You could also return a 404 "URL not found" page here

@app.callback(dash.dependencies.Output(‘page-content’, ‘children’),
[dash.dependencies.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

if name == ‘main’:
app.run_server(debug=True)

For multi-page apps, the structure we recommend can be roughly outlined as follows:

app.layout = html.Div([
  # dash core components populating page contents,
  html.Div(id='page-content')
])

Our examples usually focus around dcc.Location, but these core components can be anything.

You’re right that there is no persistence across these different locations, because the page content is completely replaced whenever you click on another link.

The key is to take whatever dash core components you want to be persistance outside of page-content. This works perfectly if your core components stay at the top, but to mimic your specfic architecture (a dcc in one page being accessed in another), you can simply add a callback that hides the content depending on which page you’re on.

Checkout this toy example that shows what this could look like. The experience of this app is that there is a dropdown on page 1, which is accessed on page 3:

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

app = dash.Dash()

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

app.config.suppress_callback_exceptions = True

app.layout = html.Div([
    # External dcc's for persistance
    dcc.Location(id='url'),
    dcc.Link('Page-1', href='/page1'),
    dcc.Link('Page-2', href='/page2'),
    dcc.Link('Page-3', href='/page3'),
    # We want a dropdown in Page 1 to be persistant
    html.Div([
        html.H2(['Welcome to Page 1']),
        dcc.Dropdown(
            options=[
                {'label': 'choice 1', 'value': 'choice 1'},
                {'label': 'choice 2', 'value': 'choice 2'},
                {'label': 'choice 3', 'value': 'choice 3'}
            ],
            value='choice 1',
            id='page-1-dropdown'
        )
    ], id='external-page-1'),

    # Page Content
    html.Div(id='page-content')
])

# Define pages

page_1_content = html.Div([
    html.P(['We are on page one now'])
])

page_2_content = html.Div([
    html.H2(['Welcome to Page 2']),
    html.P(['We are on page two '])
])

page_3_content = html.Div([
    html.H2(['Welcome to Page 3']),
    html.Div(id='dropdown-output'),
    html.P(['We are on page three'])
])


# Callback to populate page-content
@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def populate_content(url):
    if url == '/page1':
        return page_1_content
    elif url == '/page2':
        return page_2_content
    elif url == '/page3':
        return page_3_content


# Callback to hide Dropdown
@app.callback(
    Output('external-page-1', 'style'),
    [Input('url', 'pathname')]
)
def hide_external(url):
    if url == '/page1':
        return {'display': 'block'}
    else:
        return {'display': 'none'}

# Callback to show persistence: display dropdown contents from page 1 in page 3


@app.callback(
    Output('dropdown-output', 'children'),
    [Input('page-1-dropdown', 'value')]
)
def display_dropdown_contents(val):
    return 'you have selected {}'.format(val)


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

Thanks for the quick reply!
I plugged this in and was able to see the point you were making.

My real use case is that I have a dropdown component (which you’ve created in app.layout) that allows me to select from a list of white space delimited data files. The files can range in size from several K up to 8G.

I read the data files in as dataframes, and then do some processing in app.layout.

I have about 4 different ‘app.py’ files (my multipages). Each app[1-4].py file has several callbacks that do further filtering, processing,plotting of the dataframe’s content

What I really want to do is read a file in, do the processing once, and then make the dataframe available to the the different app[1-4].py files.

I had been processing and converting the dataframe to json in app.layout, and staching it in a hidden div.

I had previously been able to get the data to persist between pages.

I couldn’t figure out how to make the data in the hidden div serve as input to the callbacks in the app[1-4].py files.

I’ll try to emulate your example. Thanks

This is just what I was looking for! Is there any chance you can provide the same example in a multi-page app format? Thanks!

-Stefan

Can you elaborate on what you mean?

@charleyferrari’s example is already a multi page Dash app.

Sorry- that was unclear. I meant implemented as either structure shown by the last two examples in this tutorial:

https://dash.plot.ly/urls

It’s pretty straight forward to adapt either of them. Grab the one you want, then in index.py, just put all your components that you want to persist across pages before the div that is being swapped out for different pages. Which is to say before:

html.Div(id='page-content')

Thanks! I’ll give it a shot.

Sorry for reviving the thread, I tried to make based on the sample I found in https://dash.plot.ly/urls.

Every time I tried to input my URL manually, it gives me !
31|690x242

This is my app.py

import dash
import dash_bootstrap_components as dbc
from flask_caching import Cache
import os


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css', "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap-grid.min.css", dbc.themes.CERULEAN]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, url_base_pathname='/apps/app1/')

cache = Cache(app.server, config={
    # try 'filesystem' if you don't want to setup redis
    'CACHE_TYPE': 'filesystem',
    'CACHE_DIR': 'cache-directory'
})
server = app.server
app.config.suppress_callback_exceptions = True

timeout = 20

this is my index.py

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

from app import app
from layouts import layout1, layout2, layout3, layout4, layout5, layout6, layout7, layout8, layout9, layout10, layout11, layout12, layout0, layoutProt
import callbacks

navbar = dbc.NavbarSimple(
    children=[
        dbc.NavItem(dbc.NavLink("?~C~@?~B??~C??~C??~C??~C~I", href="/apps/app1/")), #1
        dbc.NavItem(dbc.NavLink("?~I?徴", href="/apps/app2/")), # 2,3,5,7,8
        dbc.NavItem(dbc.NavLink("OS", href="/apps/app3/")), #10
        dbc.NavItem(dbc.NavLink("?~I?徴?~H~F?~C", href="/apps/app4/")), #4,6
        dbc.NavItem(dbc.NavLink("?~H??~T?", href="/apps/app5/")),#11
        dbc.NavItem(dbc.NavLink("?~V?~C??~B??~C~W?~C??~C??~C??~B?", href="/apps/app6/")), #12
    ],
    brand="test ?~B??~C~^?~C??~C?",
    brand_href="/test",
    brand_style={'font-size':'medium'},
    sticky="top",
    style={'font-size':'medium'},
)

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

@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/apps/app1/':
        return layout1
    elif pathname == '/apps/app2/':
        return layout2
    elif pathname == '/apps/app3/':
        return layout3
    elif pathname == '/apps/app4/':
        return layout4
    elif pathname == '/apps/app5/':
        return layout5
    elif pathname == '/apps/app6/':
        return layout6
    elif pathname == '/test/':
        return layout0
    elif pathname == '/apps/appProt/':
        return layoutProt
    else:
        return layout0

if __name__ == '__main__':
    app.run_server(debug=True,dev_tools_ui=True,dev_tools_props_check=True, host='0.0.0.0')

Going to the link by typing xxxx/apps/app1/ is fine, Going to other links using hyperlink inside the page is OK.
Problem is going to every other links by typing gives me that error.