title: Callbacks between Dash pages
body: <p>I am trying to apply callbacks between different dash pages. I have setup my app components in different files:</p>
  
  <ul>
  <li>Hotspot_App.py</li>
  <li>index.py</li>
  <li>folder apps
  
  <ul>
  <li>GeoPlot.py</li>
  <li>ScatterPlot.py</li>
  </ul></li>
  </ul>
  
  <p>So the first page contains the layout of app1 and it's callbacks and components. But now I want to create a checklist in the second tab on the values that have been selected on a checklist in tab1. I tried to create the app.validation layout in the index but that does not work. See below my code:</p>
  
  <p>Index.py</p>
  
  <pre><code>import dash_bootstrap_components as dbc
  import dash_html_components as html
  from dash.dependencies import Input, Output
  import dash_auth
  import dash_core_components as dcc
  
  from Hotspot_App_Local import app
  from apps import GeoPlot, ScatterPlot
  
  
  
  
  app.title = 'Hotspot Analysis'
  auth = dash_auth.BasicAuth(
      app,
      VALID_USERNAME_PASSWORD_PAIRS
  )
  
  scr_oord = 'www.animage.com' #dummy
  nav_item = dbc.NavItem(dbc.NavLink("website.com", href="https://www.website.com/"))
  
  dropdown_menu = dbc.DropdownMenu(
      children=[
          dbc.DropdownMenuItem("GeoPlot", href= "/page-1"),
          dbc.DropdownMenuItem("ScatterPlot", href= "/page-2"),
      ],
      nav=True,
      in_navbar=True,
      label="Menu",
  )
  
  navbar = html.Div([dbc.Navbar(
      dbc.Container(
          [
              html.A(
                  # Use row and col to control vertical alignment of logo / brand
                  dbc.Row(
                      [
                          dbc.Col(html.Img(src= scr_oord, height="50px")),
                          dbc.Col(dbc.NavbarBrand("\tHotspot Analysis", className="ml-2", style= {'color': 'navy', 'font-weight': 'bold'})),
                      ],
                      align="center",
                      no_gutters=True,
                  ),
                  href="https://plot.ly",
              ),
              dbc.NavbarToggler(id="navbar-toggler2"),
              dbc.Collapse(
                  dbc.Nav(
                      [nav_item, dropdown_menu], className="ml-auto", navbar=True
                  ),
                  id="navbar-collapse2",
                  navbar=True,
              ),
          ]
      ),
      color="white",
      dark= False,
      className="mb-5",
              ),
          html.Div(id= 'page_content')
  ])
  
  
  app.layout = html.Div([
      dcc.Location(id= 'tab', refresh= False, pathname= "/page-1"),
      navbar,
      html.Div(id= 'page')
  ])
  
  app.validation_layout = html.Div([navbar, GeoPlot.layout, ScatterPlot.layout])
  
  @app.callback(Output("page", "children"), [Input("tab", "pathname")])
  def display_page(pathname):
      if pathname == "/page-1":
          return GeoPlot.layout
      if pathname == "/page-2":
          return ScatterPlot.layout
      # if not recognised, return 404 message
      return html.P("404 - page not found")
  
  if __name__ == '__main__':
      app.run_server(debug=True)
  </code></pre>
  
  <p>app.py</p>
  
  <pre><code>import dash_bootstrap_components as dbc
  import dash
  
  app = dash.Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
  
  server = app.server
  </code></pre>
  
  <p>Geoplot.py</p>
  
  <pre><code>import dash_bootstrap_components as dbc
  import dash_html_components as html
  import dash_core_components as dcc
  from dash.dependencies import Input, Output, State, ALL, MATCH
  import plotly.graph_objects as go
  import pandas as pd
  import flask
  import ast
  from flask import send_file
  import io
  import datetime
  
  from Hotspot_App_Local import app
  from Cards import Cards
  
  df = pd.read_csv('merged_layers.csv')
  df = df.round(2)
  available_indicators = ['PercBelowMSL', 'PopDens2015', 'RPC4.5', 'PortDist', 'RPC8.5']
  df['text'] = 'TotPop2020: '+ round(df["TotPop2020"], 0).astype(str)
  
  
  Storedata = dcc.Store(id= 'store_columns')
  Dropdown = dcc.Dropdown(id= 'Chose setting',
                         options= [
                             {'label': 'Marine', 'value': 'Marine'},
                             {'label': 'Environmental', 'value': 'Env'},
                             {'label': 'Urban', 'value': 'Urban'},
                             {'label': 'Ports', 'value': 'Ports'}
                                  ], value= 'Marine', clearable= False)
  card2 = Cards('Select The Setting', [Storedata, Dropdown], Header= True)
  
  checkbox = dbc.Checklist(id = 'col_list')
  card3 = Cards('Select The Layers', [checkbox], Header= True)
  
  htmlA = html.A([dbc.Button("Store Values", color= 'success', block= True)], id= 'store-values')
  #csvbutton = dbc.Button('Store Values', id= 'csv', block= True)
  card4 = Cards('', [htmlA])
  
  
  sizer = 2 * df['PopDens2020'].max() / (30 ** 2)
  fig = go.Figure(data=go.Scattermapbox(
          lon = df['center_lon'],
          lat = df['center_lat'],
          text = df['text'],
          mode = 'markers',
          marker= go.scattermapbox.Marker(
              color= df['PopDens2020'],
              cmin = df['PopDens2020'].min(),
              cmax= df['PopDens2020'].max(),
              colorscale = 'Viridis',
              colorbar=dict(
                          title="PopDens2020"
                      ),
              showscale= True,
              size= df['PopDens2020'],
              sizeref= sizer,
              sizemode= 'area'
          ),
          ))
  
  fig.update_layout(
          title= {'text': f'{len(df)} Hotspot Locations',
                  'font': {
                              'size': 36,
                              'color': 'darkblue'
                          },
                  'xref': 'container',
                  'x': 0.5},
          geo_scope='world',
          mapbox_style= 'open-street-map',
          width= 1200,
          height= 800
      )
  hotspotgraph = dcc.Graph(id= 'Hotspot_Plot', figure= fig)
  card5 = Cards("", [hotspotgraph], Header= False)
  
  rangeslider = html.Div(id='RangeSlider', children=[])
  card6 = Cards("Select the Range", [rangeslider], Header= True)
  
  size = dcc.Dropdown(id= 'markersize')
  card7 = Cards('Select Markersize', [size], Header= True)
  
  color = dcc.Dropdown(id= 'markercolor')
  card8 = Cards('Select Markercolor', [color], Header= True)
  
  
  layout = html.Div(
      [
          dbc.Row(
              [
                  dbc.Col([
                      dbc.Col(dbc.Card(card2, color="light", outline=True)),
                      dbc.Col(dbc.Card(card3, color="light", outline=True)),
                      dbc.Col(dbc.Card(card4, color="light", outline=True)),
                      dbc.Row([
                              dbc.Col(dbc.Card(card7, color="light", outline= True)),
                              dbc.Col(dbc.Card(card8, color="light", outline= True))
                              ])
                          ]),
                  dbc.Col(dbc.Card(card5, color="light", outline=True)),
              ],
              className="mb-4",
          ),
          dbc.Row(
              [
                  dbc.Col(dbc.Card(card6, color="light", outline=True), md= 12),
              ],
              className="mb-4",
          ),
      ]
  )
  
  
  @app.callback(
      Output('store_columns', 'data'),
      [Input('Chose setting', 'value')]
  )
  def change_cols(value):
      if value == 'Marine':
          data =  ['PortDist', 'PercBelow-5m', 'RPC4.5', 'RPC8.5']
      if value == 'Urban':
          data =  ['PopDens2020', 'area']
      if value == 'Env':
          data =  ['ManPc2016', 'WWCrAr2018', 'count_PA', 'NearestDist_PA']
      if value == 'Ports':
          data =  ['RPC4.5', 'PortDist']
      return data
  
  @app.callback(
      Output('col_list', 'options'),
      [Input('store_columns','data')],
  )
  def change_multiselect(data):
      if data is not None:
          return [{'label': i, 'value': i} for i in sorted(data)]
  
  @app.callback(
      Output('col_list', 'value'),
      [Input('store_columns','data')]
  )
  def change_multiselect(data):
      return sorted(data)
  
  @app.callback(
      Output('markersize', 'options'),
      [Input('col_list','value')]
  )
  def markersize(values):
      default = ['PopDens2020']
      default.extend(values)
      return [{'label': value, 'value': value} for value in default]
  
  @app.callback(
      Output('markersize', 'value'),
      [Input('col_list','value')]
  )
  def markersize(values):
      return 'PopDens2020'
  
  
  @app.callback(
      Output('markercolor', 'options'),
      [Input('col_list','value')]
  )
  def markersize(values):
      default = ['PopDens2020']
      default.extend(values)
      return [{'label': value, 'value': value} for value in default]
  
  @app.callback(
      Output('markercolor', 'value'),
      [Input('col_list','value')]
  )
  def markersize(values):
      return 'PopDens2020'
  
  @app.callback(
      Output('RangeSlider', 'children'),
      [Input('col_list', 'value')],
      [State('RangeSlider', 'children')])
  def display_dropdowns(value, ranges):
      df = pd.read_csv('merged_layers.csv')
      lst = []
      if value != None:
          for v in value:
              fig = go.Figure()
              fig.add_trace(go.Histogram(x = df[v]))
  
              fig.add_shape(
                  dict(
                      type='line',
                      yref='paper', y0=0, y1=1,
                      x0=df[v].median(), x1=df[v].median()
                  )
              )
  
              hist = dcc.Graph(id={'type': 'histogram', 'index': v}, figure=fig)
              reset_button = dbc.Button('Reset Values',id={'type': 'reset_button', 'index': v}, block= True, color= 'danger')
  
              card_button = Cards("", [reset_button])
  
              min = df[v].min()
              max = df[v].max()
              range_slider = dcc.RangeSlider(
                  id={
                      'type': 'range_slider',
                      'index': v
                  }, tooltip={'always_visible': True, 'placement': 'bottomRight'}, min=min, max=max, value=[min, max], step= 0.01)
              store_minmax = dcc.Store(id={'type': 'store', 'index': v}, data= [min, max])
              card_slider = Cards(f'{v}', [range_slider, store_minmax], Header= True)
              marklen = dcc.Markdown(children= f'**{len(df)} Hotspot Locations**', id={'type': 'len_df', 'index': v},
                                     style= {'textAlign': 'center', 'color': 'darkblue', 'font-size': 25})
              card_hist = Cards(f"", [marklen, hist])
              ap = html.Div([
                  dbc.Row(
                      [
                          dbc.Col([
                              dbc.Col(dbc.Card(card_slider, color="light", outline=True)),
                              dbc.Col(dbc.Card(card_button, color= "light", outline= True))], md= 6),
                          dbc.Col(dbc.Card(card_hist, color="light", outline=True), md=6)
  
                      ],
                      className="mb-4",
                  ),
                          ])
  
              lst.append(ap)
      return lst
  
  @app.callback(
      Output({'type': 'len_df', 'index': ALL}, 'children'),
      [Input({'type': 'range_slider', 'index': ALL}, 'value')],
      [State('col_list', 'value'),
      State({'type': 'len_df', 'index': ALL}, 'children')]
  )
  def store_len_df(values, cols, text):
      df = pd.read_csv('merged_layers.csv')
      for i in range(len(cols)):
          min = values[i][0]
          max = values[i][1]
          df = df[df[cols[i]].between(min, max)]
      text = [f'**{len(df)} Hotspot Locations**'] * len(text)
      return text
  
  
  @app.callback(
      Output({'type': 'histogram', 'index': MATCH}, 'figure'),
      [Input({'type': 'range_slider', 'index': MATCH}, 'value')],
       [State({'type': 'histogram', 'index': MATCH}, 'figure')])
  def add_verts(range, fig):
      fig['layout']['shapes'] = [{'type': 'line', 'x0': range[0], 'x1': range[0], 'y0': 0, 'y1': 1, 'yref': 'paper'},
                                 {'type': 'line', 'x0': range[1], 'x1': range[1], 'y0': 0, 'y1': 1, 'yref': 'paper'}]
      return fig
  
  @app.callback(
      Output({'type': 'range_slider', 'index': MATCH}, 'value'),
      [Input({'type': 'reset_button', 'index': MATCH}, 'n_clicks')],
      [State({'type': 'store', 'index': MATCH}, 'data')])
  def reset(click, data):
      return data
  
  @app.callback(
      Output('Hotspot_Plot', 'figure'),
      [Input({'type': 'range_slider', 'index': ALL}, 'value'),
       Input('markersize', 'value'),
       Input('markercolor', 'value')],
      [State('col_list', 'value'),
      State('Hotspot_Plot', 'figure'),]
  )
  def update_figure(values, col_size, col_color, cols, fig):
  
      if cols is not None and len(values) &gt; 0 and len(cols) &gt; 0:
          df = pd.read_csv('merged_layers.csv')
  
          if col_size != col_color:
              df['text'] = f'{col_size}: ' + round(df[col_size], 0).astype(str) + '\n' +  f'{col_color}: ' + round(df[col_color], 0).astype(str)
          else:
              df['text'] = f'{col_size}: ' + round(df[col_size], 0).astype(str)
  
          size_series = df[col_size]
          for value in df[col_size]:
              if value &lt; 0:
                  size_series += abs(df[col_size].min()) + 0.01 #No Negative Values Allowed
                  break
  
          color_series = df[col_color]
  
          sizer = 2 * df[col_size].max() / (30 ** 2)
  
          for i in range(len(cols)):
              min = values[i][0]
              max = values[i][1]
              df = df[df[cols[i]].between(min, max)]
  
  
          fig['data'][0]['lat'] = df['center_lat']
          fig['data'][0]['lon'] = df['center_lon']
          fig['data'][0]['marker'] = go.scattermapbox.Marker(
                  color= color_series,
                  cmin = color_series.min(),
                  cmax= color_series.max(),
                  colorscale = 'Viridis',
                  colorbar=dict(
                      title= col_color
                  ),
                  showscale=True,
                  size= size_series,
                  sizeref=sizer,
                  sizemode='area'
          )
          fig['data'][0]['text'] = df['text']
  
          fig['layout']['title']['text'] = f'{len(df)} Hotspot Locations'
  
  
      return fig
  
  
  @app.callback(
      Output('store-values', 'href'),
      [Input('col_list', 'value'),
       Input({'type': 'range_slider', 'index': ALL}, 'value'),
      Input('Chose setting', 'value')])
  
  def update_link(columns, ranges, setting):
      return '/dash/urlToDownload?value={}*{}*{}'.format(columns, ranges, setting)
  
  
  @app.server.route('/dash/urlToDownload')
  def download_excel():
      data = flask.request.args.get('value').split('*')
  
      columns = ast.literal_eval(data[0])
      ranges = ast.literal_eval(data[1])
      setting = data[2]
      minlst, maxlst= [], []
      for r in ranges:
          min, max = r
          minlst.append(min)
          maxlst.append(max)
      range_df = pd.DataFrame(list(zip(columns, minlst, maxlst)), columns=['Column', 'Min_Value', 'Max_Value'])
  
      df = pd.read_csv('Dash/merged_layers.csv')
      for (i, range) in enumerate(ranges):
          min, max = range
          df = df[df[columns[i]].between(min, max)]
      center_lon = df['center_lon'].values
      center_lat = df['center_lat'].values
      country = df['country'].values
      loc_df = pd.DataFrame(list(zip(center_lon, center_lat, country)), columns= ['center_lon', 'center_lat', 'country'])
      loc_df = loc_df.sort_values('country')
  
      buf = io.BytesIO()
      excel_writer = pd.ExcelWriter(buf, engine="xlsxwriter")
      range_df.to_excel(excel_writer, sheet_name="Ranges", index=False)
      loc_df.to_excel(excel_writer, sheet_name="Locations", index=False)
      excel_writer.save()
      excel_data = buf.getvalue()
      buf.seek(0)
      now = datetime.datetime.now()
      today = str(now.year) + '-' + str(now.month) + '-' + str(now.day)+ ' ' + str(now.hour) + str(":") + str(now.minute)
      return send_file(
          buf,
          mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          attachment_filename=f"{setting}_{today}.xlsx",
          as_attachment=True,
          cache_timeout=0
      )
  </code></pre>
  
  <p>Scatterplot.py</p>
  
  <pre><code>import dash_html_components as html
  import dash_core_components as dcc
  import dash_bootstrap_components as dbc
  from dash.dependencies import Input, Output, State, ALL, MATCH
  
  from Hotspot_App_Local import app
  
  from Cards import Cards
  
  test = dcc.Checklist(id= 'scatterlst')
  
  card1 = Cards('Test', test, Header= True)
  
  layout = html.Div(
      dbc.Row(dbc.Card(card1, color="light", outline=True))
  )
  
  @app.callback(
      Output('scatterlst', 'value'),
      [Input('col_list','value')]
  )
  def change_multiselect(data):
      print('hi')
      print(data)
      return sorted(data)
  
  </code></pre>
  
  <p>So the problem is that Scatterplot.py does not recognizes the indexes from Geoplot.py.</p>