Hello,
it probably is a simple beginner mistake, but I try to explain what I think happens.
I have a multipage dash app running inside of a flask app.
It has the following structure:
├── app
│ ├── init.py
│ ├── tlc
│ │ ├── assets
│ │ │ ├── bootstrap.css
│ │ │ ├── custom.css
│ │ │ ├── favicon.ico
│ │ │ ├── responsive-sidebar.css
│ │ ├── callbacks.py
│ │ ├── data
│ │ │ ├── df_tlc_aktiva.csv
│ │ │ ├── df_tlc_passiva.csv
│ │ ├── pages
│ │ │ ├── app1.py
│ │ │ ├── app2.py
│ │ │ ├── app3.py
│ │ │ ├── app4.py
│ │ ├── sidebar.py
All but one app{i}.py files have the following content, apart from different labels:
import dash_core_components as dcc
import dash_html_components as html
layout = html.Div([
html.H3('Income Statement'),
dcc.Dropdown(
id='app-2-dropdown',
options=[
{'label': 'App1 - {}'.format(i), 'value': i} for i in [
'NYC', 'MTL', 'LA'
]
]
),
html.Div(id='app-2-display-value'),
dcc.Link('Go to Balance Sheet', href='/dashboard/balancesheet')
])
The one that is different, lets call it app4.py has the following content:
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_table
import pandas as pd
import pathlib
# get relative data folder
PATH = pathlib.Path(__file__).parent
DATA_PATH = PATH.joinpath("../data").resolve()
df_tlc_aktiva = pd.read_csv(DATA_PATH.joinpath("df_tlc_aktiva.csv"), sep=';')
df_tlc_passiva = pd.read_csv(DATA_PATH.joinpath("df_tlc_passiva.csv"), sep=';')
# read in
layout = html.Div(
[
html.H3('Balance Sheet'),
html.H4('Aktiva'),
# This datatable gets populated all over the place
dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df_tlc_aktiva.columns],
data=df_tlc_aktiva.to_dict('records'),
),
html.H4('Passiva'),
#This datatable is NOT populated all over the place
dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in df_tlc_passiva.columns],
data=df_tlc_passiva.to_dict('records'),
),
dcc.Dropdown(
id='app-3-dropdown',
options=[
{'label': 'Balance Sheet - {}'.format(i), 'value': i} for i in [
'NYC', 'MTL', 'LA'
]
]
),
html.Div(id='app-3-display-value'),
dcc.Link('Go to Cashflow', href='/dashboard/cashflow')
])
my callbacks are here in callbacks.py:
from datetime import datetime as dt
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from .pages import (
quickcheck,
incomeStatement,
balanceSheet,
cashflow,
workingCapital,
debt,
indicators,
shouldIs,
model
)
def register_callbacks(dashapp):
# this callback uses the current pathname to set the active state of the
# corresponding nav link to true, allowing users to tell see page they are on
@dashapp.callback(
[Output(f"page-{i}-link", "active") for i in range(1, 10)],
[Input("url", "pathname")],
)
def toggle_active_links(pathname):
if pathname == "/dashboard":
# Treat page 1 as the homepage / index
return True, False, False
return [pathname == f"/dashboard/page-{i}" for i in range(1, 10)]
@dashapp.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
if pathname in ["/", "/dashboard/page-1"]:
return quickcheck.layout
elif pathname == "/dashboard/page-2":
return incomeStatement.layout
elif pathname == "/dashboard/page-3":
return balanceSheet.layout
elif pathname == "/dashboard/page-4":
return cashflow.layout
elif pathname == "/dashboard/page-5":
return workingCapital.layout
elif pathname == "/dashboard/page-6":
return debt.layout
elif pathname == "/dashboard/page-7":
return indicators.layout
elif pathname == "/dashboard/page-8":
return shouldIs.layout
elif pathname == "/dashboard/page-9":
return model.layout
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
@dashapp.callback(
Output("sidebar", "className"),
[Input("sidebar-toggle", "n_clicks")],
[State("sidebar", "className")],
)
def toggle_classname(n, classname):
if n and classname == "":
return "collapsed"
return ""
@dashapp.callback(
Output("collapse", "is_open"),
[Input("navbar-toggle", "n_clicks")],
[State("collapse", "is_open")],
)
def toggle_collapse(n, is_open):
if n:
return not is_open
return is_open
@dashapp.callback(
Output('app-1-display-value', 'children'),
[Input('app-1-dropdown', 'value')])
def display_value(value):
return 'You have selected "{}"'.format(value)
@dashapp.callback(
Output('app-2-display-value', 'children'),
[Input('app-2-dropdown', 'value')])
def display_value(value):
return 'You have selected "{}"'.format(value)
@dashapp.callback(
Output('app-3-display-value', 'children'),
[Input('app-3-dropdown', 'value')])
def display_value(value):
return 'You have selected "{}"'.format(value)
@dashapp.callback(
Output('app-4-display-value', 'children'),
[Input('app-4-dropdown', 'value')])
def display_value(value):
return 'You have selected "{}"'.format(value)
Then I have a sidebar that holds the navigation.
I register the callbacks on the dash instance like this in init.py:
def register_dashapps(app):
from app.tlc.sidebar import layout
from app.tlc.callbacks import register_callbacks
# link fontawesome to get the chevron icons
FA = "https://use.fontawesome.com/releases/v5.8.1/css/all.css"
# Meta tags for viewport responsiveness
meta_viewport = {"name": "viewport", "content": "width=device-width, initial-scale=1, shrink-to-fit=no"}
tlc = dash.Dash(__name__,
server=app,
url_base_pathname='/dashboard/',
assets_folder=get_root_path(__name__) + '/tlc/assets/',
meta_tags=[meta_viewport],
#external_stylesheets=[dbc.themes.SANDSTONE, FA],
)
tlc.config.suppress_callback_exceptions = True
with app.app_context():
tlc.title = 'TLC'
tlc.layout = layout
register_callbacks(tlc)
_protect_dashviews(tlc)
Current behaviour:
When I access the pages app1 to app3, everything works as expected, but when I access the page app4 with the dataframe and after that visit another page, let’s say app2, the first dataframe from app4.py is populated also on that page of app2, despite it’s not coded like that in the layout of the app2.
When I access the app4 page again, and then visit app2 again, it gets populated twice and so forth.
Only a hard reload of the whole page shows the actual layout that is supposed to show.
Expected behaviour:
Every app{i} should just show it’s own layout, and not anything from another app.
Any help would be appreciated, thanks!
Mark