Multiple page app with local storage file

Hi all,

I am new to dash and html, and learning from the dash tutorial website.

I am trying to write a multiple page dashboard, with cross page data communicated through local storage files.

Below is my example code. For the 2 pages, each has a button on the page. And they will display the button click count on both pages.

############################ import ######################

from jupyter_dash import JupyterDash
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State
from flask import request


import base64
import io
import pandas as pd
import os
import numpy as np
from datetime import datetime

import plotly.express as px
################### configure ########################

JupyterDash.infer_jupyter_proxy_config()

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

server = app.server
############ local record #################

def write_to_file( idx , val ):
    f = open( os.getcwd() + "\\file{}.txt".format( idx ) , "w" )
    
    f.write( str( val ) )
    
    f.close()
    
def read_from_file( idx ):
    f = open( os.getcwd() + "\\file{}.txt".format( idx ) , "r" )
    
    ret = int( f.read() )
    
    f.close()
    
    return ret


write_to_file( 1 , 0 )
write_to_file( 2 , 0 )
################ website ####################
url_bar_and_content_div = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

layout_index = html.Div([
    dcc.Link('Navigate to "/page-1"', href='/page-1'),
    html.Br(),
    dcc.Link('Navigate to "/page-2"', href='/page-2'),
])

layout_page_1 = html.Div([
    html.H2('Page 1'),
    
    
    html.Button(id='page1button', n_clicks=0, children='Submit'),
    
    html.Div(id='page-1-display-value'),
    
    html.Br(),
    dcc.Link('Navigate to "/"', href='/'),
    html.Br(),
    dcc.Link('Navigate to "/page-2"', href='/page-2'),
])

layout_page_2 = html.Div([
    html.H2('Page 2'),
    
    html.Button(id='page2button', n_clicks=0, children='Submit'),

    html.Div(id='page-2-display-value'),
    
    
    
    html.Br(),
    dcc.Link('Navigate to "/"', href='/'),
    html.Br(),
    dcc.Link('Navigate to "/page-1"', href='/page-1'),
])

# index layout
app.layout = url_bar_and_content_div

# "complete" layout
app.validation_layout = html.Div([
    url_bar_and_content_div,
    layout_index,
    layout_page_1,
    layout_page_2,
])


# Index callbacks
@app.callback(Output('page-content', 'children'),
              Input('url', 'pathname'))
def display_page(pathname):
    if pathname == "/page-1":
        return layout_page_1
    elif pathname == "/page-2":
        return layout_page_2
    else:
        return layout_index


# Page 1 callbacks
@app.callback(Output('page-1-display-value', 'children'),
              Input('page1button', 'n_clicks') )
def update_output( n_ ):
    
    
    n1 = read_from_file( 1 )
    n2 = read_from_file( 2 )
    
    write_to_file( 1 , n1 + 1 )
    
    
    return 'page1 button has been clicked {} times, page2 button has been clicked {} times'.format( n1+1,n2 )


# Page 2 callbacks
@app.callback(Output('page-2-display-value', 'children'),
              Input('page2button', 'n_clicks'))
def display_value( n_ ):
    
    n1 = read_from_file( 1 )
    n2 = read_from_file( 2 )
    
    write_to_file( 2 , n2 + 1 )
    
    return 'page1 button has been clicked {} times, page2 button has been clicked {} times'.format( n1,n2+1 )

################## run server ##############
app.run_server()

My original concern is about the page would not load the button click count properly, because I believe callback function is not fired when direct to a page.

But the result turns out to be different, when i first click into page 1(just from hyperlink of index page, not yet the button), displayed click for button 1 is 1.

When I click into page2, displayed click for button 2 is 1

Why does this happen? Does that mean that a hyperlink to a page will directly fire the callback function? If yes, will it fire every call back on that page? Anyway to customize this, like give a control about what callback to be fired when the page is directed from a hyperlink?

Sorry for the long post, and much appreciated for your time reading my post :slight_smile:

By default all callbacks are executed when the app is launched. You can read more about this here, under โ€œWhen are callbacks executed?โ€. And yes, it will fire all callbacks in the page, so your counter for pages 1 and 2 stars in 1.

You could change it simply by adding prevent_initial_call=True to the two button callbacks. Otherwise you can adapt the logic inside the callbacks not to count on render. Note that typically the click count is not started from the get-go (so n_clicks are None), so you could use for instance a conditional checking for not None as long as you unset the count in the layout.

Last, but not least, you could consider using dcc.Store to keep track of the click counts, granted that you want them to persist just in a given session.

thanks very much for the reply! Will go and check the link.

1 Like