Error when trying to store data in multipage app

Hi,

I am currently trying to build a multipage app where choices made by the user (e.g. in dropdown menus) are saved in dcc. Store. When returnign to a page the choices made last time are supposed to be restored. I am running in the following error:

A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `p1-dropdown` and the property is `value`. The string ids in the current layout are: [global-store, _pages_location, _pages_content, _pages_store, _pages_dummy]

app.py

from dash import Dash, html, dcc
import dash

app = Dash(__name__, use_pages=True)

app.layout = html.Div([
	html.H1('Multi-page app with Dash Pages'),
    
    dcc.Store(id='global-store', storage_type='session'),
    
    html.Div(
        [
            html.Div(
                dcc.Link(
                    f"{page['name']} - {page['path']}", href=page["relative_path"]
                )
            )
            for page in dash.page_registry.values()
        ]
    ),

	dash.page_container
])

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

page_1.py

import dash
from dash import html, dcc, callback, Input, Output
import dash_bootstrap_components as dbc

df = ['abc', 'def', 'ghi', 'xyz']

dash.register_page(__name__)

layout = html.Div(children=[
    
    dcc.Dropdown(
        id = 'p1-dropdown',
        options = [{'label':x, 'value':x} for x in df]
    )
    
])


@callback(Output('p1-dropdown', 'value'),
          Output('global-store', 'data'),
          Input('p1-dropdown', 'value'),
          Input('global-store', 'data')
)
def setup_dropdown(value_chosen, value_stored):
        
    if value_chosen is None:
        return value_stored, value_stored
    else:
        return value_chosen, value_chosen

The callback has two purposes:
(i) if there is a value stored in dcc.Store it is used as ‘value’ for the dropdown
(ii) if the user chooses a new value in the dropdown it shall be stored to dcc.Store

From reading other posts I think the problem is that the callback looks for the ‘p1-dropdown’ and does not find it, as it is created later on. What can I do about this?

Hi @Max3

An easier way to do this is to e set persistence=True in the dropdown. Then no need to use a dcc.Store or a callback :tada:

However, in cases where you do need to share data between callacks, be sure to set suppress_callback_exceptions=True

`app=Dash(name, suppress_callback_exceptions=True)

Hi @AnnMarieW, as always thank you very much for your assistance. My callback is supposed to get more Inputs and not just dropdowns, so the persistence keyword is not gonna do it in this case. I added the ‘suppress_callback_exceptions=True’ statement but still get the same error :frowning:

Did you try running this example? dash-multi-page-app-demos/multi_page_store/pages at main ¡ AnnMarieW/dash-multi-page-app-demos ¡ GitHub

Another thing to try is updating the dcc.Store in a separate callbback.

Your example works fine. However, it does not entirely match my case.

In your example

  • dcc.Store updates the figure
  • the value selected in the dropdown updates the store
  • the dropdown itself does not get updated

What I am trying to achieve is a circle where on loading a page

  • the dropdown updates the store
  • the store updates the dropdown

At first I tried two dropdowns:

@callback(Output('global-store', 'data'),
          Input('p1-dropdown', 'value')         
)
def store_choice(value_chosen):
    return value_chosen


@callback(Output('p1-dropdown', 'value'),
          Input('global-store', 'data')         
)
def fill_dropdown(value_stored):
    return value_stored

This gets me the error

Circular dependencies

Thus, I tried to put both callbacks into one, as circular callbacks are allowed like this:

@callback(Output('p1-dropdown', 'value'),
          Output('global-store', 'data'),
          Input('p1-dropdown', 'value'),
          Input('global-store', 'data')
)
def setup_dropdown(value_chosen, value_stored):
        
    if value_chosen is None:
        return value_stored, value_stored
    else:
        return value_chosen, value_chosen

Which led to the error I first mentioned:

A nonexistent object was used in an `Input` of a Dash callback. The id of this object is `p1-dropdown` and the property is `value`. The string ids in the current layout are: [url, global-store, page-navigation-content, page-content]

Ultimately I want to have several pages. On each page the same categories can be chosen, e.g. “year” or “amount”. I want to store the selected values and use them on other pages. A case could be for example:

  • User chooses Year = 2022 in dropdown on page 1
  • User switches to page 2, Year = 2022 is transfered from the store into the dropdown of page 2, User now chooses Year = 2014 on page 2
  • User return to page 1, Year = 2014 is transfered from the store into the dropdown of page 1

Like this, the value used to set the value of a dropdown (or other element) is not necessarily the last one used on the current page, but the last one chosen on any page.

I hope you can follow me

edit: Actually both callback varities described in this answer lead to the functionality I want. I guess I could just turn debug=False and ignore the errors. Does not seem very good though.

Hi, try to change from Input() to State(). I think if you have them both as Input() it gets triggerd all time

2 Likes

Thank you @AIMPED , turning the Input from the store into State and using suppress_callback_exceptions=True solved it. Now the functionality is there and no error is thrown :slight_smile:

@callback(Output('p1-dropdown', 'value'),
          Output('global-store', 'data'),
          Input('p1-dropdown', 'value'),
          State('global-store', 'data')
)
def setup_dropdown(value_chosen, value_stored):
        
    if value_chosen is None:
        return value_stored, value_stored
    else:
        return value_chosen, value_chosen
1 Like

I think the suppress_callback_exceptions isn’t even necessary.

When I switch suppress_callback_exceptions to False I get this error:

Object { message: "ID not found in layout", html: "Attempting to connect a callback Input item to component:\n  \"p1-store\"\nbut no components with that id exist in the layout.\n\nIf you are assigning callbacks to components that are\ngenerated by other callbacks (and therefore not in the\ninitial layout), you can suppress this exception by setting\n`suppress_callback_exceptions=True`.\nThis ID was used in the callback(s) for Output(s):\n  p2-textid.children" }

Its another variety of the former error, the ID is not found as it is not part of the initial layout. In this case however it explicitly hints on the possibility of suppressing callback exceptions. It seems to me, that a situation like this was anticipated and the error therefore is nothing to worry about.

2 Likes