Multi-Page App Help

Hi All,

I started using Dash about a week ago and I absolutely love it! I’m a beginner to programming but I love how intuitive the software is.

I’m posting here because I’m having a lot of trouble getting my multi-page app to work. I followed the directions in here but I think I’ve gone over my head.

I’m trying to incorporate the hidden div concept described in Example 1 here into my main page. Here’s the code I have for my callbacks:

# Callback for hidden div storing information from dropdown
@app.callback(dash.dependencies.Output('hidden', 'children'), [dash.dependencies.Input('gender_dropdown', 'value')])
def clean_data(value):
    if value is None:
        cleaned_df = df_marks
    else:
        cleaned_df = df_marks[df_marks['gender'] == value]    
    return cleaned_df.to_json(date_format='iso', orient='split')


# Callback for graph
@app.callback(
    dash.dependencies.Output(component_id='marks_graph', component_property='figure'),
    [dash.dependencies.Input(component_id='hidden', component_property='children')]
)
def update_marks_graph(jsonified_cleaned_data):
    dff = pd.read_json(jsonified_cleaned_data, orient='split')
    return plot_bar(dff,"Marks","bar","Average Score per Test")

For some reason, it always hits me with this error:
ValueError: Invalid file path or buffer object type: <class ‘list’>. There’s something wrong with the part in which I try to read the JSON file. From what I understand, the only reason it can’t read the JSON file is because my first callback isn’t populating the hidden div!

What’s weird is that I ran the exact same code on a single-page app and that worked like a charm. What’s even weirder is that when I comment out the callback for the graph and refresh the page a couple of times, the hidden div gets populated with data.

The only difference between my code from before is the multi-page structure and my use of Bootstrap Dash code available here for the HTML. I don’t think that the latter should make a difference but I’m not sure.

Can anyone please help me? I’ve been stuck on this problem for a few hours now and really don’t know how to proceed.

Any help would be much appreciated!

HI,

It’s hard to say without the code for the entire app, If you can post it, I’ll take a look.

One thing to watch out for is to ensure you don’t repeat id’s within the multi page app.

Hey @vantaka2,

Thanks for the quick response!

Here’s my file structure:
| index.py
| app.py
| data.csv
|
| apps
|----__init __.py
|---- comparison.py
|---- dashboard.py
|
| all_components
|----__init __.py
|----navbar.py

I’ve tweaked the code a bit. After a few fixes, I’ve managed to make the graphs work - however, they only work after first selecting a dropdown menu item first. The initial loading of the screen still throws the same error from above when reading the data from the JSON.

I’ve attached the code below. I’ve simplified it a lot so that it’s easier to digest. Let me know what you think.

Thanks a lot!

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 dashboard, comparison


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


@app.callback(Output('page-content', 'children'),
                     [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/apps/dashboard':
         return dashboard.layout
    elif pathname == '/apps/comparison':
         return comparison.layout
    else:
        return "error 404"

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

app.py

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

navbar.py

import dash_bootstrap_components as dbc

navbar = dbc.NavbarSimple(
    children=[
        dbc.NavItem(dbc.NavLink("Overview",href = "/apps/dashboard")),
        dbc.DropdownMenu(
            nav=True,
            in_navbar=True,
            label="Analytical Tools",
            children=[
                        dbc.DropdownMenuItem(dbc.NavLink("Student Performance Tracker", href="/apps/comparison")), 
                        dbc.DropdownMenuItem(dbc.NavLink("Analytical Tool 2", href="#")),
                        dbc.DropdownMenuItem(dbc.NavLink("Analytical Tool 3",href="#")),
                ],
        ),
        dbc.NavItem(dbc.NavLink("Feedback",href = "/apps/feedback")),
        dbc.NavItem(dbc.NavLink("Support",href = "#"))        
    ],
    brand="Dashboard",
    brand_href="#",
    sticky="top",
    dark = True,
    color = 'primary'
)

dashboard.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
import dash
import pandas as pd
import plotly.graph_objs as go
import numpy as np
from all_components.navbar import navbar

from app import app

""""""""""""""""""""""""""""""
File Extraction and Editing
""""""""""""""""""""""""""""""
df = pd.read_csv('data.csv', sep='\t', encoding='utf-8')

data manipulation stuff

print(df_marks.head())
print(type(df_marks))

"""""""""""""""""""""
Plot Bar Functions
"""""""""""""""""""""

def plot_bar(frame,type,chart,title):
   code that plots      
   return {data, layout}


"""""""""""""""""""""
Body of the Layout
"""""""""""""""""""""

body = dbc.Container(
    [
        dbc.Row(
            [
                dbc.Col(
                    [
                        dcc.Dropdown(
                            id = "gender_dropdown",
                            options = [
                                {'label': 'Male','value': 'M'},
                                {'label': 'Female', 'value':'F'}
                            ],
                            placeholder = "Gender",
                            style = dict(
                                lineHeight = '12px'
                            )
                        ),
                    ]
                ),
                dbc.Col(
                    [
                        dcc.Graph(id = 'marks_graph')
                    ]
                )
            ], className = "mt-4"
        ),
        html.Div(id = "hidden", style = dict(display='None'),children = [])
    ]
)

"""""""""
Layout
"""""""""

layout = html.Div([navbar, body])


""""""""""""
Callbacks
""""""""""""


@app.callback(dash.dependencies.Output('hidden', 'children'), [dash.dependencies.Input('gender_dropdown', 'value')])
def clean_data(value):
    if value is None:
        cleaned_df = df_marks
    else:
        cleaned_df = df_marks[df_marks['gender'] == value]    
    return cleaned_df.to_json(date_format='iso', orient='split')


@app.callback(
    dash.dependencies.Output(component_id='marks_graph', component_property='figure'),
    [dash.dependencies.Input(component_id='hidden', component_property='children')]
)
def update_marks_graph(jsonified_cleaned_data):
    dff = pd.read_json(jsonified_cleaned_data, orient='split')
    return plot_bar(dff,"Marks","bar","Average Score per Test")

comparison.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

layout = html.Div([
    html.H3('App 1'),
    dbc.NavItem(dbc.NavLink("Link", href="/apps/dashboard")),
    dcc.Dropdown(
        id='app-2-dropdown',
        options=[
            {'label': 'App 1 - {}'.format(i), 'value': i} for i in [
                'NYC', 'MTL', 'LA'
            ]
        ]
    ),
    html.Div(id='app-2-display-value'),
    dcc.Link('Go to App 1', href='/apps/dashboard')
])


@app.callback(
    Output('app-2-display-value', 'children'),
    [Input('app-2-dropdown', 'value')])
def display_value(value):
    return 'You have selected "{}"'.format(value)

That is why I prefer Multi Page App with Divs and not with Tabs. Its easier.

Just to clarify, my primary issue is in the dashboard.py file.

@MGBpy: What exactly are you referring to when you say tabs? I don’t think I’ve referred to any tabs in my code.

Never mind, I figured it out.

Apparently I had to specify the value of the gender_dropdown as None.

My revised drop down for any one who has this problem in the future:

dcc.Dropdown(
            id = "gender_dropdown",
            options = [
                {'label': 'Male','value': 'M'},
                {'label': 'Female', 'value':'F'}
            ],
            placeholder = "Gender",
            value = None,
            style = dict(
                lineHeight = '12px'
            )

However, I still am curious as to why I need to do this in the multi-page case but not in the single page?

Sorry for wasting all of your guys’ time!

1 Like

Congrats by fixing it.
By the way, what is Multi-Page?

Sorry @MGBpy, I should have been clearer with the description of multi-page.

By multi-page, I mean that the app is organized into different files (app.py, index.py, dashboard.py, etc). Each of these files can (doesn’t necessarily have to) correspond to a unique page on a website/app. This allows you to navigate through the app. Also, a multi-page app is useful in organizing large amounts of code and troubleshooting errors in these situations.

Here’s a better description of the idea and how to do it in Dash.