Using multiple inputs/outputs with dcc.Store in multipage dash app

Hi,

I have a multipage Dash app. I have a simple button that triggers a callback to get a value from a mongodb. Some queries take quite long, so I wanted to store the values to not lose them when I change the page. The callback looks like this:

@callback(
    output=[
        Output('profile-count', 'children'),
        Output('intermediate-profile-count', 'data'),
        ],
    inputs=[
        Input('profile-count-btn', 'n_clicks'),
        Input('intermediate-profile-count', 'data'),
        ])
def get_value(n_clicks, data):
    if data is None:
        if n_clicks!=0:
            num_profiles = get_value_from_db
            return html.P(f"{format(num_profiles, ',d').replace(',', '.')}", className="card-text"), json.dumps(num_profiles, default=str)
        else:
            return no_update, no_update
    else:
        num_profiles = json.loads(data)
        return html.P(f"{format(num_profiles, ',d').replace(',', '.')}", className="card-text"), data

It works as expected when I put the dcc.Store() in my app.py and the callback is located in pages/get_values.py.

But I get an error telling me that:

A nonexistent object was used in an Input of a Dash callback

This is probably because the inputs/outputs are in different locations. Putting the dcc.Store in pages/get_values.py doesn’t work.

Is there a way to avoid the error, like making the app.py aware of the button-id?

Thanks

Hello @nilsfl,

I think you should split this into different callbacks, that way you dont run into trying to query a non-existent element from the client layout.

Have one that updates the data, and the other that updates the children based upon the data.

Or, you could try to use pattern-matching on the button.

Hi @jinnyzor,

thanks for the fast answer!

I thought about splitting the callback, but how and where should I implement the trigger to get the value from the db? As I understand there is no way around having dcc.Store in my app.py, so the button must be there too.

The only solution I came up with is pulling the data without a trigger button from the app.py and have something like a while loop on my pages/get_value.py that checks for the intermediate data and shows a loading status if it’s not there.

I didn’t use pattern-matching in dash, will have a look.

Cheers

1 Like

To trigger the data, you can use something in the app’s layout, like an id of a div. Or your navigation bar that is on all pages. This will keep you from having to load it multiple times.

Then, when you navigate to get_value, have a callback that updates the children based upon the input of the dcc.Store data. This way, if it changes, you will update the children too. For this output, I’d use pattern-matching since the layout wont always have the target of the children.

This might work too:

@callback(
    output=[
        Output('intermediate-profile-count', 'data'),
        ],
    inputs=[
        Input('nav', 'id'),
        ])
def get_value(n):
    num_profiles = get_value_from_db
    return json.dumps(num_profiles, default=str)

@callback(
    output=[
        Output('profile-count', 'children'),
        ],
    inputs=[
        Input('profile-count', 'id'),
        State('intermediate-profile-count', 'data'),
        ])
def get_value(n_clicks, data):
    num_profiles = json.loads(data)
    return html.P(f"{format(num_profiles, ',d').replace(',', '.')}", className="card-text")
1 Like

Thanks @jinnyzor,

this is actually something I was doing before - getting the values automatically with a ("url", "search") trigger and without buttons from app.py.

The idea of the buttons was to trigger the process only if the value is needed, but then store it. The approach in my question works on singlepage apps and it also works on multipage apps. I just want to get rid of the error, which only appears in the multipage version.

I guess I’ll stick with the automatically triggered solution, but is there a way of using dcc.Loading() with the children that gets updated as soon as the value was pulled? I mean a loading indication for

@callback(
    output=Output('profile-count', 'children'),
    inputs=[
        Input('intermediate-profile-count', 'data'),
        State('intermediate-profile-count', 'data')])

When used with a button and without storing the value, I really liked the Loading functionality indicating if the process is still running.